Browse Source

first public release

copy 10 years ago
commit
8180d49f24
79 changed files with 25187 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 26 0
      LICENSE
  3. 72 0
      Makefile
  4. 87 0
      Readme.md
  5. 165 0
      bios/COPYING.LESSER
  6. 94 0
      bios/seabios-config
  7. BIN
      bios/seabios.bin
  8. BIN
      bios/vgabios.bin
  9. 71 0
      debug.html
  10. 15 0
      docs/Makefile
  11. 49 0
      docs/adapters.md
  12. 62 0
      index.html
  13. 28 0
      loader.js
  14. 1646 0
      src/arith.macro.js
  15. 236 0
      src/browser/keyboard.js
  16. 589 0
      src/browser/main.js
  17. 132 0
      src/browser/mouse.js
  18. 354 0
      src/browser/screen.js
  19. 155 0
      src/const.js
  20. 2344 0
      src/cpu.js
  21. 2384 0
      src/cpu.macro.js
  22. 445 0
      src/debug.macro.js
  23. 545 0
      src/disk.js
  24. 149 0
      src/dma.js
  25. 1 0
      src/externs.js
  26. 396 0
      src/floppy.js
  27. 1656 0
      src/fpu.macro.js
  28. 2263 0
      src/instructions.macro.js
  29. 175 0
      src/io.js
  30. 339 0
      src/main.js
  31. 486 0
      src/memory.js
  32. 458 0
      src/misc_instr.macro.js
  33. 176 0
      src/modrm.macro.js
  34. BIN
      src/node/ascii.ttf
  35. 35 0
      src/node/keyboard_sdl.js
  36. 60 0
      src/node/keyboard_tty.js
  37. 141 0
      src/node/main.js
  38. 5 0
      src/node/node_modules/node-sdl/.gitignore
  39. 3 0
      src/node/node_modules/node-sdl/.npmignore
  40. 72 0
      src/node/node_modules/node-sdl/Makefile
  41. 429 0
      src/node/node_modules/node-sdl/README.md.orig
  42. 4 0
      src/node/node_modules/node-sdl/Readme
  43. 21 0
      src/node/node_modules/node-sdl/binding.gyp
  44. 83 0
      src/node/node_modules/node-sdl/nodesdl.target.mk
  45. 16 0
      src/node/node_modules/node-sdl/package.json
  46. 25 0
      src/node/node_modules/node-sdl/sdl.js
  47. 437 0
      src/node/node_modules/node-sdl/src/helpers.cc
  48. 42 0
      src/node/node_modules/node-sdl/src/helpers.h
  49. 1078 0
      src/node/node_modules/node-sdl/src/nodesdl.cc
  50. 93 0
      src/node/node_modules/node-sdl/src/nodesdl.h
  51. 124 0
      src/node/screen_sdl.js
  52. 90 0
      src/node/screen_tty.js
  53. 173 0
      src/pci.js
  54. 280 0
      src/pic.js
  55. 303 0
      src/pit.js
  56. 432 0
      src/ps2.js
  57. 188 0
      src/rtc.js
  58. 281 0
      src/string.macro.js
  59. 26 0
      src/uart.js
  60. 4 0
      src/v86_all.js.map
  61. 1023 0
      src/vga.js
  62. 8 0
      tests/perf/build.sh
  63. 59 0
      tests/perf/runtest.js
  64. 12 0
      tests/perf/test-asm.js
  65. 199 0
      tests/perf/test.asm
  66. BIN
      tests/perf/test.bin
  67. 16 0
      tests/qemu/LICENSE
  68. 17 0
      tests/qemu/Makefile
  69. 8 0
      tests/qemu/Reame
  70. 58 0
      tests/qemu/compiler.h
  71. 66 0
      tests/qemu/config-host.h
  72. 79 0
      tests/qemu/test-i386-code16.S
  73. 76 0
      tests/qemu/test-i386-muldiv.h
  74. 185 0
      tests/qemu/test-i386-shift.h
  75. 103 0
      tests/qemu/test-i386-vm86.S
  76. 2774 0
      tests/qemu/test-i386.c
  77. 152 0
      tests/qemu/test-i386.h
  78. 60 0
      v86.css
  79. 247 0
      v86_all.js

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+images/*
+

+ 26 - 0
LICENSE

@@ -0,0 +1,26 @@
+Copyright (c) 2012, Fabian Hemmer
+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. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies, 
+either expressed or implied, of the FreeBSD Project.

+ 72 - 0
Makefile

@@ -0,0 +1,72 @@
+CLOSURE=~/closure/closure-compiler/build/compiler.jar
+CPP=cpp
+
+
+all: v86_all.js
+browser: v86_all.js
+node: v86_node.js
+
+src/cpu.js: src/*.macro.js
+	# build cpu.macro.js using cpp
+	$(CPP) -P -undef -Wundef -std=c99 -nostdinc -Wtrigraphs -fdollars-in-identifiers -C src/cpu.macro.js src/cpu.js
+
+
+
+# Used for nodejs builds and in order to profile code.
+# `debug` gives identifiers a readable name, make sure it doesn't have any side effects. 
+CLOSURE_READABLE=--formatting PRETTY_PRINT --debug
+
+CLOSURE_SOURCE_MAP=\
+		--source_map_format V3\
+		--create_source_map
+
+CLOSURE_FLAGS=\
+		--compilation_level ADVANCED_OPTIMIZATIONS\
+		--externs externs.js\
+		--warning_level VERBOSE\
+		--jscomp_off uselessCode\
+		--use_types_for_optimization\
+		--summary_detail_level 3\
+		--language_in ECMASCRIPT5_STRICT
+
+
+CORE_FILES=const.js io.js cpu.js main.js disk.js pci.js floppy.js memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js 
+BROWSER_FILES=browser/main.js browser/screen.js browser/keyboard.js browser/mouse.js 
+NODE_FILES=node/main.js node/keyboard.js node/screen.js 
+
+v86_all.js: src/*.js src/browser/*.js src/cpu.js
+	-ls -lh v86_all.js
+	cd src &&\
+	java -jar $(CLOSURE) \
+		--js_output_file "../v86_all.js"\
+		--define=DEBUG=false\
+		$(CLOSURE_SOURCE_MAP) v86_all.js.map\
+		$(CLOSURE_FLAGS)\
+		--js $(CORE_FILES)\
+		--js $(BROWSER_FILES)
+
+	echo "//# sourceMappingURL=src/v86_all.js.map" >> v86_all.js
+	ls -lh v86_all.js
+
+
+v86_node.js: src/*.js src/node/*.js
+	cd src &&\
+	java -jar $(CLOSURE) \
+		--js_output_file "../v86_node.js"\
+		--define=DEBUG=false\
+		$(CLOSURE_FLAGS)\
+		--js $(CORE_FILES) $(NODE_FILES)
+
+
+pack:
+	rm -f v86-latest.tar.gz 
+		# Not sure if legally necessary
+		#--exclude "qemu"
+	tar -zcvf ../v86-latest.tar.gz ../v86/ \
+		--exclude "images" \
+		--exclude ".git"
+
+
+
+clean:
+	rm -f v86-latest.tar.gz v86_all.js src/v86_all.js.map src/cpu.js

+ 87 - 0
Readme.md

@@ -0,0 +1,87 @@
+How does it work?
+-
+
+v86 emulates an x86-compatible CPU and hardware. Here's a list of emulated hardware:
+
+- An x86 compatible CPU. The instruction set is around Pentium 1 level. Some
+  features are missing, more specifically:
+  - Task gates, far calls in protected mode
+  - 16 bit protected mode features and Virtual 8086 mode
+  - Single stepping
+  - MMX, SSE
+  - A bunch of FPU instructions, FPU exceptions
+  - Some other exceptions
+- A floating point unit (FPU). Calculations are done with JavaScript's double
+  precision numbers (64 bit), so they are not as precise as calculations on a
+  real FPU (80 bit).
+- An ISA bus.
+- A floppy disk controller (8272A).
+- A DMA (direct memory access) controller, currently only used by the FDC.
+- An 8042 Keyboard Controller, PS2. With mouse support.
+- An 8254 Programmable Interval Timer (PIT).
+- An 8259 Programmable Interrupt Controller (PIC).
+- A CMOS Real Time Clock (RTC).
+- A VGA controller with SVGA support and Bochs VBE Extensions. This includes
+  support for large resolutions.
+- A PCI bus. This one is mostly incomplete and not used by every device.
+- An AT disk controller with some ATAPI and some SCSI commands. This is quite
+  incomplete and possibly buggy, too.
+
+
+How to build, run and embed?
+-
+
+- In order to build the `cpu.js` file, you need `make` and `cpp` (the C preprocessor).
+  Run: `make src/cpu.js`.
+- If you want a compressed and fast (ie, with debug code removed) version, you
+  need Closure Compiler. 
+  Set the path to `compiler.jar` in the Makefile and run `make v86_all.js`.
+- For more details on how to customize the behaviour and interface, see [docs/adapters.md](docs/adapters.md).
+
+
+Why? 
+- 
+
+Similiar projects have been done before, but I decided to work on this as a fun
+project and learn something about the x86 architecture. It has grown pretty
+advanced and I just got Linux and KolibriOS working recently and
+there might be some actual uses.
+
+If you build something interesting, let me know. However, keep in mind that the project
+is not very stable at the moment.
+
+
+How can I contribute?
+-
+
+- Someone who could work on hardware devices, such as a modem or the AT
+  controller.  I'll write an overview for that at a later point, if people are
+  interested. 
+- Donate. Since Bitcoin is the new cool thing, here's my address:
+  `14KBXSoewGzbQY8VoznJ5MZXGxoia8RxC9`
+
+License
+-
+
+Simplified BSD License, see [LICENSE](LICENSE), unless otherwise noted.
+
+
+Credits
+-
+
+- Test cases via QEMU, http://wiki.qemu.org/Main_Page 
+- https://github.com/creationix/node-sdl
+- ascii.ttf (used in node) from http://www.apollosoft.de/ASCII/indexen.htm 
+
+
+More questions?
+-
+
+Shoot me an email to `copy@copy.sh`. Please don't tell about bugs via mail,
+create a bug report on github instead.
+
+
+Author
+-
+
+Fabian Hemmer (http://copy.sh/, `copy@copy.sh`)

+ 165 - 0
bios/COPYING.LESSER

@@ -0,0 +1,165 @@
+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions. 
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version. 
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU 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 as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.

+ 94 - 0
bios/seabios-config

@@ -0,0 +1,94 @@
+#
+# Automatically generated make config: don't edit
+# SeaBIOS Configuration
+# Sun Nov 18 21:22:50 2012
+#
+
+#
+# General Features
+#
+# CONFIG_COREBOOT is not set
+CONFIG_XEN=y
+CONFIG_THREADS=y
+# CONFIG_THREAD_OPTIONROMS is not set
+CONFIG_RELOCATE_INIT=y
+# CONFIG_BOOTMENU is not set
+# CONFIG_BOOTORDER is not set
+
+#
+# Hardware support
+#
+CONFIG_ATA=y
+# CONFIG_ATA_DMA is not set
+# CONFIG_ATA_PIO32 is not set
+CONFIG_AHCI=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_SCSI=y
+CONFIG_ESP_SCSI=y
+CONFIG_LSI_SCSI=y
+CONFIG_FLOPPY=y
+CONFIG_PS2PORT=y
+CONFIG_USB=y
+CONFIG_USB_UHCI=y
+CONFIG_USB_OHCI=y
+CONFIG_USB_EHCI=y
+CONFIG_USB_MSC=y
+CONFIG_USB_UAS=y
+CONFIG_USB_HUB=y
+CONFIG_USB_KEYBOARD=y
+CONFIG_USB_MOUSE=y
+CONFIG_SERIAL=y
+CONFIG_LPT=y
+CONFIG_USE_SMM=y
+CONFIG_MTRR_INIT=y
+
+#
+# BIOS interfaces
+#
+CONFIG_DRIVES=y
+CONFIG_CDROM_BOOT=y
+CONFIG_CDROM_EMU=y
+CONFIG_PCIBIOS=n
+CONFIG_APMBIOS=n
+CONFIG_PNPBIOS=n
+CONFIG_OPTIONROMS=y
+CONFIG_OPTIONROMS_DEPLOYED=y
+CONFIG_PMM=y
+CONFIG_BOOT=y
+CONFIG_KEYBOARD=y
+CONFIG_KBD_CALL_INT15_4F=y
+CONFIG_MOUSE=y
+CONFIG_S3_RESUME=y
+CONFIG_VGAHOOKS=y
+# CONFIG_DISABLE_A20 is not set
+
+#
+# BIOS Tables
+#
+CONFIG_PIRTABLE=n
+CONFIG_MPTABLE=n
+CONFIG_SMBIOS=n
+CONFIG_ACPI=n
+
+#
+# VGA ROM
+#
+# CONFIG_NO_VGABIOS is not set
+CONFIG_VGA_STANDARD_VGA=y
+# CONFIG_VGA_CIRRUS is not set
+# CONFIG_VGA_BOCHS is not set
+# CONFIG_VGA_GEODEGX2 is not set
+# CONFIG_VGA_GEODELX is not set
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_VBE=y
+CONFIG_VGA_PCI=y
+# CONFIG_OVERRIDE_PCI_ID is not set
+CONFIG_VGA_VID=0x0000
+CONFIG_VGA_DID=0x0000
+
+#
+# Debugging
+#
+CONFIG_DEBUG_LEVEL=2
+# CONFIG_DEBUG_SERIAL is not set
+CONFIG_DEBUG_IO=y

BIN
bios/seabios.bin


BIN
bios/vgabios.bin


+ 71 - 0
debug.html

@@ -0,0 +1,71 @@
+<!doctype html>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+<title>Virtual x86</title>
+
+<script src="loader.js"></script>
+<link rel="stylesheet" href="v86.css">
+
+<textarea readonly id="log"></textarea>
+
+<div>
+
+    <a href="/v86/">To fast version</a>
+
+    <div id="boot_options">
+        CD image: <input type="file" id="cd_image"><br>
+        <small>(Bootable El Torito)</small><br><br>
+        Floppy disk image: <input type="file" id="floppy_image"><br>
+        <small>(1.44 or 2.88)</small><br><br>
+        Hard drive disk image: <input type="file" id="hd_image"><br>
+        <small></small><br><br>
+        <hr>
+        or    <input type="button" value="Start FreeDOS" id="start_freedos">
+        <br>
+        <br>
+        <input type="button" value="Start Linux" id="start_linux">
+        <input type="button" value="Start KolibriOS" id="start_koli">
+        <input type="button" value="Start Windows" id="start_win101">
+        <input type="button" value="Start OpenBSD" id="start_bsd">
+        <input type="button" value="Start Test" id="start_test">
+    </div>
+    <div id="runtime_options" style="display: none">
+        <input type="button" value="Step" id="step">
+        <input type="button" value="Run until" id="run_until">
+        <input type="button" value="Debugger" id="debugger">
+        <br>
+        <div id="log_levels"></div>
+
+        <input type="button" value="Pause" id="run">
+        <input type="button" value="Reset" id="reset">
+        <input type="button" value="Send Ctrl-Alt-Del" id="ctrlaltdel">
+        <input type="button" value="Get floppy image" id="get_floppy">
+        <input type="button" value="Lock mouse" id="lock_mouse">
+        <input type="button" value="Go fullscreen" id="fullscreen">
+
+        Scale: 
+        <input type="number" min="0.25" step="0.25" value="1.0" id="scale" style="width: 50px">
+
+        <div id="info">
+            <div>running <span id="running_time"></span>s</div>
+            <div>speed <span id="speed"></span>kIPS</div>
+        </div>
+
+    </div>
+
+    <pre style="display: none" id="loading"></pre>
+
+    <a href="readme.html">readme</a>
+    <br>
+    <br>
+</div>
+
+<div id="screen_container">
+    <div id="screen"></div>
+    <canvas id="vga"></canvas>
+</div>
+
+<br style="clear:both">
+
+<input class="phone_keyboard">
+

+ 15 - 0
docs/Makefile

@@ -0,0 +1,15 @@
+
+DOCS=adapters embedding
+HTML=$(addsuffix .html, $(DOCS))
+MD=$(addsuffix .md, $(DOCS))
+
+
+all: $(HTML)
+
+
+%.html: %.md
+	markdown $< > $@
+
+
+clean:
+	rm -f *.html

+ 49 - 0
docs/adapters.md

@@ -0,0 +1,49 @@
+
+
+Adapters are used to communicate between virtual hardware and the browser (or
+nodejs, or anything else). Currently, there are 3 adapters: Keyboard, Mouse and
+Screen. Adapters are passed through `settings.keyboard_adapter`,
+`settings.mouse_adapter` and `settings.screen_adapter` respectively, but they
+can also be undefined. 
+
+Here is a list of functions that must be implemented by adapters:
+
+**ScreenAdapter:**
+
+- `put_pixel(x, y, color)`
+- `put_pixel_linear(offset, color_part)`
+- `put_char(row, col, chr, bg_color, fg_color)`
+- `update_cursor(row, col)`
+- `update_cursor_scanline(start, end)`
+- `clear_screen()`
+- `timer_graphical()`
+- `timer_text()`
+- `set_mode(is_graphical)`
+- `set_size_graphical(width, height)`
+- `set_size_text(rows, cols)`
+- `destroy()`
+
+**KeyboardAdapter:**
+
+- `init(send_code_fn)`
+- `destroy()`
+- `enabled`
+
+**MouseAdapter:**
+
+- `init(click_fn, move_fn, wheel_fn)`
+- `destroy()`
+- `enabled`
+
+**More**
+
+In addition to adapters, the following functions must be provided in global
+scope (TODO: Improve that).
+
+- `next_tick()`
+- `set_tick(fn)`
+- `log(str)` - only in debug modes
+
+
+<br>
+Everything on this page may be subject to change.

+ 62 - 0
index.html

@@ -0,0 +1,62 @@
+<!doctype html>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+<title>Virtual x86</title>
+
+<script src="v86_all.js"></script>
+<link rel="stylesheet" href="v86.css">
+
+<textarea readonly id="log"></textarea>
+
+<div>
+    <div id="boot_options">
+        CD image: <input type="file" id="cd_image"><br>
+        <br>
+        Floppy disk image: <input type="file" id="floppy_image"><br>
+        <br>
+        Hard drive disk image: <input type="file" id="hd_image"><br>
+        <br>
+        - OR -
+        <br>
+        <br>
+        <input type="button" value="KolibriOS (1.4 MB)" id="start_koli"> - Graphical OS, takes about 60 seconds to boot<br>
+        <input type="button" value="Linux 2.6 (4.9 MB)" id="start_linux"> - With busybox and lua interpreter, takes about 20 seconds to boot<br>
+        <input type="button" value="Windows 1.01 (1.4 MB)" id="start_win101"> - Takes 1 second to boot<br>
+        <input type="button" value="FreeDOS (0.7 MB)" id="start_freedos"> - With nasm, vim, debug.com, some games and demos, takes 1 second to boot<br>
+        <input type="button" value="OpenBSD (1.4 MB)" id="start_bsd"> - Random boot floppy, takes very long to boot<br>
+    </div>
+    <div id="runtime_options" style="display: none">
+        <input type="button" value="Pause" id="run">
+        <input type="button" value="Reset" id="reset">
+        <input type="button" value="Send Ctrl-Alt-Del" id="ctrlaltdel">
+        <input type="button" value="Get modified floppy image" id="get_floppy">
+        <input type="button" value="Lock mouse" id="lock_mouse">
+        <input type="button" value="Go fullscreen" id="fullscreen">
+
+        Scale: 
+        <input type="number" min="0.25" step="0.25" value="1.0" id="scale" style="width: 50px">
+
+        <div id="info">
+            <div>running <span id="running_time"></span>s</div>
+            <div>speed <span id="speed"></span>kIPS</div>
+        </div>
+
+    </div>
+    <pre style="display: none" id="loading"></pre>
+    <br>
+    <br>
+</div>
+
+
+<div id="screen_container">
+    <div id="screen"></div>
+    <canvas id="vga"></canvas>
+</div>
+
+<br style="clear: both">
+<input type="text" class="phone_keyboard" style="display: none">
+<br>
+
+<b>News:</b> Source Code now available: <a href="https://github.com/copy/iwbtc">Github</a>, <a href="v86-latest.tar.gz">tarball</a>.</li>
+
+

+ 28 - 0
loader.js

@@ -0,0 +1,28 @@
+// load all files to run v86 in browser, uncompiled
+
+(function()
+{
+    var PATH = "src/",
+        CORE_FILES="const.js io.js cpu.js main.js disk.js pci.js floppy.js memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js"
+        BROWSER_FILES="browser/main.js browser/screen.js browser/keyboard.js browser/mouse.js"
+
+    window.onload = function()
+    {
+        load_scripts(CORE_FILES);
+        load_scripts(BROWSER_FILES);
+
+        function load_scripts(resp)
+        {
+            var files = resp.split(" "),
+                script;
+
+            for(var i = 0; i < files.length; i++)
+            {
+                script = document.createElement("script");
+                script.src = PATH + files[i] + "?" + Math.random();
+                document.body.appendChild(script);
+            }
+        }
+    };
+
+})();

+ 1646 - 0
src/arith.macro.js

@@ -0,0 +1,1646 @@
+/*
+ * Arithmatic functions
+ * This file contains:
+ *
+ * add, adc, sub, sbc, cmp
+ * inc, dec
+ * neg, not
+ * imul, mul, idiv, div
+ * xadd
+ *
+ * das, daa, aad, aam
+ *
+ * and, or, xor, test
+ * shl, shr, sar, ror, rol, rcr, rcl
+ * shld, shrd
+ *
+ * bts, btr, btc, bt
+ * bsf, bsr
+ *
+ * Gets #included by cpu.macro.js
+ *
+*/
+"use strict";
+
+/**
+ * Helper function for multiplying 2 32 bit numbers
+ * Returns the low 32 bit (which would normally get cut off)
+ *
+ * @param {number} n1
+ * @param {number} n2
+ */
+function multiply_low(n1, n2)
+{
+    var low1 = n1 & 0xFFFF,
+        low2 = n2 & 0xFFFF,
+        high1 = n1 & ~0xFFFF,
+        high2 = n2 & ~0xFFFF;
+    
+    return low1 * low2 + low1 * high2 + high1 * low2;
+}
+
+
+function add8(dest_operand, source_operand)
+{
+    // very likely to be a crash
+    if(DEBUG && memory.read32s(translate_address_read(instruction_pointer)) === 0)
+    {
+        dump_regs();
+        throw "detected jump to 00000000"; 
+    }
+
+    last_op1 = dest_operand;
+    last_op2 = source_operand;
+    last_result = last_op1 + source_operand | 0;
+    
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function add16(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = source_operand;
+    last_result = last_op1 + source_operand | 0;
+    
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function add32(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = source_operand;
+    last_result = last_op1 + source_operand;
+    
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function adc8(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = source_operand;
+    last_result = last_op1 + last_op2 + getcf() | 0;
+    
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function adc16(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = source_operand;
+    last_result = last_op1 + last_op2 + getcf() | 0;
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function adc32(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = source_operand;
+    last_result = last_op1 + last_op2 + getcf();
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function cmp8(dest_operand, source_operand)
+{
+    dbg_assert(source_operand >= 0 && source_operand < 0x100);
+    dbg_assert(dest_operand >= 0 && dest_operand < 0x100);
+
+    last_op1 = dest_operand;
+    last_op2 = ~source_operand;
+    last_result = last_op1 - source_operand;
+
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all;
+}
+
+function cmp16(dest_operand, source_operand)
+{
+    dbg_assert(source_operand >= 0 && source_operand < 0x10000);
+    dbg_assert(dest_operand >= 0 && dest_operand < 0x10000);
+
+    last_op1 = dest_operand;
+    last_op2 = ~source_operand;
+    last_result = last_op1 - source_operand;
+    
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all;
+}
+
+function cmp32(dest_operand, source_operand)
+{
+    dbg_assert(source_operand >= 0 && source_operand < 0x100000000);
+    dbg_assert(dest_operand >= 0 && dest_operand < 0x100000000);
+
+    last_op1 = dest_operand;
+    last_op2 = -source_operand - 1;
+    last_result = last_op1 - source_operand;
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all;
+}
+
+function sub8(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = ~source_operand;
+    last_result = last_op1 - source_operand | 0;
+    
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function sub16(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = ~source_operand;
+    last_result = last_op1 - source_operand | 0;
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function sub32(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = -source_operand - 1;
+    last_result = last_op1 - source_operand;
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function sbb8(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = ~source_operand;
+    last_result = last_op1 - source_operand - getcf() | 0;
+    
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function sbb16(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = ~source_operand;
+    last_result = last_op1 - source_operand - getcf() | 0;
+    
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+function sbb32(dest_operand, source_operand)
+{
+    last_op1 = dest_operand;
+    last_op2 = -source_operand - 1;
+    last_result = last_op1 - source_operand - getcf();
+    
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all;
+
+    return last_result;
+}
+
+/*
+ * inc and dec
+ */
+
+function inc8(dest_operand)
+{
+    flags = (flags & ~1) | getcf();
+    last_op1 = dest_operand;
+    last_op2 = 1;
+    last_result = last_op1 + 1 | 0;
+    
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all & ~flag_carry;
+
+    return last_result;
+}
+
+function inc16(dest_operand)
+{
+    flags = (flags & ~1) | getcf();
+    last_op1 = dest_operand;
+    last_op2 = 1;
+    last_result = last_op1 + 1 | 0;
+    
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all & ~flag_carry;
+
+    return last_result;
+}
+
+function inc32(dest_operand)
+{
+    flags = (flags & ~1) | getcf();
+    last_op1 = dest_operand;
+    last_op2 = 1;
+    last_result = last_op1 + 1;
+    
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all & ~flag_carry;
+
+    return last_result;
+}
+
+
+
+function dec8(dest_operand)
+{
+    flags = (flags & ~1) | getcf();
+    last_op1 = dest_operand;
+    last_op2 = -1;
+    last_result = last_op1 - 1 | 0;
+    
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all & ~flag_carry;
+
+    return last_result;
+}
+
+function dec16(dest_operand)
+{
+    flags = (flags & ~1) | getcf();
+    last_op1 = dest_operand;
+    last_op2 = -1;
+    last_result = last_op1 - 1 | 0;
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all & ~flag_carry;
+
+    return last_result;
+}
+
+function dec32(dest_operand)
+{
+    flags = (flags & ~1) | getcf();
+    last_op1 = dest_operand;
+    last_op2 = -1;
+    last_result = last_op1 - 1;
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all & ~flag_carry;
+
+    return last_result;
+}
+
+
+/*
+ * neg and not
+ */
+
+function not8(dest_operand)
+{
+    return ~dest_operand;
+}
+
+function not16(dest_operand)
+{
+    return ~dest_operand;
+}
+
+function not32(dest_operand)
+{
+    return ~dest_operand;
+}
+
+function neg8(dest_operand)
+{
+    last_result = -dest_operand;
+    
+    flags_changed = flags_all;
+    last_op_size = OPSIZE_8;
+    last_op1 = 0;
+    last_op2 = last_result - 1;
+
+    return last_result;
+}
+
+function neg16(dest_operand)
+{
+    last_result = -dest_operand;
+    
+    flags_changed = flags_all;
+    last_op_size = OPSIZE_16;
+    last_op1 = 0;
+    last_op2 = last_result - 1;
+
+    return last_result;
+}
+
+function neg32(dest_operand)
+{
+    last_result = -dest_operand;
+    
+    flags_changed = flags_all;
+    last_op_size = OPSIZE_32;
+    last_op1 = 0;
+    last_op2 = last_result - 1;
+
+    return last_result;
+}
+
+/*
+ * mul, imul, div, idiv
+ *
+ * Note: imul has some extra opcodes
+ *       while other functions only allow
+ *       ax * modrm
+ */
+
+function mul8(source_operand)
+{
+    var result = source_operand * reg8[reg_al];
+
+    reg16[reg_ax] = result;
+
+    if(result < 0x100)
+    {
+        flags = flags & ~1 & ~flag_overflow;
+    }
+    else
+    {
+        flags = flags | 1 | flag_overflow;
+    }
+
+    flags_changed = 0;
+}
+
+function imul8(source_operand)
+{
+    var result = source_operand * reg8s[reg_al];
+
+    reg16[reg_ax] = result;
+
+    if(result > 0x7F || result < -0x80)
+    {
+        flags = flags | 1 | flag_overflow;
+    }
+    else
+    {
+        flags = flags & ~1 & ~flag_overflow;
+    }
+    flags_changed = 0;
+}
+
+function mul16(source_operand)
+{
+    var result = source_operand * reg16[reg_ax],
+        high_result = result >>> 16;
+    //console.log(h(a) + " * " + h(reg16[reg_ax]) + " = " + h(result));
+
+    reg16[reg_ax] = result;
+    reg16[reg_dx] = high_result;
+
+    if(high_result === 0)
+    {
+        flags &= ~1 & ~flag_overflow;
+    }
+    else
+    {
+        flags |= 1 | flag_overflow;
+    }
+    flags_changed = 0;
+}
+
+/*
+ * imul with 1 argument
+ * ax = ax * r/m
+ */
+function imul16(source_operand)
+{
+    var result = source_operand * reg16s[reg_ax];
+
+    reg16[reg_ax] = result;
+    reg16[reg_dx] = result >> 16;
+
+    if(result > 0x7FFF || result < -0x8000)
+    {
+        flags |= 1 | flag_overflow;
+    }
+    else
+    {
+        flags &= ~1 & ~flag_overflow;
+    }
+    flags_changed = 0;
+}
+
+/*
+ * imul with 2 or 3 arguments
+ * reg = reg * r/m
+ * reg = imm * r/m
+ */
+function imul_reg16(operand1, operand2)
+{
+    dbg_assert(operand1 < 0x8000 && operand1 >= -0x8000);
+    dbg_assert(operand2 < 0x8000 && operand2 >= -0x8000);
+
+    var result = operand1 * operand2;
+
+    if(result > 0x7FFF || result < -0x8000)
+    {
+        flags |= 1 | flag_overflow;
+    }
+    else
+    {
+        flags &= ~1 & ~flag_overflow;
+    }
+    flags_changed = 0;
+
+    return result;
+}
+
+function mul32(source_operand)
+{
+    var dest_operand = reg32[reg_eax],
+        high_result = source_operand * dest_operand / 0x100000000 | 0;
+
+    reg32[reg_eax] = multiply_low(source_operand, dest_operand);
+    reg32[reg_edx] = high_result;
+
+    if(high_result === 0)
+    {
+        flags &= ~1 & ~flag_overflow;
+    }
+    else
+    {
+        flags |= 1 | flag_overflow;
+    }
+    flags_changed = 0;
+
+    //console.log(memory.read32s(address) + " * " + old);
+    //console.log("= " + reg32[reg_edx] + " " + reg32[reg_eax]);
+}
+
+function imul32(source_operand)
+{
+    dbg_assert(source_operand < 0x80000000 && source_operand >= -0x80000000);
+
+    var dest_operand = reg32s[reg_eax],
+        high_result = source_operand * dest_operand / 0x100000000 | 0,
+        low_result = multiply_low(source_operand, dest_operand);
+
+    if(high_result === 0 && low_result < 0)
+    {
+        high_result = -1;
+    }
+    
+    reg32[reg_eax] = low_result;
+    reg32[reg_edx] = high_result;
+
+    if(high_result === (reg32[reg_eax] < 0x80000000 ? 0 : -1))
+    {
+        flags &= ~1 & ~flag_overflow;
+    }
+    else
+    {
+        flags |= 1 | flag_overflow;
+    }
+    flags_changed = 0;
+
+
+    //console.log(target_operand + " * " + source_operand);
+    //console.log("= " + h(reg32[reg_edx]) + " " + h(reg32[reg_eax]));
+}
+
+/*
+ * imul with 2 or 3 arguments
+ * reg = reg * r/m
+ * reg = imm * r/m
+ */
+function imul_reg32(operand1, operand2)
+{
+    dbg_assert(operand1 < 0x80000000 && operand1 >= -0x80000000);
+    dbg_assert(operand2 < 0x80000000 && operand2 >= -0x80000000);
+
+    var result = multiply_low(operand1, operand2),
+        high_result = operand1 * operand2 / 0x100000000 | 0;
+
+    if(high_result === 0)
+    {
+        flags &= ~1 & ~flag_overflow;
+    }
+    else
+    {
+        flags |= 1 | flag_overflow;
+    }
+    flags_changed = 0;
+
+    return result;
+
+    //console.log(operand + " * " + source_operand);
+    //console.log("= " + reg32[reg]);
+}
+
+function div8(source_operand)
+{
+    dbg_assert(source_operand >= 0 && source_operand < 0x100);
+
+    var target_operand = reg16[reg_ax],
+        result = target_operand / source_operand | 0;
+
+    if(result > 0xFF || source_operand === 0)
+    {
+        trigger_de();
+    }
+    else
+    {
+        reg8[reg_al] = result;
+        reg8[reg_ah] = target_operand % source_operand;
+    }
+}
+
+function idiv8(source_operand)
+{
+    dbg_assert(source_operand >= -0x80 && source_operand < 0x80);
+
+    var target_operand = reg16s[reg_ax],
+        result = target_operand / source_operand | 0;
+
+    if(result > 0x7F || result < -0x80 || source_operand === 0)
+    {
+        trigger_de();
+    }
+    else
+    {
+        reg8[reg_al] = result;
+        reg8[reg_ah] = target_operand % source_operand;
+    }
+}
+
+function div16(source_operand)
+{
+    dbg_assert(source_operand >= 0 && source_operand < 0x10000);
+
+    var 
+        target_operand = (reg16[reg_ax] | reg16[reg_dx] << 16) >>> 0,
+        result = target_operand / source_operand | 0;
+
+    if(result > 0xFFFF || source_operand === 0)
+    {
+        trigger_de();
+    }
+    else
+    {
+        reg16[reg_ax] = result;
+        reg16[reg_dx] = target_operand % source_operand;
+    }
+}
+
+function idiv16(source_operand)
+{
+    dbg_assert(source_operand >= -0x8000 && source_operand < 0x8000);
+
+    var target_operand = reg16[reg_ax] | (reg16[reg_dx] << 16),
+        result = target_operand / source_operand | 0;
+    
+    if(result > 0x7FFF || result < -0x8000 || source_operand === 0)
+    {
+        trigger_de();
+    }
+    else
+    {    
+        reg16[reg_ax] = result;
+        reg16[reg_dx] = target_operand % source_operand;
+    }
+}
+
+function div32(source_operand)
+{
+    dbg_assert(source_operand >= 0 && source_operand <= 0xffffffff);
+
+    var 
+        dest_operand_low = reg32[reg_eax],
+        dest_operand_high = reg32[reg_edx],
+
+        // Wat? Not sure if seriös ...
+        mod = (0x100000000 * dest_operand_high % source_operand + dest_operand_low % source_operand) % source_operand,
+        result = dest_operand_low / source_operand + dest_operand_high * 0x100000000 / source_operand;
+
+    if(result > 0xFFFFFFFF || source_operand === 0)
+    {
+        trigger_de();
+    }
+    else
+    {    
+        reg32[reg_eax] = result;
+        reg32[reg_edx] = mod;
+    }
+
+    //console.log(h(dest_operand_high) + ":" + h(dest_operand_low) + " / " + h(source_operand));
+    //console.log("= " + h(reg32[reg_eax]) + " rem " + h(reg32[reg_edx]));
+}
+
+function idiv32(source_operand)
+{
+    dbg_assert(source_operand < 0x80000000 && source_operand >= -0x80000000);
+
+    var 
+        dest_operand_low = reg32[reg_eax],
+        dest_operand_high = reg32s[reg_edx],
+        mod = (0x100000000 * dest_operand_high % source_operand + dest_operand_low % source_operand) % source_operand,
+        result = dest_operand_low / source_operand + dest_operand_high * 0x100000000 / source_operand;
+
+    if(result > 0x7FFFFFFF || result < -0x80000000 || source_operand === 0)
+    {
+        trigger_de();
+    }
+    else
+    {    
+        reg32[reg_eax] = result;
+        reg32[reg_edx] = mod;
+    }
+
+    //console.log(h(dest_operand_high) + ":" + h(dest_operand_low) + " / " + h(source_operand));
+    //console.log("= " + h(reg32[reg_eax]) + " rem " + h(reg32[reg_edx]));
+}
+
+
+function xadd8(source_operand, reg)
+{
+    var tmp = reg8[reg];
+
+    reg8[reg] = source_operand;
+
+    return add8(source_operand, tmp);
+}
+
+
+function xadd16(source_operand, reg)
+{
+    var tmp = reg16[reg];
+
+    reg16[reg] = source_operand;
+
+    return add16(source_operand, tmp);
+}
+
+
+function xadd32(source_operand, reg)
+{
+    var tmp = reg32[reg];
+
+    reg32[reg] = source_operand;
+
+    return add32(source_operand, tmp);
+}
+
+
+function bcd_daa()
+{
+    //dbg_log("daa");
+    // decimal adjust after addition
+    var old_al = reg8[reg_al],
+        old_cf = getcf(),
+        old_af = getaf();
+
+    flags &= ~1 & ~flag_adjust
+
+    if((old_al & 0xF) > 9 || old_af)
+    {
+        reg8[reg_al] += 6;
+        flags |= flag_adjust;
+    }
+    if(old_al > 0x99 || old_cf)
+    {
+        reg8[reg_al] += 0x60;
+        flags |= 1;
+    }
+
+    last_result = reg8[reg_al];
+    last_op_size = OPSIZE_8;
+    last_op1 = last_op2 = 0;
+    flags_changed = flags_all & ~1 & ~flag_adjust & ~flag_overflow;
+}
+
+function bcd_das()
+{
+    //dbg_log("das");
+    // decimal adjust after subtraction
+    var old_al = reg8[reg_al],
+        old_cf = getcf();
+
+    flags &= ~1;
+
+    if((old_al & 0xF) > 9 || getaf())
+    {
+        reg8[reg_al] -= 6;
+        flags |= flag_adjust;
+        flags = flags & ~1 | old_cf | reg8[reg_al] >> 7;
+    }
+    else
+    {
+        flags &= ~flag_adjust;
+    }
+
+    if(old_al > 0x99 || old_cf)
+    {
+        reg8[reg_al] -= 0x60;
+        flags |= 1;
+    }
+
+    last_result = reg8[reg_al];
+    last_op_size = OPSIZE_8;
+    last_op1 = last_op2 = 0;
+    flags_changed = flags_all & ~1 & ~flag_adjust & ~flag_overflow;
+}
+
+function bcd_aam()
+{
+    // ascii adjust after multiplication
+    var imm8 = read_imm8();
+
+    if(imm8 === 0)
+    {
+        trigger_de();
+    }
+    else
+    {
+        var temp = reg8[reg_al];
+        reg8[reg_ah] = temp / imm8;
+        reg8[reg_al] = temp % imm8;
+
+        last_result = reg8[reg_al];
+        flags_changed = flags_all;
+    }
+}
+
+function bcd_aad()
+{
+    // ascii adjust after division
+    var imm8 = read_imm8();
+
+    last_result = reg8[reg_al] + reg8[reg_ah] * imm8;
+    reg16[reg_ax] = last_result & 0xFF;
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all;
+}
+
+function bcd_aaa()
+{
+    if((reg8[reg_al] & 0xF) > 9 || getaf())
+    {
+        reg16[reg_ax] += 6;
+        reg8[reg_ah] += 1;
+        flags |= flag_adjust | 1;
+    }
+    else
+    {
+        flags &= ~flag_adjust & ~1;
+    }
+    reg8[reg_al] &= 0xF;
+
+    flags_changed &= ~flag_adjust & ~1;
+}
+
+
+function bcd_aas()
+{
+    if((reg8[reg_al] & 0xF) > 9 || getaf())
+    {
+        reg16[reg_ax] -= 6;
+        reg8[reg_ah] -= 1;
+        flags |= flag_adjust | 1;
+    }
+    else
+    {
+        flags &= ~flag_adjust & ~1;
+    }
+    reg8[reg_al] &= 0xF;
+
+    flags_changed &= ~flag_adjust & ~1;
+}
+
+
+/*                     \O
+ * bitwise functions    |\
+ *                     / \
+ *
+ * and, or, xor, test
+ * shl, shr, sar, rol, ror, rcl, ror
+ * shrd, shld
+ *
+ * bt, bts, btr, btc
+ * bsf, bsr
+ */
+
+
+function and8(dest_operand, source_operand)
+{
+    last_result = dest_operand & source_operand;
+    
+    last_op_size = OPSIZE_8;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function and16(dest_operand, source_operand)
+{
+    last_result = dest_operand & source_operand;
+    
+    last_op_size = OPSIZE_16;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function and32(dest_operand, source_operand)
+{
+    last_result = dest_operand & source_operand;
+    
+    last_op_size = OPSIZE_32;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function test8(dest_operand, source_operand)
+{
+    last_result = dest_operand & source_operand;
+
+    last_op_size = OPSIZE_8;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+}
+
+function test16(dest_operand, source_operand)
+{
+    last_result = dest_operand & source_operand;
+
+    last_op_size = OPSIZE_16;
+    flags &= ~1 & ~flag_overflow;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+}
+
+function test32(dest_operand, source_operand)
+{
+    last_result = dest_operand & source_operand;
+
+    last_op_size = OPSIZE_32;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+}
+
+function or8(dest_operand, source_operand)
+{
+    last_result = dest_operand | source_operand;
+    
+    last_op_size = OPSIZE_8;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function or16(dest_operand, source_operand)
+{
+    last_result = dest_operand | source_operand;
+    
+    last_op_size = OPSIZE_16;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function or32(dest_operand, source_operand)
+{
+    last_result = dest_operand | source_operand;
+    
+    last_op_size = OPSIZE_32;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function xor8(dest_operand, source_operand)
+{
+    last_result = dest_operand ^ source_operand;
+    
+    last_op_size = OPSIZE_8;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function xor16(dest_operand, source_operand)
+{
+    last_result = dest_operand ^ source_operand;
+    
+    last_op_size = OPSIZE_16;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+
+function xor32(dest_operand, source_operand)
+{
+    last_result = dest_operand ^ source_operand;
+    
+    last_op_size = OPSIZE_32;
+    flags &= ~1 & ~flag_overflow & ~flag_adjust;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow & ~flag_adjust;
+
+    return last_result;
+}
+    
+
+/*
+ * rotates and shifts
+ */
+
+function rol8(dest_operand, count)
+{
+    if(!count)
+    {
+        return dest_operand;
+    }
+    count &= 7;
+
+    var result = dest_operand << count | dest_operand >> (8 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result & 1)
+                | (result << 11 ^ result << 4) & flag_overflow;
+
+    return result;
+}
+
+function rol16(dest_operand, count)
+{
+    if(!count)
+    {
+        return dest_operand;
+    }
+    count &= 15;
+
+    var result = dest_operand << count | dest_operand >> (16 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result & 1)
+                | (result << 11 ^ result >> 4) & flag_overflow;
+
+    return result;
+}
+
+function rol32(dest_operand, count)
+{
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand << count | dest_operand >>> (32 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result & 1)
+                | (result << 11 ^ result >> 20) & flag_overflow;
+
+    return result;
+}
+
+function rcl8(dest_operand, count)
+{
+    count %= 9;
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand << count | getcf() << (count - 1) | dest_operand >> (9 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result >> 8 & 1)
+                | (result << 3 ^ result << 4) & flag_overflow;
+
+    return result;
+}
+
+function rcl16(dest_operand, count)
+{
+    count %= 17;
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand << count | getcf() << (count - 1) | dest_operand >> (17 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result >> 16 & 1) 
+                | (result >> 5 ^ result >> 4) & flag_overflow;
+
+    return result;
+}
+
+function rcl32(dest_operand, count)
+{
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand << count | getcf() << (count - 1);
+
+    if(count > 1)
+    {
+        result |= dest_operand >>> (33 - count);
+    }
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) | (dest_operand >>> (32 - count) & 1);
+    flags |= (flags << 11 ^ result >> 20) & flag_overflow;
+
+    return result;
+}
+
+function ror8(dest_operand, count)
+{
+    count &= 7;
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand >> count | dest_operand << (8 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result >> 7 & 1)
+                | (result << 4 ^ result << 5) & flag_overflow;
+
+    return result;
+} 
+
+function ror16(dest_operand, count)
+{
+    count &= 15;
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand >> count | dest_operand << (16 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result >> 15 & 1) 
+                | (result >> 4 ^ result >> 3) & flag_overflow;
+
+    return result;
+}    
+
+function ror32(dest_operand, count)
+{
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand >>> count | dest_operand << (32 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result >> 31 & 1) 
+                | (result >> 20 ^ result >> 19) & flag_overflow;
+
+    return result;
+}
+
+function rcr8(dest_operand, count)
+{
+    count %= 9;
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand >> count | getcf() << (8 - count) | dest_operand << (9 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result >> 8 & 1)
+                | (result << 4 ^ result << 5) & flag_overflow;
+
+    return result;
+}    
+
+function rcr16(dest_operand, count)
+{
+    count %= 17;
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand >> count | getcf() << (16 - count) | dest_operand << (17 - count);
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (result >> 16 & 1)
+                | (result >> 4 ^ result >> 3) & flag_overflow;
+
+    return result;
+}
+
+function rcr32(dest_operand, count)
+{
+    if(!count)
+    {
+        return dest_operand;
+    }
+
+    var result = dest_operand >>> count | getcf() << (32 - count);
+
+    if(count > 1)
+    {
+        result |= dest_operand << (33 - count);
+    }
+
+    flags_changed &= ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (dest_operand >> (count - 1) & 1)
+                | (result >> 20 ^ result >> 19) & flag_overflow;
+
+    return result;
+}
+
+function shl8(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand << count;
+
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (last_result >> 8 & 1)
+                | (last_result << 3 ^ last_result << 4) & flag_overflow;
+
+    return last_result;
+}
+
+function shl16(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand << count;
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (last_result >> 16 & 1)
+                | (last_result >> 5 ^ last_result >> 4) & flag_overflow;
+
+    return last_result;
+}
+
+function shl32(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand << count;
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    // test this
+    flags = (flags & ~1 & ~flag_overflow) | (dest_operand >>> (32 - count) & 1);
+    flags |= ((flags & 1) ^ (last_result >> 31 & 1)) << 11 & flag_overflow;
+
+    return last_result;
+}
+    
+function shr8(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand >> count;
+
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (dest_operand >> (count - 1) & 1)
+                | (dest_operand >> 7 & 1) << 11 & flag_overflow;
+
+    return last_result;
+}    
+
+function shr16(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand >> count;
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (dest_operand >> (count - 1) & 1) 
+                | (dest_operand >> 4)  & flag_overflow;
+
+    return last_result;
+}    
+
+function shr32(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand >>> count;
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) 
+                | (dest_operand >>> (count - 1) & 1)
+                | (dest_operand >> 20) & flag_overflow;
+
+    return last_result;
+}
+
+function sar8(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand >> count;
+
+    last_op_size = OPSIZE_8;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) | (dest_operand >> (count - 1) & 1);
+    // of is zero
+
+    return last_result;
+}    
+
+function sar16(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand >> count;
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) | (dest_operand >> (count - 1) & 1);
+
+    return last_result;
+}
+
+function sar32(dest_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand >> count;
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all & ~flag_carry & ~flag_overflow;
+    flags = (flags & ~1 & ~flag_overflow) | (dest_operand >>> (count - 1) & 1);
+
+    return last_result;
+}
+
+
+function shrd16(dest_operand, source_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    if(count <= 16)
+    {
+        last_result = dest_operand >> count | source_operand << (16 - count);
+        flags = (flags & ~1) | (dest_operand >> (count - 1) & 1);
+    }
+    else
+    {
+        last_result = dest_operand << (32 - count) | source_operand >> (count - 16);
+        flags = (flags & ~1) | (source_operand >> (count - 17) & 1);
+    }
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all & ~1 & ~flag_overflow;
+    flags = (flags & ~flag_overflow) | ((last_result ^ dest_operand) >> 4 & flag_overflow);
+
+    return last_result;
+}
+
+function shrd32(dest_operand, source_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand >>> count | source_operand << (32 - count);
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all & ~1 & ~flag_overflow;
+    flags = (flags & ~1) | (dest_operand >>> (count - 1) & 1);
+    flags = (flags & ~flag_overflow) | ((last_result ^ dest_operand) >> 20 & flag_overflow);
+
+    return last_result;
+}
+
+function shld16(dest_operand, source_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    if(count <= 16)
+    {
+        last_result = dest_operand << count | source_operand >>> (16 - count);
+        flags = (flags & ~1) | (dest_operand >>> (16 - count) & 1);
+    }
+    else
+    {
+        last_result = dest_operand >> (32 - count) | source_operand << (count - 16);
+        flags = (flags & ~1) | (source_operand >>> (32 - count) & 1);
+    }
+
+    last_op_size = OPSIZE_16;
+    flags_changed = flags_all & ~1 & ~flag_overflow;
+    flags = (flags & ~flag_overflow) | ((flags & 1) ^ (last_result >> 15 & 1)) << 11;
+
+    return last_result;
+}
+
+function shld32(dest_operand, source_operand, count)
+{
+    if(count === 0)
+    {
+        return dest_operand;
+    }
+
+    last_result = dest_operand << count | source_operand >>> (32 - count);
+
+    last_op_size = OPSIZE_32;
+    flags_changed = flags_all & ~1 & ~flag_overflow;
+    // test this
+    flags = (flags & ~1) | (dest_operand >>> (32 - count) & 1);
+    flags = (flags & ~flag_overflow) | ((flags & 1) ^ (last_result >> 31 & 1)) << 11;
+
+    return last_result;
+}
+
+
+function bt_reg(bit_base, bit_offset)
+{
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+}
+
+function btc_reg(bit_base, bit_offset)
+{
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+
+    return bit_base ^ 1 << bit_offset;
+}
+
+function bts_reg(bit_base, bit_offset)
+{
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+
+    return bit_base | 1 << bit_offset;
+}
+
+function btr_reg(bit_base, bit_offset)
+{
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+
+    return bit_base & ~(1 << bit_offset);
+}
+
+function bt_mem(virt_addr, bit_offset)
+{
+    var bit_base = safe_read8(virt_addr + (bit_offset >> 3));
+    bit_offset &= 7;
+
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+}
+
+function btc_mem(virt_addr, bit_offset)
+{
+    var phys_addr = translate_address_write(virt_addr + (bit_offset >> 3));
+    var bit_base = memory.read8(phys_addr);
+
+    bit_offset &= 7;
+
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+
+    memory.write8(phys_addr, bit_base ^ 1 << bit_offset);
+}
+
+function btr_mem(virt_addr, bit_offset)
+{
+    var phys_addr = translate_address_write(virt_addr + (bit_offset >> 3));
+    var bit_base = memory.read8(phys_addr);
+
+    bit_offset &= 7;
+
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+
+    memory.write8(phys_addr, bit_base & ~(1 << bit_offset));
+}
+
+function bts_mem(virt_addr, bit_offset)
+{
+    var phys_addr = translate_address_write(virt_addr + (bit_offset >> 3));
+    var bit_base = memory.read8(phys_addr);
+
+    bit_offset &= 7;
+
+    flags = (flags & ~1) | (bit_base >> bit_offset & 1);
+    flags_changed = 0;
+
+    memory.write8(phys_addr, bit_base | 1 << bit_offset);
+}
+
+var mod37_bit_position = new Uint8Array([
+    32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
+    7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
+    20, 8, 19, 18
+]);
+
+function bsf16(old, bit_base)
+{
+    flags_changed = 0;
+
+    if(bit_base === 0)
+    {
+        flags |= flag_zero;
+        
+        // not defined in the docs, but value doesn't change on my intel cpu
+        return old;
+    }
+    else
+    {
+        flags &= ~flag_zero;
+
+        return mod37_bit_position[((-bit_base & bit_base) >>> 0) % 37];
+    }
+}
+
+function bsf32(old, bit_base)
+{
+    flags_changed = 0;
+
+    if(bit_base === 0)
+    {
+        flags |= flag_zero;
+
+        return old;
+    }
+    else
+    {
+        flags &= ~flag_zero;
+
+        return mod37_bit_position[((-bit_base & bit_base) >>> 0) % 37];
+    }
+}
+
+function bsr16(old, bit_base)
+{
+    flags_changed = 0;
+
+    if(bit_base === 0)
+    {
+        flags |= flag_zero;
+        return old;
+    }
+    else
+    {
+        flags &= ~flag_zero;
+
+        var t = bit_base >>> 8;
+
+        if(t)
+        {
+            return 8 + log2_table[t];
+        }
+        else
+        {
+            return log2_table[bit_base];
+        }
+    }
+}
+
+function bsr32(old, bit_base)
+{
+    flags_changed = 0;
+
+    if(bit_base === 0)
+    {
+        flags |= flag_zero;
+        return old;
+    }
+    else
+    {
+        flags &= ~flag_zero;
+
+        var tt = bit_base >>> 16,
+            t;
+
+        if(tt)
+        {
+            t = tt >>> 8;
+
+            if(t)
+            {
+                return 24 + log2_table[t];
+            }
+            else
+            {
+                return 16 + log2_table[tt];
+            }
+        }
+        else
+        {
+            t = bit_base >>> 8;
+
+            if(t)
+            {
+                return 8 + log2_table[t];
+            }
+            else
+            {
+                return log2_table[bit_base];
+            }
+        }
+    }
+}
+
+

+ 236 - 0
src/browser/keyboard.js

@@ -0,0 +1,236 @@
+"use strict";
+
+/**
+ * @constructor
+ */
+function KeyboardAdapter()
+{
+    var 
+        /**
+         * @type {!Object.<boolean>}
+         */
+        keys_pressed = {},
+
+        keyboard = this,
+
+        // callback to call on a keypress
+        send_code;
+
+    this.enabled = true;
+
+    /** 
+     * Format:
+     * Javascript event.keyCode -> make code
+     * @const 
+     */
+    var charmap = new Uint16Array([
+        0, 0, 0, 0, 0, 0, 0, 0,
+        // 0x08: backspace, tab, enter
+        0x0E, 0x0F, 0, 0, 0, 0x1C, 0, 0,
+
+        // 0x10: shift, ctrl, alt, pause, caps lock
+        0x2A, 0x1D, 0x38, 0, 0x3A, 0, 0, 0,
+
+        // 0x18: escape
+        0, 0, 0, 0x01, 0, 0, 0, 0,
+
+        // 0x20: spacebar, page down/up, end, home, arrow keys, ins, del
+        0x39, 0xE049, 0xE051, 0x4F, 0x47, 0x4B, 0x48, 0x4D,
+        0x50, 0, 0, 0, 0, 0x52, 0x53, 0,
+
+        // 0x30: numbers
+        0x0B, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
+        0x09, 0x0A,
+
+        // 0x3B: ;= (firefox only)
+        0, 0x27, 0, 0x0D, 0, 0, 0,
+
+        // 0x65: letters
+        0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 
+        0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C,
+
+        0, 0, 0, 0, 0,
+
+        // 0x60: keypad
+        0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47,
+        0x48, 0x49, 0, 0, 0, 0, 0, 0,
+
+        // 0x70: F1 to F12
+        0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x57, 0x58,
+
+        0, 0, 0, 0,
+
+        // 0x80
+        0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0,
+
+        // 0x90
+        0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0,
+
+        // 0xA0: - (firefox only)
+        0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0x0C, 0, 0,
+
+        // 0xB0
+        // ,
+        0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0x27, 0x0D, 0x33, 0x0C, 0x34, 0x35,
+        
+        // 0xC0
+        // `
+        0x29, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0,
+
+        // 0xD0
+        // [']\
+        0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0x1A, 0x2B, 0x1B, 0x28, 0
+    ]);
+
+    this.init = function(code_fn)
+    {
+        this.destroy();
+
+        send_code = code_fn;
+
+        window.addEventListener("keyup", keyup_handler, false);
+        window.addEventListener("keydown", keydown_handler, false);
+        window.addEventListener("blur", blur_handler, false);
+    };
+
+    this.destroy = function() 
+    {
+        window.removeEventListener("keyup", keyup_handler, false);
+        window.removeEventListener("keydown", keydown_handler, false);
+        window.removeEventListener("blur", blur_handler, false);
+    };
+
+
+    function may_handle(e)
+    {
+        if(
+            (e.shiftKey && e.ctrlKey && e.keyCode === 74) ||
+            e.keyCode === 116
+        ) {
+              // don't prevent opening chromium dev tools or F5
+              // maybe add other important combinations here, too
+              return false;
+        }
+
+        if(!keyboard.enabled)
+        {
+            return false;
+        }
+
+        if(e.target)
+        {
+            return e.target.className === "phone_keyboard" ||
+                (e.target.nodeName !== "INPUT" && e.target.nodeName !== "TEXTAREA");
+        }
+        else
+        {
+            return true;
+        }
+    }
+
+    function keyup_handler(e)
+    {
+        if(!may_handle(e))
+        {
+            return;
+        }
+
+        var code = e.keyCode;
+
+        if(!keys_pressed[code])
+        {
+            // stray keyup
+            return false;
+        }
+
+        keys_pressed[code] = false;
+
+        if(!handler(code, false))
+        {
+            e.preventDefault();
+        }
+    }
+
+    function keydown_handler(e)
+    {
+        if(!may_handle(e))
+        {
+            return;
+        }
+
+        var code = e.keyCode;
+
+        if(keys_pressed[code])
+        {
+            handler(code, false);
+        }
+
+        keys_pressed[code] = true;
+
+
+        if(!handler(code, true))
+        {
+            e.preventDefault();
+        }
+    }
+
+    function blur_handler(e)
+    {
+        // trigger keyup for all pressed keys
+        var keys = Object.keys(keys_pressed),
+            key;
+
+        for(var i = 0; i < keys.length; i++)
+        {
+            key = +keys[i];
+
+            if(keys_pressed[key])
+            {
+                handler(key, false);
+            }
+        }
+
+        keys_pressed = {};
+    }
+
+    /**
+     * @param {number} chr
+     * @param {boolean} keydown
+     */
+    function handler(chr, keydown)
+    {
+        if(chr >= charmap.length || charmap[chr] === 0)
+        {
+            dbg_log("missing char: " + h(chr), LOG_PS2);
+            return true;
+        }
+
+        var code = charmap[chr];
+
+        if(!keydown)
+        {
+            code |= 0x80;
+        }
+        dbg_log("Key: " + h(code) + " from " + h(chr) + " down=" + keydown, LOG_PS2);
+
+        if(code > 0xFF)
+        {
+            // prefix
+            send_code(code >> 8);
+            send_code(code & 0xFF);
+        }
+        else
+        {
+            send_code(code);
+        }
+
+        return false;
+    }
+}
+

+ 589 - 0
src/browser/main.js

@@ -0,0 +1,589 @@
+"use strict";
+
+
+// setImmediate for the browser
+var next_tick, set_tick;
+
+(function()
+{
+    var fn,
+        host = location.protocol + "//" + location.hostname;
+
+    set_tick = function(f)
+    {
+        fn = f;
+
+        window.removeEventListener("message", tick_handler, false);
+        window.addEventListener("message", tick_handler, false);
+    };
+
+    next_tick = function()
+    {
+        window.postMessage(null, host);
+    };
+
+    function tick_handler(e)
+    {
+        if(e.origin === host)
+        {
+            fn();
+        }
+    }
+})();
+
+function log(data)
+{
+    var log_element = document.getElementById("log");
+
+    log_element.textContent += data + "\n";
+    log_element.scrollTop = 1e9;
+}
+
+function dump_text(text)
+{
+    var box = document.createElement("textarea");
+
+    box.appendChild(document.createTextNode(text));
+    document.body.appendChild(box);
+}
+
+function dump_file(ab, name)
+{
+    var blob = new Blob([ab]),
+        a;
+
+    a = document.createElement("a");
+    a["download"] = name;
+    a.href = window.URL.createObjectURL(blob),
+    a.textContent = "Download " + name;
+    a.onclick = function() { a.parentNode.removeChild(a); };
+
+    a.dataset["downloadurl"] = ["application/octet-stream", a["download"], a.href].join(":");
+
+    document.body.appendChild(a);
+}
+
+(function()
+{
+
+    /** @param {?=} progress */
+    function load_file(filename, done, progress)
+    {
+        var http = new XMLHttpRequest();
+
+        http.open("get", filename, true);
+        http.responseType = "arraybuffer";
+
+        http.onload = function(e)
+        {
+            //if(http.readyState === 4 && http.status === 200)
+            if(http.response)
+            {
+                done(http.response);
+            }
+        };
+
+        if(progress)
+        {
+            http.onprogress = function(e)
+            {
+                progress(e);
+            };
+        }
+        
+        http.send(null);
+    }
+
+    /**
+     * Asynchronous access to ArrayBuffer, loading blocks lazily as needed.
+     * This is just a prototype and partly incomplete.
+     *
+     * @constructor
+     * @param {string} filename   Name of the file to download parts
+     *                            from. Replaces %d with the block number (padded)
+     */
+    function AsyncXHRBuffer(filename, block_size, size)
+    {
+        var block_count = size / block_size,
+            loaded_blocks,
+            padding_width;
+
+        dbg_assert(block_count === (block_count | 0));
+        loaded_blocks = Array(block_count);
+        
+        padding_width = ("" + (block_count - 1)).length;
+
+        this.byteLength = size;
+
+        // warning: fn may be called synchronously or asynchronously
+        this.get = function(start, len, fn)
+        {
+            // TODO: Unaligned read
+            dbg_assert(start % block_size === 0);
+            dbg_assert(len % block_size === 0);
+            dbg_assert(len);
+
+            var blocks_to_load = len / block_size,
+                data,
+                loaded_count = 0,
+                start_block = start / block_size;
+
+            if(blocks_to_load > 1)
+            {
+                // copy blocks in this buffer if there is more than one
+                data = new Uint8Array(len);
+            }
+
+            for(var i = start_block; i < start_block + blocks_to_load; i++)
+            {
+                this.load_block(i, block_loaded);
+            }
+
+            function block_loaded(buffer, i)
+            {
+                var block = new Uint8Array(buffer);
+                loaded_count++;
+
+                if(blocks_to_load === 1)
+                {
+                    data = block;
+                }
+                else
+                {
+                    data.set(block, (i - start_block) * block_size);
+                }
+
+                if(loaded_count === blocks_to_load)
+                {
+                    fn(data);
+                }
+            }
+        };
+
+        this.load_block = function(i, fn)
+        {
+            var cached_block = loaded_blocks[i];
+
+            if(cached_block === undefined)
+            {
+                var file = filename.replace("%d", String.pad0(i, padding_width));
+
+                load_file(file, function(buffer)
+                {
+                    loaded_blocks[i] = buffer;
+                    fn(buffer, i);
+                });
+            }
+            else
+            {
+                fn(cached_block, i);
+            }
+        };
+
+        this.get_buffer = function(fn)
+        {
+            // We must download all parts, unlikely a good idea for big files
+            if(size > 32 * 1024 * 1024)
+            {
+                dbg_log("Warning: Downloading all parts of a huge file. Will probably " + 
+                        "crash or never finish");
+            }
+
+            this.get(0, size, function(data)
+            {
+                return data.buffer;   
+            });
+        };
+
+        this.set = function(start, slice, fn)
+        {
+            // Discard (we can't write to the server)
+            // TODO: Put data into cache
+        };
+    }
+
+    function lock_mouse(elem)
+    {
+        var fn = elem["requestPointerLock"] ||
+                    elem["mozRequestPointerLock"] ||
+                    elem["webkitRequestPointerLock"];
+
+        if(fn)
+        {
+            fn.call(elem);
+        }
+    }
+
+    function show_progress(e)
+    {
+        var el = $("loading");
+        el.style.display = "block";
+
+        if(e.lengthComputable)
+        {
+            var per50 = e.loaded / e.total * 50 | 0;
+
+            el.textContent = "Loading: " + 2 * per50 + "% [" + 
+                String.chr_repeat("#", per50) + 
+                String.chr_repeat(" ", 50 - per50) + "]";
+        }
+        else
+        {
+            el.textContent = "Loading ...";
+        }
+    }
+
+    function $(id)
+    {
+        return document.getElementById(id);
+    }
+
+    window.onload = function()
+    {
+        if(!("responseType" in new XMLHttpRequest))
+        {
+            log("Your browser is not supported because it doesn't have XMLHttpRequest.responseType");
+            return;
+        }
+
+        var settings = {
+            load_devices: true
+        };
+
+
+        $("lock_mouse").onclick = function()
+        {
+            lock_mouse(document.body);
+            $("lock_mouse").blur();
+        };
+
+        load_file("bios/seabios.bin", function(img)
+        {
+            settings.bios = img;
+        });
+
+        load_file("bios/vgabios.bin", function(img)
+        {
+            settings.vga_bios = img;
+        });
+
+        function load_local(me, type)
+        {
+            if(me.files.length)
+            {
+                var reader = new FileReader();
+                
+                reader.onload = function(e)
+                {
+                    var buffer = new SyncBuffer(e.target.result);
+
+                    switch(type)
+                    {
+                    case "floppy": 
+                       settings.floppy_disk = buffer;
+                       break;
+                    case "hd": 
+                       settings.hda_disk = buffer;
+                       break;
+                    case "cdrom": 
+                       settings.cdrom_disk = buffer;
+                       break;
+                    }
+
+                    init(settings);
+                };
+                
+                //reader.readAsBinaryString($("file").files[0]);
+                reader.readAsArrayBuffer(me.files[0]);
+            }
+        };
+
+        $("floppy_image").onchange = function() 
+        {
+            load_local(this, "floppy"); 
+        };
+
+        $("cd_image").onchange = function() 
+        {
+            load_local(this, "cdrom");
+        };
+
+        $("hd_image").onchange = function() 
+        {
+            load_local(this, "hd");
+        };
+
+        $("start_freedos").onclick = function()
+        {
+            load_file("images/freedos722.img", function(buffer)
+            {
+                settings.floppy_disk = new SyncBuffer(buffer);
+                init(settings);
+            }, show_progress);
+
+            $("start_freedos").blur();
+        };
+
+        $("start_win101").onclick = function()
+        {
+            load_file("images/windows101.img", function(buffer)
+            {
+                settings.floppy_disk = new SyncBuffer(buffer);
+                init(settings);
+            }, show_progress);
+
+            $("start_win101").blur();
+        };
+
+
+        $("start_linux").onclick = function()
+        {
+            load_file("images/linux.iso", function(buffer)
+            {
+                settings.cdrom_disk = new SyncBuffer(buffer);
+                init(settings);
+            }, show_progress);
+
+            $("start_linux").blur();
+        };
+
+        $("start_koli").onclick = function()
+        {
+            load_file("images/kolibri.img", function(buffer)
+            {
+                settings.floppy_disk = new SyncBuffer(buffer);
+                init(settings);
+            }, show_progress);
+
+            $("start_koli").blur();
+        };
+
+        $("start_bsd").onclick = function()
+        {
+            load_file("images/openbsd.img", function(buffer)
+            {
+                settings.floppy_disk = new SyncBuffer(buffer);
+                init(settings);
+            }, show_progress);
+
+            $("start_bsd").blur();
+        };
+
+        if(DEBUG)
+        {
+            $("start_test").onclick = function()
+            {
+                settings.floppy_disk = new AsyncXHRBuffer("images/fd/freedos.part%d.img", 512, 720 * 1024);
+                init(settings);
+
+                //settings.bios = settings.vga_bios = undefined;
+                //settings.linux = {};
+                ////settings.linux.cmdline = "console=ttyS0 root=/dev/hda ro init=/sbin/init notsc=1 hdb=none"
+                //settings.linux.cmdline = "root=/dev/ram0 rw init=/sbin/init notsc=1";
+
+                //load_file("images/linux/vmlinux.bin", function(buffer)
+                //{
+                //    settings.linux.vmlinux = buffer;
+                //    load_file("images/linux/linuxstart.bin", function(buffer)
+                //    {
+                //        settings.linux.linuxstart = buffer;
+                //        load_file("images/linux/root.bin", function(buffer)
+                //        {
+                //            settings.linux.root = buffer;
+                //            init(settings);
+                //        });
+                //    });
+                //});
+            }
+        }
+    };
+
+    // works in firefox and chromium
+    if(document.readyState === "complete")
+    {
+        window.onload();
+    }
+
+
+    // load_external("https://dl.dropbox.com/example/freedos.img.js");
+    function load_external(url)
+    {
+        window["loaded"] = function(bin_image)
+        {
+            var buffer = new ArrayBuffer(bin_image.length),
+                buffer_array = new Uint8Array(buffer);
+
+            for(var i = 0; i < bin_image.length; i++)
+            {
+                buffer_array[i] = bin_image.charCodeAt(i);
+            }
+
+            window["loaded"] = function() { 
+                dbg_log("load_external: result loaded twice ?"); 
+            };
+        };
+
+        var script = document.createElement("script");
+        script.src = url;
+
+        document.body.appendChild(script);
+    }
+
+
+    function init(settings)
+    {
+        var cpu = new v86(),
+            screen_adapter = new ScreenAdapter();
+
+        $("boot_options").parentNode.removeChild($("boot_options"));
+        $("loading").style.display = "none";
+        $("runtime_options").style.display = "block";
+        document.getElementsByClassName("phone_keyboard")[0].style.display = "block";
+
+        if(DEBUG)
+        {
+            $("step").onclick = function()
+            { 
+                debug.step();
+            }
+
+            $("run_until").onclick = function()
+            {
+                debug.run_until();
+            };
+
+            $("debugger").onclick = function()
+            {
+                debug.debugger();
+            };
+        }
+
+        var running = true;
+
+        $("run").onclick = function()
+        {
+            if(running)
+            {
+                running_time += Date.now() - last_tick;
+                $("run").value = "Run";
+                cpu.stop();
+            }
+            else
+            {
+                $("run").value = "Pause";
+                cpu.run();
+                last_tick = Date.now();
+            }
+
+            running = !running;
+            $("run").blur();
+        };
+
+        var time = document.getElementById("running_time"),
+            ips = document.getElementById("speed"),
+            last_tick = Date.now(),
+            running_time = 0,
+            last_instr_counter = 0;
+
+        function update_info()
+        {
+            if(running)
+            {
+                var now = Date.now();
+
+                running_time += now - last_tick;
+                last_tick = now;
+
+                ips.textContent = (cpu.instr_counter - last_instr_counter) / 1000 | 0;
+                time.textContent = (running_time / 1000 | 0);
+
+                last_instr_counter = cpu.instr_counter;
+            }
+        }
+
+        setInterval(update_info, 1000);
+
+        $("reset").onclick = function()
+        {
+            cpu.restart();
+            $("reset").blur();
+        };
+
+        $("get_floppy").onclick = function()
+        {
+            var buffer = cpu.dev.fdc.buffer;
+
+            if(!buffer)
+            {
+                return;
+            }
+
+            buffer.get_buffer(function(b)
+            {
+                dump_file(b, "floppy.img");
+            });
+
+            $("get_floppy").blur();
+        };
+
+        $("ctrlaltdel").onclick = function()
+        {
+            var ps2 = cpu.dev.ps2;
+
+            ps2.kbd_send_code(0x1D); // ctrl
+            ps2.kbd_send_code(0x38); // alt
+            ps2.kbd_send_code(0x53); // delete
+
+            // break codes
+            ps2.kbd_send_code(0x1D | 0x80); 
+            ps2.kbd_send_code(0x38 | 0x80);
+            ps2.kbd_send_code(0x53 | 0x80);
+
+            $("ctrlaltdel").blur();
+        };
+
+        $("scale").onchange = function()
+        {
+            var n = parseFloat(this.value);
+
+            if(n || n > 0)
+            {
+                screen_adapter.set_scale(n, n);
+            }
+            else
+            {
+                this.value = "1";
+            }
+        };
+
+        $("fullscreen").onclick = function()
+        {
+            var elem = document.getElementById("screen_container"),
+
+                // bracket notation because otherwise they get renamed by closure compiler
+                fn = elem["requestFullScreen"] || 
+                    elem["webkitRequestFullscreen"] || 
+                    elem["mozRequestFullScreen"] || 
+                    elem["msRequestFullScreen"];
+
+            if(fn)
+            {
+                fn.call(elem);
+
+                // This is necessary, because otherwise chromium keyboard doesn't work anymore.
+                // Might (but doesn't seem to) break something else
+                document.getElementsByClassName("phone_keyboard")[0].focus();
+            }
+
+            lock_mouse(elem);
+        };
+
+        settings.screen_adapter = screen_adapter;
+        settings.keyboard_adapter = new KeyboardAdapter();
+        settings.mouse_adapter = new MouseAdapter();
+
+        cpu.init(settings);
+        cpu.run();
+    }
+
+})();

+ 132 - 0
src/browser/mouse.js

@@ -0,0 +1,132 @@
+
+/** @constructor */
+function MouseAdapter()
+{
+    /** @const */
+    var SPEED_FACTOR = .5;
+
+    var left_down = false,
+        right_down = false,
+        middle_down = false,
+
+        last_x = 0,
+        last_y = 0,
+
+        // callback to call on a mouse click
+        send_click,
+
+        // callback to call on a mouse move
+        send_delta,
+
+        mouse = this;
+
+    this.enabled = false;
+
+    function may_handle(e)
+    {
+        return mouse.enabled && 
+            (!e.target || e.type === "mousemove" || (e.target.nodeName !== "INPUT" && e.target.nodeName !== "TEXTAREA"));
+    }
+
+    this.destroy = function()
+    {
+        window.removeEventListener("mousemove", mousemove_handler, false);
+        document.removeEventListener("contextmenu", contextmenu_handler, false);
+        window.removeEventListener("mousedown", mousedown_handler, false);
+        window.removeEventListener("mouseup", mouseup_handler, false);
+    };
+
+    this.init = function(click_fn, delta_fn, wheel_fn)
+    {
+        this.destroy();
+
+        send_click = click_fn;
+        send_delta = delta_fn;
+
+        // TODO: wheel_fn
+
+        window.addEventListener("mousemove", mousemove_handler, false);
+        document.addEventListener("contextmenu", contextmenu_handler, false);
+        window.addEventListener("mousedown", mousedown_handler, false);
+        window.addEventListener("mouseup", mouseup_handler, false);
+    };
+
+    function mousemove_handler(e)
+    {
+        if(!may_handle(e))
+        {
+            return;
+        }
+
+        var delta_x, delta_y;
+
+        if(true)
+        {
+            delta_x = e["webkitMovementX"] || e["mozMovementX"] || 0;
+            delta_y = e["webkitMovementY"] || e["mozMovementY"] || 0;
+        }
+        else
+        {
+            // Fallback for other browsers?
+            delta_x = e.clientX - last_x;
+            delta_y = e.clientY - last_y;
+
+            last_x = e.clientX;
+            last_y = e.clientY;
+        }
+
+
+        delta_x = Math.roundInfinity(delta_x * SPEED_FACTOR);
+        delta_y = Math.roundInfinity(delta_y * SPEED_FACTOR);
+
+        send_delta(delta_x, -delta_y);
+
+    }
+
+    function contextmenu_handler(e)
+    {
+        if(may_handle(e))
+        {
+            e.preventDefault();
+        }
+    }
+
+    function mousedown_handler(e)
+    {
+        if(may_handle(e))
+        {
+            click_event(e, true);
+        }
+    }
+
+    function mouseup_handler(e)
+    {
+        if(may_handle(e))
+        {
+            click_event(e, false);
+        }
+    }
+
+    function click_event(e, down)
+    {
+        if(e.which === 1)
+        {
+            left_down = down;
+        }
+        else if(e.which === 2)
+        {
+            middle_down = down;
+        }
+        else if(e.which === 3)
+        {
+            right_down = down;
+        }
+        else
+        {
+            dbg_log("Unknown event.which: " + e.which, LOG_MOUSE);
+        }
+        send_click(left_down, middle_down, right_down);
+
+        e.preventDefault();
+    }
+}

+ 354 - 0
src/browser/screen.js

@@ -0,0 +1,354 @@
+"use strict";
+
+if(!window.requestAnimationFrame)
+{
+    window.requestAnimationFrame = 
+        window.mozRequestAnimationFrame || 
+        window.webkitRequestAnimationFrame;
+}
+
+
+/**
+ * Adapter to use visual screen in browsers (in constrast to node)
+ * @constructor
+ */
+function ScreenAdapter()
+{
+    var 
+        dom_target = document.body,
+        text_screen = document.getElementById("screen"),
+        graphic_screen = document.getElementById("vga"),
+        graphic_context = graphic_screen.getContext("2d"),
+        cursor_element = document.createElement("div"),
+
+
+        graphic_image_data,
+        graphic_buffer,
+
+        /** @type {number} */
+        cursor_row,
+
+        /** @type {number} */
+        cursor_col,
+
+        /** @type {number} */
+        scale_x = 1,
+
+        /** @type {number} */
+        scale_y = 1,
+
+        graphical_mode_width,
+
+        screen = this,
+
+        changed_rows,
+
+        did_redraw = true,
+
+        // Index 0: ASCII code
+        // Index 1: Background color
+        // Index 2: Foreground color
+        text_mode_data,
+
+        // number of columns
+        text_mode_width,
+
+        // number of rows
+        text_mode_height;
+
+
+
+    /**
+     * Charmaps that containt unicode sequences for the default dospage
+     * @const
+     */
+    var charmap_high = new Uint16Array([
+        0xC7, 0xFC, 0xE9, 0xE2, 0xE4, 0xE0, 0xE5, 0xE7,
+        0xEA, 0xEB, 0xE8, 0xEF, 0xEE, 0xEC, 0xC4, 0xC5, 
+        0xC9, 0xE6, 0xC6, 0xF4, 0xF6, 0xF2, 0xFB, 0xF9, 
+        0xFF, 0xD6, 0xDC, 0xA2, 0xA3, 0xA5, 0x20A7, 0x192,
+        0xE1, 0xED, 0xF3, 0xFA, 0xF1, 0xD1, 0xAA, 0xBA,
+        0xBF, 0x2310, 0xAC, 0xBD, 0xBC, 0xA1, 0xAB, 0xBB,
+        0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+        0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+        0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+        0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 
+        0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+        0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+        0x3B1, 0xDF, 0x393, 0x3C0, 0x3A3, 0x3C3, 0xB5, 0x3C4,
+        0x3A6, 0x398, 0x3A9, 0x3B4, 0x221E, 0x3C6, 0x3B5, 0x2229, 
+        0x2261, 0xB1, 0x2265, 0x2264, 0x2320, 0x2321, 0xF7,
+        0x2248, 0xB0, 0x2219, 0xB7, 0x221A, 0x207F, 0xB2, 0x25A0, 0xA0
+    ]);
+
+    /** @const */
+    var charmap_low = new Uint16Array([
+        0x20,   0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
+        0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
+        0x25BA, 0x25C4, 0x2195, 0x203C, 0xB6,   0xA7,   0x25AC, 0x21A8,
+        0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC
+    ]);
+
+    graphic_context["imageSmoothingEnabled"] = false;
+    graphic_context["mozImageSmoothingEnabled"] = false;
+    graphic_context["webkitImageSmoothingEnabled"] = false;
+
+    cursor_element.id = "cursor";
+    text_screen.style.display = "block";
+
+    graphic_screen.style.display = "none";
+
+    this.put_char = function(row, col, chr, bg_color, fg_color)
+    {
+        changed_rows[row] = 1;
+
+        var p = 3 * (row * text_mode_width + col);
+        text_mode_data[p] = chr;
+        text_mode_data[p + 1] = bg_color;
+        text_mode_data[p + 2] = fg_color;
+    };
+
+    this.timer_text = function()
+    {
+        if(!did_redraw)
+        {
+            return;
+        }
+        did_redraw = false;
+
+        requestAnimationFrame(update_text);
+    };
+
+    function update_text()
+    {
+        did_redraw = true;
+
+        for(var i = 0; i < text_mode_height; i++)
+        {
+            if(changed_rows[i])
+            {
+                screen.text_update_row(i);
+                changed_rows[i] = 0;
+            }
+        }
+    }
+
+    this.put_pixel = function(x, y, color)
+    {
+        var offset = y * graphical_mode_width + x << 2;
+
+        graphic_buffer[offset] = color >> 16 & 0xFF;
+        graphic_buffer[offset + 1] = color >> 8 & 0xFF;
+        graphic_buffer[offset + 2] = color & 0xFF;
+    };
+
+    this.put_pixel_linear = function(index, color)
+    {
+        // (addr ^ 3) - 1: Change BGR (svga) order to RGB (canvas)
+        graphic_buffer[(index ^ 3) - 1] = color;
+    };
+
+    this.timer_graphical = function()
+    {
+        if(!did_redraw)
+        {
+            return;
+        }
+        did_redraw = false;
+
+        requestAnimationFrame(function()
+        {
+            did_redraw = true;
+            graphic_context.putImageData(graphic_image_data, 0, 0);
+        });
+    };
+
+    this.destroy = function()
+    {
+        //dom_target.removeChild(text_screen);
+        //dom_target.removeChild(graphic_screen);
+    };
+
+    this.set_mode = function(graphical)
+    {
+        if(graphical)
+        {
+            text_screen.style.display = "none";
+            graphic_screen.style.display = "block";
+        }
+        else
+        {
+            text_screen.style.display = "block";
+            graphic_screen.style.display = "none";
+        }
+    };
+
+    this.clear_screen = function()
+    {
+        graphic_context.fillStyle = "#000";
+        graphic_context.fillRect(0, 0, graphic_screen.width, graphic_screen.height);
+    };
+
+    /**
+     * @param {number} cols 
+     * @param {number} rows
+     */
+    this.set_size_text = function(cols, rows)
+    {
+        changed_rows = new Int8Array(rows);
+        text_mode_data = new Int32Array(cols * rows * 3);
+
+        text_mode_width = cols;
+        text_mode_height = rows;
+
+        while(text_screen.firstChild)
+        {
+            text_screen.removeChild(text_screen.firstChild);
+        }
+
+        for(var i = 0; i < rows; i++)
+        {
+            text_screen.appendChild(document.createElement("div"));
+        }
+    };
+
+    this.set_size_graphical = function(width, height)
+    {
+        graphic_screen.style.display = "block";
+
+        graphic_screen.width = width;
+        graphic_screen.height = height;
+        
+        //graphic_screen.style.width = width * scale_x + "px";
+        //graphic_screen.style.height = height * scale_y + "px";
+
+        // Make sure to call this here, because pixels are transparent otherwise
+        screen.clear_screen();
+
+        graphic_image_data = graphic_context.getImageData(0, 0, width, height);
+        graphic_buffer = graphic_image_data.data;
+
+        graphical_mode_width = width;
+    };
+
+    this.set_scale = function(s_x, s_y)
+    {
+        scale_x = s_x;
+        scale_y = s_y;
+
+        elem_set_scale(graphic_screen, scale_x, scale_y);
+        elem_set_scale(text_screen, scale_x, scale_y);
+    };
+    this.set_scale(scale_x, scale_y);
+
+    function elem_set_scale(elem, scale_x, scale_y)
+    {
+        var scale_str = "";
+
+        scale_str += scale_x === 1 ? "" : " scaleX(" + scale_x + ")";
+        scale_str += scale_y === 1 ? "" : " scaleY(" + scale_y + ")";
+
+        elem.style.webkitTransform = elem.style.MozTransform = scale_str;
+    }
+
+    this.update_cursor_scanline = function(start, end)
+    {
+        if(start & 0x20)
+        {
+            cursor_element.style.display = "none";
+        }
+        else
+        {
+            cursor_element.style.display = "inline";
+
+            cursor_element.style.height = (end - start) + "px";
+            cursor_element.style.marginTop = start + "px";
+        }
+    };
+
+    this.update_cursor = function(row, col)
+    {
+        if(row !== cursor_row || col !== cursor_col)
+        {
+            changed_rows[row] = 1;
+            changed_rows[cursor_row] = 1;
+
+            cursor_row = row;
+            cursor_col = col;
+        }
+    };
+
+    this.text_update_row = function(row)
+    {
+        var offset = 3 * row * text_mode_width,
+            row_element, 
+            color_element, 
+            bg_color,
+            fg_color,
+            text;
+
+        row_element = document.createElement("div");
+
+        for(var i = 0; i < text_mode_width; )
+        {
+            color_element = document.createElement("span");
+
+            bg_color = text_mode_data[offset + 1];
+            fg_color = text_mode_data[offset + 2];
+
+            color_element.style.backgroundColor = "#" + h(bg_color, 6);
+            color_element.style.color = "#" + h(fg_color, 6);
+            
+            text = "";
+
+            // put characters of the same color in one element
+            while(i < text_mode_width 
+                    && text_mode_data[offset + 1] === bg_color
+                    && text_mode_data[offset + 2] === fg_color)
+            {
+                var ascii = text_mode_data[offset],
+                    chr;
+
+                // use of utf-8
+                if(ascii > 127)
+                {
+                    chr = String.fromCharCode(charmap_high[ascii - 0x80]);
+                }
+                else if(ascii < 32)
+                {
+                    chr = String.fromCharCode(charmap_low[ascii]);
+                }
+                else
+                {
+                    chr = String.fromCharCode(ascii);
+                }
+
+                text += chr;
+
+                i++;
+                offset += 3;
+
+                if(row === cursor_row)
+                {
+                    if(i === cursor_col)
+                    {
+                        // next row will be cursor
+                        // create new element
+                        break;
+                    }
+                    else if(i === cursor_col + 1)
+                    {
+                        // found the cursor
+                        row_element.appendChild(cursor_element);
+                        break;
+                    }
+                }
+            }
+
+            color_element.textContent = text;
+            row_element.appendChild(color_element);
+        }
+
+        text_screen.replaceChild(row_element, text_screen.childNodes[row]);
+    };
+}

+ 155 - 0
src/const.js

@@ -0,0 +1,155 @@
+
+/** @define {boolean} */            
+var DEBUG = true;
+
+
+var
+
+/** 
+ * @const 
+ * @type {number}
+ */
+memory_size = 1024 * 1024 * 64;
+
+var 
+
+/** @const */ LOG_ALL = -1,
+/** @const */ LOG_NONE = 0,
+
+/** @const */ LOG_OTHER =  0x00001,
+/** @const */ LOG_CPU =    0x00002,
+/** @const */ LOG_FPU =    0x00004,
+/** @const */ LOG_MEM =    0x00008,
+/** @const */ LOG_DMA =    0x00010,
+/** @const */ LOG_IO =     0x00020,
+/** @const */ LOG_PS2 =    0x00040,
+/** @const */ LOG_PIC =    0x00080,
+/** @const */ LOG_VGA =    0x00100,
+/** @const */ LOG_PIT =    0x00200,
+/** @const */ LOG_MOUSE =  0x00400,
+/** @const */ LOG_PCI =    0x00800,
+/** @const */ LOG_BIOS =   0x01000,
+/** @const */ LOG_CD =     0x02000,
+/** @const */ LOG_SERIAL = 0x04000,
+/** @const */ LOG_DISK =   0x08000,
+/** @const */ LOG_RTC =    0x10000,
+
+
+
+///** @const */ LOG_LEVEL = LOG_OTHER | LOG_PS2 | LOG_BIOS;
+///** @const */ LOG_LEVEL = LOG_PS2 | LOG_OTHER | LOG_IO;
+///** @const */ LOG_LEVEL = LOG_PS2;
+///** @const */ LOG_LEVEL = LOG_OTHER | LOG_CPU | LOG_BIOS;
+///** @const */ LOG_LEVEL = LOG_VGA | LOG_IO | LOG_BIOS | LOG_OTHER;
+///** @const */ LOG_LEVEL = LOG_FPU | LOG_OTHER;
+///** @const */ LOG_LEVEL = LOG_DMA | LOG_DISK | LOG_IO | LOG_PCI;
+///** @const */ LOG_LEVEL = LOG_DMA | LOG_DISK | LOG_PCI | LOG_CD | LOG_BIOS;
+/** @const */ LOG_LEVEL = LOG_ALL & ~LOG_DISK & ~LOG_DMA & ~LOG_VGA & ~LOG_PS2 & ~LOG_FPU;
+///** @const */ LOG_LEVEL = LOG_SERIAL | LOG_IO;
+///** @const */ LOG_LEVEL = LOG_PIT | LOG_RTC;
+///** @const */ LOG_LEVEL = 0;
+
+
+var
+/** @const */ TLB_SYSTEM_READ = 1,
+/** @const */ TLB_SYSTEM_WRITE = 2,
+/** @const */ TLB_USER_READ = 4,
+/** @const */ TLB_USER_WRITE = 8;
+
+
+var 
+
+
+// flags register bitflags
+/** @const */ flag_carry = 1,
+/** @const */ flag_parity = 4,
+/** @const */ flag_adjust = 16,
+/** @const */ flag_zero = 64,
+/** @const */ flag_sign = 128, 
+/** @const */ flag_trap = 256,
+/** @const */ flag_interrupt = 512,
+/** @const */ flag_direction = 1024,
+/** @const */ flag_overflow = 2048,
+/** @const */ flag_iopl = 1 << 12 | 1 << 13,
+/** @const */ flag_nt = 1 << 14,
+/** @const */ flag_vm = 1 << 17,
+
+/** 
+ * default values of unused flags bits
+ * @const
+ */
+flags_default = 1 << 1,
+
+/** 
+ * bitmask to select used flags bits
+ * @const
+ */
+flags_mask = 1 << 0 | 1 << 2 | 1 << 4 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 9
+                        | 1 << 10 | 1 << 11 | 1 << 12 | 1 << 13 | 1 << 14 | 
+                        1 << 16 | 1 << 17 | 1 << 18 | 1 << 19 | 1 << 20 | 1 << 21,
+
+/**
+ * all arithmetic flags
+ * @const
+ */
+flags_all = flag_carry | flag_parity | flag_adjust | flag_zero | flag_sign | flag_overflow,
+
+
+
+/**
+ * opsizes used by get flag functions
+ *
+ * @const
+ */
+OPSIZE_8 = 8,
+/** @const */
+OPSIZE_16 = 16,
+/** @const */
+OPSIZE_32 = 32,
+
+/** @const */
+PSE_ENABLED = 128,
+
+/** @const */ reg_eax = 0,
+/** @const */ reg_ecx = 1,
+/** @const */ reg_edx = 2,
+/** @const */ reg_ebx = 3,
+/** @const */ reg_esp = 4,
+/** @const */ reg_ebp = 5,
+/** @const */ reg_esi = 6,
+/** @const */ reg_edi = 7,
+
+/** @const */ reg_ax = 0,
+/** @const */ reg_cx = 2,
+/** @const */ reg_dx = 4,
+/** @const */ reg_bx = 6,
+/** @const */ reg_sp = 8,
+/** @const */ reg_bp = 10,
+/** @const */ reg_si = 12,
+/** @const */ reg_di = 14,
+
+/** @const */ reg_al = 0,
+/** @const */ reg_cl = 4,
+/** @const */ reg_dl = 8,
+/** @const */ reg_bl = 12,
+/** @const */ reg_ah = 1,
+/** @const */ reg_ch = 5,
+/** @const */ reg_dh = 9,
+/** @const */ reg_bh = 13,
+
+
+/** @const */ reg_es = 0,
+/** @const */ reg_cs = 1,
+/** @const */ reg_ss = 2,
+/** @const */ reg_ds = 3,
+/** @const */ reg_fs = 4,
+/** @const */ reg_gs = 5,
+/** @const */ reg_noseg = 6,
+
+
+
+/** @const */ LOOP_COUNTER = 20001,
+/** @const */ TIME_PER_FRAME = 33;
+
+
+

File diff suppressed because it is too large
+ 2344 - 0
src/cpu.js


+ 2384 - 0
src/cpu.macro.js

@@ -0,0 +1,2384 @@
+"use strict";
+
+#define read_imm16s() (read_imm16() << 16 >> 16)
+#define read_imm32() (read_imm32s() >>> 0)
+
+#define safe_read16s(addr) (safe_read16(addr) << 16 >> 16)
+#define safe_read32(addr) (safe_read32s(addr) >>> 0)
+
+var debug = {};
+
+
+/** @constructor */
+function v86()
+{
+
+var cpu = this;
+
+this.run = function() 
+{
+    if(!running)
+    {
+        cpu_run();
+    }
+}
+
+this.stop = cpu_stop;
+this.init = cpu_init;
+this.restart = cpu_restart;
+
+this.dev = {};
+
+this.instr_counter = 0;
+
+
+var
+    segment_is_null,
+    segment_offsets,
+    segment_limits,
+    segment_infos,
+
+    /*
+     * Translation Lookaside Buffer 
+     */
+    tlb_user_read,
+    tlb_user_write,
+    tlb_system_read,
+    tlb_system_write,
+
+    /*
+     * Information about which pages are cached in the tlb.
+     * By bit:
+     *   0 system, read
+     *   1 system, write
+     *   2 user, read
+     *   3 user, write
+     */
+    tlb_info,
+
+    /*
+     * Same as tlb_info, except it only contains global pages
+     */
+    tlb_info_global,
+
+    /** 
+     * Wheter or not in protected mode
+     * @type {boolean} 
+     */
+    protected_mode,
+
+    /** 
+     * interrupt descriptor table
+     * @type {number}
+     */
+    idtr_size,
+    /** @type {number} */
+    idtr_offset,
+
+    /** 
+     * global descriptor table register
+     * @type {number}
+     */
+    gdtr_size,
+    /** @type {number} */
+    gdtr_offset,
+
+    /** 
+     * local desciptor table
+     * @type {number}
+     */
+    ldtr_size,
+    /** @type {number} */
+    ldtr_offset,
+
+    /**
+     * task register 
+     * @type {number} 
+     */
+    tsr_size,
+    /** @type {number} */
+    tsr_offset,
+
+    /*
+     * whether or not a page fault occured
+     */
+    page_fault,
+
+    /** @type {number} */
+    cr0,
+    /** @type {number} */
+    cr2,
+    /** @type {number} */
+    cr3,
+    /** @type {number} */
+    cr4,
+
+    // current privilege level
+    /** @type {number} */
+    cpl,
+
+    // paging enabled
+    /** @type {boolean} */
+    paging,
+
+    // if false, pages are 4 KiB, else 4 Mib
+    /** @type {number} */
+    page_size_extensions,
+
+    // current operand/address/stack size
+    /** @type {boolean} */
+    is_32,
+    /** @type {boolean} */
+    operand_size_32,
+    /** @type {boolean} */
+    stack_size_32,
+
+    /** 
+     * Cycles since last cpu reset, used by rdtsc instruction
+     * @type {number}
+     */
+    cpu_timestamp_counter,
+
+    /** @type {number} */
+    previous_ip,
+
+    /** 
+     * wheter or not in step mode
+     * used for debugging
+     * @type {boolean}
+     */
+    step_mode,
+
+    /**
+     * was the last instruction a hlt
+     * @type {boolean}
+     */
+    in_hlt,
+
+    /** @type {VGAScreen} */
+    vga,
+
+    /** @type {PS2} */
+    ps2,
+
+    /** 
+     * Programmable interval timer
+     * @type {PIT}
+     */
+    timer,
+
+
+    /** 
+     * Real Time Clock
+     * @type {RTC}
+     */
+    rtc,
+
+    /**
+     * Floppy Disk controller
+     * @type {FloppyController}
+     */
+    fdc,
+
+    /**
+     * Serial controller
+     * @type {UART}
+     */
+    uart,
+
+    /** @type {boolean} */
+    running,
+
+    /** @type {boolean} */
+    stopped,
+
+    /** @type {number} */
+    loop_counter,
+
+    /** @type {Memory} */
+    memory,
+
+    /** @type {(FPU|NoFPU)} */
+    fpu,
+
+
+    /**
+     * Programmable interrupt controller
+     * @type {PIC}
+     */
+    pic,
+
+    /**
+     * @type {IO}
+     */
+    io,
+
+    /**
+     * @type {PCI}
+     */
+    pci,
+
+    /**
+     * @type {CDRom}
+     */
+    cdrom,
+
+    /**
+     * @type {HDD}
+     */
+    hda,
+
+    /**
+     * @type {HDD}
+     */
+    hdb,
+
+    /**
+     * Direct Memory Access Controller
+     * @type {DMA}
+     */
+    dma,
+
+    translate_address_read,
+    translate_address_write,
+
+    ops,
+
+    /** @type {boolean} */
+    address_size_32,
+
+    /** @type {number} */
+    instruction_pointer,
+
+    /** @type {number} */
+    last_virt_eip,
+
+    /** @type {number} */
+    eip_phys,
+
+    /** @type {number} */
+    last_virt_esp,
+
+    /** @type {number} */
+    esp_phys,
+
+
+    // current state of prefixes
+    segment_prefix,
+
+
+    /** @type {boolean} */
+    repeat_string_prefix,
+
+    /** @type {boolean} */
+    repeat_string_type,
+
+    /** @type {number} */
+    last_result,
+
+    /** @type {number} */
+    flags,
+
+    /** 
+     * bitmap of flags which are not updated in the flags variable
+     * changed by arithmetic instructions, so only relevant to arithmetic flags
+     * @type {number}
+     */
+    flags_changed,
+
+    /** 
+     * the last 2 operators and the result and size of the last arithmetic operation
+     * @type {number} 
+     */
+    last_op1,
+    /** @type {number} */
+    last_op2,
+    /** @type {number} */
+    last_op_size,
+
+
+    // registers
+    reg32,
+    reg32s,
+    reg16,
+    reg16s,
+    reg8,
+    reg8s,
+
+    sreg,
+
+    
+    // sp or esp, depending on stack size attribute
+    stack_reg,
+    reg_vsp,
+    reg_vbp,
+
+    // reg16 or reg32, depending on address size attribute
+    regv,
+    reg_vcx,
+    reg_vsi,
+    reg_vdi,
+
+    // functions that are set depending on whether paging is enabled or not
+    read_imm8,
+    read_imm8s,
+    read_imm16,
+    read_imm32s,
+
+    safe_read8,
+    safe_read8s,
+    safe_read16,
+    safe_read32s,
+
+    get_esp_read,
+    get_esp_write,
+
+
+    table,
+    table0F,
+
+    modrm_resolve,
+
+
+    current_settings
+;
+
+
+function cpu_run()
+{
+    if(stopped)
+    {
+        stopped = running = false;
+        return;
+    }
+
+    running = true;
+
+    try {
+        do_run();
+    }
+    catch(e)
+    {
+        if(e === 0xDEADBEE)
+        {
+            // A legit CPU exception (for instance, a page fault happened)
+            // call_interrupt_vector has already been called at this point,
+            // so we just need to reset some state
+
+            page_fault = false;
+            repeat_string_prefix = false;
+            segment_prefix = -1;
+            
+            address_size_32 = is_32;
+            update_address_size();
+            operand_size_32 = is_32;
+            update_operand_size();
+
+            cpu_run();
+        }
+        else
+        {
+            throw e;
+        }
+    }
+};
+
+function cpu_stop()
+{
+    if(running)
+    {
+        stopped = true;
+    }
+}
+
+function cpu_restart()
+{
+    var was_running = running;
+
+    stopped = true;
+    running = false;
+
+    setTimeout(function()
+    {
+        ps2.destroy();
+        vga.destroy();
+
+        cpu_init(current_settings);
+
+        if(was_running)
+        {
+            cpu_run();
+        }
+    }, 10);
+}
+
+function cpu_reboot_internal()
+{
+    dbg_assert(running);
+
+    ps2.destroy();
+    vga.destroy();
+
+    cpu_init(current_settings);
+
+    throw 0xDEADBEE;
+}
+
+function cpu_init(settings)
+{
+    // see browser/main.js or node/main.js
+    if(typeof set_tick !== "undefined")
+    {
+        set_tick(cpu_run);
+    }
+
+    current_settings = settings;
+
+    cpu.memory = memory = new Memory(new ArrayBuffer(memory_size), memory_size); 
+
+    segment_is_null = new Uint8Array(8);
+    segment_limits = new Uint32Array(8);
+    segment_infos = new Uint32Array(8);
+    segment_offsets = new Int32Array(8);
+
+    // 16 MB in total
+    tlb_user_read = new Int32Array(1 << 20);
+    tlb_user_write = new Int32Array(1 << 20);
+    tlb_system_read = new Int32Array(1 << 20);
+    tlb_system_write = new Int32Array(1 << 20);
+
+    tlb_info = new Uint8Array(1 << 20);
+    tlb_info_global = new Uint8Array(1 << 20);
+
+
+    reg32 = new Uint32Array(8);
+    reg32s = new Int32Array(reg32.buffer);
+    reg16 = new Uint16Array(reg32.buffer);
+    reg16s = new Int16Array(reg32.buffer);
+    reg8  = new Uint8Array(reg32.buffer);
+    reg8s  = new Int8Array(reg32.buffer);
+    sreg = new Uint16Array(8);
+    protected_mode = false;
+
+    idtr_size = 0;
+    idtr_offset = 0;
+
+    gdtr_size = 0;
+    gdtr_offset = 0;
+
+    ldtr_size = 0;
+    ldtr_offset = 0;
+
+    tsr_size = 0;
+    tsr_offset = 0;
+
+    page_fault = false;
+    cr0 = 0;
+    cr2 = 0;
+    cr3 = 0;
+    cr4 = 0;
+    cpl = 0;
+    paging = false;
+    page_size_extensions = 0;
+    is_32 = false;
+    operand_size_32 = false;
+    stack_size_32 = false;
+    address_size_32 = false;
+
+    paging_changed();
+
+    update_operand_size();
+    update_address_size();
+
+    stack_reg = reg16;
+    reg_vsp = reg_sp;
+    reg_vbp = reg_bp;
+
+    cpu_timestamp_counter = 0;
+    previous_ip = 0;
+    step_mode = false;
+    in_hlt = false;
+
+    running = false;
+    stopped = false;
+    loop_counter = 20;
+
+    translate_address_read = translate_address_disabled;
+    translate_address_write = translate_address_disabled;
+
+    segment_prefix = -1;
+    repeat_string_prefix = false;
+    last_result = 0;
+    flags = flags_default;
+    flags_changed = 0;
+    last_op1 = 0;
+    last_op2 = 0;
+    last_op_size = 0;
+
+
+
+    if(settings.bios)
+    {
+        // load bios
+        var data = new Uint8Array(settings.bios),
+            start = 0x100000 - settings.bios.byteLength;
+
+        for(var i = 0; i < settings.bios.byteLength; i++)
+        {
+            memory.mem8[start + i] = data[i];
+        }
+
+        if(settings.vga_bios)
+        {
+            // load vga bios
+            data = new Uint8Array(settings.vga_bios);
+
+            for(var i = 0; i < settings.vga_bios.byteLength; i++)
+            {
+                memory.mem8[0xC0000 + i] = data[i];
+            }
+        }
+
+        // ip initial value
+        instruction_pointer = 0xFFFF0;
+
+        // ss and sp inital value
+        switch_seg(reg_ss, 0x30);
+        reg16[reg_sp] = 0x100;
+    }
+    else if(settings.linux)
+    {
+        instruction_pointer = 0x10000;
+
+        memory.write_blob(new Uint8Array(settings.linux.vmlinux), 0x100000);
+        memory.write_blob(new Uint8Array(settings.linux.linuxstart), instruction_pointer);
+
+        if(settings.linux.root)
+        {
+            memory.write_blob(new Uint8Array(settings.linux.root), 0x00400000);
+            reg32[reg_ebx] = settings.linux.root.byteLength;
+        }
+
+        memory.write_string(settings.linux.cmdline, 0xF800);
+
+        reg32[reg_eax] = memory_size;
+        reg32[reg_ecx] = 0xF800;
+
+        switch_seg(reg_cs, 0);
+        switch_seg(reg_ss, 0);
+        switch_seg(reg_ds, 0);
+        switch_seg(reg_es, 0);
+        switch_seg(reg_gs, 0);
+        switch_seg(reg_fs, 0);
+
+        is_32 = true;
+        address_size_32 = true;
+        operand_size_32 = true;
+        stack_size_32 = true;
+        protected_mode = true;
+
+        update_operand_size();
+        update_address_size();
+
+        regv = reg32;
+        reg_vsp = reg_esp;
+        reg_vbp = reg_ebp;
+
+        cr0 = 1;
+    }
+    else
+    {
+        switch_seg(reg_ss, 0x30);
+        reg16[reg_sp] = 0x100;
+
+        instruction_pointer = 0;
+    }
+
+
+    cpu.dev = {};
+
+    if(settings.load_devices)
+    {
+        var devapi = {
+            memory: memory,   
+            reboot: cpu_reboot_internal,
+        };
+
+        devapi.io = cpu.dev.io = io = new IO();
+        devapi.pic = pic = new PIC(devapi, call_interrupt_vector, handle_irqs);
+        devapi.pci = pci = new PCI(devapi);
+        devapi.dma = dma = new DMA(devapi);
+ 
+
+        cpu.dev.vga = vga = new VGAScreen(devapi, settings.screen_adapter)
+        cpu.dev.ps2 = ps2 = new PS2(devapi, settings.keyboard_adapter, settings.mouse_adapter);
+        
+        //fpu = new NoFPU();
+        fpu = new FPU(devapi);
+
+        uart = new UART(devapi);
+
+        cpu.dev.fdc = fdc = new FloppyController(devapi, settings.floppy_disk);
+
+        if(settings.cdrom_disk)
+        {
+            cpu.dev.cdrom = cdrom = new CDRom(devapi, settings.cdrom_disk);
+        }
+
+        if(settings.hda_disk)
+        {
+            cpu.dev.hda = hda = new HDD(devapi, settings.hda_disk, 0);
+        }
+        if(settings.hdb_disk)
+        {
+            cpu.dev.hdb = hdb = new HDD(devapi, settings.hdb_disk, 1);
+        }
+
+        timer = new PIT(devapi);
+        rtc = new RTC(devapi, fdc.type);
+    }
+
+    if(DEBUG)
+    {
+        // used for debugging 
+        ops = new CircularQueue(30000);
+
+        if(typeof window !== "undefined")
+        {
+            window.memory = memory;
+            window.vga = vga;
+        }
+
+        if(io)
+        {
+            // write seabios debug output to console
+            var seabios_debug = "";
+
+            io.register_write(0x402, function(out_byte)
+            {
+                // seabios debug
+                //
+                if(out_byte === 10)
+                {
+                    dbg_log(seabios_debug, LOG_BIOS);
+                    seabios_debug = "";
+                }
+                else
+                {
+                    seabios_debug += String.fromCharCode(out_byte);
+                }
+            });
+        }
+    }
+}
+
+function do_run()
+{
+    var 
+        /** 
+         * @type {number}
+         */
+        now, 
+        start = Date.now();
+
+    vga.timer(start);
+
+    // outer loop:
+    // runs cycles + timers
+    for(var j = loop_counter; j--;)
+    {
+        // inner loop:
+        // runs only cycles
+        for(var k = LOOP_COUNTER; k--;)
+        {
+            previous_ip = instruction_pointer;
+
+            cycle();
+            cpu_timestamp_counter++;
+        }
+
+        now = Date.now();
+        timer.timer(now);
+        rtc.timer(now);
+    }
+
+    cpu.instr_counter += loop_counter * LOOP_COUNTER;
+
+    if(now - start > TIME_PER_FRAME)
+    {
+        loop_counter--;
+    }
+    else
+    {
+        loop_counter++;
+    }
+
+    next_tick();
+}
+
+// do_run must not be inlined into cpu_run, because then more code 
+// is in the deoptimized try-catch. 
+// This trick is a bit ugly, but it works without further complication.
+if(typeof window !== "undefined")
+{
+    window.__no_inline = do_run;
+}
+
+
+/**
+ * execute a single instruction cycle on the cpu
+ * this includes reading all prefixes and the whole instruction
+ */
+function cycle()
+{
+    var opcode = read_imm8();
+
+    logop(instruction_pointer - 1, opcode);
+
+    // call the instruction
+    table[opcode]();
+
+    // TODO
+    //if(flags & flag_trap)
+    //{
+    //    
+    //}
+}
+
+cpu.cycle = function()
+{
+    table[read_imm8()]();
+}
+
+function cr0_changed()
+{
+    //protected_mode = (cr0 & 1) === 1;
+    //dbg_log("cr0 = " + h(cr0));
+
+    var new_paging = (cr0 & 0x80000000) !== 0;
+
+    if(fpu.is_fpu)
+    {
+        cr0 &= ~4;
+    }
+    else
+    {
+        cr0 |= 4;
+    }
+
+    if(new_paging !== paging)
+    {
+        paging = new_paging;
+
+        paging_changed();
+    }
+}
+
+function paging_changed()
+{
+    var table = paging ? pe_functions : npe_functions;
+
+    read_imm8 = table.read_imm8;
+    read_imm8s = table.read_imm8s;
+    read_imm16 = table.read_imm16;
+    read_imm32s = table.read_imm32s;
+    
+    safe_read8 = table.safe_read8;
+    safe_read8s = table.safe_read8s;
+    safe_read16 = table.safe_read16;
+    safe_read32s = table.safe_read32s;
+
+    get_esp_read = table.get_esp_read;
+    get_esp_write = table.get_esp_write;
+
+
+    // set translate_address_* depending on cpl and paging
+    cpl_changed();
+}
+
+function cpl_changed()
+{
+    last_virt_eip = -1;
+    last_virt_esp = -1;
+
+    if(!paging)
+    {
+        translate_address_write = translate_address_disabled;
+        translate_address_read = translate_address_disabled;
+    }
+    else if(cpl)
+    {
+        translate_address_write = translate_address_user_write;
+        translate_address_read = translate_address_user_read;
+    }
+    else
+    {
+        translate_address_write = translate_address_system_write;
+        translate_address_read = translate_address_system_read;
+    }
+}
+
+
+// functions that are used when paging is disabled
+var npe_functions = {
+    get_esp_read: get_esp_npe,
+    get_esp_write: get_esp_npe,
+
+    read_imm8: function()
+    {
+        return memory.mem8[instruction_pointer++];
+    },
+
+    read_imm8s: function()
+    {
+        return memory.mem8s[instruction_pointer++];
+    },
+
+    read_imm16 : function()
+    {
+        var data16 = memory.read16(instruction_pointer);
+        instruction_pointer = instruction_pointer + 2 | 0;
+        return data16;
+    },
+
+    read_imm32s : function()
+    {
+        var data32 = memory.read32s(instruction_pointer);
+        instruction_pointer = instruction_pointer + 4 | 0;
+        return data32;
+    },
+
+    safe_read8 : function(addr) { return memory.read8(addr) },
+    safe_read8s : function(addr) { return memory.read8s(addr); },
+    safe_read16 : function(addr) { return memory.read16(addr); },
+    safe_read32s : function(addr) { return memory.read32s(addr); },
+};
+
+// functions that are used when paging is enabled
+var pe_functions = 
+{
+    get_esp_read: get_esp_pe_read,
+    get_esp_write: get_esp_pe_write,
+
+    read_imm8 : function()
+    {
+        if((instruction_pointer & ~0xFFF) ^ last_virt_eip)
+        {
+            eip_phys = translate_address_read(instruction_pointer) ^ instruction_pointer;
+            last_virt_eip = instruction_pointer & ~0xFFF;
+        }
+
+        // memory.read8 inlined under the assumption that code never runs in 
+        // memory-mapped io
+        return memory.mem8[eip_phys ^ instruction_pointer++];
+    },
+
+    read_imm8s : function()
+    {
+        if((instruction_pointer & ~0xFFF) ^ last_virt_eip)
+        {
+            eip_phys = translate_address_read(instruction_pointer) ^ instruction_pointer;
+            last_virt_eip = instruction_pointer & ~0xFFF;
+        }
+
+        return memory.mem8s[eip_phys ^ instruction_pointer++];
+    },
+
+    read_imm16 : function()
+    {
+        // Two checks in one comparison:
+        //    1. Did the high 20 bits of eip change
+        // or 2. Are the low 12 bits of eip 0xFFF (and this read crosses a page boundary)
+        if((instruction_pointer ^ last_virt_eip) > 0xFFE)
+        {
+            return read_imm8() | read_imm8() << 8;
+        }
+
+        var data16 = memory.read16(eip_phys ^ instruction_pointer);
+        instruction_pointer = instruction_pointer + 2 | 0;
+
+        return data16;
+    },
+
+
+    read_imm32s : function()
+    {
+        // Analogue to the above comment
+        if((instruction_pointer ^ last_virt_eip) > 0xFFC)
+        {
+            return read_imm16() | read_imm16() << 16;
+        }
+
+        var data32 = memory.read32s(eip_phys ^ instruction_pointer);
+        instruction_pointer = instruction_pointer + 4 | 0;
+
+        return data32;
+    },
+
+    safe_read8 : do_safe_read8,
+    safe_read8s : do_safe_read8s,
+    safe_read16 : do_safe_read16,
+    safe_read32s : do_safe_read32s,
+};
+
+
+// read word from a page boundary, given 2 physical addresses
+function virt_boundary_read16(low, high)
+{
+    dbg_assert((low & 0xFFF) === 0xFFF);
+    dbg_assert((high & 0xFFF) === 0);
+
+    return memory.read8(low) | memory.read8(high) << 8;
+}
+
+// read doubleword from a page boundary, given 2 addresses
+function virt_boundary_read32s(low, high)
+{
+    dbg_assert((low & 0xFFF) >= 0xFFD);
+    dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF));
+
+    var result = memory.read8(low) | memory.read8(high) << 24;
+
+    if(low & 1)
+    {
+        if(low & 2)
+        {
+            // 0xFFF
+            result |= memory.read8(high - 2) << 8 | 
+                        memory.read8(high - 1) << 16;
+        }
+        else
+        {
+            // 0xFFD
+            result |= memory.read8(low + 1) << 8 | 
+                        memory.read8(low + 2) << 16;
+        }
+    }
+    else
+    {
+        // 0xFFE
+        result |= memory.read8(low + 1) << 8 | 
+                    memory.read8(high - 1) << 16;
+    }
+
+    return result;
+}
+
+function virt_boundary_write16(low, high, value)
+{
+    dbg_assert((low & 0xFFF) === 0xFFF);
+    dbg_assert((high & 0xFFF) === 0);
+
+    memory.write8(low, value);
+    memory.write8(high, value >> 8);
+}
+
+function virt_boundary_write32(low, high, value)
+{
+    dbg_assert((low & 0xFFF) >= 0xFFD);
+    dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF));
+
+    memory.write8(low, value);
+    memory.write8(high, value >> 24);
+
+    if(low & 1)
+    {
+        if(low & 2)
+        {
+            // 0xFFF
+            memory.write8(high - 2, value >> 8);
+            memory.write8(high - 1, value >> 16);
+        }
+        else
+        {
+            // 0xFFD
+            memory.write8(low + 1, value >> 8);
+            memory.write8(low + 2, value >> 16);
+        }
+    }
+    else
+    {
+        // 0xFFE
+        memory.write8(low + 1, value >> 8);
+        memory.write8(high - 1, value >> 16);
+    }
+}
+
+// safe_read, safe_write
+// read or write byte, word or dword to the given *virtual* address,
+// and be safe on page boundaries
+
+function do_safe_read8(addr)
+{
+    return memory.read8(translate_address_read(addr));
+}
+
+function do_safe_read8s(addr)
+{
+    return memory.read8s(translate_address_read(addr));
+}
+
+function do_safe_read16(addr)
+{
+    if((addr & 0xFFF) === 0xFFF)
+    {
+        return safe_read8(addr) | safe_read8(addr + 1) << 8;
+    }
+    else
+    {
+        return memory.read16(translate_address_read(addr));
+    }
+}
+
+function do_safe_read32s(addr)
+{
+    if((addr & 0xFFF) >= 0xFFD)
+    {
+        return safe_read16(addr) | safe_read16(addr + 2) << 16;
+    }
+    else
+    {
+        return memory.read32s(translate_address_read(addr));
+    }
+}
+
+function safe_write8(addr, value)
+{
+    memory.write8(translate_address_write(addr), value);
+}
+
+function safe_write16(addr, value)
+{
+    var phys_low = translate_address_write(addr);
+
+    if((addr & 0xFFF) === 0xFFF)
+    {
+        virt_boundary_write16(phys_low, translate_address_write(addr + 1), value);
+    }
+    else
+    {
+        memory.write16(phys_low, value);
+    }
+}
+
+function safe_write32(addr, value)
+{
+    var phys_low = translate_address_write(addr);
+
+    if((addr & 0xFFF) >= 0xFFD)
+    {
+        virt_boundary_write32(phys_low, translate_address_write(addr + 3), value);
+    }
+    else
+    {
+        memory.write32(phys_low, value);
+    }
+}
+
+// read 2 or 4 byte from ip, depending on address size attribute
+function read_moffs()
+{
+    if(address_size_32)
+    {
+        return get_seg_prefix(reg_ds) + read_imm32s();
+    }
+    else
+    {
+        return get_seg_prefix(reg_ds) + read_imm16();
+    }
+}
+
+function get_flags()
+{
+    return (flags & ~flags_all) | getcf() | getpf() | getaf() | getzf() | getsf() | getof();
+}
+
+function load_flags()
+{
+    flags = get_flags();
+    flags_changed = 0;
+}
+
+// get esp with paging disabled
+function get_esp_npe(mod)
+{
+    if(stack_size_32)
+    {
+        return get_seg(reg_ss) + stack_reg[reg_vsp] + mod;
+    }
+    else
+    {
+        return get_seg(reg_ss) + (stack_reg[reg_vsp] + mod & 0xFFFF);
+    }
+}
+
+function get_esp_pe_read(mod)
+{
+    // UNSAFE: stack_reg[reg_vsp]+mod needs to be masked in 16 bit mode 
+    //   (only if paging is enabled and in 16 bit mode)
+
+    return translate_address_read(get_seg(reg_ss) + stack_reg[reg_vsp] + mod);
+}
+
+function get_esp_pe_write(mod)
+{
+    return translate_address_write(get_seg(reg_ss) + stack_reg[reg_vsp] + mod);
+}
+
+
+/*
+ * returns the "real" instruction pointer, 
+ * without segment offset
+ */
+function get_real_ip()
+{
+    return instruction_pointer - get_seg(reg_cs);
+}
+
+function call_interrupt_vector(interrupt_nr, is_software_int, error_code)
+{
+    if(DEBUG)
+    {
+        ops.add(instruction_pointer);
+        ops.add("-- INT " + h(interrupt_nr));
+        ops.add(1);
+    }
+
+    //if(interrupt_nr == 0x13)
+    //{
+    //    dbg_log("INT 13");
+    //    dbg_log(memory.read8(ch) + "/" + memory.read8(dh) + "/" + memory.read8(cl) + "   |" + memory.read8(al));
+    //    dbg_log("=> ", h(memory.read16(es) * 16 + memory.read16(bx)));
+    //}
+
+
+    //if(interrupt_nr == 0x10)
+    //{
+    //    dbg_log("int10 ax=" + h(reg16[reg_ax], 4) + " '" + String.fromCharCode(reg8[reg_al]) + "'"); 
+    //    dump_regs_short();
+    //    if(reg8[reg_ah] == 0xe) vga.tt_write(reg8[reg_al]);
+    //}
+
+    //dbg_log("int " + h(interrupt_nr));
+
+    //if(interrupt_nr === 0x13)
+    //{
+    //    dump_regs_short();
+    //}
+
+    //if(interrupt_nr === 0x80)
+    //{
+    //    dbg_log("linux syscall");
+    //    dump_regs_short();
+    //}
+
+
+    if(interrupt_nr === 14)
+    {
+        dbg_log("int14 error_code=" + error_code + " cr2=" + h(cr2) + " prev=" + h(previous_ip) + " cpl=" + cpl, LOG_CPU);
+    }
+
+
+    if(in_hlt)
+    {
+        // return to the instruction following the hlt
+        instruction_pointer++;
+        in_hlt = false;
+    }
+
+    if(protected_mode)
+    {
+        if((interrupt_nr << 3 | 7) > idtr_size)
+        {
+            dbg_log(interrupt_nr, LOG_CPU);
+            dbg_trace();
+            throw unimpl("#GP handler");
+        }
+
+
+        var addr = idtr_offset + (interrupt_nr << 3) | 0;
+        dbg_assert((addr & 0xFFF) < 0xFF8);
+
+        if(paging)
+        {
+            addr = translate_address_system_read(addr);
+        }
+
+        var base = memory.read16(addr) | memory.read16(addr + 6) << 16,
+            selector = memory.read16(addr + 2),
+            type = memory.read8(addr + 5),
+            dpl = type >> 5 & 3,
+            is_trap;
+
+        if((type & 128) === 0)
+        {
+            // present bit not set
+            throw unimpl("#NP handler");
+        }
+
+        if(is_software_int && dpl < cpl)
+        {
+            trigger_gp(interrupt_nr << 3 | 2);
+        }
+
+        type &= 31;
+        
+        if(type === 14)
+        {
+            is_trap = false;
+        }
+        else if(type === 15)
+        {
+            is_trap = true;
+        }
+        else if(type === 5)
+        {
+            throw unimpl("call int to task gate");
+        }
+        else if(type === 6)
+        {
+            throw unimpl("16 bit interrupt gate");
+        }
+        else if(type === 7)
+        {
+            throw unimpl("16 bit trap gate");
+        }
+        else
+        {
+            // invalid type
+            dbg_trace();
+            dbg_log("invalid type: " + h(type));
+            dbg_log(h(addr) + " " + h(base) + " " + h(selector));
+            throw unimpl("#GP handler");
+        }
+
+        var info = lookup_segment_selector(selector);
+
+        if(info.is_null)
+        {
+            dbg_log("is null");
+            throw unimpl("#GP handler");
+        }
+        if(info === -1)
+        {
+            dbg_log("is -1");
+            throw unimpl("#GP handler");
+        }
+        if(!info.is_executable || info.dpl > cpl)
+        {
+            dbg_log("not exec");
+            throw unimpl("#GP handler");
+        }
+        if(!info.is_present)
+        {
+            dbg_log("not present");
+            throw unimpl("#NP handler");
+        }
+
+        if(flags & flag_vm)
+        {
+            throw unimpl("VM flag");
+        }
+            
+
+        if(!info.dc_bit && info.dpl < cpl)
+        {
+            // inter privilege level interrupt
+
+            var tss_stack_addr = (info.dpl << 3) + 4;
+
+            if(tss_stack_addr + 5 > tsr_size)
+            {
+                throw unimpl("#TS handler");
+            }
+
+            tss_stack_addr += tsr_offset;
+            
+            if(paging)
+            {
+                tss_stack_addr = translate_address_system_read(tss_stack_addr);
+            }
+
+            var new_esp = memory.read32s(tss_stack_addr),
+                new_ss = memory.read16(tss_stack_addr + 4),
+                ss_info = lookup_segment_selector(new_ss);
+
+            if(ss_info.is_null)
+            {
+                throw unimpl("#TS handler");
+            }
+            if(ss_info.rpl !== info.dpl)
+            {
+                throw unimpl("#TS handler");
+            }
+            if(ss_info.dpl !== info.dpl || !ss_info.rw_bit)
+            {
+                throw unimpl("#TS handler");
+            }
+            if(!ss_info.is_present)
+            {
+                throw unimpl("#TS handler");
+            }
+
+            var old_esp = reg32s[reg_esp],
+                old_ss = sreg[reg_ss];
+
+            reg32[reg_esp] = new_esp;
+            sreg[reg_ss] = new_ss;
+
+            cpl = info.dpl;
+            //dbg_log("int" + h(interrupt_nr, 2) +" from=" + h(instruction_pointer, 8) 
+            //        + " cpl=" + cpl + " old ss:esp=" + h(old_ss,4) + ":" + h(old_esp,8), LOG_CPU);
+
+            cpl_changed();
+
+            push32(old_ss);
+            push32(old_esp);
+        }
+        else if(info.dc_bit || info.dpl === cpl)
+        {
+            // intra privilege level interrupt
+
+            //dbg_log("int" + h(interrupt_nr, 2) +" from=" + h(instruction_pointer, 8), LOG_CPU);
+        }
+
+
+        load_flags();
+        push32(flags);
+
+        push32(sreg[reg_cs]);
+        push32(get_real_ip());
+        //dbg_log("pushed eip to " + h(reg32[reg_esp], 8), LOG_CPU);
+
+
+        if(error_code !== false)
+        {
+            dbg_assert(typeof error_code == "number");
+            push32(error_code);
+        }
+        
+
+        // TODO
+        sreg[reg_cs] = selector;
+        //switch_seg(reg_cs);
+
+        //dbg_log("current esp: " + h(reg32[reg_esp]), LOG_CPU);
+        //dbg_log("call int " + h(interrupt_nr) + " from " + h(instruction_pointer) + " to " + h(base) + " with error_code=" + error_code, LOG_CPU);
+
+        instruction_pointer = get_seg(reg_cs) + base | 0;
+        
+        //dbg_log("int" + h(interrupt_nr) + " trap=" + is_trap + " if=" + +!!(flags & flag_interrupt));
+    
+        if(!is_trap)
+        {
+            // clear int flag for interrupt gates
+            flags &= ~flag_interrupt;
+        }
+        else
+        {
+            handle_irqs();
+        }
+    }
+    else
+    {
+        // call 4 byte cs:ip interrupt vector from ivt at memory 0
+        
+        //logop(instruction_pointer, "callu " + h(interrupt_nr) + "." + h(memory.read8(ah)));
+        //dbg_log("callu " + h(interrupt_nr) + "." + h(memory.read8(ah)) + " at " + h(instruction_pointer, 8), LOG_CPU, LOG_CPU);
+
+        // push flags, cs:ip
+        load_flags();
+        push16(flags);
+        push16(sreg[reg_cs]);
+        push16(get_real_ip());
+
+        flags = flags & ~flag_interrupt;
+
+        switch_seg(reg_cs, memory.read16((interrupt_nr << 2) + 2));
+        instruction_pointer = get_seg(reg_cs) + memory.read16(interrupt_nr << 2) | 0;
+    }
+}
+
+// assumes ip to point to the byte before the next instruction
+function raise_exception(interrupt_nr)
+{
+    if(DEBUG)
+    {
+        // warn about error
+        dbg_log("Exception " + h(interrupt_nr), LOG_CPU);
+        dbg_trace();
+        //throw "exception: " + interrupt_nr;
+    }
+
+
+    // TODO
+
+    call_interrupt_vector(interrupt_nr, false, false);
+    throw 0xDEADBEE;
+}
+
+function raise_exception_with_code(interrupt_nr, error_code)
+{
+    if(DEBUG)
+    {
+        dbg_log("Exception " + h(interrupt_nr) + " err=" + h(error_code), LOG_CPU);
+        dbg_trace();
+        //throw "exception: " + interrupt_nr;
+    }
+
+    call_interrupt_vector(interrupt_nr, false, error_code);
+    throw 0xDEADBEE;
+}
+
+function trigger_de()
+{
+    instruction_pointer = previous_ip;
+    raise_exception(0);
+}
+
+function trigger_ud()
+{
+    instruction_pointer = previous_ip;
+    raise_exception(6);
+}
+
+function trigger_gp(code)
+{
+    instruction_pointer = previous_ip;
+    raise_exception_with_code(13, code);
+}
+
+function trigger_np(code)
+{
+    instruction_pointer = previous_ip;
+    raise_exception_with_code(11, code);
+}
+
+function trigger_ss(code)
+{
+    instruction_pointer = previous_ip;
+    raise_exception_with_code(12, code);
+}
+
+
+/**
+ * @param {number} seg
+ */
+function seg_prefix(seg)
+{
+    dbg_assert(segment_prefix === -1);
+    dbg_assert(seg >= 0 && seg <= 5);
+
+    segment_prefix = seg;
+    table[read_imm8()]();
+    segment_prefix = -1;
+}
+
+/**
+ * Get segment base by prefix or default
+ * @param {number} default_segment
+ */
+function get_seg_prefix(default_segment /*, offset*/)
+{
+    if(segment_prefix === -1)
+    {
+        return get_seg(default_segment /*, offset*/);
+    }
+    else
+    {
+        return get_seg(segment_prefix /*, offset*/);
+    }
+}
+
+/**
+ * Get segment base
+ * @param {number} segment
+ */
+function get_seg(segment /*, offset*/)
+{
+    dbg_assert(segment >= 0 && segment < 8);
+    dbg_assert(protected_mode || (sreg[segment] << 4) == segment_offsets[segment]);
+    
+    if(protected_mode)
+    {
+        if(segment_is_null[segment])
+        {
+            // trying to access null segment
+            if(DEBUG)
+            {
+                dbg_log("Load null segment: " + h(segment), LOG_CPU);
+                throw unimpl("#GP handler");
+            }
+        }
+
+        // TODO: 
+        // - validate segment limits
+        // - validate if segment is writable
+        // - set accessed bit
+    }
+
+    return segment_offsets[segment];
+}
+
+function arpl(seg, r16)
+{
+    flags_changed &= ~flag_zero;
+
+    if((seg & 3) < (reg16[r16] & 3))
+    {
+        flags |= flag_zero;
+        return seg & ~3 | reg16[r16] & 3;
+    }
+    else
+    {
+        flags &= ~flag_zero;
+        return seg;
+    }
+}
+
+
+function handle_irqs()
+{
+    if(pic)
+    {
+        if((flags & flag_interrupt) && !page_fault)
+        {
+            pic.handle_irqs();
+        }
+    }
+}
+
+// any two consecutive 8-bit ports can be treated as a 16-bit port;
+// and four consecutive 8-bit ports can be treated as a 32-bit port
+//
+// http://css.csail.mit.edu/6.858/2012/readings/i386/s08_01.htm
+
+function out8(port_addr, out_byte)
+{
+    if(privileges_for_io())
+    {
+        io.port_write(port_addr, out_byte);
+    }
+    else
+    {
+        trigger_gp(0);
+    }
+}
+
+function out16(port_addr, out_word)
+{
+    if(privileges_for_io())
+    {
+        io.port_write(port_addr, out_word & 0xFF);
+        io.port_write(port_addr + 1, out_word >> 8 & 0xFF);
+    }
+    else
+    {
+        trigger_gp(0);
+    }
+}
+
+function out32(port_addr, out_dword)
+{
+    if(privileges_for_io())
+    {
+        io.port_write(port_addr, out_dword & 0xFF);
+        io.port_write(port_addr + 1, out_dword >> 8 & 0xFF);
+        io.port_write(port_addr + 2, out_dword >> 16 & 0xFF);
+        io.port_write(port_addr + 3, out_dword >> 24 & 0xFF);
+    }
+    else
+    {
+        trigger_gp(0);
+    }
+}
+
+function in8(port_addr)
+{
+    if(privileges_for_io())
+    {
+        return io.port_read(port_addr);
+    }
+    else
+    {
+        trigger_gp(0);
+    }
+}
+
+function in16(port_addr)
+{
+    if(privileges_for_io())
+    {
+        return io.port_read(port_addr) | 
+                io.port_read(port_addr + 1) << 8;
+    }
+    else
+    {
+        trigger_gp(0);
+    }
+}
+
+function in32(port_addr)
+{
+    if(privileges_for_io())
+    {
+        return io.port_read(port_addr) |
+                io.port_read(port_addr + 1) << 8 | 
+                io.port_read(port_addr + 2) << 16 |
+                io.port_read(port_addr + 3) << 24;
+    }
+    else
+    {
+        trigger_gp(0);
+    }
+}
+
+
+/**
+ * returns the current iopl from the eflags register
+ */
+function getiopl()
+{
+    return flags >> 12 & 3;
+}
+
+function privileges_for_io()
+{
+    return !protected_mode || cpl <= getiopl();
+}
+
+function cpuid()
+{
+    // cpuid
+    // TODO: Fill in with less bogus values
+    
+    // http://lxr.linux.no/linux+%2a/arch/x86/include/asm/cpufeature.h
+    
+    var id = reg32s[reg_eax];
+    
+    if((id & 0x7FFFFFFF) === 0)
+    {
+        reg32[reg_eax] = 2;
+
+        if(id === 0)
+        {
+            reg32[reg_ebx] = 0x756E6547; // Genu
+            reg32[reg_edx] = 0x49656E69; // ineI
+            reg32[reg_ecx] = 0x6C65746E; // ntel
+        }
+    }
+    else if(id === 1)
+    {
+        // pentium
+        reg32[reg_eax] = 0x513;
+        reg32[reg_ebx] = 0;
+        reg32[reg_ecx] = 0;
+        reg32[reg_edx] = fpu.is_fpu | 1 << 3 | 1 << 4 | 1 << 8| 1 << 13 | 1 << 15;
+    }
+    else if(id === 2)
+    {
+        // Taken from http://siyobik.info.gf/main/reference/instruction/CPUID
+        reg32[reg_eax] = 0x665B5001;
+        reg32[reg_ebx] = 0;
+        reg32[reg_ecx] = 0;
+        reg32[reg_edx] = 0x007A7000;
+    }
+    else if(id === (0x80860000 | 0))
+    {
+        reg32[reg_eax] = 0;
+        reg32[reg_ebx] = 0;
+        reg32[reg_ecx] = 0;
+        reg32[reg_edx] = 0;
+    }
+    else if((id & 0xF0000000) === ~~0x40000000)
+    {
+        // Invalid
+    }
+    else
+    {
+        if(DEBUG) throw "cpuid: unimplemented eax: " + h(id);
+    }
+}
+
+/**
+ * Update the flags register depending on iopl and cpl
+ */
+function update_flags(new_flags)
+{
+    if(cpl === 0 || !protected_mode)
+    {
+        // can update all flags
+        flags = new_flags;
+    }
+    else if(cpl <= getiopl())
+    {
+        // cpl != 0 and iopl <= cpl
+        // can update interrupt flag but not iopl
+        flags = (new_flags & ~flag_iopl) | (flags & flag_iopl);
+    }
+    else
+    {
+        // cannot update interrupt flag or iopl
+        flags = (new_flags & ~flag_iopl & ~flag_interrupt) | (flags & (flag_iopl | flag_interrupt));
+    }
+
+    flags_changed = 0;
+    //flags = (flags & flags_mask) | flags_default;
+}
+
+function update_operand_size()
+{
+    if(operand_size_32)
+    {
+        table = table32;
+        table0F = table0F_32;
+    }
+    else
+    {
+        table = table16;
+        table0F = table0F_16;
+    }
+}
+
+function update_address_size()
+{
+    if(address_size_32)
+    {
+        modrm_resolve = modrm_resolve32;
+
+        regv = reg32;
+        reg_vcx = reg_ecx;
+        reg_vsi = reg_esi;
+        reg_vdi = reg_edi;
+    }
+    else
+    {
+        modrm_resolve = modrm_resolve16;
+
+        regv = reg16;
+        reg_vcx = reg_cx;
+        reg_vsi = reg_si;
+        reg_vdi = reg_di;
+    }
+}
+
+/**
+ * @param {number} selector
+ */
+function lookup_segment_selector(selector)
+{
+    var is_gdt = (selector & 4) === 0,
+        selector_offset = selector & ~7,
+        info,
+        table_offset,
+        table_limit;
+
+    info = {
+        rpl: selector & 3,
+        from_gdt: is_gdt,
+        is_null: false,
+        is_valid: true,
+    };
+
+    if(is_gdt)
+    {
+        table_offset = gdtr_offset;
+        table_limit = gdtr_size;
+    }
+    else
+    {
+        table_offset = ldtr_offset
+        table_limit = ldtr_size;
+    }
+
+    if(selector_offset === 0)
+    {
+        info.is_null = true;
+        return info;
+    }
+
+    // limit is the number of entries in the table minus one
+    if((selector_offset >> 3) > table_limit)
+    {
+        info.is_valid = false;
+        return info;
+    }
+
+    table_offset += selector_offset;
+
+    if(paging)
+    {
+        table_offset = translate_address_system_read(table_offset);
+    }
+
+    info.base = memory.read16(table_offset + 2) | memory.read8(table_offset + 4) << 16 | 
+            memory.read8(table_offset + 7) << 24,
+    info.access = memory.read8(table_offset + 5),
+    info.flags = memory.read8(table_offset + 6) >> 4,
+    info.limit = memory.read16(table_offset) | (memory.read8(table_offset + 6) & 0xF) << 16,
+
+    // used if system
+    info.type = info.access & 0xF;
+
+    info.dpl = info.access >> 5 & 3;
+
+    info.is_system = (info.access & 0x10) === 0;
+    info.is_present = (info.access & 0x80) === 0x80;
+    info.is_executable = (info.access & 8) === 8;
+
+    info.rw_bit = (info.access & 2) === 2;
+    info.dc_bit = (info.access & 4) === 4;
+
+    info.size = (info.flags & 4) === 4;
+    info.granularity = (info.flags & 8) === 8;
+
+
+    if(info.gr_bit)
+    {
+        info.real_limit = (info.limit << 12 | 0xFFF) >>> 0;
+    }
+    else
+    {
+        info.real_limit = info.limit;
+    }
+
+    info.is_writable = info.rw_bit && !info.is_executable;
+    info.is_readable = info.rw_bit || !info.is_executable;
+
+    return info;
+}
+
+/**
+ * @param {number} reg
+ * @param {number} selector
+ */
+function switch_seg(reg, selector)
+{
+    dbg_assert(reg >= 0 && reg <= 5);
+    dbg_assert(typeof selector === "number" && selector < 0x10000 && selector >= 0);
+    
+    if(reg === reg_cs)
+    {
+        protected_mode = (cr0 & 1) === 1;
+    }
+
+    if(!protected_mode)
+    {
+        sreg[reg] = selector;
+        segment_is_null[reg] = 0;
+        segment_limits[reg] = 0xFFFFF;
+        segment_offsets[reg] = selector << 4;
+        return;
+    }
+
+    var info = lookup_segment_selector(selector);
+
+    if(reg === reg_ss)
+    {
+        if(info.is_null)
+        {
+            trigger_gp(0);
+            return false;
+        }
+        if(!info.is_valid || 
+                info.is_system ||
+                info.rpl !== cpl ||
+                !info.is_writable ||
+                info.dpl !== cpl)
+        {
+            trigger_gp(selector & ~3);
+            return false;
+        }
+        if(!info.is_present)
+        {
+            trigger_ss(selector & ~3);
+            return false;
+        }
+
+        stack_size_32 = info.size;
+
+        if(info.size)
+        {
+            stack_reg = reg32s;
+            reg_vsp = reg_esp;
+            reg_vbp = reg_ebp;
+        }
+        else
+        {
+            stack_reg = reg16;
+            reg_vsp = reg_sp;
+            reg_vbp = reg_bp;
+        }
+    }
+    else if(reg === reg_cs)
+    {
+        if(!info.is_executable)
+        {
+            // cs not executable
+            dbg_log(info + " " + h(selector & ~3), LOG_CPU);
+            throw unimpl("#GP handler");
+        }
+
+        if(info.is_system)
+        {
+            dbg_log(info + " " + h(selector & ~3), LOG_CPU);
+            throw unimpl("load system segment descriptor, type = " + (info.access & 15));
+        }
+
+        if(info.dc_bit && (info.dpl !== info.rpl))
+        {
+            dbg_log(info + " " + h(selector & ~3), LOG_CPU);
+            throw unimpl("#GP handler");
+        }
+
+        if(info.rpl !== cpl)
+        {
+            dbg_log(info + " " + h(selector & ~3), LOG_CPU);
+            throw unimpl("privilege change");
+        }
+
+        dbg_assert(cpl === info.dpl);
+
+        if(!info.dc_bit && info.dpl < cpl)
+        {
+            throw unimpl("inter privilege interrupt");
+        }
+        else
+        {
+            if(info.dc_bit || info.dpl === cpl)
+            {
+                // ok
+            }
+            else
+            {
+                // PE = 1, interrupt or trap gate, nonconforming code segment, DPL > CPL
+                dbg_log(info + " " + h(selector & ~3), LOG_CPU);
+                throw unimpl("#GP handler");
+            }
+        }
+
+
+        operand_size_32 = address_size_32 = is_32 = info.size;
+
+        update_operand_size();
+        update_address_size();
+    }
+    else
+    {
+        // es, ds, fs, gs
+        if(info.is_null)
+        {
+            sreg[reg] = selector;
+            segment_is_null[reg] = 1;
+            return true;
+        }
+        if(!info.is_valid || 
+                info.is_system || 
+                !info.is_readable ||
+                ((!info.is_executable || !info.dc_bit) &&
+                 info.rpl > info.dpl &&
+                 cpl > info.dpl))
+        {
+            trigger_gp(selector & ~3);
+            return false;
+        }
+        if(!info.is_present)
+        {
+            trigger_np(selector & ~3);
+            return false;
+        }
+    }
+
+    //dbg_log("seg " + reg + " " + h(info.base));
+
+    segment_is_null[reg] = 0;
+    segment_limits[reg] = info.real_limit;
+    segment_infos[reg] = 0; // TODO
+    
+    segment_offsets[reg] = info.base;
+
+    sreg[reg] = selector;
+
+    return true;
+}
+
+function load_tr(selector)
+{
+    var info = lookup_segment_selector(selector);
+
+    //dbg_log("load tr");
+
+    if(!info.from_gdt)
+    {
+        throw unimpl("TR can only be loaded from GDT");
+    }
+
+    if(info.is_null)
+    {
+        dbg_log("#GP(0) | tried to load null selector (ltr)");
+        throw unimpl("#GP handler");
+    }
+
+    if(!info.is_present)
+    {
+        dbg_log("#GP | present bit not set (ltr)");
+        throw unimpl("#GP handler");
+    }
+
+    if(!info.is_system)
+    {
+        dbg_log("#GP | ltr: not a system entry");
+        throw unimpl("#GP handler");
+    }
+
+    if(info.type !== 9)
+    {
+        dbg_log("#GP | ltr: invalid type (type = " + info.type + ")");
+        throw unimpl("#GP handler");
+    }
+
+    tsr_size = info.limit;
+    tsr_offset = info.base;
+
+    //dbg_log("tsr at " + h(tsr_offset) + "; (" + tsr_size + " bytes)");
+}
+
+function load_ldt(selector)
+{
+    var info = lookup_segment_selector(selector);
+
+    if(info.is_null)
+    {
+        // invalid
+        ldtr_size = 0;
+        ldtr_offset = 0;
+        return;
+    }
+
+    if(!info.from_gdt)
+    {
+        throw unimpl("LDTR can only be loaded from GDT");
+    }
+
+    if(!info.is_present)
+    {
+        dbg_log("lldt: present bit not set");
+        throw unimpl("#GP handler");
+    }
+
+    if(!info.is_system)
+    {
+        dbg_log("lldt: not a system entry");
+        throw unimpl("#GP handler");
+    }
+
+    if(info.type !== 2)
+    {
+        dbg_log("lldt: invalid type (" + info.type + ")");
+        throw unimpl("#GP handler");
+    }
+
+    ldtr_size = info.limit;
+    ldtr_offset = info.base;
+
+    //dbg_log("ldt at " + h(ldtr_offset) + "; (" + ldtr_size + " bytes)");
+}
+
+
+
+function clear_tlb()
+{
+    // clear tlb excluding global pages
+    last_virt_eip = -1;
+    last_virt_esp = -1;
+
+    tlb_info.set(tlb_info_global);
+
+    //dbg_log("page table loaded", LOG_CPU);
+}
+
+function full_clear_tlb()
+{
+    // clear tlb including global pages
+    tlb_info_global = new Uint8Array(1 << 20);
+
+    clear_tlb();
+
+}
+
+function invlpg(addr)
+{
+    var page = addr >>> 12;
+    dbg_log("invlpg: " + h(page), LOG_CPU);
+
+    tlb_info[page] = 0;
+    tlb_info_global[page] = 0;
+
+    last_virt_eip = -1;
+    last_virt_esp = -1;
+}
+
+/**
+ * @param {number} addr
+ */
+function translate_address_disabled(addr)
+{
+    return addr;
+}
+
+function translate_address_user_write(addr)
+{
+    var base = addr >>> 12;
+    
+    if(tlb_info[base] & TLB_USER_WRITE)
+    {
+        return tlb_user_write[base] ^ addr;
+    }
+    else
+    {
+        return do_page_translation(addr, 1, 1) | addr & 0xFFF;
+    }
+}
+
+function translate_address_user_read(addr)
+{
+    var base = addr >>> 12;
+    
+    if(tlb_info[base] & TLB_USER_READ)
+    {
+        return tlb_user_read[base] ^ addr;
+    }
+    else
+    {
+        return do_page_translation(addr, 0, 1) | addr & 0xFFF;
+    }
+}
+
+function translate_address_system_write(addr)
+{
+    var base = addr >>> 12;
+    
+    if(tlb_info[base] & TLB_SYSTEM_WRITE)
+    {
+        return tlb_system_write[base] ^ addr;
+    }
+    else
+    {
+        return do_page_translation(addr, 1, 0) | addr & 0xFFF;
+    }
+}
+
+function translate_address_system_read(addr)
+{
+    var base = addr >>> 12;
+    
+    if(tlb_info[base] & TLB_SYSTEM_READ)
+    {
+        return tlb_system_read[base] ^ addr;
+    }
+    else
+    {
+        return do_page_translation(addr, 0, 0) | addr & 0xFFF;
+    }
+}
+
+/**
+ * @return {number} 
+ */
+function do_page_translation(addr, for_writing, user)
+{
+    var page = addr >>> 12,
+        page_dir_addr = (cr3 >>> 2) + (page >> 10),
+        page_dir_entry = memory.mem32s[page_dir_addr],
+        high,
+        can_write = true,
+        global,
+        cachable = true,
+        allow_user = true;
+
+    if(!(page_dir_entry & 1))
+    {
+        // to do at this place:
+        //
+        // - set cr2 = addr (which caused the page fault)
+        // - call_interrupt_vector  with id 14, error code 0-7 (requires information if read or write)
+        // - prevent execution of the function that triggered this call
+        dbg_log("#PF not present", LOG_CPU);
+
+        cr2 = addr;
+        trigger_pagefault(for_writing, user, 0);
+
+        // never reached as trigger_pagefault throws up
+        dbg_assert(false);
+    }
+
+    if((page_dir_entry & 2) === 0)
+    {
+        can_write = false;
+
+        if(for_writing)
+        {
+            cr2 = addr;
+            trigger_pagefault(for_writing, user, 1);
+            dbg_assert(false);
+        }
+    }
+
+    if((page_dir_entry & 4) === 0)
+    {
+        allow_user = false;
+
+        if(user)
+        {
+            // "Page Fault: page table accessed by non-supervisor";
+            dbg_log("#PF supervisor", LOG_CPU);
+            cr2 = addr;
+            trigger_pagefault(for_writing, user, 1);
+            dbg_assert(false);
+        }
+    }
+    
+    if((page_dir_entry & 0x10) === 0)
+    {
+        cachable = false;
+    }
+
+    if(page_dir_entry & page_size_extensions)
+    {
+        // size bit is set
+
+        // set the accessed and dirty bits
+        memory.mem32s[page_dir_addr] = page_dir_entry | 0x20 | for_writing << 6;
+
+        high = (page_dir_entry & 0xFFC00000) | (page << 12 & 0x3FF000);
+
+        global = page_dir_entry & 0x100;
+    }
+    else
+    {
+        var page_table_addr = ((page_dir_entry & 0xFFFFF000) >>> 2) + (page & 0x3FF),
+            page_table_entry = memory.mem32s[page_table_addr];
+
+        if(!(page_table_entry & 1))
+        {
+            dbg_log("#PF not present table", LOG_CPU);
+            cr2 = addr;
+            trigger_pagefault(for_writing, user, 0);
+            dbg_assert(false);
+        }
+
+        if((page_table_entry & 2) === 0)
+        {
+            can_write = false;
+
+            if(for_writing)
+            {
+                dbg_log("#PF not writable page", LOG_CPU);
+                cr2 = addr;
+                trigger_pagefault(for_writing, user, 1);
+                dbg_assert(false);
+            }
+        }
+
+        if((page_table_entry & 4) === 0)
+        {
+            allow_user = false;
+
+            if(user)
+            {
+                dbg_log("#PF not supervisor page", LOG_CPU);
+                cr2 = addr;
+                trigger_pagefault(for_writing, user, 1);
+                dbg_assert(false);
+            }
+        }
+    
+        if((page_table_entry & 0x10) === 0)
+        {
+            cachable = false;
+        }
+
+        // set the accessed and dirty bits
+        memory.mem32s[page_dir_addr] = page_dir_entry | 0x20;
+        memory.mem32s[page_table_addr] = page_table_entry | 0x20 | for_writing << 6;
+
+        high = page_table_entry & 0xFFFFF000;
+
+        global = page_table_entry & 0x100;
+    }
+
+    if(cachable)
+    {
+        var cache_entry = high ^ page << 12,
+            info = 0;
+
+        if(allow_user)
+        {
+            tlb_user_read[page] = cache_entry;
+            info |= TLB_USER_READ;
+
+            if(can_write)
+            {
+                tlb_user_write[page] = cache_entry;
+                info |= TLB_USER_WRITE;
+            }
+        }
+        
+        tlb_system_read[page] = cache_entry;
+        info |= TLB_SYSTEM_READ;
+
+        if(can_write)
+        {
+            tlb_system_write[page] = cache_entry;
+            info |= TLB_SYSTEM_WRITE;
+        }
+
+        tlb_info[page] |= info;
+
+        if(global)
+        {
+            tlb_info_global[page] = info;
+        }
+    }
+
+    return high ;
+}
+
+function trigger_pagefault(write, user, present)
+{
+    if(LOG_LEVEL & LOG_CPU)
+    {
+        dbg_trace();
+    }
+
+    if(page_fault)
+    {
+        dbg_trace();
+        throw unimpl("Double fault");
+    }
+
+    instruction_pointer = previous_ip;
+    page_fault = true;
+    call_interrupt_vector(14, false, user << 2 | write << 1 | present);
+
+    throw 0xDEADBEE;
+}
+
+
+// it looks pointless to have these two here, but 
+// Closure Compiler is able to remove unused functions
+//#include "test_helpers.js"
+#include "debug.macro.js"
+
+
+#include "modrm.macro.js"
+#include "arith.macro.js"
+#include "misc_instr.macro.js"
+#include "string.macro.js"
+
+#include "fpu.macro.js"
+
+#include "instructions.macro.js"
+
+
+}

+ 445 - 0
src/debug.macro.js

@@ -0,0 +1,445 @@
+"use strict";
+
+debug.dump_regs = dump_regs;
+debug.dump_regs_short = dump_regs_short;
+debug.dump_stack = dump_stack;
+
+debug.dump_page_directory = dump_page_directory;
+debug.dump_gdt_ldt = dump_gdt_ldt;
+debug.dump_idt = dump_idt;
+
+debug.step = step;
+debug.run_until = run_until;
+
+debug.debugger = function()
+{
+    debugger;
+}
+
+function step()
+{
+    step_mode = true;
+
+    if(!running)
+    {
+        cycle(); 
+    }
+
+    dump_regs(); 
+    var now = Date.now();
+
+    vga.timer(now);
+    timer.timer(now);
+    rtc.timer(now);
+
+    running = false;
+}
+
+function run_until()
+{
+    running = false;
+    var a = parseInt(prompt("input hex", ""), 16); 
+    if(a) while(instruction_pointer != a) cycle()
+    dump_regs();
+}
+
+// http://ref.x86asm.net/x86reference.xml
+// for debuggin' purposes
+var opcode_map = [
+    "ADD", "ADD", "ADD", "ADD", "ADD", "ADD", "PUSH", "POP",
+    "OR", "OR", "OR", "OR", "OR", "OR", "PUSH", "0F:",
+    "ADC", "ADC", "ADC", "ADC", "ADC", "ADC", "PUSH", "POP",
+    "SBB", "SBB", "SBB", "SBB", "SBB", "SBB", "PUSH", "POP",
+    "AND", "AND", "AND", "AND", "AND", "AND", "ES", "DAA",
+    "SUB", "SUB", "SUB", "SUB", "SUB", "SUB", "CS", "DAS",
+    "XOR", "XOR", "XOR", "XOR", "XOR", "XOR", "SS", "AAA",
+    "CMP", "CMP", "CMP", "CMP", "CMP", "CMP", "DS", "AAS",
+    "INC", "INC", "INC", "INC", "INC", "INC", "INC", "INC",
+    "DEC", "DEC", "DEC", "DEC", "DEC", "DEC", "DEC", "DEC",
+    "PUSH", "PUSH", "PUSH", "PUSH", "PUSH", "PUSH", "PUSH", "PUSH",
+    "POP", "POP", "POP", "POP", "POP", "POP", "POP", "POP",
+    "PUSHA", "POPA", "BOUND", "ARPL", "FS", "GS", "none", "none",
+    "PUSH", "IMUL", "PUSH", "IMUL", "INS", "INS", "OUTS", "OUTS",
+    "JO", "JNO", "JB", "JNB", "JZ", "JNZ", "JBE", "JNBE",
+    "JS", "JNS", "JP", "JNP", "JL", "JNL", "JLE", "JNLE",
+    "ADD", "ADD", "ADD", "ADD", "TEST", "TEST", "XCHG", "XCHG",
+    "MOV", "MOV", "MOV", "MOV", "MOV", "LEA", "MOV", "POP",
+    "NOP", "XCHG", "XCHG", "XCHG", "XCHG", "XCHG", "XCHG", "XCHG",
+    "CBW", "CWD", "CALLF", "FWAIT", "PUSHF", "POPF", "SAHF", "LAHF",
+    "MOV", "MOV", "MOV", "MOV", "MOVS", "MOVS", "CMPS", "CMPS",
+    "TEST", "TEST", "STOS", "STOS", "LODS", "LODS", "SCAS", "SCAS",
+    "MOV", "MOV", "MOV", "MOV", "MOV", "MOV", "MOV", "MOV",
+    "MOV", "MOV", "MOV", "MOV", "MOV", "MOV", "MOV", "MOV",
+    "ROL", "ROL", "RETN", "RETN", "LES", "LDS", "MOV", "MOV",
+    "ENTER", "LEAVE", "RETF", "RETF", "INT", "INT", "INTO", "IRET",
+    "ROL", "ROL", "ROL", "ROL", "AAM", "AAD", "none", "XLAT",
+    "FADD", "FLD", "FIADD", "FILD", "FADD", "FLD", "FIADD", "FILD",
+    "LOOPNZ", "LOOPZ", "LOOP", "JCXZ", "IN", "IN", "OUT", "OUT",
+    "CALL", "JMP", "JMPF", "JMP", "IN", "IN", "OUT", "OUT",
+    "LOCK", "none", "REPNZ", "REPZ", "HLT", "CMC", "TEST", "TEST",
+    "CLC", "STC", "CLI", "STI", "CLD", "STD", "INC", "INC"
+];
+
+function logop(_ip, op)
+{
+    if(!DEBUG || !ops)
+    {
+        return;
+    }
+    if(!step_mode)
+    {
+        //return;
+    }
+    
+
+    ops.add(_ip);
+    ops.add(opcode_map[op] || "unkown");
+    ops.add(op);
+}
+
+function dump_stack(start, end)
+{
+    var esp = reg32[reg_esp];
+    dbg_log("========= STACK ==========");
+
+    if(end >= start || end === undefined)
+    {
+        start = 5;
+        end = -5;
+    }
+
+    for(var i = start; i > end; i--)
+    {
+        var line = "    ";
+
+        if(!i) line = "=>  ";
+
+        line += h(i, 2) + " | ";
+
+        dbg_log(line + h(esp + 4 * i, 8) + " | " + h(memory.read32s(esp + 4 * i) >>> 0));
+    }
+}
+
+function dump_regs_short()
+{
+    var
+        r32 = { "eax": reg_eax, "ecx": reg_ecx, "edx": reg_edx, "ebx": reg_ebx, 
+                "esp": reg_esp, "ebp": reg_ebp, "esi": reg_esi, "edi": reg_edi },
+        r32_names = ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"],
+        s = { "cs": reg_cs, "ds": reg_ds, "es": reg_es, "fs": reg_fs, "gs": reg_gs, "ss": reg_ss },
+        line1 = "",
+        line2 = "";
+
+
+    
+    for(var i = 0; i < 4; i++)
+    {
+        line1 += r32_names[i] + "="  + h(reg32[r32[r32_names[i]]], 8) + " ";
+        line2 += r32_names[i+4] + "="  + h(reg32[r32[r32_names[i+4]]], 8) + " ";
+    }
+
+    line1 += " eip=" + h(get_real_ip(), 8);
+    line2 += " flg=" + h(get_flags());
+
+    line1 += "  ds=" + h(sreg[reg_ds], 4) + " es=" + h(sreg[reg_es], 4) + "  fs=" + h(sreg[reg_fs], 4);
+    line2 += "  gs=" + h(sreg[reg_gs], 4) + " cs=" + h(sreg[reg_cs], 4) + "  ss=" + h(sreg[reg_ss], 4);
+
+    dbg_log(line1);
+    dbg_log(line2);
+}
+
+function dump_regs()
+{
+    var
+        r32 = { "eax": reg_eax, "ecx": reg_ecx, "edx": reg_edx, "ebx": reg_ebx, 
+                "esp": reg_esp, "ebp": reg_ebp, "esi": reg_esi, "edi": reg_edi },
+
+        s = { "cs": reg_cs, "ds": reg_ds, "es": reg_es, 
+              "fs": reg_fs, "gs": reg_gs, "ss": reg_ss },
+
+        out = "";
+
+    
+    var opcodes = ops.toArray();
+    for(var i = 0; i < opcodes.length; i += 3)
+    {
+        if(opcodes[i])
+        {
+            out += h(opcodes[i], 6)  + ":        " + 
+                String.pads(opcodes[i + 1], 20) + h(opcodes[i + 2], 2) + "\n";
+        }
+    }
+
+    log(out.substr(0, out.length - 1));
+    ops.clear();
+    
+    dbg_log("----- DUMP (ip = 0x" + h(instruction_pointer >>> 0) + ") ----------")
+    dbg_log("protected mode: " + protected_mode);
+    
+    for(i in r32)
+    {
+        dbg_log(i + " =  0x" + h(reg32[r32[i]], 8));
+    }
+    dbg_log("eip =  0x" + h(get_real_ip(), 8));
+    
+    for(i in s)
+    {
+        dbg_log(i + "  =  0x" + h(sreg[s[i]], 4));
+    }
+    
+    out = "";
+    
+    var flg = { "cf": getcf, "pf": getpf, "zf": getzf,  "sf": getsf, 
+                "of": getof, "df": flag_direction, "if": flag_interrupt };
+    
+    for(var i in flg)
+    {
+        if(+flg[i])
+        {
+            out += i + "=" + Number(!!(flags & flg[i])) + " | ";
+        }
+        else
+        {
+            out += i + "=" + Number(!!flg[i]()) + " | ";
+        }
+    }
+    out += "iopl=" + getiopl();
+    dbg_log(out);
+    
+    
+    //dbg_log("last operation: " + h(last_op1 | 0) + ", " +  h(last_op2 | 0) + " = " +
+            //h(last_result | 0) + " (" + last_op_size + " bit)")
+    
+}
+
+function dump_gdt_ldt()
+{
+    dbg_log("gdt: (len = " + h(gdtr_size) + ")");
+    dump_table(translate_address_read(gdtr_offset), gdtr_size);
+
+    dbg_log("\nldt: (len = " + h(ldtr_size) + ")");
+    dump_table(translate_address_read(ldtr_offset), ldtr_size);
+
+    function dump_table(addr, size)
+    {
+        for(var i = 0; i < size; i += 8, addr += 8)
+        {
+            var base = memory.read16(addr + 2) | 
+                    memory.read8(addr + 4) << 16 | 
+                    memory.read8(addr + 7) << 24,
+
+                limit = (memory.read16(addr) | memory.read8(addr + 6) & 0xF) + 1,
+                access = memory.read8(addr + 5),
+                flags = memory.read8(addr + 6) >> 4,
+                flags_str = '',
+                dpl = access >> 5 & 3;
+
+            if(!(access & 128))
+            {
+                // present bit not set
+                //continue;
+                flags_str += 'NP ';
+            }
+            else
+            {
+                flags_str += ' P ';
+            }
+
+            if(access & 16)
+            {
+                if(flags & 4) 
+                {
+                    flags_str += '32b ';
+                }
+                else
+                {
+                    flags_str += '16b ';
+                }
+
+                if(access & 8)
+                {
+                    // executable
+                    flags_str += 'X ';
+
+                    if(access & 4)
+                    {
+                        flags_str += 'C ';
+                    }
+                }
+                else
+                {
+                    // data
+                    flags_str += 'R ';
+                }
+            }
+            else
+            {
+                // system
+                flags_str += 'sys: ' + h(access & 15);
+            }
+
+            if(flags & 8)
+            {
+                limit <<= 12;
+            }
+            
+            dbg_log(h(i & ~7, 4) + " " + h(base >>> 0, 8) + " (" + h(limit, 8) + " bytes) " +
+                    flags_str + ";  dpl = " + dpl + ", a = " + access.toString(2) +
+                    ", f = " + flags.toString(2));
+        }
+    }
+}
+
+function dump_idt()
+{
+    for(var i = 0; i < idtr_size; i += 8)
+    {
+        var addr = do_page_translation(idtr_offset + i, 0, 0),
+            base = memory.read16(addr) | memory.read16(addr + 6) << 16,
+            selector = memory.read16(addr + 2),
+            type = memory.read8(addr + 5),
+            line,
+            dpl = type >> 5 & 3;
+
+        if((type & 31) === 5)
+        {
+            line = 'task gate ';
+        }
+        else if((type & 31) === 14)
+        {
+            line = 'intr gate ';
+        }
+        else if((type & 31) === 15)
+        {
+            line = 'trap gate ';
+        }
+        else
+        {
+            line = 'invalid   ';
+        }
+
+
+        if(type & 128)
+        {
+            line += ' P';
+        }
+        else
+        {
+            // present bit not set
+            //continue;
+            line += 'NP';
+        }
+
+    
+        dbg_log(h(i >> 3, 4) + " " + h(base >>> 0, 8) + ", " + 
+                h(selector, 4) + "; " + line + ";  dpl = " + dpl + ", t = " + type.toString(2));
+    }
+}
+
+function load_page_entry(dword_entry, is_directory)
+{
+    if(!(dword_entry & 1))
+    {
+        // present bit not set
+        return false;
+    }
+
+    var size = (dword_entry & 128) === 128,
+        address;
+
+    if(size && !is_directory)
+    {
+        address = dword_entry & 0xFFC00000;
+    }
+    else
+    {
+        address = dword_entry & 0xFFFFF000;
+    }
+
+    return {
+        size: size,
+        global: (dword_entry & 256) === 256,
+        accessed: (dword_entry & 0x20) === 0x20,
+        dirty: (dword_entry & 0x40) === 0x40,
+        cache : (dword_entry & 16) === 16,
+        user : (dword_entry & 4) === 4,
+        read_write : (dword_entry & 2) === 2,
+        address : address >>> 0
+    };
+}
+
+function dump_page_directory()
+{
+    for(var i = 0; i < 1024; i++)
+    {
+        var dword = memory.read32s(cr3 + 4 * i),
+            entry = load_page_entry(dword, true);
+
+        if(!entry)
+        {
+            continue;
+        }
+
+        var flags = '';
+
+        if(entry.size)
+            flags += 'S ';
+
+        if(entry.cache)
+            flags += 'D ';
+
+        if(entry.user)
+            flags += 'U ';
+
+        if(entry.read_write)
+            flags += 'R ';
+
+        if(entry.accessed)
+            flags += 'A ';
+
+        dbg_log("=== " + h(entry.address >>> 0, 8) + " | " + flags);
+        
+        if(entry.size)
+        {
+            continue;
+        }
+
+        for(var j = 0; j < 1024; j++)
+        {
+            dword = memory.read32s(entry.address + 4 * j);
+            
+            var subentry = load_page_entry(dword, false);
+
+            if(subentry)
+            {
+                flags = '';
+
+                if(subentry.size)
+                    flags += 'S ';
+
+                if(subentry.cache)
+                    flags += 'D ';
+
+                if(subentry.user)
+                    flags += 'U ';
+
+                if(subentry.read_write)
+                    flags += 'R ';
+
+                if(subentry.global)
+                    flags += 'G ';
+                
+                if(subentry.accessed)
+                    flags += 'A ';
+
+                if(subentry.dirty)
+                    flags += 'Di ';
+
+                dbg_log("# " + h((i << 22 | j << 12) >>> 0, 8) + " -> " +
+                        h(subentry.address, 8) + " | " + flags);
+            }
+        }
+    }
+}
+
+

+ 545 - 0
src/disk.js

@@ -0,0 +1,545 @@
+"use strict";
+
+
+var 
+    /** @const */
+    CDROM_SECTOR_SIZE = 2048,
+    /** @const */
+    HD_SECTOR_SIZE = 512;
+
+
+/** @constructor */
+function CDRom(dev, cd_buffer)
+{
+    this.io = dev.io;
+    this.memory = dev.memory;
+    this.pic = dev.pic;
+    this.pci = dev.pci;
+
+    this.vendor_id = 0x1002;
+    this.class_revision = 0x106 << 16 | 0x01 << 8;
+    this.irq = 14;
+    this.iobase = 0xFFF10000;
+    this.sector_size = CDROM_SECTOR_SIZE;
+    this.buffer = cd_buffer;
+    this.atapi = true;
+    this.pci_id = 8;
+
+    this.init();
+}
+CDRom.prototype = new AHCIDevice();
+
+/** @constructor */
+function HDD(dev, disk_buffer, nr)
+{
+    var port = nr === 0 ? 0x1F0 : 0x170,
+        irq = nr === 0 ? 14 : 15;
+
+    var pic = dev.pic;
+
+    this.io = dev.io;
+    this.memory = dev.memory;
+    this.pic = dev.pic;
+    this.pci = dev.pci;
+
+    this.vendor_id = 0x1002;
+    this.class_revision = 0x106 << 16 | 0x01 << 8;
+    this.irq = irq;
+    this.iobase = 0xFFF00000;
+    this.sector_size = HD_SECTOR_SIZE;
+    this.sector_count = disk_buffer.byteLength / this.sector_size;
+    this.buffer = disk_buffer;
+    this.atapi = false;
+    this.pci_id = 0x10;
+
+    this.head_count = 16;
+    this.sectors_per_track = 63;
+
+    this.cylinder_count = disk_buffer.byteLength / 
+        this.head_count / (this.sectors_per_track + 1) / this.sector_size;
+
+    dbg_assert(this.cylinder_count === (this.cylinder_count | 0));
+    dbg_assert(this.cylinder_count <= 16383);
+
+    var me = this;
+
+    // status
+    this.io.register_read(port | 7, read_status);
+
+    // alternate status, starting at 3f6/376
+    this.io.register_read(port | 0x206, read_status);
+
+    function read_status()
+    {
+        dbg_log("ATA read status", LOG_DISK);
+
+        var status = 0x50;
+
+        if(data_pointer < pio_data.length)
+            status |= 8;
+
+        return status;
+    }
+
+    var last_drive = 0xFF,
+        data_pointer = 0,
+        pio_data = [],
+        drq = false,
+        is_lba = 0,
+        slave = 0,
+        bytecount = 0,
+        sector = 0,
+        cylinder = 0,
+        head = 0;
+
+
+    function push_irq()
+    {
+        pic.push_irq(me.irq);
+    }
+
+    this.io.register_write(port | 6, function(data)
+    {
+        dbg_log("1F6 write " + h(data), LOG_DISK);
+
+        var slave = data & 0x10,
+            mode = data & 0xE0,
+            low = data & 0xF;
+
+
+        if(slave)
+        {
+            //drq = false;
+            return;
+        }
+
+        is_lba = data >> 6 & 1;
+        head = data & 0xF;
+        last_drive = data;
+    });
+
+    this.io.register_write(port | 2, function(data)
+    {
+        dbg_log("1F2 write: " + data, LOG_DISK);
+        if(data)
+        {
+            bytecount = data << 9;
+        }
+        else
+        {
+            bytecount = 256 << 9;
+        }
+        //bytecount = 1 << 9;
+    });
+    this.io.register_write(port | 3, function(data)
+    {
+        sector = data;
+    });
+    this.io.register_write(port | 4, function(data)
+    {
+        cylinder = cylinder & 0xFF00 | data;
+    });
+    this.io.register_write(port | 5, function(data)
+    {
+        cylinder = cylinder & 0xFF | data << 8;
+    });
+
+    this.io.register_write(port | 7, function(cmd)
+    {
+        if(cmd === 0xEC)
+        {
+            dbg_log("ATA identify device", LOG_DISK);
+            // identify device
+            // http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/harddrv.cc#L2821
+
+            data_pointer = 0;
+
+            pio_data = new Uint8Array([
+                0x40, 0, 
+                // 1 cylinders
+                me.cylinder_count, me.cylinder_count >> 8, 
+                0, 0, 
+        
+                // 3 heads
+                me.head_count, me.head_count >> 8, 
+                0, 0,
+                // 5
+                0, 0, 
+                // sectors per track
+                me.sectors_per_track, 0, 
+                0, 0, 0, 0, 0, 0,
+                // 10-19 serial number
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 15
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 20
+                3, 0, 0, 2, 4, 0, 
+                // 23-26 firmware revision
+                0, 0, 0, 0, 0, 0, 0, 0, 
+
+                // 27 model number
+                32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 
+                32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+
+                // 47
+                0, 0, 
+                1, 0, 
+                0, 3,  // capabilities
+                // 50
+                0, 0, 
+                0, 2, 
+                0, 2, 
+                7, 0, 
+
+                // 54 cylinders
+                me.cylinder_count, me.cylinder_count >> 8, 
+                // 55 heads
+                me.head_count, me.head_count >> 8, 
+                // 56 sectors per track
+                me.sectors_per_track, 0, 
+                // capacity in sectors
+                this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, 
+                this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, 
+                
+                0, 0,
+                // 60
+                this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, 
+                this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, 
+                
+                0, 0, 0, 0, 0, 0,
+                // 65
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 70
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 75
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 80
+                0x7E, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 85
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 90
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 95
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                // 100
+                this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, 
+                this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, 
+                
+
+            ]);
+
+            push_irq();
+        }
+        else if(cmd === 0x91)
+        {
+            dbg_log("ATA cmd 91", LOG_DISK);
+            push_irq();
+        }
+        else if(cmd === 0x10)
+        {
+            // obsolete
+            dbg_log("ATA cmd 10", LOG_DISK);
+            push_irq();
+        }
+        else if(cmd = 0x27)
+        {
+            // READ NATIVE MAX ADDRESS EXT - read the actual size of the HD
+            // https://en.wikipedia.org/wiki/Host_protected_area
+            dbg_log("ATA cmd 27", LOG_DISK);
+            push_irq();
+            pio_data = [
+                0, 0, // error
+                0, 0, // count
+
+                // result
+                disk_buffer.byteLength & 0xff,
+                disk_buffer.byteLength >> 8 & 0xff,
+                disk_buffer.byteLength >> 16 & 0xff,
+                disk_buffer.byteLength >> 24 & 0xff,
+                0, 0,
+
+                0, 0, //
+            ];
+        }
+        else if(cmd === 0x20)
+        {
+            if(DEBUG && is_lba)
+                throw "unimplemented";
+
+            var lba = (cylinder * me.head_count + head) * me.sectors_per_track + sector - 1;
+            dbg_log("ATA read: from=" + h(lba * me.sector_size) + " chs=" + cylinder + "/" + head + "/" + sector + " length=" + h(bytecount), LOG_DISK);
+
+            me.buffer.get(lba * me.sector_size, bytecount, function(data)
+            {
+                data_pointer = 0;
+                pio_data = data;
+
+                push_irq();
+            });
+        }
+        else
+        {
+            dbg_log("New ATA cmd on 1F7: " + h(cmd), LOG_DISK);
+        }
+    });
+
+    this.io.register_read(port | 0, function()
+    {
+        if(data_pointer < pio_data.length)
+        {
+            dbg_log("Read 1F0: " + h(pio_data[data_pointer], 2), LOG_DISK);
+
+            if((data_pointer & 511)  === 0)
+                push_irq();
+
+            return pio_data[data_pointer++] & 0xFF;
+        }
+        else
+        {
+            dbg_log("Read 1F0: empty", LOG_DISK);
+            return 0;
+        }
+    });
+
+
+    this.io.register_read(port | 1, function()
+    {
+        dbg_log("Read 1F1", LOG_DISK);
+        return 0xFF;
+    });
+
+    this.io.register_read(port | 2, function()
+    {
+        dbg_log("Read 1F2", LOG_DISK);
+        return 0xFF;
+    });
+
+
+    this.io.register_read(port | 3, function()
+    {
+        dbg_log("Read 1F3", LOG_DISK);
+        return 0xFF;
+    });
+
+    this.io.register_read(port | 6, function()
+    {
+        dbg_log("Read 1F6", LOG_DISK);
+        return last_drive;
+    });
+
+    this.init();
+}
+HDD.prototype = new AHCIDevice();
+
+
+/** @constructor */
+function AHCIDevice()
+{
+    var me,
+        memory;
+
+    this.init = function()
+    {
+        me = this;
+        memory = this.memory;
+
+        this.pci.register_device(this, this.pci_id);
+
+        this.memory.mmap_register(this.iobase, 0x4000, true, mmio_read, mmio_write);
+
+    };
+
+    var host_ctl = 0,
+        host_caps = 1,
+        host_ports_impl = 1,
+        host_intbits = 1,
+        port_lst_addr,
+        port_fis_addr;
+
+    function atapi_command_read(atapi, dest, byte_len)
+    {
+        var lba = Math.to_be32(memory.read32s(atapi + 2)),
+            count = Math.to_be16(memory.read16(atapi + 7)),
+            flags = memory.read8(atapi + 1),
+
+            //bytecount = Math.min(count * me.sector_size, byte_len + 1);
+            bytecount = count * me.sector_size;
+
+        dbg_log("CD read lba=" + h(lba) + 
+                " lbacount=" + h(count) +
+                " bytelen=" + h(byte_len) +
+                " copycount=" + h(bytecount) +
+                " flags=" + h(flags) +
+                " buf=" + h(dest, 8), LOG_CD);
+
+        me.buffer.get(lba * me.sector_size, bytecount, function(data)
+        {
+            memory.write_blob(data, dest);
+        });
+
+        //pic.push_irq(me.irq);
+    }
+
+    function ata_command_rw(cmd_fis, dest, is_read)
+    {
+        var lba = memory.read32s(cmd_fis + 4) & 0xFFFFFF,
+            count = memory.read16(cmd_fis + 12),
+            bytecount = count * me.sector_size;
+
+        dbg_log("ahci " + (is_read ? "read" : "write") + ": lba=" + h(lba, 8) +
+                " count=" + h(count, 4) +
+                " dest=" + h(dest, 8));
+
+        if(is_read)
+        {
+            this.buffer.get(lba * me.sector_size, bytecount, function(data)
+            {
+                memory.write_blob(data, dest);
+            });
+        }
+        else
+        {
+            this.buffer.set(lba * me.sector_size, 
+                new Uint8Array(memory.buffer, dest, bytecount), 
+                function()
+                {
+                });
+        }
+    }
+
+
+    function mmio_read(addr)
+    {
+        switch(addr)
+        {
+            case 0: 
+                return host_caps;
+
+            case 4: 
+                return host_ctl;
+
+            case 0xC:
+                return host_ports_impl;
+
+            case 0x128:
+                return 0x03;
+
+            case 0x110:
+                return host_intbits;
+
+            default: 
+                dbg_log("New PCI mmio read from " + h(addr, 8), LOG_CD);
+        }
+    }
+
+    function mmio_write(addr, value)
+    {
+        switch(addr)
+        {
+            case 0x100:
+                port_lst_addr = value;
+                dbg_log("lst at " + h(value, 8), LOG_CD);
+                break;
+            case 0x108:
+                port_fis_addr = value;
+                dbg_log("fis at " + h(value, 8), LOG_CD);
+                break;
+
+            case 0x118:
+                dbg_log("port cmd: " + h(value, 8), LOG_CD);
+                break;
+
+            case 0x138:
+                var 
+                    
+                    ctba_addr = memory.read32s(port_lst_addr + 8),
+
+                    first_prdt_start = ctba_addr + 0x80,
+                    flags = memory.read16(port_lst_addr),
+                    prdt_addr = memory.read32s(first_prdt_start) + 0x100000000 * memory.read32s(first_prdt_start + 4),
+                    prdt_len = memory.read32s(ctba_addr + 0x80 + 0xC) & 0xFFF,
+                    atapi_command = memory.read8(ctba_addr + 0x40),
+                    fis_command = memory.read8(ctba_addr + 2),
+
+                    dma_fis_start = port_fis_addr + 0,
+                    pio_fis_start = port_fis_addr + 0x20,
+                    d2h_fis_start = port_fis_addr + 0x40,
+                    ufis_start = port_fis_addr + 0x60,
+
+                    command_fis_start = ctba_addr + 0,
+                    atapi_command_start = ctba_addr + 0x40;
+
+                if((fis_command === 0xA0 || fis_command === 0xA1) &&
+                        !me.atapi)
+                {
+                    return;
+                }
+
+                // status success
+                memory.write8(d2h_fis_start + 2, 0x40);
+
+                dbg_log("ctba at " + h(ctba_addr), LOG_CD);
+                dbg_log("prdt at " + h(prdt_addr), LOG_CD);
+                dbg_log("flags: " + h(flags, 2), LOG_CD);
+                dbg_log("cmd fis command: " + h(fis_command, 2), LOG_CD);
+
+                dbg_log("fis LBA=" + h(memory.read32s(command_fis_start + 4) & 0xffffff), LOG_CD);
+
+                dbg_log("Prdts count: " + h(memory.read16(port_lst_addr + 2)), LOG_CD);
+                dbg_log("PRD byte count: " + h(memory.read32s(port_lst_addr + 4)), LOG_CD);
+
+                dbg_log("First prdt byte count: " + h(memory.read32s(ctba_addr + 0x80 + 0xC)), LOG_CD);
+
+                if(fis_command === 0xC8 || fis_command === 0xCA)
+                {
+                    ata_command_rw(command_fis_start, prdt_addr, fis_command === 0xC8);
+                }
+                else if(fis_command === 0xEC)
+                {
+                    // ATA_CMD_IDENTIFY_DEVICE
+
+                    // number of sectors
+                    memory.write32(prdt_addr + 120, me.buffer.byteLength / me.sector_size);
+                }
+                else if(fis_command === 0xA1)
+                {
+                    // ATA_CMD_IDENTIFY_PACKET_DEVICE
+
+                    // is CD
+                    memory.write32(prdt_addr, 0x0500);
+                }
+                else if(fis_command === 0xA0)
+                {
+                    // ATA_CMD_PACKET
+                    if(atapi_command === 0x28)
+                    {
+                        atapi_command_read(ctba_addr + 0x40, prdt_addr, prdt_len);
+                    }
+                    else if(atapi_command === 0x2a)
+                    {
+                        // write
+                        dbg_log("atapi - unimplemented write", LOG_CD);
+                    }
+                    else if(atapi_command === 0x25)
+                    {
+                        // read capacity
+                        dbg_log("atapi - unimplemented read cap", LOG_CD);
+                    }
+                    else
+                    {
+                        dbg_log("atapi - unimplemented " + h(atapi_command, 2), LOG_CD);
+                    }
+                }
+                else 
+                {
+                    dbg_log("unimplemented fis command: " + h(fis_command, 2));
+                }
+
+                break;
+
+            default: 
+                dbg_log("PCI mmio write addr=" + h(addr, 8) + " value=" + h(value, 8), LOG_CD);
+        }
+    }
+}
+

+ 149 - 0
src/dma.js

@@ -0,0 +1,149 @@
+"use strict";
+
+/**
+ * @constructor
+ */
+function DMA(dev)
+{
+    var io = dev.io,
+        memory = dev.memory,
+
+        channels = [
+            { address: 0, count: 0 },
+            { address: 0, count: 0 },
+            { address: 0, count: 0 },
+            { address: 0, count: 0 }
+        ],
+
+        lsb_msb_flipflop = 0;
+
+    io.register_write(0x04, port_write.bind(0, 0x04));
+    io.register_write(0x05, port_write.bind(0, 0x05));
+    io.register_write(0x0A, portA_write);
+    io.register_write(0x0B, portB_write);
+    io.register_write(0x0C, portC_write);
+    io.register_write(0x81, port81_write);
+
+    function port_write(port, data_byte)
+    {
+        dbg_log("port " + port + " write " + data_byte, LOG_DMA);
+
+        if(port < 8)
+        {
+            var channel = port >> 1;
+
+            if(port & 1)
+            {
+                channels[channel].count = flipflop_get(channels[channel].count, data_byte);
+            }
+            else
+            {
+                channels[channel].address = flipflop_get(channels[channel].address, data_byte);
+            }
+        }
+    };
+
+    function port_read(port)
+    {
+        if(port < 8)
+        {
+            var channel = port >> 1;
+
+            if(port & 1)
+            {
+                return channels[channel].count;
+            }
+            else
+            {
+                // Bug?
+                return channels[channel].address;
+            }
+        }
+        else
+        {
+            dbg_log("port " + h(port) + " read", LOG_DMA);
+        }
+    };
+
+    function portA_write(data_byte)
+    {
+        dbg_log("port A write: " + h(data_byte), LOG_DMA);
+    };
+
+    function portB_write(data_byte)
+    {
+        dbg_log("port B write: " + h(data_byte), LOG_DMA);
+    };
+
+    function portC_write(data_byte)
+    {
+        lsb_msb_flipflop = 0;
+    }
+
+    function port81_write(data_byte)
+    {
+        channels[2].address = channels[2].address & 0xFFFF | data_byte << 16;
+    }
+
+    // read data, write to memory
+    this.do_read = function(buffer, start, len, channel, fn)
+    {
+        var read_count = channels[channel].count,
+            addr = channels[channel].address;
+
+        dbg_log("DMA write channel " + channel, LOG_DMA);
+        dbg_log("to " + h(addr) + " len " + h(read_count), LOG_DMA);
+
+        if(len < read_count)
+        {
+            dbg_log("DMA should read more than provided: " + h(len) + " " + h(read_count), LOG_DMA);
+        }
+
+        channels[channel].address += len;
+
+        buffer.get(start, len, function(data)
+        {
+            memory.write_blob(data, addr);
+            fn();
+        });
+    };
+
+    // write data, read memory
+    this.do_write = function(buffer, start, len, channel, fn)
+    {
+        var read_count = channels[channel].count,
+            addr = channels[channel].address;
+
+        dbg_log("DMA write channel " + channel, LOG_DMA);
+        dbg_log("to " + h(addr) + " len " + h(read_count), LOG_DMA);
+        dbg_log(channels[channel], LOG_DMA);
+
+        if(len < read_count)
+        {
+            dbg_log("DMA should read more than provided", LOG_DMA);
+        }
+
+        buffer.set(start,
+                new Uint8Array(memory.buffer, addr, read_count + 1),
+                function() {
+                    fn();
+                }
+            );
+    }
+
+    function flipflop_get(old_dword, new_byte)
+    {
+        lsb_msb_flipflop ^= 1;
+
+        if(lsb_msb_flipflop)
+        {
+            // low byte
+            return old_dword & ~0xFF | new_byte;
+        }
+        else
+        {
+            // high byte
+            return old_dword & ~0xFF00 | new_byte << 8;
+        }
+    }
+}

+ 1 - 0
src/externs.js

@@ -0,0 +1 @@
+var console = {};

+ 396 - 0
src/floppy.js

@@ -0,0 +1,396 @@
+"use strict";
+
+/** @constructor */
+function FloppyController(dev, floppy_image)
+{
+    var 
+        io = dev.io,
+        pic = dev.pic,
+        dma = dev.dma,
+
+        bytes_expecting = 0,
+        receiving_command = new Uint8Array(10),
+        receiving_index = 0,
+        next_command,
+
+        response_data = new Uint8Array(10),
+        response_index = 0,
+        response_length = 0,
+
+        /** @const */
+        byte_per_sector = 512,
+
+        /** @const */
+        number_of_heads = 2;
+
+    this.buffer = floppy_image;
+
+    if(!floppy_image)
+    {
+        this.type = 0;
+        return;
+    }
+
+    var number_of_cylinders,
+        number_of_sectors,
+        sectors_per_track;
+
+    if(floppy_image.byteLength === 1024 * 360)
+    {
+        this.type = 1;
+        number_of_cylinders = 40;
+        number_of_sectors = 2880;
+        sectors_per_track = 9;
+    }
+    if(floppy_image.byteLength === 1024 * 1200)
+    {
+        this.type = 2;
+        number_of_cylinders = 80;
+        number_of_sectors = 2400;
+        sectors_per_track = 15;
+    }
+    else if(floppy_image.byteLength === 1024 * 720)
+    {
+        this.type = 3;
+        number_of_cylinders = 80;
+        number_of_sectors = 2880;
+        sectors_per_track = 9;
+    }
+    else if(floppy_image.byteLength === 1024 * 1440)
+    {
+        this.type = 4;
+        number_of_cylinders = 80;
+        number_of_sectors = 2880;
+        sectors_per_track = 18;
+    }
+    else if(floppy_image.byteLength === 1024 * 2880)
+    {
+        this.type = 5;
+        number_of_cylinders = 80;
+        number_of_sectors = 5760;
+        sectors_per_track = 36;
+    }
+    else if(floppy_image.byteLength === 1024 * 1722)
+    {
+        // type is wrong, but only this works for seabios
+        this.type = 5;
+        number_of_cylinders = 82;
+        number_of_sectors = 3444;
+        sectors_per_track = 21;
+    }
+    else
+    {
+        throw unimpl("Unknown floppy size: " + h(floppy_image.byteLength));
+    }
+
+    var status_reg0 = 0,
+        status_reg1 = 0,
+        status_reg2 = 0,
+        drive = 0;
+
+    var last_cylinder = 0,
+        last_head = 0,
+        last_sector = 1;
+
+    io.register_read(0x3F0, port3F0_read);
+    io.register_read(0x3F4, port3F4_read);
+    io.register_read(0x3F5, port3F5_read);
+    io.register_read(0x3F7, port3F7_read);
+
+    io.register_write(0x3F5, port3F5_write);
+
+    function port3F0_read()
+    {
+        dbg_log("3F0 read", LOG_DISK);
+
+        return 0;
+    };
+
+
+    function port3F4_read()
+    {
+        dbg_log("3F4 read", LOG_DISK);
+
+        var return_byte = 0x80;
+
+        if(response_index < response_length)
+        {
+            return_byte |= 0x40 | 0x10;
+        }
+
+        if((dor & 8) === 0)
+        {
+            return_byte |= 0x20;
+        }
+
+        return return_byte;
+    };
+
+    function port3F7_read()
+    {
+        dbg_log("3F7 read", LOG_DISK);
+        return 0x00;
+    }
+
+    function port3F5_read()
+    {
+        if(response_index < response_length)
+        {
+            dbg_log("3F5 read: " + response_data[response_index], LOG_DISK);
+            return response_data[response_index++];
+        }
+        else
+        {
+            dbg_log("3F5 read, empty", LOG_DISK);
+            return 0xFF;
+        }
+    };
+
+    function port3F5_write(reg_byte)
+    {
+        dbg_log("3F5 write " + h(reg_byte), LOG_DISK);
+
+        if(bytes_expecting > 0)
+        {
+            receiving_command[receiving_index++] = reg_byte;
+
+            bytes_expecting--;
+
+            if(bytes_expecting === 0)
+            {
+                if(DEBUG)
+                {
+                    var log = "3F5 command received: ";
+                    for(var i = 0; i < receiving_index; i++) 
+                        log += h(receiving_command[i]) + " ";
+                    dbg_log(log, LOG_DISK);
+                }
+
+                next_command(receiving_command);
+            }
+        }
+        else
+        {
+            switch(reg_byte)
+            {
+                // TODO
+                //case 2:
+                    //next_command = read_complete_track;
+                    //bytes_expecting = 8;
+                    //break;
+                case 0x03:
+                    next_command = fix_drive_data;
+                    bytes_expecting = 2;
+                    break;
+                case 0x04:
+                    next_command = check_drive_status;
+                    bytes_expecting = 1;
+                    break;
+                case 0x05:
+                case 0xC5:
+                    next_command = function(args) { do_sector(true, args); };
+                    bytes_expecting = 8;
+                    break;
+                case 0xE6:
+                    next_command = function(args) { do_sector(false, args); };
+                    bytes_expecting = 8;
+                    break;
+                case 0x07:
+                    next_command = calibrate;
+                    bytes_expecting = 1;
+                    break;
+                case 0x08:
+                    check_interrupt_status();
+                    break;
+                case 0x4A:
+                    next_command = read_sector_id;
+                    bytes_expecting = 1;
+                    break;
+                case 0x0F:
+                    bytes_expecting = 2;
+                    next_command = seek;
+                    break;
+                case 0x0E:
+                    // dump regs 
+                    dbg_log("dump registers", LOG_DISK);
+                    response_data[0] = 0x80;
+                    response_index = 0;
+                    response_length = 1;
+
+                    bytes_expecting = 0;
+                    break;
+                default:
+                    if(DEBUG) throw "unimpl floppy command call " + h(reg_byte);
+            }
+
+            receiving_index = 0;
+        }
+    };
+
+
+    // this should actually be write-only ... but people read it anyway
+    var dor = 0;
+
+    function port3F2_read()
+    {
+        dbg_log("read 3F2: DOR", LOG_DISK);
+        return dor;
+    }
+    io.register_read(0x3F2, port3F2_read);
+
+    function port3F2_write(value)
+    {
+        if((value & 4) === 4 && (dor & 4) === 0)
+        {
+            // reset
+            pic.push_irq(6);
+        }
+
+        dbg_log("start motors: " + h(value >> 4), LOG_DISK);
+        dbg_log("enable dma: " + !!(value & 8), LOG_DISK);
+        dbg_log("reset fdc: " + !!(value & 4), LOG_DISK);
+        dbg_log("drive select: " + (value & 3), LOG_DISK);
+        dbg_log("DOR = " + h(value), LOG_DISK);
+
+        dor = value;
+
+    }
+    io.register_write(0x3F2, port3F2_write);
+
+    function check_drive_status(args)
+    {
+        dbg_log("check drive status", LOG_DISK);
+
+        response_index = 0;
+        response_length = 1;
+        response_data[0] = 1 << 5;
+    }
+
+    function seek(args)
+    {
+        dbg_log("seek", LOG_DISK);
+
+        last_cylinder = args[1];
+        last_head = args[0] >> 2 & 1;
+        
+        if(dor & 8)
+        {
+            pic.push_irq(6);
+        }
+    }
+
+    function calibrate(args)
+    {
+        dbg_log("floppy calibrate", LOG_DISK);
+
+        if(dor & 8)
+        {
+            pic.push_irq(6);
+        }
+    }
+
+    function check_interrupt_status()
+    {
+        // do not trigger an interrupt here
+        dbg_log("floppy check interrupt status", LOG_DISK);
+
+        response_index = 0;
+        response_length = 2;
+
+        response_data[0] = 1 << 5;
+        response_data[1] = last_cylinder;
+    }
+
+    function do_sector(is_write, args)
+    {
+        var head = args[2],
+            cylinder = args[1],
+            sector = args[3],
+            sector_size = 128 * (1 << args[4]),
+            read_count = args[5] - args[3] + 1,
+
+            read_offset = ((head + number_of_heads * cylinder) * sectors_per_track + sector - 1) * sector_size;
+        
+        dbg_log("Floppy Read", LOG_DISK);
+        dbg_log("from " + h(read_offset) + " length " + h(read_count * sector_size), LOG_DISK);
+        dbg_log(cylinder + " / " + head + " / " + sector, LOG_DISK);
+
+        if(!args[4])
+        {
+            dbg_log("FDC: sector count is zero, use data length instead", LOG_DISK);
+        }
+
+        if(is_write)
+        {
+            dma.do_write(floppy_image, read_offset, read_count * sector_size, 2, done);
+        }
+        else
+        {
+            dma.do_read(floppy_image, read_offset, read_count * sector_size, 2, done);
+        }
+
+        function done()
+        {
+            sector++;
+
+            if(sector > sectors_per_track)
+            {
+                sector = 1;
+                head++;
+
+                if(head > 1)
+                {
+                    head = 0;
+                    cylinder++;
+                }
+            }
+
+            last_cylinder = cylinder;
+            last_head = head;
+            last_sector = sector;
+
+            response_index = 0;
+            response_length = 7;
+
+            response_data[0] = head << 2 | 0x20; 
+            response_data[1] = 0; 
+            response_data[2] = 0; 
+            response_data[3] = cylinder; 
+            response_data[4] = head; 
+            response_data[5] = sector; 
+            response_data[6] = args[4];
+
+            if(dor & 8)
+            {
+                pic.push_irq(6);
+            }
+        }
+    }
+    
+    function fix_drive_data(args)
+    {
+        dbg_log("floppy fix drive data " + args, LOG_DISK);
+    }
+
+    function read_sector_id(args)
+    {
+        dbg_log("floppy read sector id " + args, LOG_DISK);
+
+        response_index = 0;
+        response_length = 7;
+
+        response_data[0] = 0;
+        response_data[1] = 0;
+        response_data[2] = 0;
+        response_data[3] = 0;
+        response_data[4] = 0;
+        response_data[5] = 0;
+        response_data[6] = 0;
+
+        if(dor & 8)
+        {
+            pic.push_irq(6);
+        }
+    }
+}
+

+ 1656 - 0
src/fpu.macro.js

@@ -0,0 +1,1656 @@
+"use strict";
+
+/** @const */
+var FPU_LOG_OP = true;
+
+
+/** 
+ * this behaves as if no x87 fpu existed
+ * @constructor
+ */
+function NoFPU(io)
+{
+    this.is_fpu = 0;
+    //cr0 |= 4;
+
+    this.fwait = function()
+    {
+
+    };
+
+    this.op_D8_reg = function(imm8)
+    {
+        trigger_ud();
+    };
+
+    this.op_D8_mem = function(imm8, addr)
+    {
+        trigger_ud();
+    };
+
+    this.op_D9_reg = function(imm8)
+    {
+        trigger_ud();
+    };
+
+    this.op_D9_mem = function(imm8, addr)
+    {
+        var mod = imm8 >> 3 & 7;
+
+        if(mod === 7)
+        {
+            // FNSTCW
+            dbg_log("Unimplemented D9", LOG_FPU);
+            safe_write16(addr, 0);
+        }
+        else
+        {
+            trigger_ud();
+        }
+    };
+
+    this.op_DA = function(imm8)
+    {
+        trigger_ud();
+    };
+
+    this.op_DA_mem = function(imm8, addr)
+    {
+        trigger_ud();
+    };
+
+    this.op_DB_reg = function(imm8)
+    {
+        if(imm8 === 0xE3)
+        {
+            // fninit
+            // don't error, even if no fpu is present
+            dbg_log("Unimplemented DB", LOG_FPU);
+        }
+        else
+        {
+            trigger_ud();
+        }
+    };
+
+    this.op_DB_mem = function(imm8, addr)
+    {
+        trigger_ud();
+    };
+
+    this.op_DC_reg = function(imm8)
+    {
+        trigger_ud();
+    };
+
+    this.op_DC_mem = function(imm8, addr)
+    {
+        trigger_ud();
+    };
+
+    this.op_DD_reg = function(imm8)
+    {
+        trigger_ud();
+    };
+
+    this.op_DD_mem = function(imm8, addr)
+    {
+        var mod = imm8 >> 3 & 7;
+
+        switch(mod)
+        {
+            case 7:
+                // fnstsw / store status word
+                // no fpu -> write nonzero
+                dbg_log("Unimplemented DD", LOG_FPU);
+                safe_write16(addr, 1);
+                break;
+            default:
+                trigger_ud();
+        }
+    };
+
+    this.op_DE_reg = function(imm8)
+    {
+        trigger_ud();
+    };
+
+    this.op_DE_mem = function(imm8, addr)
+    {
+        trigger_ud();
+    };
+
+    this.op_DF_reg = function(imm8)
+    {
+        if(imm8 === 0xE0)
+        {
+            // fnstsw
+            // no fpu -> write nonzero
+            dbg_log("Unimplemented DF", LOG_FPU);
+            reg16[reg_ax] = 1;
+        }
+        else
+        {
+            trigger_ud();
+        }
+    };
+
+    this.op_DF_mem = function(imm8, addr)
+    {
+        trigger_ud();
+    };
+}
+
+/**
+ * @constructor
+ */
+function FPU(io)
+{
+    this.is_fpu = 1;
+
+    // TODO:
+    // - Precision Control
+    // - QNaN, unordered comparison
+    // - Exceptions
+
+    var 
+        /** @const */
+        C0 = 0x100,
+        /** @const */
+        C1 = 0x200,
+        /** @const */
+        C2 = 0x400,
+        /** @const */
+        C3 = 0x4000,
+        /** @const */
+        RESULT_FLAGS = C0 | C1 | C2 | C3,
+        /** @const */
+        STACK_TOP = 0x3800;
+
+    var 
+        // precision, round & infinity control
+        /** @const */
+        PC = 3 << 8,
+        /** @const */
+        RC = 3 << 10,
+        /** @const */
+        IF = 1 << 12;
+
+    // exception bits in the status word
+    var EX_SF = 1 << 6,
+        EX_P = 1 << 5,
+        EX_U = 1 << 4,
+        EX_O = 1 << 3,
+        EX_Z = 1 << 2,
+        EX_D = 1 << 1,
+        EX_I = 1 << 0;
+
+    var 
+        // Why no Float80Array :-(
+        st = new Float64Array(8),
+        st8 = new Uint8Array(st.buffer),
+        st32 = new Uint32Array(st.buffer),
+
+        // bitmap of which stack registers are empty
+        stack_empty = 0xff,
+        stack_ptr = 0,
+
+        // used for conversion
+        float32 = new Float32Array(1),
+        float32_byte = new Uint8Array(float32.buffer),
+        float32_int = new Uint32Array(float32.buffer),
+
+        float64 = new Float64Array(1),
+        float64_byte = new Uint8Array(float64.buffer),
+        float64_int = new Uint32Array(float64.buffer),
+
+        float80_int = new Uint8Array(10),
+
+
+        control_word = 0x37F,
+        status_word = 0,
+        fpu_ip = 0,
+        fpu_ip_selector = 0,
+        fpu_opcode = 0,
+        fpu_dp = 0,
+        fpu_dp_selector = 0,
+
+
+        /** @const */
+        indefinite_nan = NaN;
+
+
+    var constants = new Float64Array([
+        1, Math.log(10) / Math.LN2, Math.LOG2E, Math.PI,
+        Math.log(2) / Math.LN10, Math.LN2, 0
+    ]);
+
+    function fpu_unimpl()
+    {
+        dbg_trace();
+        if(DEBUG) throw "fpu: unimplemented";
+        else trigger_ud();
+    }
+
+    function stack_fault()
+    {
+        // TODO: Interrupt
+        status_word |= EX_SF | EX_I;
+    }
+
+    function invalid_arithmatic()
+    {
+        status_word |= EX_I;
+    }
+
+    function fcom(y)
+    {
+        var x = get_st0();
+
+        status_word &= ~RESULT_FLAGS;
+
+        if(x > y)
+        {
+        }
+        else if(y > x)
+        {
+            status_word |= C0;
+        }
+        else if(x === y)
+        {
+            status_word |= C3;
+        }
+        else
+        {
+            status_word |= C0 | C2 | C3;
+        }
+    }
+
+    function fucom(y)
+    {
+        // TODO
+        fcom(y);
+    }
+
+
+    function fcomi(y)
+    {
+        var x = st[stack_ptr];
+
+        flags_changed &= ~(1 | flag_parity | flag_zero);
+        flags &= ~(1 | flag_parity | flag_zero);
+
+        if(x > y)
+        {
+        }
+        else if(y > x)
+        {
+            flags |= 1;
+        }
+        else if(x === y)
+        {
+            flags |= flag_zero;
+        }
+        else
+        {
+            flags |= 1 | flag_parity | flag_zero;
+        }
+    }
+
+    function fucomi(y)
+    {
+        // TODO
+        fcomi(y);
+    }
+
+    function ftst()
+    {
+        var st0 = get_st0();
+
+        status_word &= ~RESULT_FLAGS;
+
+        if(isNaN(st0))
+        {
+            status_word |= C3 | C2 | C0;
+        }
+        else if(st0 === 0)
+        {
+            status_word |= C3;
+        }
+        else if(st0 < 0)
+        {
+            status_word |= C0;
+        }
+
+        // TODO: unordered (st0 is nan, etc)
+    }
+
+    function fxam()
+    {
+        var x = get_st0();
+
+        status_word &= ~RESULT_FLAGS;
+        status_word |= sign(0) << 9;
+
+        if(stack_empty >> stack_ptr & 1)
+        {
+            status_word |= C3 | C0;
+        }
+        else if(isNaN(x))
+        {
+            status_word |= C0;
+        }
+        else if(x === 0)
+        {
+            status_word |= C3;
+        }
+        else if(x === Infinity || x === -Infinity)
+        {
+            status_word |= C2 | C0;
+        }
+        else
+        {
+            status_word |= C2;
+        }
+        // TODO:
+        // Unsupported, Denormal
+    }
+
+    function finit()
+    {
+        control_word = 0x37F;
+        status_word = 0;
+        fpu_ip = 0;
+        fpu_dp = 0;
+        fpu_opcode = 0;
+
+        stack_empty = 0xFF;
+        stack_ptr = 0;
+    }
+
+    function load_status_word()
+    {
+        return status_word & ~(7 << 11) | stack_ptr << 11;
+    }
+
+    function safe_status_word(sw)
+    {
+        status_word = sw & ~(7 << 11);
+        stack_ptr = sw >> 11 & 7;
+    }
+
+    function load_tag_word()
+    {
+        var tag_word = 0,
+            value;
+
+        for(var i = 0; i < 8; i++)
+        {
+            value = st[i];
+
+            if(stack_empty >> i & 1)
+            {
+                tag_word |= 3 << (i << 1);
+            }
+            else if(value === 0)
+            {
+                tag_word |= 1 << (i << 1);
+            }
+            else if(isNaN(value) || value === Infinity || value === -Infinity)
+            {
+                tag_word |= 2 << (i << 1);
+            }
+        }
+
+        //dbg_log("load  tw=" + h(tag_word) + " se=" + h(stack_empty) + " sp=" + stack_ptr, LOG_FPU);
+
+        return tag_word;
+    }
+
+    function safe_tag_word(tag_word)
+    {
+        stack_empty = 0;
+
+        for(var i = 0; i < 8; i++)
+        {
+            stack_empty |= (tag_word >> i) & (tag_word >> i + 1) & 1 << i;
+        }
+
+        //dbg_log("safe  tw=" + h(tag_word) + " se=" + h(stack_empty), LOG_FPU);
+    }
+
+    function fstenv(addr)
+    {
+        if(operand_size_32)
+        {
+            safe_write16(addr, control_word);
+
+            safe_write16(addr + 4, load_status_word());
+            safe_write16(addr + 8, load_tag_word());
+
+            safe_write32(addr + 12, fpu_ip);
+            safe_write16(addr + 16, fpu_ip_selector);
+            safe_write16(addr + 18, fpu_opcode);
+            safe_write32(addr + 20, fpu_dp);
+            safe_write16(addr + 24, fpu_dp_selector);
+        }
+        else
+        {
+            fpu_unimpl();
+        }
+    }
+
+    function fldenv(addr)
+    {
+        if(operand_size_32)
+        {
+            control_word = safe_read16(addr);
+
+            safe_status_word(safe_read16(addr + 4));
+            safe_tag_word(safe_read16(addr + 8));
+            
+            fpu_ip = safe_read32(addr + 12);
+            fpu_ip_selector = safe_read16(addr + 16);
+            fpu_opcode = safe_read16(addr + 18);
+            fpu_dp = safe_read32(addr + 20);
+            fpu_dp_selector = safe_read16(addr + 24);
+        }
+        else
+        {
+            fpu_unimpl();
+        }
+    }
+
+    function fsave(addr)
+    {
+        fstenv(addr);
+        addr += 28;
+
+        for(var i = 0; i < 8; i++)
+        {
+            store_m80(addr, i - stack_ptr & 7);
+            addr += 10;
+        }
+
+        //dbg_log("save " + [].slice.call(st), LOG_FPU);
+
+        finit();
+    }
+
+    function frstor(addr)
+    {
+        fldenv(addr);
+        addr += 28;
+
+        for(var i = 0; i < 8; i++)
+        {
+            st[i] = load_m80(addr);
+            addr += 10;
+        }
+
+        //dbg_log("rstor " + [].slice.call(st), LOG_FPU);
+    }
+
+    function integer_round(f)
+    {
+        var rc = control_word >> 10 & 3;
+
+        if(rc === 0)
+        {
+            // Round to nearest, or even if equidistant
+            var rounded = Math.round(f);
+
+            if(rounded - f === 0.5 && (rounded & 1))
+            {
+                // Special case: Math.round rounds to positive infinity
+                // if equidistant
+                rounded--;
+            }
+
+            return rounded;
+        }
+            // rc=3 is truncate -> floor for positive numbers
+        else if(rc === 1 || (rc === 3 && f > 0))
+        {
+            return Math.floor(f);
+        }
+        else 
+        {
+            return Math.ceil(f);
+        }
+    }
+
+    function truncate(x)
+    {
+        return x > 0 ? Math.floor(x) : Math.ceil(x);
+    }
+
+    function push(x)
+    {
+        stack_ptr = stack_ptr - 1 & 7;
+
+        if(stack_empty >> stack_ptr & 1)
+        {
+            status_word &= ~C1;
+            stack_empty &= ~(1 << stack_ptr);
+            st[stack_ptr] = x;
+        }
+        else
+        {
+            status_word |= C1;
+            stack_fault();
+            st[stack_ptr] = indefinite_nan;
+        }
+    }
+
+    function pop()
+    {
+        stack_empty |= 1 << stack_ptr;
+        stack_ptr = stack_ptr + 1 & 7;
+    }
+
+    function get_sti(i)
+    {
+        dbg_assert(typeof i === "number" && i >= 0 && i < 8);
+
+        i = i + stack_ptr & 7;
+
+        if(stack_empty >> i & 1)
+        {
+            status_word &= ~C1;
+            stack_fault();
+            return indefinite_nan;
+        }
+        else
+        {
+            return st[i];
+        }
+    }
+
+    function get_st0()
+    {
+        if(stack_empty >> stack_ptr & 1)
+        {
+            status_word &= ~C1;
+            stack_fault();
+            return indefinite_nan;
+        }
+        else
+        {
+            return st[stack_ptr];
+        }
+    }
+
+    function assert_not_empty(i)
+    {
+        if(stack_empty >> (i + stack_ptr & 7) & 1)
+        {
+            status_word &= ~C1;
+        }
+        else
+        {
+        }
+    }
+
+    function load_m80(addr)
+    {
+        var exponent = safe_read16(addr + 8),
+            sign,
+
+            low = safe_read32(addr), 
+            high = safe_read32(addr + 4);
+
+        sign = exponent >> 15;
+        exponent &= ~0x8000;
+
+        if(exponent === 0)
+        {
+            // TODO: denormal numbers
+            return 0;
+        }
+
+        if(exponent < 0x7FFF)
+        {
+            exponent -= 0x3FFF;
+        }
+        else
+        {
+            // TODO: NaN, Infinity
+            //dbg_log("Load m80 TODO", LOG_FPU);
+            float64_byte[7] = 0x7F | sign << 7;
+            float64_byte[6] = 0xF0 | high >> 30 << 3 & 0x08;
+
+            float64_byte[5] = 0;
+            float64_byte[4] = 0;
+
+            float64_int[0] = 0;
+
+            return float64[0];
+        }
+
+        // Note: some bits might be lost at this point
+        var mantissa = low + 0x100000000 * high;
+        
+        if(sign)
+        {
+            mantissa = -mantissa;
+        }
+
+        //console.log("m: " + mantissa);
+        //console.log("e: " + exponent);
+        //console.log("s: " + sign);
+        //console.log("f: " + mantissa * Math.pow(2, exponent - 63));
+
+        // Simply compute the 64 bit floating point number.
+        // An alternative write the mantissa, sign and exponent in the
+        // float64_byte and return float64[0]
+
+        return mantissa * Math.pow(2, exponent - 63);
+    }
+
+    function store_m80(addr, i)
+    {
+        float64[0] = st[stack_ptr + i & 7];
+
+        var sign = float64_byte[7] & 0x80,
+            exponent = (float64_byte[7] & 0x7f) << 4 | float64_byte[6] >> 4,
+            low,
+            high;
+
+        if(exponent === 0x7FF)
+        {
+            // all bits set (NaN and infinity)
+            exponent = 0x7FFF;
+            low = 0;
+            high = 0x80000000 | (float64_int[1] & 0x80000) << 11;
+        }
+        else if(exponent === 0)
+        {
+            // zero and denormal numbers
+            // Just assume zero for now
+            low = 0;
+            high = 0;
+        }
+        else
+        {
+            exponent += 0x3FFF - 0x3FF;
+
+            // does the mantissa need to be adjusted?
+            low = float64_int[0] << 11;
+            high = 0x80000000 | (float64_int[1] & 0xFFFFF) << 11 | (float64_int[0] >>> 21);
+        }
+
+        dbg_assert(exponent >= 0 && exponent < 0x8000);
+
+        safe_write32(addr, low);
+        safe_write32(addr + 4, high);
+
+        safe_write16(addr + 8, sign << 8 | exponent);
+    }
+
+    function load_m64(addr)
+    {
+        float64_int[0] = safe_read32s(addr);
+        float64_int[1] = safe_read32s(addr + 4);
+
+        return float64[0];
+    };
+
+    function store_m64(addr, i)
+    {
+        // protect against writing only a single dword
+        // and then page-faulting
+        translate_address_write(addr + 7);
+
+        float64[0] = get_sti(i);
+
+        safe_write32(addr, float64_int[0]);
+        safe_write32(addr + 4, float64_int[1]);
+    };
+
+    function load_m32(addr)
+    {
+        float32_int[0] = safe_read32s(addr);
+
+        return float32[0];
+    };
+
+    function store_m32(addr, i)
+    {
+        float32[0] = get_sti(i);
+
+        safe_write32(addr, float32_int[0]);
+    };
+
+    // sign of a number on the stack
+    function sign(i)
+    {
+        return st8[(stack_ptr + i & 7) << 3 | 7] >> 7;
+    };
+
+
+    function dbg_log_fpu_op(op, imm8)
+    {
+        if(!FPU_LOG_OP)
+        {
+            return;
+        }
+
+        if(imm8 >= 0xC0)
+        {
+            dbg_log(h(op, 2) + " " + h(imm8, 2) + "/" + (imm8 >> 3 & 7) + "/" + (imm8 & 7) +
+                    " @" + h(instruction_pointer, 8) + " sp=" + stack_ptr + " st=" + h(stack_empty, 2), LOG_FPU);
+        }
+        else
+        {
+            dbg_log(h(op, 2) + " /" + (imm8 >> 3 & 7) + 
+                    "     @" + h(instruction_pointer, 8) + " sp=" + stack_ptr + " st=" + h(stack_empty, 2), LOG_FPU);
+        }
+    }
+
+
+    this.fwait = function()
+    {
+        // TODO:
+        // Exceptions
+    };
+
+
+    this.op_D8_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xD8, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7,
+            sti = get_sti(low),
+            st0 = get_st0();
+
+        switch(mod)
+        {
+            case 0:
+                // fadd
+                st[stack_ptr] = st0 + sti;
+                break;
+            case 1:
+                // fmul
+                st[stack_ptr] = st0 * sti;
+                break;
+            case 2:
+                // fcom
+                fcom(sti);
+                break;
+            case 3:
+                // fcomp
+                fcom(sti);
+                pop();
+                break;
+            case 4:
+                // fsub
+                st[stack_ptr] = st0 - sti;
+                break;
+            case 5:
+                // fsubr
+                st[stack_ptr] = sti - st0;
+                break;
+            case 6:
+                // fdiv
+                st[stack_ptr] = st0 / sti;
+                break;
+            case 7:
+                // fdivr
+                st[stack_ptr] = sti / st0;
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_D8_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xD8, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            m32 = load_m32(addr);
+
+        var st0 = get_st0();
+
+        switch(mod)
+        {
+            case 0:
+                // fadd
+                st[stack_ptr] = st0 + m32;
+                break;
+            case 1:
+                // fmul
+                st[stack_ptr] = st0 * m32;
+                break;
+            case 2:
+                // fcom
+                fcom(m32);
+                break;
+            case 3:
+                // fcomp
+                fcom(m32);
+                pop();
+                break;
+            case 4:
+                // fsub
+                st[stack_ptr] = st0 - m32;
+                break;
+            case 5:
+                // fsubr
+                st[stack_ptr] = m32 - st0;
+                break;
+            case 6:
+                // fdiv
+                st[stack_ptr] = st0 / m32;
+                break;
+            case 7:
+                // fdivr
+                st[stack_ptr] = m32 / st0;
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_D9_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xD9, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                // fld
+                var sti = get_sti(low);
+                push(sti);
+                break;
+            case 1:
+                // fxch
+                var sti = get_sti(low);
+
+                st[stack_ptr + low & 7] = get_st0();
+                st[stack_ptr] = sti;
+                break;
+            case 4:
+                switch(low)
+                {
+                    case 0:
+                        // fchs
+                        st[stack_ptr] = -get_st0();
+                        break;
+                    case 1:
+                        // fabs
+                        st[stack_ptr] = Math.abs(get_st0());
+                        break;
+                    case 4:
+                        ftst();
+                        break;
+                    case 5:
+                        fxam();
+                        break;
+                    default:
+                        dbg_log(low); fpu_unimpl();
+                }
+                break;
+            case 5:
+                push(constants[low]);
+                break;
+            case 6:
+                switch(low)
+                {
+                    case 0:
+                        // f2xm1
+                        st[stack_ptr] = Math.pow(2, get_st0()) - 1;
+                        break;
+                    case 1:
+                        // fyl2x
+                        st[stack_ptr + 1 & 7] = get_sti(1) * Math.log(get_st0()) / Math.LN2;
+                        pop();
+                        break;
+                    case 2:
+                        // fptan
+                        st[stack_ptr] = Math.tan(get_st0());
+                        push(1); // no bug: push constant 1
+                        break;
+                    case 3:
+                        // fpatan
+                        //st[stack_ptr + 1 & 7] = Math.atan(get_sti(1) / get_st0());
+                        st[stack_ptr + 1 & 7] = Math.atan2(get_sti(1), get_st0());
+                        pop();
+                        break;
+                    case 5:
+                        // fprem1
+                        st[stack_ptr] = get_st0() % get_sti(1);
+                        break;
+                    default:
+                        dbg_log(low); fpu_unimpl();
+                }
+                break;
+            case 7:
+                switch(low)
+                {
+                    case 0:
+                        // fprem
+                        st[stack_ptr] = get_st0() % get_sti(1);
+                        break;
+                    case 2:
+                        st[stack_ptr] = Math.sqrt(get_st0());
+                        break;
+                    case 3:
+                        var st0 = get_st0();
+
+                        st[stack_ptr] = Math.sin(st0);
+                        push(Math.cos(st0));
+                        break;
+                    case 4:
+                        // frndint
+                        st[stack_ptr] = integer_round(get_st0());
+                        break;
+                    case 5:
+                        // fscale
+                        st[stack_ptr] = get_st0() * Math.pow(2, truncate(get_sti(1)));
+                        break;
+                    case 6:
+                        st[stack_ptr] = Math.sin(get_st0());
+                        break;
+                    case 7:
+                        st[stack_ptr] = Math.cos(get_st0());
+                        break;
+                    default:
+                        dbg_log(low); fpu_unimpl();
+                }
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_D9_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xD9, imm8);
+
+        var mod = imm8 >> 3 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                var data = load_m32(addr);
+
+                push(data);
+                break;
+            case 2:
+                store_m32(addr, 0);
+                break;
+            case 3:
+                store_m32(addr, 0);
+                pop();
+                break;
+            case 4:
+                fldenv(addr);
+                break;
+            case 5:
+                var word = safe_read16(addr);
+                control_word = word;
+                break;
+            case 6:
+                fstenv(addr);
+                break;
+            case 7:
+                safe_write16(addr, control_word);
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DA_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xDA, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                // fcmovb
+                if(test_b())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 1:
+                // fcmove
+                if(test_z())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 2:
+                // fcmovbe
+                if(test_be())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 3:
+                // fcmovu
+                if(test_p())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 5:
+                if(low === 1)
+                {
+                    // fucompp
+                    fucom(get_sti(1));
+                    pop();
+                    pop();
+                }
+                else
+                {
+                    dbg_log(mod); fpu_unimpl();
+                }
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DA_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xDA, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            m32 = safe_read32s(addr);
+
+        var st0 = get_st0();
+
+        switch(mod)
+        {
+            case 0:
+                // fadd
+                st[stack_ptr] = st0 + m32;
+                break;
+            case 1:
+                // fmul
+                st[stack_ptr] = st0 * m32;
+                break;
+            case 2:
+                // fcom
+                fcom(m32);
+                break;
+            case 3:
+                // fcomp
+                fcom(m32);
+                pop();
+                break;
+            case 4:
+                // fsub
+                st[stack_ptr] = st0 - m32;
+                break;
+            case 5:
+                // fsubr
+                st[stack_ptr] = m32 - st0;
+                break;
+            case 6:
+                // fdiv
+                st[stack_ptr] = st0 / m32;
+                break;
+            case 7:
+                // fdivr
+                st[stack_ptr] = m32 / st0;
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DB_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xDB, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                // fcmovnb
+                if(!test_b())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 1:
+                // fcmovne
+                if(!test_z())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 2:
+                // fcmovnbe
+                if(!test_be())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 3:
+                // fcmovnu
+                if(!test_p())
+                {
+                    st[stack_ptr] = get_sti(low);
+                    stack_empty &= ~(1 << stack_ptr);
+                }
+                break;
+            case 4:
+                if(imm8 === 0xE3)
+                {
+                    finit();
+                }
+                else if(imm8 === 0xE4)
+                {
+                    // fsetpm
+                    // treat as nop
+                }
+                else
+                {
+                    fpu_unimpl();
+                }
+                break;
+            case 5:
+                fucomi(get_sti(low));
+                break;
+            case 6:
+                fcomi(get_sti(low));
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DB_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xDB, imm8);
+
+        var mod = imm8 >> 3 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                // fild
+                var int32 = safe_read32s(addr);
+                push(int32);
+                break;
+            case 2:
+                // fist
+                var st0 = get_st0();
+                if(isNaN(st0) || st0 > 0x7FFFFFFF || st0 < -0x80000000)
+                {
+                    invalid_arithmatic();
+                    safe_write32(addr, 0x80000000);
+                }
+                else
+                {   
+                    // TODO: Invalid operation
+                    safe_write32(addr, integer_round(st0));
+                }
+                break;
+            case 3:
+                // fistp
+                var st0 = get_st0();
+                if(isNaN(st0) || st0 > 0x7FFFFFFF || st0 < -0x80000000)
+                {
+                    invalid_arithmatic();
+                    safe_write32(addr, 0x80000000);
+                }
+                else
+                {   
+                    safe_write32(addr, integer_round(st0));
+                }
+                pop();
+                break;
+            case 5:
+                // fld
+                push(load_m80(addr));
+                break;
+            case 7:
+                // fstp
+                store_m80(addr, 0);
+                pop();
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DC_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xDC, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7,
+            low_ptr = stack_ptr + low & 7,
+            sti = get_sti(low),
+            st0 = get_st0();
+
+        switch(mod)
+        {
+            case 0:
+                // fadd
+                st[low_ptr] = sti + st0;
+                break;
+            case 1:
+                // fmul
+                st[low_ptr] = sti * st0;
+                break;
+            case 2:
+                // fcom
+                fcom(sti);
+                break;
+            case 3:
+                // fcomp
+                fcom(sti);
+                pop();
+                break;
+            case 4:
+                // fsubr
+                st[low_ptr] = st0 - sti;
+                break;
+            case 5:
+                // fsub
+                st[low_ptr] = sti - st0;
+                break;
+            case 6:
+                // fdivr
+                st[low_ptr] = st0 / sti;
+                break;
+            case 7:
+                // fdiv
+                st[low_ptr] = sti / st0;
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DC_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xDC, imm8);
+
+        var
+            mod = imm8 >> 3 & 7,
+            m64 = load_m64(addr);
+
+        var st0 = get_st0();
+
+        switch(mod)
+        {
+            case 0:
+                // fadd
+                st[stack_ptr] = st0 + m64;
+                break;
+            case 1:
+                // fmul
+                st[stack_ptr] = st0 * m64;
+                break;
+            case 2:
+                // fcom
+                fcom(m64);
+                break;
+            case 3:
+                // fcomp
+                fcom(m64);
+                pop();
+                break;
+            case 4:
+                // fsub
+                st[stack_ptr] = st0 - m64;
+                break;
+            case 5:
+                // fsubr
+                st[stack_ptr] = m64 - st0;
+                break;
+            case 6:
+                // fdiv
+                st[stack_ptr] = st0 / m64;
+                break;
+            case 7:
+                // fdivr
+                st[stack_ptr] = m64 / st0;
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DD_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xDD, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                // ffree
+                stack_empty |= 1 << (stack_ptr + low & 7);
+                break;
+            case 2:
+                // fst
+                st[stack_ptr + low & 7] = get_st0();
+                break;
+            case 3:
+                // fstp
+                if(low === 0)
+                {
+                    pop();
+                }
+                else 
+                {
+                    st[stack_ptr + low & 7] = get_st0();
+                    pop();
+                }
+                break;
+            case 4:
+                fucom(get_sti(low));
+                break;
+            case 5:
+                // fucomp
+                fucom(get_sti(low));
+                pop();
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DD_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xDD, imm8);
+
+        var mod = imm8 >> 3 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                // fld
+                var data = load_m64(addr);
+                push(data);
+                break;
+            case 2:
+                // fst
+                store_m64(addr, 0);
+                break;
+            case 3:
+                // fstp
+                store_m64(addr, 0);
+                pop();
+                break;
+            case 4:
+                frstor(addr);
+                break;
+            case 6:
+                // fsave
+                fsave(addr);
+                break;
+            case 7:
+                // fnstsw / store status word
+                safe_write16(addr, load_status_word());
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+
+    this.op_DE_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xDE, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7,
+            low_ptr = stack_ptr + low & 7,
+            sti = get_sti(low),
+            st0 = get_st0();
+
+        switch(mod)
+        {
+            case 0:
+                // faddp
+                st[low_ptr] = sti + st0;
+                break;
+            case 1:
+                // fmulp
+                st[low_ptr] = sti * st0;
+                break;
+            case 2:
+                // fcomp
+                fcom(sti);
+                break;
+            case 3:
+                // fcompp
+                if(low === 1)
+                {
+                    fcom(st[low_ptr]);
+                    pop();
+                }
+                else
+                {
+                    // not a valid encoding
+                    dbg_log(mod); 
+                    fpu_unimpl();
+                }
+                break;
+            case 4:
+                // fsubrp
+                st[low_ptr] = st0 - sti;
+                break;
+            case 5:
+                // fsubp
+                st[low_ptr] = sti - st0;
+                break;
+            case 6:
+                // fdivrp
+                st[low_ptr] = st0 / sti;
+                break;
+            case 7:
+                // fdivp
+                st[low_ptr] = sti / st0;
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+
+        pop();
+    };
+
+    this.op_DE_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xDE, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            m16 = safe_read16s(addr);
+
+        var st0 = get_st0();
+
+        switch(mod)
+        {
+            case 0:
+                // fadd
+                st[stack_ptr] = st0 + m16;
+                break;
+            case 1:
+                // fmul
+                st[stack_ptr] = st0 * m16;
+                break;
+            case 2:
+                // fcom
+                fcom(m16);
+                break;
+            case 3:
+                // fcomp
+                fcom(m16);
+                pop();
+                break;
+            case 4:
+                // fsub
+                st[stack_ptr] = st0 - m16;
+                break;
+            case 5:
+                // fsubr
+                st[stack_ptr] = m16 - st0;
+                break;
+            case 6:
+                // fdiv
+                st[stack_ptr] = st0 / m16;
+                break;
+            case 7:
+                // fdivr
+                st[stack_ptr] = m16 / st0;
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DF_reg = function(imm8)
+    {
+        dbg_log_fpu_op(0xDF, imm8);
+
+        var mod = imm8 >> 3 & 7,
+            low = imm8 & 7;
+
+        switch(mod)
+        {
+            case 4:
+                if(imm8 === 0xE0)
+                {
+                    // fnstsw
+                    reg16[reg_ax] = load_status_word();
+                }
+                else
+                {
+                    dbg_log(imm8);
+                    fpu_unimpl();
+                }
+                break;
+            case 5:
+                // fucomip
+                fucomi(get_sti(low));
+                pop();
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+
+    this.op_DF_mem = function(imm8, addr)
+    {
+        dbg_log_fpu_op(0xDF, imm8);
+
+        var mod = imm8 >> 3 & 7;
+
+        switch(mod)
+        {
+            case 0:
+                var m16 = safe_read16s(addr);
+
+                push(m16);
+                break;
+            case 2:
+                // fist
+                var st0 = get_st0();
+                if(isNaN(st0) || st0 > 0x7FFF || st0 < -0x8000)
+                {
+                    invalid_arithmatic();
+                    safe_write16(addr, 0x8000);
+                }
+                else
+                {   
+                    safe_write16(addr, integer_round(st0));
+                }
+                break;
+            case 3:
+                // fistp
+                var st0 = get_st0();
+                if(isNaN(st0) || st0 > 0x7FFF || st0 < -0x8000)
+                {
+                    invalid_arithmatic();
+                    safe_write16(addr, 0x8000);
+                }
+                else
+                {   
+                    safe_write16(addr, integer_round(st0));
+                }
+                pop();
+                break;
+            case 5:
+                // fild
+                var low = safe_read32(addr);
+
+                var high = safe_read32(addr + 4);
+
+                var m64 = low + 0x100000000 * high;
+
+                if(high >> 31)
+                {
+                    m64 -= 0x10000000000000000;
+                }
+
+                push(m64);
+                break;
+            case 7:
+                // fistp
+                var st0 = integer_round(get_st0());
+
+                if(isNaN(st0) || st0 > 0x7FFFFFFFFFFFFFFF || st0 < -0x8000000000000000)
+                {
+                    st0 = 0x8000000000000000;
+                    invalid_arithmatic();
+                }
+                pop();
+                safe_write32(addr, st0);
+
+                st0 /= 0x100000000;
+
+                if(st0 < 0 && st0 > -1)
+                    st0 = -1;
+
+                safe_write32(addr + 4, st0);
+                break;
+            default:
+                dbg_log(mod); 
+                fpu_unimpl();
+        }
+    };
+}

+ 2263 - 0
src/instructions.macro.js

@@ -0,0 +1,2263 @@
+"use strict";
+
+
+
+var table16 = [], 
+    table32 = [], 
+    table0F_16 = [],
+    table0F_32 = [];
+
+
+#define do_op() table[read_imm8()]()
+
+#define unimplemented_sse(num) op(num, {\
+    dbg_log("No SSE", LOG_CPU);\
+    trigger_ud();\
+})
+
+#define undefined_instruction(num) op(num, {\
+    if(DEBUG) throw "Possible fault: undefined instruction"; \
+    trigger_ud();\
+})
+
+#define todo_op(num) op(num, {\
+    todo();\
+})
+
+#define todo()\
+    if(DEBUG) { dbg_trace(); throw "TODO"; }\
+    trigger_ud();
+
+
+#define each_jcc(macro)\
+    macro(0x0, (test_o()));\
+    macro(0x1, (!test_o()));\
+    macro(0x2, (test_b()));\
+    macro(0x3, (!test_b()));\
+    macro(0x4, (test_z()));\
+    macro(0x5, (!test_z()));\
+    macro(0x6, (test_be()));\
+    macro(0x7, (!test_be()));\
+    macro(0x8, (test_s()));\
+    macro(0x9, (!test_s()));\
+    macro(0xA, (test_p()));\
+    macro(0xB, (!test_p()));\
+    macro(0xC, (test_l()));\
+    macro(0xD, (!test_l()));\
+    macro(0xE, (test_le()));\
+    macro(0xF, (!test_le()));
+
+#define each_reg(macro)\
+    macro(0, reg_ax, reg_eax)\
+    macro(1, reg_cx, reg_ecx)\
+    macro(2, reg_dx, reg_edx)\
+    macro(3, reg_bx, reg_ebx)\
+    macro(4, reg_sp, reg_esp)\
+    macro(5, reg_bp, reg_ebp)\
+    macro(6, reg_si, reg_esi)\
+    macro(7, reg_di, reg_edi)
+
+#define each_reg8(macro)\
+    macro(0, reg_al)\
+    macro(1, reg_cl)\
+    macro(2, reg_dl)\
+    macro(3, reg_bl)\
+    macro(4, reg_ah)\
+    macro(5, reg_ch)\
+    macro(6, reg_dh)\
+    macro(7, reg_bh)
+
+// no cmp, because it uses different arguments
+#define each_arith(macro)\
+    macro(0, add)\
+    macro(1,  or)\
+    macro(2, adc)\
+    macro(3, sbb)\
+    macro(4, and)\
+    macro(5, sub)\
+    macro(6, xor)
+
+
+
+#define safe_pop32s(dest) dest = pop32s();
+#define safe_pop16(dest) dest = pop16();
+
+
+// very special, should be somewhere else?
+#define lss_op(sreg)\
+    if(modrm_byte >= 0xC0) { raise_exception(6); return; }\
+    if(operand_size_32) { lss32(sreg, modrm_resolve(modrm_byte), modrm_byte >> 3 & 7); }\
+    else { lss16(sreg, modrm_resolve(modrm_byte), modrm_byte >> 2 & 14); }
+
+
+#define bt_op(op, arg16, arg32)\
+    if(operand_size_32) {\
+        if(modrm_byte < 0xC0) {\
+            op ## _mem(modrm_resolve(modrm_byte), arg32);\
+        } else {\
+            reg_e32 = op ## _reg(reg_e32s, arg32 & 31);\
+        }\
+    } else {\
+        if(modrm_byte < 0xC0) {\
+            op ## _mem(modrm_resolve(modrm_byte), arg16);\
+        } else {\
+            reg_e16 = op ## _reg(reg_e16, arg16 & 15);\
+        }\
+    }
+
+
+// equivalent to switch(modrm_byte >> 3 & 7)
+//#define sub_op(i0, i1, i2, i3, i4, i5, i6, i7) \
+//    if(modrm_byte & 0x20) { sub_op1(i4, i5, i6, i7) }\
+//    else { sub_op1(i0, i1, i2, i3) }
+//
+//#define sub_op1(i0, i1, i2, i3)\
+//    if(modrm_byte & 0x10) { sub_op2(i2, i3) }\
+//    else { sub_op2(i0, i1) }
+//
+//#define sub_op2(i0, i1)\
+//    if(modrm_byte & 0x08) { i1 }\
+//    else { i0 }
+
+
+#define sub_op(i0, i1, i2, i3, i4, i5, i6, i7) \
+    switch(modrm_byte >> 3 & 7) {\
+        case 0: i0; break;\
+        case 1: i1; break;\
+        case 2: i2; break;\
+        case 3: i3; break;\
+        case 4: i4; break;\
+        case 5: i5; break;\
+        case 6: i6; break;\
+        case 7: i7; break;\
+    }
+
+#define pop_sreg_op(n, reg)\
+    op2(n, \
+        { switch_seg(reg, memory.read16(get_esp_read(0))); stack_reg[reg_vsp] += 2; }, \
+        { switch_seg(reg, memory.read16(get_esp_read(0))); stack_reg[reg_vsp] += 4; });
+
+
+#define reg_e8 reg8[modrm_byte << 2 & 0xC | modrm_byte >> 2 & 1]
+#define reg_e8s reg8s[modrm_byte << 2 & 0xC | modrm_byte >> 2 & 1]
+#define reg_g8 reg8[modrm_byte >> 1 & 0xC | modrm_byte >> 5 & 1]
+
+#define reg_e16 reg16[modrm_byte << 1 & 14]
+#define reg_e16s reg16s[modrm_byte << 1 & 14]
+#define reg_g16 reg16[modrm_byte >> 2 & 14] 
+#define reg_g16s reg16s[modrm_byte >> 2 & 14] 
+
+#define reg_e32 reg32[modrm_byte & 7]
+#define reg_e32s reg32s[modrm_byte & 7]
+#define reg_g32 reg32[modrm_byte >> 3 & 7] 
+#define reg_g32s reg32s[modrm_byte >> 3 & 7] 
+
+
+#define modrm_read(size)\
+    if(modrm_byte < 0xC0) {\
+        var data = safe_read ## size(modrm_resolve(modrm_byte)); \
+    } else {\
+        data = reg_e ## size;\
+    }
+
+
+#define read_e8 modrm_read(8)
+#define read_e8s modrm_read(8s)
+#define read_e16 modrm_read(16)
+#define read_e16s modrm_read(16s)
+#define read_e32 modrm_read(32)
+#define read_e32s modrm_read(32s)
+
+
+
+// use modrm_byte to write a value to memory or register 
+// (without reading it beforehand)
+#define modrm_set(arg, size) \
+    if(modrm_byte < 0xC0) {\
+        safe_write ## size(modrm_resolve(modrm_byte), arg);\
+    } else {\
+        reg_e ## size = arg;\
+    }
+
+#define set_eb(arg) modrm_set(arg, 8)
+#define set_ev16(arg) modrm_set(arg, 16)
+#define set_ev32(arg) modrm_set(arg, 32)
+
+
+// use modrm_byte to write a value to memory or register,
+// using the previous data from memory or register.
+// op is a function call that needs to return the result
+#define write_e8(op)\
+    var data;\
+    var addr;\
+    if(modrm_byte < 0xC0) {\
+        addr = translate_address_write(modrm_resolve(modrm_byte));\
+        data = memory.read8(addr);\
+        memory.write8(addr, op);\
+    } else {\
+        data = reg_e8;\
+        reg_e8 = op;\
+    }
+
+
+#define write_ev16(op)\
+    var data;\
+    var virt_addr;\
+    var phys_addr;\
+    var phys_addr_high;\
+    if(modrm_byte < 0xC0) {\
+        virt_addr = modrm_resolve(modrm_byte);\
+        phys_addr = translate_address_write(virt_addr);\
+        if(paging && (virt_addr & 0xFFF) === 0xFFF) {\
+            phys_addr_high = translate_address_write(virt_addr + 1);\
+            data = virt_boundary_read16(phys_addr, phys_addr_high);\
+            virt_boundary_write16(phys_addr, phys_addr_high, op);\
+        } else {\
+            data = memory.read16(phys_addr);\
+            memory.write16(phys_addr, op);\
+        }\
+    } else {\
+        data = reg_e16;\
+        reg_e16 = op;\
+    }
+
+
+#define write_ev32(op)\
+    var data;\
+    var virt_addr;\
+    var phys_addr;\
+    var phys_addr_high;\
+    if(modrm_byte < 0xC0) {\
+        virt_addr = modrm_resolve(modrm_byte);\
+        phys_addr = translate_address_write(virt_addr);\
+        if(paging && (virt_addr & 0xFFF) >= 0xFFD) {\
+            phys_addr_high = translate_address_write(virt_addr + 3);\
+            data = virt_boundary_read32s(phys_addr, phys_addr_high) >>> 0;\
+            virt_boundary_write32(phys_addr, phys_addr_high, op);\
+        } else {\
+            data = memory.read32s(phys_addr) >>> 0;\
+            memory.write32(phys_addr, op);\
+        }\
+    } else {\
+        data = reg_e32;\
+        reg_e32s = op;\
+    }
+
+
+
+#define write_ev32s(op)\
+    var data;\
+    var virt_addr;\
+    var phys_addr;\
+    var phys_addr_high;\
+    if(modrm_byte < 0xC0) {\
+        virt_addr = modrm_resolve(modrm_byte);\
+        phys_addr = translate_address_write(virt_addr);\
+        if(paging && (virt_addr & 0xFFF) >= 0xFFD) {\
+            phys_addr_high = translate_address_write(virt_addr + 3);\
+            data = virt_boundary_read32s(phys_addr, phys_addr_high);\
+            virt_boundary_write32(phys_addr, phys_addr_high, op);\
+        } else {\
+            data = memory.read32s(phys_addr);\
+            memory.write32(phys_addr, op);\
+        }\
+    } else {\
+        data = reg_e32s;\
+        reg_e32s = op;\
+    }
+
+
+#define op(n, code) table16[n] = table32[n] = function() { code };
+
+// opcode with modrm byte
+#define opm(n, code)\
+    table16[n] = table32[n] = function() { var modrm_byte = read_imm8(); code };
+
+// opcode that has a 16 and a 32 bit version
+#define op2(n, code16, code32)\
+    table16[n] = function() { code16 };\
+    table32[n] = function() { code32 };\
+
+#define opm2(n, code16, code32)\
+    table16[n] = function() { var modrm_byte = read_imm8(); code16 };\
+    table32[n] = function() { var modrm_byte = read_imm8(); code32 };\
+
+
+#define arith_group(n, instr, sign)\
+    opm(n, { write_e8(instr ## 8(data, reg_g8)) })\
+    opm2(n | 1, { write_ev16(instr ## 16(data, reg_g16)) }, { write_ev32 ## sign(instr ## 32(data, reg_g32 ## sign)) })\
+    opm(n | 2, { read_e8; reg_g8 = instr ## 8(reg_g8, data); })\
+    opm2(n | 3, { read_e16; reg_g16 = instr ## 16(reg_g16, data); }, { read_e32 ## sign; reg_g32s = instr ## 32(reg_g32 ## sign, data); })\
+    op(n | 4, { reg8[reg_al] = instr ## 8(reg8[reg_al], read_imm8()); })\
+    op2(n | 5, { reg16[reg_ax] = instr ## 16(reg16[reg_ax], read_imm16()); }, { reg32[reg_eax] = instr ## 32(reg32 ## sign[reg_eax], read_imm32 ## sign()); })\
+    
+
+
+// instructions start here
+
+arith_group(0x00, add, );
+
+op2(0x06, { push16(sreg[reg_es]); }, { push32(sreg[reg_es]); });
+pop_sreg_op(0x07, reg_es);
+//op2(0x07, 
+//    { safe_pop16(sreg[reg_es]); switch_seg(reg_es, memory.read16(get_esp_read(0))); }, 
+//    { safe_pop32s(sreg[reg_es]); switch_seg(reg_es); });
+
+arith_group(0x08, or, s);
+
+op2(0x0E, { push16(sreg[reg_cs]); }, { push32(sreg[reg_cs]); });
+op(0x0F, { table0F[read_imm8()](); });
+
+arith_group(0x10, adc, );
+
+op2(0x16, { push16(sreg[reg_ss]); }, { push32(sreg[reg_ss]); });
+pop_sreg_op(0x17, reg_ss);
+//op2(0x17, 
+//    { safe_pop16(sreg[reg_ss]); switch_seg(reg_ss); }, 
+//    { safe_pop32s(sreg[reg_ss]); switch_seg(reg_ss); });
+
+arith_group(0x18, sbb, );
+
+op2(0x1E, { push16(sreg[reg_ds]); }, { push32(sreg[reg_ds]); });
+pop_sreg_op(0x1F, reg_ds);
+//op2(0x1F, 
+//    { safe_pop16(sreg[reg_ds]); switch_seg(reg_ds); }, 
+//    { safe_pop32s(sreg[reg_ds]); switch_seg(reg_ds); });
+
+arith_group(0x20, and, s);
+
+op(0x26, { seg_prefix(reg_es); });
+op(0x27, { bcd_daa(); });
+
+arith_group(0x28, sub, );
+
+op(0x2E, { seg_prefix(reg_cs); });
+op(0x2F, { bcd_das(); });
+
+arith_group(0x30, xor, s);
+
+op(0x36, { seg_prefix(reg_ss); });
+op(0x37, { bcd_aaa(); });
+
+opm(0x38, { read_e8; cmp8(data, reg_g8); })
+opm2(0x39, { read_e16; cmp16(data, reg_g16); }, { read_e32; cmp32(data, reg_g32); })
+opm(0x3A, { read_e8; cmp8(reg_g8, data); })
+opm2(0x3B, { read_e16; cmp16(reg_g16, data); }, { read_e32; cmp32(reg_g32, data); })
+op(0x3C, { cmp8(reg8[reg_al], read_imm8()); })
+op2(0x3D, { cmp16(reg16[reg_ax], read_imm16()); }, { cmp32(reg32[reg_eax], read_imm32()); })
+
+op(0x3E, { seg_prefix(reg_ds); });
+op(0x3F, { bcd_aas(); });
+
+
+#define group40(n, r16, r32)\
+    op2(0x40 | n, { reg16[r16] = inc16(reg16[r16]); }, { reg32[r32] = inc32(reg32[r32]); });
+each_reg(group40);
+
+
+#define group48(n, r16, r32)\
+    op2(0x48 | n, { reg16[r16] = dec16(reg16[r16]); }, { reg32[r32] = dec32(reg32[r32]); });
+each_reg(group48);
+
+
+#define group50(n, r16, r32)\
+    op2(0x50 | n, { push16(reg16[r16]); }, { push32(reg32s[r32]); })
+each_reg(group50);
+
+#define group58(n, r16, r32)\
+    op2(0x58 | n, { safe_pop16(reg16[r16]); }, { safe_pop32s(reg32[r32]); })
+each_reg(group58);
+
+
+op2(0x60, { pusha16(); }, { pusha32(); });
+op2(0x61, { popa16(); }, { popa32(); });
+
+op(0x62, { throw unimpl("bound instruction"); });
+opm(0x63, { 
+    // arpl
+    write_ev16(arpl(data, modrm_byte >> 2 & 14));
+});
+
+op(0x64, { seg_prefix(reg_fs); });
+op(0x65, { seg_prefix(reg_gs); });
+
+op(0x66, {
+    // Operand-size override prefix
+    dbg_assert(operand_size_32 === is_32);
+
+    operand_size_32 = !is_32;
+    update_operand_size();
+
+    do_op();
+
+    operand_size_32 = is_32;
+    update_operand_size();
+});
+
+op(0x67, {
+    // Address-size override prefix
+    dbg_assert(address_size_32 === is_32);
+
+    address_size_32 = !is_32;
+    update_address_size();
+
+    do_op();
+
+    address_size_32 = is_32;
+    update_address_size();
+});
+
+op2(0x68, { push16(read_imm16()); }, { push32(read_imm32s()); });
+
+opm2(0x69, {
+    read_e16s;
+    reg_g16 = imul_reg16(read_imm16s(), data);
+}, {
+    read_e32s;
+    reg_g32 = imul_reg32(read_imm32s(), data);
+});
+
+op2(0x6A, { push16(read_imm8s()); }, { push32(read_imm8s()); });
+
+opm2(0x6B, {
+    read_e16s;
+    reg_g16 = imul_reg16(read_imm8s(), data);
+}, {
+    read_e32s;
+    reg_g32 = imul_reg32(read_imm8s(), data);
+});
+
+op(0x6C, { insb(); });
+op2(0x6D, { insw(); }, { insd(); });
+op(0x6E, { outsb(); });
+op2(0x6F, { outsw(); }, { outsd(); });
+
+
+#define group70(n, test) \
+    op(0x70 | n, { \
+        if(test) { \
+            instruction_pointer = instruction_pointer + read_imm8s() | 0;\
+        }\
+        instruction_pointer++;\
+    });
+
+each_jcc(group70);
+
+
+opm(0x80, { 
+    sub_op(
+        { write_e8(add8(data, read_imm8())); },
+        { write_e8( or8(data, read_imm8())); },
+        { write_e8(adc8(data, read_imm8())); },
+        { write_e8(sbb8(data, read_imm8())); },
+        { write_e8(and8(data, read_imm8())); },
+        { write_e8(sub8(data, read_imm8())); },
+        { write_e8(xor8(data, read_imm8())); },
+        { read_e8; cmp8(data, read_imm8()); }
+    )
+});
+opm2(0x81, {
+    sub_op(
+        { write_ev16(add16(data, read_imm16())); },
+        { write_ev16( or16(data, read_imm16())); },
+        { write_ev16(adc16(data, read_imm16())); },
+        { write_ev16(sbb16(data, read_imm16())); },
+        { write_ev16(and16(data, read_imm16())); },
+        { write_ev16(sub16(data, read_imm16())); },
+        { write_ev16(xor16(data, read_imm16())); },
+        { read_e16;  cmp16(data, read_imm16()); }
+    )
+}, {
+    sub_op(
+        { write_ev32(add32(data, read_imm32())); },
+        { write_ev32s( or32(data, read_imm32s())); },
+        { write_ev32(adc32(data, read_imm32())); },
+        { write_ev32(sbb32(data, read_imm32())); },
+        { write_ev32s(and32(data, read_imm32s())); },
+        { write_ev32(sub32(data, read_imm32())); },
+        { write_ev32s(xor32(data, read_imm32s())); },
+        { read_e32;  cmp32(data, read_imm32()); }
+    )
+});
+op(0x82, { 
+    table[0x80](); // alias
+});
+opm2(0x83, {
+    sub_op(
+        { write_ev16(add16(data, read_imm8s() & 0xFFFF)); },
+        { write_ev16( or16(data, read_imm8s())); },
+        { write_ev16(adc16(data, read_imm8s() & 0xFFFF)); },
+        { write_ev16(sbb16(data, read_imm8s() & 0xFFFF)); },
+        { write_ev16(and16(data, read_imm8s())); },
+        { write_ev16(sub16(data, read_imm8s() & 0xFFFF)); },
+        { write_ev16(xor16(data, read_imm8s())); },
+        { read_e16;  cmp16(data, read_imm8s() & 0xFFFF); }
+    )
+}, {
+    sub_op(
+        { write_ev32(add32(data, read_imm8s() >>> 0)); },
+        { write_ev32s( or32(data, read_imm8s())); },
+        { write_ev32(adc32(data, read_imm8s() >>> 0)); },
+        { write_ev32(sbb32(data, read_imm8s() >>> 0)); },
+        { write_ev32s(and32(data, read_imm8s())); },
+        { write_ev32(sub32(data, read_imm8s() >>> 0)); },
+        { write_ev32s(xor32(data, read_imm8s())); },
+        { read_e32;  cmp32(data, read_imm8s() >>> 0); }
+    )
+});
+
+opm(0x84, { read_e8; test8(data, reg_g8); })
+opm2(0x85, { read_e16; test16(data, reg_g16); }, { read_e32s; test32(data, reg_g32s); })
+
+
+opm(0x86, { write_e8(xchg8(data, modrm_byte)); });
+opm2(0x87, { 
+    write_ev16(xchg16(data, modrm_byte)); 
+}, {
+    write_ev32(xchg32(data, modrm_byte)); 
+});
+
+opm(0x88, { set_eb(reg_g8); })
+opm2(0x89, { set_ev16(reg_g16); }, { set_ev32(reg_g32s); })
+
+opm(0x8A, {
+    read_e8;
+    reg_g8 = data;
+});
+opm2(0x8B, {
+    read_e16;
+    reg_g16 = data;
+}, {
+    read_e32s;
+    reg_g32s = data;
+});
+
+opm2(0x8C, { set_ev16(sreg[modrm_byte >> 3 & 7]); }, { set_ev32(sreg[modrm_byte >> 3 & 7]); })
+
+op2(0x8D, { lea16(); }, { lea32(); });
+
+opm(0x8E, {
+    var mod = modrm_byte >> 3 & 7;
+
+    read_e16;
+
+    switch_seg(mod, data);
+
+    if(mod === reg_ss)
+    {
+        // TODO
+        // run next instruction, so no irqs are handled
+    }
+});
+
+opm(0x8F, {
+    // pop
+    if(operand_size_32)
+    {
+        // change esp first, then resolve modrm address
+        var sp = get_esp_read(0);
+        // TODO unsafe
+
+        stack_reg[reg_vsp] += 4;
+        set_ev32(memory.read32s(sp));
+    }
+    else
+    {
+        var sp = get_esp_read(0);
+
+        stack_reg[reg_vsp] += 2;
+        set_ev16(memory.read16(sp));
+    }
+});
+
+#define group90(n, r16, r32) op2(0x90 | n, { xchg16r(r16) }, { xchg32r(r32) })
+each_reg(group90)
+
+op(0x90,  /* nop */ );
+
+
+op2(0x98, 
+    { /* cbw */ reg16[reg_ax] = reg8s[reg_al]; },
+    { /* cwde */ reg32[reg_eax] = reg16s[reg_ax]; });
+
+op2(0x99, 
+    { /* cwd */ reg16[reg_dx] = reg16s[reg_ax] >> 15; },
+    { /* cdq */ reg32[reg_edx] = reg32s[reg_eax] >> 31; });
+    
+op2(0x9A, {
+    // callf
+
+    if(protected_mode)
+    {
+        throw unimpl("16 bit callf in protected mode");
+    }
+    else
+    {
+        var new_ip = read_imm16();
+        var new_cs = read_imm16();
+
+        push16(sreg[reg_cs]);
+        push16(get_real_ip());
+
+        switch_seg(reg_cs, new_cs);
+        instruction_pointer = get_seg(reg_cs) + new_ip | 0;
+    }
+}, {
+    if(protected_mode)
+    {
+        throw unimpl("callf");
+    }
+    else
+    {
+        var new_ip = read_imm32s();
+        var new_cs = read_imm16();
+
+        push32(sreg[reg_cs]);
+        push32(get_real_ip());
+
+        switch_seg(reg_cs, new_cs);
+        instruction_pointer = get_seg(reg_cs) + new_ip | 0;
+    }
+});
+
+op(0x9B, {
+    // fwait: check for pending fpu exceptions
+    fpu.fwait();
+});
+op2(0x9C, {
+    // pushf
+    load_flags();
+    push16(flags);
+}, {
+    // pushf
+    load_flags();
+    push32(flags);
+});
+op2(0x9D, {
+    // popf
+    var tmp;
+    safe_pop16(tmp);
+    update_flags(tmp);
+
+    handle_irqs();
+}, {
+    // popf
+    update_flags(pop32s());
+
+    handle_irqs();
+});
+op(0x9E, {
+    // sahf
+    flags = (flags & ~0xFF) | reg8[reg_ah];
+    flags = (flags & flags_mask) | flags_default;
+    flags_changed = 0;
+});
+op(0x9F, {
+    // lahf
+    load_flags();
+    reg8[reg_ah] = flags;
+});
+
+op(0xA0, {
+    // mov
+    var data = safe_read8(read_moffs());
+    reg8[reg_al] = data;
+});
+op2(0xA1, {
+    // mov
+    var data = safe_read16(read_moffs());
+    reg16[reg_ax] = data;
+}, {
+    var data = safe_read32s(read_moffs());
+    reg32[reg_eax] = data;
+});
+op(0xA2, {
+    // mov
+    safe_write8(read_moffs(), reg8[reg_al]);
+});
+op2(0xA3, {
+    // mov
+    safe_write16(read_moffs(), reg16[reg_ax]);
+}, {
+    safe_write32(read_moffs(), reg32s[reg_eax]);
+});
+
+op(0xA4, { movsb(); });
+op2(0xA5, { movsw(); }, { movsd(); });
+op(0xA6, { cmpsb(); });
+op2(0xA7, { cmpsw(); }, { cmpsd(); });
+
+op(0xA8, {
+    test8(reg8[reg_al], read_imm8());
+});
+op2(0xA9, {
+    test16(reg16[reg_ax], read_imm16());
+}, {
+    test32(reg32s[reg_eax], read_imm32s());
+});
+
+op(0xAA, { stosb(); });
+op2(0xAB, { stosw(); }, { stosd(); });
+op(0xAC, { lodsb(); });
+op2(0xAD, { lodsw(); }, { lodsd(); });
+op(0xAE, { scasb(); });
+op2(0xAF, { scasw(); }, { scasd(); });
+
+
+#define groupB0(n, r8) op(0xB0 | n, { reg8[r8] = read_imm8(); });
+each_reg8(groupB0);
+
+
+#define groupB8(n, r16, r32)\
+    op2(0xB8 | n, { reg16[r16] = read_imm16(); }, { reg32s[r32] = read_imm32s(); });
+each_reg(groupB8);
+
+
+opm(0xC0, { 
+    sub_op(
+        { write_e8(rol8(data, read_imm8() & 31)); },
+        { write_e8(ror8(data, read_imm8() & 31)); },
+        { write_e8(rcl8(data, read_imm8() & 31)); },
+        { write_e8(rcr8(data, read_imm8() & 31)); },
+        { write_e8(shl8(data, read_imm8() & 31)); },
+        { write_e8(shr8(data, read_imm8() & 31)); },
+        { write_e8(shl8(data, read_imm8() & 31)); },
+        { write_e8(sar8(data, read_imm8() & 31)); }
+    )
+});
+opm2(0xC1, { 
+    sub_op(
+        { write_ev16(rol16(data, read_imm8() & 31)); },
+        { write_ev16(ror16(data, read_imm8() & 31)); },
+        { write_ev16(rcl16(data, read_imm8() & 31)); },
+        { write_ev16(rcr16(data, read_imm8() & 31)); },
+        { write_ev16(shl16(data, read_imm8() & 31)); },
+        { write_ev16(shr16(data, read_imm8() & 31)); },
+        { write_ev16(shl16(data, read_imm8() & 31)); },
+        { write_ev16(sar16(data, read_imm8() & 31)); }
+    )
+}, {
+    sub_op(
+        { write_ev32(rol32(data, read_imm8() & 31)); },
+        { write_ev32(ror32(data, read_imm8() & 31)); },
+        { write_ev32(rcl32(data, read_imm8() & 31)); },
+        { write_ev32(rcr32(data, read_imm8() & 31)); },
+        { write_ev32(shl32(data, read_imm8() & 31)); },
+        { write_ev32(shr32(data, read_imm8() & 31)); },
+        { write_ev32(shl32(data, read_imm8() & 31)); },
+        { write_ev32(sar32(data, read_imm8() & 31)); }
+    )
+});
+
+op2(0xC2, {
+    // retn
+    var imm16 = read_imm16();
+
+    instruction_pointer = get_seg(reg_cs) + pop16() | 0;
+    // TODO regv
+    reg32[reg_esp] += imm16;
+}, {
+    // retn
+    var imm16 = read_imm16();
+
+    instruction_pointer = get_seg(reg_cs) + pop32s() | 0;
+    reg32[reg_esp] += imm16;
+});
+op2(0xC3, {
+    // retn
+    instruction_pointer = get_seg(reg_cs) + pop16() | 0;;
+}, {
+    // retn
+    instruction_pointer = get_seg(reg_cs) + pop32s() | 0;;
+});
+
+opm(0xC4, {
+    lss_op(reg_es);
+});
+opm(0xC5, {
+    lss_op(reg_ds);
+});
+
+opm(0xC6, { set_eb(read_imm8()); })
+opm2(0xC7, { set_ev16(read_imm16()); }, { set_ev32(read_imm32s()); })
+
+op2(0xC8, { enter16(); }, { enter32(); });
+op2(0xC9, {
+    // leave
+    stack_reg[reg_vsp] = stack_reg[reg_vbp];
+    reg16[reg_bp] = pop16();
+}, {
+    stack_reg[reg_vsp] = stack_reg[reg_vbp];
+    reg32[reg_ebp] = pop32s();
+});
+op2(0xCA, {
+    // retf
+    if(protected_mode)
+    {
+        throw unimpl("16 bit retf in protected mode");
+    }
+    var imm16 = read_imm16();
+    var ip = pop16();
+
+    switch_seg(reg_cs, pop16());
+    instruction_pointer = get_seg(reg_cs) + ip | 0;
+    reg16[reg_sp] += imm16;
+}, {
+    // retf 
+    var imm16 = read_imm16();
+
+    if(protected_mode)
+    {
+        //dbg_log("retf");
+        var ip = pop32s();
+
+        switch_seg(reg_cs, pop32s() & 0xFFFF);
+        instruction_pointer = get_seg(reg_cs) + ip | 0;
+
+        stack_reg[reg_vsp] += imm16;
+    }
+    else
+    {
+        throw unimpl("32 bit retf in real mode");
+    }
+});
+op2(0xCB, {
+    // retf
+    if(protected_mode)
+    {
+        throw unimpl("16 bit retf in protected mode");
+    }
+    else
+    {
+        var ip = pop16();
+        switch_seg(reg_cs, pop16());
+        instruction_pointer = get_seg(reg_cs) + ip | 0;
+    }
+}, {
+    // retf 
+
+    if(protected_mode)
+    {
+        var ip = pop32s();
+
+        switch_seg(reg_cs, pop32s() & 0xFFFF);
+        instruction_pointer = get_seg(reg_cs) + ip | 0;
+    }
+    else
+    {
+        var ip = pop32s();
+
+        switch_seg(reg_cs, pop32s() & 0xFFFF);
+        instruction_pointer = get_seg(reg_cs) + ip | 0;
+    }
+});
+
+op(0xCC, {
+    // INT3
+    call_interrupt_vector(3, true, false);
+});
+op(0xCD, {
+    // INT 
+    var imm8 = read_imm8();
+
+    call_interrupt_vector(imm8, true, false);
+});
+op(0xCE, {
+    // INTO
+    if(getof())
+    {
+        call_interrupt_vector(4, true, false);
+    }
+});
+
+op2(0xCF, {
+    // iret
+    if(protected_mode)
+    {
+        throw unimpl("16 bit iret in protected mode");
+    }
+    var ip = pop16();
+
+    switch_seg(reg_cs, pop16());
+    var new_flags = pop16();
+
+    instruction_pointer = ip + get_seg(reg_cs) | 0;
+    flags = new_flags;
+    flags_changed = 0;
+
+    handle_irqs();
+}, {
+    // iret
+    if(!protected_mode)
+    {
+        throw unimpl("32 bit iret in real mode");
+    }
+    else
+    {
+        if(flags & flag_nt)
+        {
+            if(DEBUG) throw "unimplemented nt";
+        }
+        if(flags & flag_vm)
+        {
+            if(DEBUG) throw "unimplemented vm";
+        }
+
+    }
+    //dbg_log("pop eip from " + h(reg32[reg_esp], 8));
+    instruction_pointer = pop32s();
+    //dbg_log("IRET | from " + h(previous_ip) + " to " + h(instruction_pointer));
+
+    sreg[reg_cs] = pop32s();
+
+    //instruction_pointer += get_seg(reg_cs);
+
+    var new_flags = pop32s();
+
+    if(new_flags & flag_vm)
+    {
+        if(DEBUG) throw "unimplemented";
+    }
+
+    // protected mode return
+
+    var info = lookup_segment_selector(sreg[reg_cs]);
+
+    if(info.is_null)
+    {
+        throw unimpl("is null");
+    }
+    if(!info.is_present)
+    {
+        throw unimpl("not present");
+    }
+    if(!info.is_executable)
+    {
+        throw unimpl("not exec");
+    }
+    if(info.rpl < cpl)
+    {
+        throw unimpl("rpl < cpl");
+    }
+    if(info.dc_bit && info.dpl > info.rpl)
+    {
+        throw unimpl("conforming and dpl > rpl");
+    }
+
+    if(info.rpl > cpl)
+    {
+        // outer privilege return
+        var temp_esp = pop32s();
+        var temp_ss = pop32s();
+
+
+        reg32[reg_esp] = temp_esp;
+
+        update_flags(new_flags);
+
+        cpl = info.rpl;
+        switch_seg(reg_ss, temp_ss & 0xFFFF);
+
+        //dbg_log("iret cpl=" + cpl + " to " + h(instruction_pointer) + 
+        //        " cs:eip=" + h(sreg[reg_cs],4) + ":" + h(get_real_ip(), 8) +
+        //        " ss:esp=" + h(temp_ss & 0xFFFF, 2) + ":" + h(temp_esp, 8), LOG_CPU);
+
+        cpl_changed();
+    }
+    else
+    {
+        update_flags(new_flags);
+        // same privilege return
+
+        //dbg_log(h(new_flags) + " " + h(flags));
+        //dbg_log("iret to " + h(instruction_pointer));
+    }
+
+    //dbg_log("iret if=" + (flags & flag_interrupt) + " cpl=" + cpl);
+    dbg_assert(!page_fault);
+
+    handle_irqs();
+
+});
+
+opm(0xD0, { 
+    sub_op(
+        { write_e8(rol8(data, 1)); },
+        { write_e8(ror8(data, 1)); },
+        { write_e8(rcl8(data, 1)); },
+        { write_e8(rcr8(data, 1)); },
+        { write_e8(shl8(data, 1)); },
+        { write_e8(shr8(data, 1)); },
+        { write_e8(shl8(data, 1)); },
+        { write_e8(sar8(data, 1)); }
+    )
+});
+opm2(0xD1, { 
+    sub_op(
+        { write_ev16(rol16(data, 1)); },
+        { write_ev16(ror16(data, 1)); },
+        { write_ev16(rcl16(data, 1)); },
+        { write_ev16(rcr16(data, 1)); },
+        { write_ev16(shl16(data, 1)); },
+        { write_ev16(shr16(data, 1)); },
+        { write_ev16(shl16(data, 1)); },
+        { write_ev16(sar16(data, 1)); }
+    )
+}, {
+    sub_op(
+        { write_ev32(rol32(data, 1)); },
+        { write_ev32(ror32(data, 1)); },
+        { write_ev32(rcl32(data, 1)); },
+        { write_ev32(rcr32(data, 1)); },
+        { write_ev32(shl32(data, 1)); },
+        { write_ev32(shr32(data, 1)); },
+        { write_ev32(shl32(data, 1)); },
+        { write_ev32(sar32(data, 1)); }
+    )
+});
+
+opm(0xD2, { 
+    var shift = reg8[reg_cl] & 31;
+    sub_op(
+        { write_e8(rol8(data, shift)); },
+        { write_e8(ror8(data, shift)); },
+        { write_e8(rcl8(data, shift)); },
+        { write_e8(rcr8(data, shift)); },
+        { write_e8(shl8(data, shift)); },
+        { write_e8(shr8(data, shift)); },
+        { write_e8(shl8(data, shift)); },
+        { write_e8(sar8(data, shift)); }
+    )
+});
+opm2(0xD3, { 
+    var shift = reg8[reg_cl] & 31;
+    sub_op(
+        { write_ev16(rol16(data, shift)); },
+        { write_ev16(ror16(data, shift)); },
+        { write_ev16(rcl16(data, shift)); },
+        { write_ev16(rcr16(data, shift)); },
+        { write_ev16(shl16(data, shift)); },
+        { write_ev16(shr16(data, shift)); },
+        { write_ev16(shl16(data, shift)); },
+        { write_ev16(sar16(data, shift)); }
+    )
+}, {
+    var shift = reg8[reg_cl] & 31;
+    sub_op(
+        { write_ev32(rol32(data, shift)); },
+        { write_ev32(ror32(data, shift)); },
+        { write_ev32(rcl32(data, shift)); },
+        { write_ev32(rcr32(data, shift)); },
+        { write_ev32(shl32(data, shift)); },
+        { write_ev32(shr32(data, shift)); },
+        { write_ev32(shl32(data, shift)); },
+        { write_ev32(sar32(data, shift)); }
+    )
+});
+
+op(0xD4, {
+    bcd_aam();
+});
+op(0xD5, {
+    bcd_aad();
+});
+
+op(0xD6, {
+    // salc
+    throw unimpl("salc instruction");
+});
+op(0xD7, {
+    // xlat
+    if(address_size_32)
+    {
+        reg8[reg_al] = safe_read8(get_seg_prefix(reg_ds) + reg32s[reg_ebx] + reg8[reg_al]);
+    }
+    else
+    {
+        reg8[reg_al] = safe_read8(get_seg_prefix(reg_ds) + reg16[reg_bx] + reg8[reg_al]);
+    }
+});
+
+
+// fpu instructions
+#define fpu_op(n, op)\
+    opm(n, { \
+        if(modrm_byte < 0xC0)\
+            fpu.op_ ## op ## _mem(modrm_byte, modrm_resolve(modrm_byte));\
+        else\
+            fpu.op_ ## op ## _reg(modrm_byte);\
+    })
+
+fpu_op(0xD8, D8);
+fpu_op(0xD9, D9);
+fpu_op(0xDA, DA);
+fpu_op(0xDB, DB);
+fpu_op(0xDC, DC);
+fpu_op(0xDD, DD);
+fpu_op(0xDE, DE);
+fpu_op(0xDF, DF);
+
+
+op(0xE0, { loopne(); });
+op(0xE1, { loope(); });
+op(0xE2, { loop(); });
+op(0xE3, { jcxz(); });
+
+op(0xE4, { reg8[reg_al] = in8(read_imm8()); });
+op2(0xE5, { reg16[reg_ax] = in16(read_imm8()); }, { reg32[reg_eax] = in32(read_imm8()); });
+op(0xE6, { out8(read_imm8(), reg8[reg_al]); });
+op2(0xE7, { out16(read_imm8(), reg16[reg_ax]); }, { out32(read_imm8(), reg32s[reg_eax]); });
+
+op2(0xE8, {
+    // call
+    var imm16s = read_imm16s();
+    push16(get_real_ip());
+
+    jmp_rel16(imm16s);
+}, {
+    // call
+    var imm32s = read_imm32s();
+    push32(get_real_ip());
+
+    instruction_pointer = instruction_pointer + imm32s | 0;
+});
+op2(0xE9, {
+    // jmp
+    var imm16s = read_imm16s();
+    jmp_rel16(imm16s);
+}, {
+    // jmp
+    var imm32s = read_imm32s();
+    instruction_pointer = instruction_pointer + imm32s | 0;
+});
+op2(0xEA, {
+    // jmpf
+    var ip = read_imm16();
+    switch_seg(reg_cs, read_imm16());
+
+    instruction_pointer = ip + get_seg(reg_cs) | 0;
+}, {
+    // jmpf
+    var ip = read_imm32s();
+    switch_seg(reg_cs, read_imm16());
+
+    instruction_pointer = ip + get_seg(reg_cs) | 0;
+});
+op(0xEB, {
+    // jmp near
+    var imm8 = read_imm8s();
+    instruction_pointer = instruction_pointer + imm8 | 0;
+});
+
+op(0xEC, { reg8[reg_al] = in8(reg16[reg_dx]); });
+op2(0xED, { reg16[reg_ax] = in16(reg16[reg_dx]); }, { reg32[reg_eax] = in32(reg16[reg_dx]); });
+op(0xEE, { out8(reg16[reg_dx], reg8[reg_al]); });
+op2(0xEF, { out16(reg16[reg_dx], reg16[reg_ax]); }, { out32(reg16[reg_dx], reg32s[reg_eax]); });
+
+op(0xF0, {
+    // lock
+
+    // TODO
+    // This triggers UD when used with
+    // some instructions that don't write to memory
+});
+op(0xF1, {
+    // INT1
+    // https://code.google.com/p/corkami/wiki/x86oddities#IceBP
+    throw unimpl("int1 instruction");
+});
+
+op(0xF2, {
+    // repnz
+    dbg_assert(!repeat_string_prefix);
+    repeat_string_prefix = true;
+    repeat_string_type = false;
+    do_op();
+    repeat_string_prefix = false;
+});
+op(0xF3, {
+    // repz
+    dbg_assert(!repeat_string_prefix);
+    repeat_string_prefix = true;
+    repeat_string_type = true;
+    do_op();
+    repeat_string_prefix = false;
+});
+
+op(0xF4, {
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+
+    // hlt
+    if((flags & flag_interrupt) === 0)
+    {
+        log("cpu halted");
+        stopped = true;
+        if(DEBUG) dump_regs();
+        throw "HALT";
+    }
+    else
+    {
+        // infinite loop until an irq happens
+        // this is handled in call_interrupt_vector
+        instruction_pointer--;
+        in_hlt = true;
+    }
+});
+
+op(0xF5, {
+    // cmc
+    flags = (flags | 1) ^ getcf();
+    flags_changed &= ~1;
+});
+
+opm(0xF6, {
+    sub_op(
+        { read_e8; test8(data, read_imm8()); },
+        { read_e8; test8(data, read_imm8()); },
+        { write_e8(not8(data)); },
+        { write_e8(neg8(data)); },
+        { read_e8; mul8(data); },
+        { read_e8s; imul8(data); },
+        { read_e8; div8(data); },
+        { read_e8s; idiv8(data); }
+    )
+});
+
+opm2(0xF7, {
+    sub_op (
+        { read_e16; test16(data, read_imm16()); },
+        { read_e16; test16(data, read_imm16()); },
+        { write_ev16(not16(data)); },
+        { write_ev16(neg16(data)); },
+        { read_e16; mul16(data); },
+        { read_e16s; imul16(data); },
+        { read_e16; div16(data); },
+        { read_e16s; idiv16(data); }
+    )
+}, {
+    sub_op (
+        { read_e32s; test32(data, read_imm32s()); },
+        { read_e32s; test32(data, read_imm32s()); },
+        { write_ev32(not32(data)); },
+        { write_ev32(neg32(data)); },
+        { read_e32; mul32(data); },
+        { read_e32s; imul32(data); },
+        { read_e32; div32(data); },
+        { read_e32s; idiv32(data); }
+    )
+});
+
+op(0xF8, {
+    // clc
+    flags &= ~flag_carry;
+    flags_changed &= ~1;
+});
+op(0xF9, {
+    // stc
+    flags |= flag_carry;
+    flags_changed &= ~1;
+});
+
+op(0xFA, {
+    // cli
+    //dbg_log("interrupts off");
+    if(!privileges_for_io())
+    {
+        trigger_gp(0);
+    }
+    else
+    {
+        flags &= ~flag_interrupt;
+    }
+});
+op(0xFB, {
+    // sti
+    //dbg_log("interrupts on");
+    if(!privileges_for_io())
+    {
+        trigger_gp(0);
+    }
+    else
+    {
+        flags |= flag_interrupt;
+        handle_irqs();
+    }
+
+});
+
+op(0xFC, {
+    // cld
+    flags &= ~flag_direction;
+});
+op(0xFD, {
+    // std
+    flags |= flag_direction;
+});
+
+opm(0xFE, {
+    var mod = modrm_byte & 56;
+
+    if(mod === 0)
+    {
+        write_e8(inc8(data)); 
+    }
+    else if(mod === 8)
+    {
+        write_e8(dec8(data)); 
+    }
+    else
+    {
+        todo();
+    }
+});
+opm2(0xFF, {
+    sub_op(
+        { write_ev16(inc16(data)); },
+        { write_ev16(dec16(data)); },
+        { 
+            // 2, call near
+            read_e16;
+            push16(get_real_ip());
+            
+            instruction_pointer = get_seg(reg_cs) + data | 0;
+        },
+        {
+            // 3, callf
+            if(modrm_byte >= 0xC0)
+            {
+                raise_exception(6);
+                dbg_assert(false);
+            }
+
+            var virt_addr = modrm_resolve(modrm_byte);
+
+            push16(sreg[reg_cs]);
+            push16(get_real_ip());
+
+            switch_seg(reg_cs, safe_read16(virt_addr + 2));
+            instruction_pointer = get_seg(reg_cs) + safe_read16(virt_addr) | 0;
+            dbg_assert(!page_fault);
+        },
+        {
+            // 4, jmp near
+            read_e16;
+            instruction_pointer = get_seg(reg_cs) + data | 0;
+        },
+        {
+            // 5, jmpf
+            if(modrm_byte >= 0xC0)
+            {
+                raise_exception(6);
+                dbg_assert(false);
+            }
+
+            var virt_addr = modrm_resolve(modrm_byte);
+
+            switch_seg(reg_cs, safe_read16(virt_addr + 2));
+            instruction_pointer = get_seg(reg_cs) + safe_read16(virt_addr) | 0;
+
+            // TODO safe read
+        },
+        {
+            // 6, push
+            read_e16;
+            push16(data);
+        },
+        {
+            todo();
+        }
+    )
+}, {
+    sub_op(
+
+        { write_ev32(inc32(data)); },
+        { write_ev32(dec32(data)); },
+        { 
+            // 2, call near
+            read_e32s;
+            push32(get_real_ip());
+
+            instruction_pointer = get_seg(reg_cs) + data | 0;
+        },
+        {
+            // 3, callf
+            if(modrm_byte >= 0xC0)
+            {
+                raise_exception(6);
+                dbg_assert(false);
+            }
+
+            var virt_addr = modrm_resolve(modrm_byte);
+            var new_cs = safe_read16(virt_addr + 4);
+            var new_ip = safe_read32s(virt_addr);
+
+
+            push32(sreg[reg_cs]);
+            push32(get_real_ip());
+
+            switch_seg(reg_cs, new_cs);
+            instruction_pointer = get_seg(reg_cs) + new_ip | 0;
+        },
+        {
+            // 4, jmp near
+            read_e32s;
+            instruction_pointer = get_seg(reg_cs) + data | 0;
+        },
+        {
+            // 5, jmpf
+            if(modrm_byte >= 0xC0)
+            {
+                raise_exception(6);
+                dbg_assert(false);
+            }
+
+            var virt_addr = modrm_resolve(modrm_byte);
+            var new_cs = safe_read16(virt_addr + 4);
+            var new_ip = safe_read32s(virt_addr);
+
+            switch_seg(reg_cs, new_cs);
+            instruction_pointer = get_seg(reg_cs) + new_ip | 0;
+        },
+        {
+            // push
+            read_e32s;
+            push32(data);
+        },
+        {
+            todo();
+        }
+    )
+});
+
+
+// 0F ops start here
+#define table16 table0F_16
+#define table32 table0F_32
+
+
+opm(0x00, {
+    read_e16;
+
+    if(!protected_mode)
+    {
+        // No GP, UD is correct
+        trigger_ud();
+    }
+
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+
+    switch(modrm_byte >> 3 & 7)
+    {
+        case 2:
+            load_ldt(data);
+            break;
+        case 3:
+            load_tr(data);
+            break;
+        default:
+            dbg_log(modrm_byte >> 3 & 7, LOG_CPU);
+            todo();
+    }
+});
+
+opm(0x01, {
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+
+    var mod = modrm_byte >> 3 & 7;
+
+    if(mod === 4)
+    {
+        // smsw
+        set_ev16(cr0);
+        return;
+    }
+    else if(mod === 6)
+    {
+        // lmsw
+        read_e16;
+
+        cr0 = (cr0 & ~0xF) | (data & 0xF);
+        cr0_changed();
+        return;
+    }
+
+    if(modrm_byte >= 0xC0)
+    {
+        // only memory
+        raise_exception(6);
+        dbg_assert(false);
+    }
+
+    if((mod === 2 || mod === 3) && protected_mode)
+    {
+        // override prefix, so modrm_resolve does not return the segment part
+        // only lgdt and lidt and only in protected mode
+        segment_prefix = reg_noseg; 
+    }
+
+    var addr = modrm_resolve(modrm_byte);
+    segment_prefix = -1;
+
+    switch(mod)
+    {
+        case 0:
+            // sgdt
+            safe_write16(addr, gdtr_size);
+            safe_write32(addr + 2, gdtr_offset);
+            break;
+        case 1:
+            // sidt
+            safe_write16(addr, idtr_size);
+            safe_write32(addr + 2, idtr_offset);
+            break;
+        case 2:
+            // lgdt
+            var size = safe_read16(addr);
+            var offset = safe_read32s(addr + 2);
+
+            gdtr_size = size;
+            gdtr_offset = offset;
+
+            if(!operand_size_32)
+            {
+                gdtr_offset &= 0xFFFFFF;
+            }
+
+            dbg_log("eax " + h(reg32[reg_eax]), LOG_CPU);
+            dbg_log("gdt loaded from " + h(addr), LOG_CPU);
+            dbg_log("gdt at " + h(gdtr_offset) + ", " + gdtr_size + " bytes", LOG_CPU);
+            //dump_gdt_ldt();
+            break;
+        case 3:
+            // lidt
+            var size = safe_read16(addr);
+            var offset = safe_read32s(addr + 2);
+
+            idtr_size = size;
+            idtr_offset = offset;
+
+            if(!operand_size_32)
+            {
+                idtr_offset &= 0xFFFFFF;
+            }
+
+            //dbg_log("[" + h(instruction_pointer) + "] idt at " + 
+            //        h(idtr_offset) + ", " + idtr_size + " bytes " + h(addr), LOG_CPU);
+            break;
+        case 7:
+            // flush translation lookaside buffer
+            invlpg(addr);
+            break;
+        default:
+            dbg_log(mod);
+            todo();
+    }
+});
+
+opm(0x02, {
+    todo();
+    // lar
+});
+
+opm(0x03, {
+    todo();
+    // lsl
+});
+
+undefined_instruction(0x04);
+undefined_instruction(0x05);
+
+op(0x06, {
+    // clts
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+    else
+    {
+        //dbg_log("clts", LOG_CPU);
+        cr0 &= ~8;
+        // do something here ?
+    }
+});
+
+undefined_instruction(0x07);
+// invd
+todo_op(0x08);
+
+op(0x09, {
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+    // wbinvd
+});
+
+
+undefined_instruction(0x0A);
+op(0x0B, {
+    trigger_ud();
+});
+undefined_instruction(0x0C);
+todo_op(0x0D);
+undefined_instruction(0x0E);
+undefined_instruction(0x0F);
+
+
+unimplemented_sse(0x10);
+unimplemented_sse(0x11);
+unimplemented_sse(0x12);
+unimplemented_sse(0x13);
+unimplemented_sse(0x14);
+unimplemented_sse(0x15);
+unimplemented_sse(0x16);
+unimplemented_sse(0x17);
+
+opm(0x18, {
+    // prefetch
+    // nop for us 
+    if(operand_size_32) {
+        read_e32s;
+    }
+    else {
+        read_e16;
+    }
+});
+
+unimplemented_sse(0x19);
+unimplemented_sse(0x1A);
+unimplemented_sse(0x1B);
+unimplemented_sse(0x1C);
+unimplemented_sse(0x1D);
+unimplemented_sse(0x1E);
+unimplemented_sse(0x1F);
+
+
+opm(0x20, {
+
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+    //dbg_log("cr" + mod + " read", LOG_CPU);
+
+    // mov addr, cr
+    // mod = which control register
+    switch(modrm_byte >> 3 & 7)
+    {
+        case 0:
+            reg_e32 = cr0;
+            break;
+        case 2:
+            reg_e32 = cr2;
+            break;
+        case 3:
+            //dbg_log("read cr3 (" + h(cr3, 8) + ")", LOG_CPU);
+            reg_e32 = cr3;
+            break;
+        case 4:
+            reg_e32 = cr4;
+            break;
+        default:
+            dbg_log(modrm_byte >> 3 & 7);
+            todo();
+    }
+});
+
+opm(0x21, {
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+
+    // TODO: mov from debug register
+    dbg_assert(modrm_byte >= 0xC0);
+});
+
+opm(0x22, {
+
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+
+    var data = reg_e32;
+    //dbg_log("cr" + mod + " written: " + h(reg32[reg]), LOG_CPU);
+
+    // mov cr, addr
+    // mod = which control register
+    switch(modrm_byte >> 3 & 7)
+    {
+        case 0:
+            if((data & 0x80000001) === (0x80000000 | 0))
+            {
+                // cannot load PG without PE
+                throw unimpl("#GP handler");
+            }
+
+            if((cr0 & 0x80000000) && !(data & 0x80000000))
+            {
+                full_clear_tlb();
+            }
+
+            cr0 = data;
+            cr0_changed();
+            //dbg_log("cr1 = " + bits(memory.read32s(addr)), LOG_CPU);
+            break;
+        case 3: 
+            cr3 = data;
+            dbg_assert((cr3 & 0xFFF) === 0);
+            clear_tlb();
+
+            //dump_page_directory();
+            //dbg_log("page directory loaded at " + h(cr3, 8), LOG_CPU);
+            break;
+        case 4:
+            if((cr4 ^ data) & 128)
+            {
+                full_clear_tlb();
+            }
+
+            cr4 = data;
+            page_size_extensions = (cr4 & 16) ? PSE_ENABLED : 0;
+            dbg_log("cr4 set to " + h(cr4), LOG_CPU);
+                
+            break;
+        default:
+            dbg_log(modrm_byte >> 3 & 7);
+            todo();
+    }
+});
+opm(0x23, {
+    if(cpl)
+    {
+        trigger_gp(0);
+    }
+
+    // TODO: mov to debug register
+    dbg_assert(modrm_byte >= 0xC0);
+});
+
+undefined_instruction(0x24);
+undefined_instruction(0x25);
+undefined_instruction(0x26);
+undefined_instruction(0x27);
+
+unimplemented_sse(0x28);
+unimplemented_sse(0x29);
+unimplemented_sse(0x2A);
+unimplemented_sse(0x2B);
+unimplemented_sse(0x2C);
+unimplemented_sse(0x2D);
+unimplemented_sse(0x2E);
+unimplemented_sse(0x2F);
+
+// wrmsr
+todo_op(0x30);
+
+op(0x31, {
+    // rdtsc - read timestamp counter
+    
+    //var cycles = (Date.now() - emulation_start) / 1000 * 3000000;
+    //reg32[reg_eax] = cycles;
+    //reg32[reg_edx] = cycles / 0x100000000;
+
+    reg32[reg_eax] = cpu_timestamp_counter;
+    reg32[reg_edx] = cpu_timestamp_counter / 0x100000000;
+});
+
+// rdmsr
+todo_op(0x32);
+// rdpmc
+todo_op(0x33);
+// sysenter
+todo_op(0x34);
+// sysexit
+todo_op(0x35);
+
+undefined_instruction(0x36);
+
+// getsec
+todo_op(0x37);
+
+unimplemented_sse(0x38);
+unimplemented_sse(0x39);
+unimplemented_sse(0x3A);
+unimplemented_sse(0x3B);
+unimplemented_sse(0x3C);
+unimplemented_sse(0x3D);
+unimplemented_sse(0x3E);
+unimplemented_sse(0x3F);
+
+
+#define group0F40(n, test)\
+    opm2(0x40 | n, {\
+        if(test) {\
+            read_e16;\
+            reg_g16 = data;\
+        } else if(modrm_byte < 0xC0)\
+            modrm_resolve(modrm_byte)\
+    }, {\
+        if(test) {\
+            read_e32s;\
+            reg_g32s = data;\
+        } else if(modrm_byte < 0xC0)\
+            modrm_resolve(modrm_byte)\
+    });
+
+each_jcc(group0F40);
+
+
+unimplemented_sse(0x50);
+unimplemented_sse(0x51);
+unimplemented_sse(0x52);
+unimplemented_sse(0x53);
+unimplemented_sse(0x54);
+unimplemented_sse(0x55);
+unimplemented_sse(0x56);
+unimplemented_sse(0x57);
+
+unimplemented_sse(0x58);
+unimplemented_sse(0x59);
+unimplemented_sse(0x5A);
+unimplemented_sse(0x5B);
+unimplemented_sse(0x5C);
+unimplemented_sse(0x5D);
+unimplemented_sse(0x5E);
+unimplemented_sse(0x5F);
+
+unimplemented_sse(0x60);
+unimplemented_sse(0x61);
+unimplemented_sse(0x62);
+unimplemented_sse(0x63);
+unimplemented_sse(0x64);
+unimplemented_sse(0x65);
+unimplemented_sse(0x66);
+unimplemented_sse(0x67);
+
+unimplemented_sse(0x68);
+unimplemented_sse(0x69);
+unimplemented_sse(0x6A);
+unimplemented_sse(0x6B);
+unimplemented_sse(0x6C);
+unimplemented_sse(0x6D);
+unimplemented_sse(0x6E);
+unimplemented_sse(0x6F);
+
+unimplemented_sse(0x70);
+unimplemented_sse(0x71);
+unimplemented_sse(0x72);
+unimplemented_sse(0x73);
+unimplemented_sse(0x74);
+unimplemented_sse(0x75);
+unimplemented_sse(0x76);
+unimplemented_sse(0x77);
+
+unimplemented_sse(0x78);
+unimplemented_sse(0x79);
+unimplemented_sse(0x7A);
+unimplemented_sse(0x7B);
+unimplemented_sse(0x7C);
+unimplemented_sse(0x7D);
+unimplemented_sse(0x7E);
+unimplemented_sse(0x7F);
+
+
+#define group0F80(n, test) op2(0x80 | n, { jmpcc16(test); }, { jmpcc32(test); })
+each_jcc(group0F80)
+
+
+#define group0F90(n, test) opm(0x90 | n, { set_eb(!test ^ 1); });
+each_jcc(group0F90);
+
+
+op2(0xA0, { push16(sreg[reg_fs]); }, { push32(sreg[reg_fs]); });
+pop_sreg_op(0xA1, reg_fs);
+//op2(0xA1, 
+//    { safe_pop16(sreg[reg_fs]); switch_seg(reg_fs); }, 
+//    { safe_pop32s(sreg[reg_fs]); switch_seg(reg_fs); });
+
+op(0xA2, { cpuid(); });
+
+opm(0xA3, {
+    if(operand_size_32)
+    {
+        if(modrm_byte < 0xC0)
+        {
+            bt_mem(modrm_resolve(modrm_byte), reg_g32s);
+        }
+        else
+        {
+            bt_reg(reg_e32, reg_g32 & 31);
+        }
+    }
+    else
+    {
+        if(modrm_byte < 0xC0)
+        {
+            bt_mem(modrm_resolve(modrm_byte), reg_g16s);
+        }
+        else
+        {
+            bt_reg(reg_e16, reg_g16 & 15);
+        }
+    }
+});
+
+opm2(0xA4, {
+    write_ev16(shld16(data, reg_g16, read_imm8() & 31));
+}, {
+    write_ev32(shld32(data, reg_g32, read_imm8() & 31));
+});
+opm2(0xA5, {
+    write_ev16(shld16(data, reg_g16, reg8[reg_cl] & 31));
+}, {
+    write_ev32(shld32(data, reg_g32, reg8[reg_cl] & 31));
+});
+
+undefined_instruction(0xA6);
+undefined_instruction(0xA7);
+
+op2(0xA8, { push16(sreg[reg_gs]); }, { push32(sreg[reg_gs]); });
+pop_sreg_op(0xA9, reg_gs);
+//op2(0xA9, 
+//    { safe_pop16(sreg[reg_gs]); switch_seg(reg_gs); }, 
+//    { safe_pop32s(sreg[reg_gs]); switch_seg(reg_gs); });
+
+// rsm
+todo_op(0xAA);
+
+opm(0xAB, {
+    bt_op(bts, reg_g16s, reg_g32s);
+});
+
+
+opm2(0xAC, {
+    write_ev16(shrd16(data, reg_g16, read_imm8() & 31));
+}, {
+    write_ev32(shrd32(data, reg_g32, read_imm8() & 31));
+});
+opm2(0xAD, {
+    write_ev16(shrd16(data, reg_g16, reg8[reg_cl] & 31));
+}, {
+    write_ev32(shrd32(data, reg_g32, reg8[reg_cl] & 31));
+});
+
+todo_op(0xAE);
+
+opm2(0xAF, {
+    read_e16s;
+    reg_g16 = imul_reg16(reg_g16s, data);
+}, {
+    read_e32s;
+    reg_g32 = imul_reg32(reg_g32s, data);
+});
+
+
+opm(0xB0, {
+    // cmpxchg8
+    if(modrm_byte < 0xC0)
+    {
+        var virt_addr = modrm_resolve(modrm_byte);
+        var data = safe_read8(virt_addr);
+    }
+    else
+        data = reg_e8;
+
+
+    cmp8(data, reg8[reg_al]);
+
+    if(getzf())
+    {
+        if(modrm_byte < 0xC0)
+            safe_write8(virt_addr, reg_g8);
+        else
+            reg_e8 = reg_g8;
+    }
+    else
+    {
+        reg8[reg_al] = data;
+    }
+});
+opm(0xB1, {
+    // cmpxchg16/32
+    if(operand_size_32)
+    {
+        if(modrm_byte < 0xC0)
+        {
+            var virt_addr = modrm_resolve(modrm_byte);
+            var data = safe_read32(virt_addr);
+        }
+        else
+            data = reg_e32;
+
+        cmp32(data, reg32[reg_eax]);
+
+        if(getzf())
+        {
+            if(modrm_byte < 0xC0)
+                safe_write32(virt_addr, reg_g32);
+            else
+                reg_e32 = reg_g32;
+        }
+        else
+        {
+            reg32[reg_eax] = data;
+        }
+    }
+    else
+    {
+        if(modrm_byte < 0xC0)
+        {
+            var virt_addr = modrm_resolve(modrm_byte);
+            var data = safe_read16(virt_addr);
+        }
+        else
+            data = reg_e16;
+        
+        cmp16(data, reg16[reg_ax]);
+
+        if(getzf())
+        {
+            if(modrm_byte < 0xC0)
+                safe_write16(virt_addr, reg_g16);
+            else
+                reg_e16 = reg_g16;
+        }
+        else
+        {
+            reg16[reg_ax] = data;
+        }
+    }
+});
+
+// lss
+opm(0xB2, { 
+    lss_op(reg_ss);
+});
+
+opm(0xB3, {
+    bt_op(btr, reg_g16s, reg_g32s);
+});
+
+// lfs, lgs
+opm(0xB4, { 
+    lss_op(reg_fs);
+});
+opm(0xB5, {
+    lss_op(reg_gs);
+});
+
+opm2(0xB6, {
+    // movzx
+    read_e8;
+    reg_g16 = data;
+}, {
+    read_e8;
+    reg_g32 = data;
+});
+
+opm(0xB7, {
+    // movzx
+    read_e16;
+    reg_g32 = data;
+});
+
+// popcnt
+todo_op(0xB8);
+
+// UD
+todo_op(0xB9);
+
+opm(0xBA, {
+    //dbg_log("BA " + mod + " " + imm8);
+
+    switch(modrm_byte >> 3 & 7)
+    {
+        case 4:
+            if(operand_size_32)
+            {
+                if(modrm_byte < 0xC0)
+                {
+                    bt_mem(modrm_resolve(modrm_byte), read_imm8() & 31);
+                }
+                else
+                {
+                    bt_reg(reg_e32, read_imm8() & 31);
+                }
+            }
+            else
+            {
+                if(modrm_byte < 0xC0)
+                {
+                    bt_mem(modrm_resolve(modrm_byte), read_imm8() & 31);
+                }
+                else
+                {
+                    bt_reg(reg_e16, read_imm8() & 15);
+                }
+            }
+            break;
+        case 5:
+            bt_op(bts, read_imm8() & 31, read_imm8() & 31);
+            break;
+        case 6:
+            bt_op(btr, read_imm8() & 31, read_imm8() & 31);
+            break;
+        case 7:
+            bt_op(btc, read_imm8() & 31, read_imm8() & 31);
+            break;
+        default:
+            dbg_log(modrm_byte >> 3 & 7);
+            todo();
+    }
+});
+opm(0xBB, {
+    bt_op(btc, reg_g16s, reg_g32s);
+});
+
+opm2(0xBC, {
+    read_e16;
+    reg_g16 = bsf16(reg_g16, data);
+}, {
+    read_e32s;
+    reg_g32 = bsf32(reg_g32, data);
+});
+
+opm2(0xBD, {
+    read_e16;
+    reg_g16 = bsr16(reg_g16, data);
+}, {
+    read_e32s;
+    reg_g32 = bsr32(reg_g32, data);
+});
+
+opm2(0xBE, {
+    // movsx
+    read_e8s;
+    reg_g16 = data;
+}, {
+    read_e8s;
+    reg_g32s = data;
+});
+
+opm(0xBF, {
+    // movsx
+    read_e16s;
+    reg_g32s = data;
+});
+
+opm(0xC0, {
+    write_e8(xadd8(data, modrm_byte >> 1 & 0xC | modrm_byte >> 5 & 1));
+});
+
+opm2(0xC1, {
+    write_ev16(xadd16(data, modrm_byte >> 2 & 14));
+}, {
+    write_ev32(xadd32(data, modrm_byte >> 3 & 7));
+});
+
+
+unimplemented_sse(0xC2);
+unimplemented_sse(0xC3);
+unimplemented_sse(0xC4);
+unimplemented_sse(0xC5);
+unimplemented_sse(0xC6);
+
+opm(0xC7, {
+    // cmpxchg8b
+    var addr = modrm_resolve(modrm_byte);
+    
+    var m64_low = safe_read32(addr);
+    var m64_high = safe_read32(addr + 4);
+
+    if(reg32[reg_eax] === m64_low &&
+            reg32[reg_edx] === m64_high)
+    {
+        flags |= flag_zero;
+
+        safe_write32(addr, reg32[reg_ebx]);
+        safe_write32(addr + 4, reg32[reg_ecx]);
+    }
+    else
+    {
+        flags &= ~flag_zero;
+
+        reg32[reg_eax] = m64_low;
+        reg32[reg_edx] = m64_high;
+    }
+
+    flags_changed &= ~flag_zero;
+});
+
+#define group0FC8(n, r16, r32) op(0xC8 | n, { bswap(r32); });
+each_reg(group0FC8)
+
+unimplemented_sse(0xD0);
+unimplemented_sse(0xD1);
+unimplemented_sse(0xD2);
+unimplemented_sse(0xD3);
+unimplemented_sse(0xD4);
+unimplemented_sse(0xD5);
+unimplemented_sse(0xD6);
+unimplemented_sse(0xD7);
+
+unimplemented_sse(0xD8);
+unimplemented_sse(0xD9);
+unimplemented_sse(0xDA);
+unimplemented_sse(0xDB);
+unimplemented_sse(0xDC);
+unimplemented_sse(0xDD);
+unimplemented_sse(0xDE);
+unimplemented_sse(0xDF);
+
+unimplemented_sse(0xE0);
+unimplemented_sse(0xE1);
+unimplemented_sse(0xE2);
+unimplemented_sse(0xE3);
+unimplemented_sse(0xE4);
+unimplemented_sse(0xE5);
+unimplemented_sse(0xE6);
+unimplemented_sse(0xE7);
+
+unimplemented_sse(0xE8);
+unimplemented_sse(0xE9);
+unimplemented_sse(0xEA);
+unimplemented_sse(0xEB);
+unimplemented_sse(0xEC);
+unimplemented_sse(0xED);
+unimplemented_sse(0xEE);
+unimplemented_sse(0xEF);
+
+unimplemented_sse(0xF0);
+unimplemented_sse(0xF1);
+unimplemented_sse(0xF2);
+unimplemented_sse(0xF3);
+unimplemented_sse(0xF4);
+unimplemented_sse(0xF5);
+unimplemented_sse(0xF6);
+unimplemented_sse(0xF7);
+
+unimplemented_sse(0xF8);
+unimplemented_sse(0xF9);
+unimplemented_sse(0xFA);
+unimplemented_sse(0xFB);
+unimplemented_sse(0xFC);
+unimplemented_sse(0xFD);
+unimplemented_sse(0xFE);
+
+// NSA backdoor instruction
+undefined_instruction(0xFF);
+
+
+#undef table16
+#undef table32

+ 175 - 0
src/io.js

@@ -0,0 +1,175 @@
+"use strict";
+
+/**
+ * The ISA IO bus
+ * Devices register their ports here
+ *
+ * @constructor
+ */
+function IO()
+{
+    var a20_byte = 0,
+        me = this;
+
+    function get_port_description(addr)
+    {
+        // via seabios ioport.h
+        var ports = {
+            0x0004: "PORT_DMA_ADDR_2",
+            0x0005: "PORT_DMA_CNT_2",
+            0x000a: "PORT_DMA1_MASK_REG",
+            0x000b: "PORT_DMA1_MODE_REG",
+            0x000c: "PORT_DMA1_CLEAR_FF_REG",
+            0x000d: "PORT_DMA1_MASTER_CLEAR",
+            0x0020: "PORT_PIC1_CMD",
+            0x0021: "PORT_PIC1_DATA",
+            0x0040: "PORT_PIT_COUNTER0",
+            0x0041: "PORT_PIT_COUNTER1",
+            0x0042: "PORT_PIT_COUNTER2",
+            0x0043: "PORT_PIT_MODE",
+            0x0060: "PORT_PS2_DATA",
+            0x0061: "PORT_PS2_CTRLB",
+            0x0064: "PORT_PS2_STATUS",
+            0x0070: "PORT_CMOS_INDEX",
+            0x0071: "PORT_CMOS_DATA",
+            0x0080: "PORT_DIAG",
+            0x0081: "PORT_DMA_PAGE_2",
+            0x0092: "PORT_A20",
+            0x00a0: "PORT_PIC2_CMD",
+            0x00a1: "PORT_PIC2_DATA",
+            0x00b2: "PORT_SMI_CMD",
+            0x00b3: "PORT_SMI_STATUS",
+            0x00d4: "PORT_DMA2_MASK_REG",
+            0x00d6: "PORT_DMA2_MODE_REG",
+            0x00da: "PORT_DMA2_MASTER_CLEAR",
+            0x00f0: "PORT_MATH_CLEAR",
+            0x0170: "PORT_ATA2_CMD_BASE",
+            0x01f0: "PORT_ATA1_CMD_BASE",
+            0x0278: "PORT_LPT2",
+            0x02e8: "PORT_SERIAL4",
+            0x02f8: "PORT_SERIAL2",
+            0x0374: "PORT_ATA2_CTRL_BASE",
+            0x0378: "PORT_LPT1",
+            0x03e8: "PORT_SERIAL3",
+            //0x03f4: "PORT_ATA1_CTRL_BASE",
+            0x03f0: "PORT_FD_BASE",
+            0x03f2: "PORT_FD_DOR",
+            0x03f4: "PORT_FD_STATUS",
+            0x03f5: "PORT_FD_DATA",
+            0x03f6: "PORT_HD_DATA",
+            0x03f7: "PORT_FD_DIR",
+            0x03f8: "PORT_SERIAL1",
+            0x0cf8: "PORT_PCI_CMD",
+            0x0cf9: "PORT_PCI_REBOOT",
+            0x0cfc: "PORT_PCI_DATA",
+            0x0402: "PORT_BIOS_DEBUG",
+            0x0510: "PORT_QEMU_CFG_CTL",
+            0x0511: "PORT_QEMU_CFG_DATA",
+            0xb000: "PORT_ACPI_PM_BASE",
+            0xb100: "PORT_SMB_BASE",
+            0x8900:  "PORT_BIOS_APM"
+        };
+
+        if(ports[addr])
+        {
+            return "  (" + ports[addr] + ")";
+        }
+        else
+        {
+            return "";
+        }
+    }
+
+    function empty_port_read_debug(port_addr)
+    {
+        dbg_log(
+            "read port  #" + h(port_addr, 3) + get_port_description(port_addr),
+            LOG_IO
+        );
+
+        return 0xFF;
+    }
+
+    function empty_port_write_debug(port_addr, out_byte)
+    {
+        dbg_log(
+            "write port #" + h(port_addr, 3) + " <- " + h(out_byte, 2) + get_port_description(port_addr),
+            LOG_IO
+        );
+    }
+
+    function empty_port_read()
+    {
+        return 0xFF;
+    }
+
+    function empty_port_write(x)
+    {
+    }
+
+    var read_callbacks = [],
+        write_callbacks = [];
+
+    for(var i = 0; i < 0x10000; i++)
+    {
+        // avoid sparse arrays
+
+        if(DEBUG)
+        {
+            read_callbacks[i] = empty_port_read_debug.bind(0, i);
+            write_callbacks[i] = empty_port_write_debug.bind(0, i);
+        }
+        else
+        {
+            read_callbacks[i] = empty_port_read;
+            write_callbacks[i] = empty_port_write;
+        }
+    }
+
+    /**
+     * @param {number} port_addr
+     * @param {function():number} callback
+     */
+    this.register_read = function(port_addr, callback)
+    {
+        read_callbacks[port_addr] = callback;
+    };
+
+    /**
+     * @param {number} port_addr
+     * @param {function(number)} callback
+     */
+    this.register_write = function(port_addr, callback)
+    {
+        write_callbacks[port_addr] = callback;
+    };
+
+
+    // should maybe be somewhere else?
+    this.register_read(0x92, function()
+    {
+        return a20_byte;
+    });
+
+    this.register_write(0x92, function(out_byte)
+    {
+        a20_byte = out_byte;
+    });
+
+    // use by linux for timing
+    this.register_write(0x80, function(out_byte)
+    {
+    });
+
+    this.port_write = function(port_addr, out_byte)
+    {
+        write_callbacks[port_addr](out_byte);
+    };
+
+    // read byte from port
+    this.port_read = function(port_addr)
+    {
+        return read_callbacks[port_addr]();
+    };
+}
+

+ 339 - 0
src/main.js

@@ -0,0 +1,339 @@
+"use strict";
+
+Object.fromList = function(xs)
+{
+    var result = {};
+
+    for(var i = 0; i < xs.length; i++)
+    {
+        result[xs[i][0]] = xs[i][1];
+    }
+
+    return result;
+};
+
+
+
+var dbg_names = Object.fromList([
+    [1, ""],
+    [LOG_CPU, "CPU"],
+    [LOG_DISK, "DISK"],
+    [LOG_FPU, "FPU"],
+    [LOG_MEM, "MEM"],
+    [LOG_DMA, "DMA"],
+    [LOG_IO, "IO"],
+    [LOG_PS2, "PS2"],
+    [LOG_PIC, "PIC"],
+    [LOG_VGA, "VGA"],
+    [LOG_PIT, "PIT"],
+    [LOG_MOUSE, "MOUS"],
+    [LOG_PCI, "PCI"],
+    [LOG_BIOS, "BIOS"],
+    [LOG_CD, "CD"],
+    [LOG_SERIAL, "SERI"],
+    [LOG_RTC, "RTC"],
+]);
+
+
+/** 
+ * @param {number=} level
+ */
+function dbg_log(stuff, level)
+{
+    if(!DEBUG) return;
+
+    level = level || 1;
+
+    if(level & LOG_LEVEL)
+    {
+        var level_name = dbg_names[level] || "";
+
+        console.log("[" + String.pads(level_name, 4) + "] " + stuff);
+    }
+};
+
+function dbg_trace()
+{
+    if(!DEBUG) return;
+
+    dbg_log(Error().stack);
+}
+
+/** 
+ * console.assert is fucking slow
+ * @param {string=} msg
+ */
+function dbg_assert(cond, msg) 
+{ 
+    if(!DEBUG) return;
+
+    if(!cond) 
+    { 
+        //dump_regs();
+        console.log(Error().stack);
+        console.trace();
+        if(msg)
+        {
+            throw "Assert failed: " + msg;
+        }
+        else
+        {
+            throw "Assert failed";
+        }
+    } 
+};
+
+
+Object.extend = function(target, src)
+{
+    var keys = Object.keys(src);
+
+    for(var i = 0; i < keys.length; i++)
+    {
+        target[keys[i]] = src[keys[i]];
+    }
+}
+
+
+// pad string with spaces on the right
+String.pads = function(str, len)
+{
+    str = str ? str + "" : "";
+
+    while(str.length < len)
+    {
+        str = str + " ";
+    }
+    
+    return str;
+}
+
+// pad string with zeros on the left
+String.pad0 = function(str, len)
+{
+    str = str ? str + "" : "";
+
+    while(str.length < len)
+    {
+        str = "0" + str;
+    }
+    
+    return str;
+}
+
+Array.range = function(n)
+{
+    var a = [];
+
+    for(var i = 0; i < n; i++)
+    {
+        a[i] = i;
+    }
+
+    return a;
+}
+
+/**
+ * number to hex
+ * @param {number=} len
+ */
+function h(n, len)
+{
+    //dbg_assert(typeof n === "number");
+
+    if(!n) return String.pad0("", len || 1);
+
+    if(len)
+    {
+        return String.pad0(n.toString(16).toUpperCase(), len);
+    }
+    else
+    {
+        return n.toString(16).toUpperCase();
+    }
+}
+
+Number.bits = function(n)
+{
+    var result = [];
+
+    for(var bit = 31; bit > -1; bit--)
+    {
+        if(n & 1 << bit)
+        {
+            result.push(bit);
+        }
+    }
+
+    return result.join(', ');
+}
+
+String.chr_repeat = function(chr, count)
+{
+    var result = "";
+
+    while(count--)
+    {
+        result += chr;
+    }
+
+    return result;
+}
+
+
+Math.bcd_pack = function(n)
+{ 
+    var i = 0, 
+        result = 0,
+        digit;
+    
+    while(n)
+    {
+        digit = n % 10; 
+        
+        result |= digit << (4 * i); 
+        i++; 
+        n = (n - digit) / 10;
+    } 
+    
+    return result;
+}
+
+/** 
+ * @param {string=} msg
+ * */
+function unimpl(msg)
+{
+    var s = "Unimplemented" + (msg ? ": " + msg : "");
+
+    log(s);
+
+    if(DEBUG)
+    {
+        console.trace();
+        return s;
+    }
+    else
+    {
+        log("Execution stopped");
+        return s;
+    }
+    //this.name = "Unimplemented";
+}
+
+/** 
+ * Synchronous access to ArrayBuffer
+ * @constructor
+ */
+function SyncBuffer(buffer)
+{
+    this.byteLength = buffer.byteLength;
+
+    // warning: fn may be called synchronously or asynchronously
+    this.get = function(start, len, fn)
+    {
+        fn(new Uint8Array(buffer, start, len));
+    };
+
+    this.set = function(start, slice, fn)
+    {
+        new Uint8Array(buffer, start, slice.byteLength).set(slice);
+        fn();
+    };
+
+    this.get_buffer = function(fn)
+    {
+        fn(buffer);
+    };
+}
+
+/**
+ * Simple circular queue for logs
+ *
+ * @param {number} size
+ * @param {?=} Proto
+ * @constructor
+ */
+function CircularQueue(size, Proto)
+{
+    var data,
+        index;
+
+    this.add = function(item)
+    {
+        data[index] = item;
+
+        index = (index + 1) % size;
+    };
+
+    this.toArray = function()
+    {
+        return [].slice.call(data, index).concat([].slice.call(data, 0, index));
+    };
+
+    this.clear = function()
+    {
+        if(Proto)
+        {
+            data = new Proto(size);
+        }
+        else
+        {
+            data = [];
+        }
+
+        index = 0;
+    };
+
+    this.set = function(new_data)
+    {
+        data = new_data;
+        index = 0;
+    };
+
+
+    this.clear();
+}
+
+
+
+// switch number to big endian
+Math.to_be32 = function(dword)
+{
+    return dword >>> 24 | 
+        dword >> 8 & 0xff00 | 
+        dword << 8 & 0xff0000 | 
+        dword << 24;
+}
+
+Math.to_be16 = function(word)
+{
+    return word >>> 8 & 0xff | word << 8 & 0xff00;
+}
+
+
+// used in several places
+// the first entry is -1
+//   http://jsperf.com/integer-log2/2
+var log2_table = (function()
+{
+    var t = new Int8Array(256);
+
+    for(var i = 0, b = -2; i < 256; i++)
+    {
+        if(!(i & i - 1))
+            b++;
+
+        t[i] = b;
+    }
+
+    return t;
+})();
+
+
+// round away from zero, opposite of truncation
+Math.roundInfinity = function(x)
+{
+    return x > 0 ? Math.ceil(x) : Math.floor(x);
+};
+

+ 486 - 0
src/memory.js

@@ -0,0 +1,486 @@
+"use strict";
+
+/**
+ * @constructor
+ */
+function Memory(buffer, memory_size)
+{
+    var int8array = new Uint8Array(buffer),
+        int16array = new Uint16Array(buffer),
+        int8sarray = new Int8Array(buffer),
+        int32sarray = new Int32Array(buffer);
+
+    this.mem8 = int8array;
+    this.mem8s = int8sarray;
+    this.mem32s = int32sarray;
+
+    this.buffer = buffer;
+
+    // debug function called by all memory reads and writes
+
+    function debug_write(addr, size, value)
+    {
+        if(!DEBUG)
+        {
+            return;
+        }
+
+        //dbg_assert(typeof value === "number" && !isNaN(value));
+        debug_read(addr, size, true);
+    }
+
+    /** @param {boolean=} is_write */
+    function debug_read(addr, size, is_write)
+    {
+        if(!DEBUG)
+        {
+            return;
+        }
+
+        dbg_assert(typeof addr === "number");
+        dbg_assert(!isNaN(addr));
+
+        if((addr >= memory_size || addr < 0) && !memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            dbg_log("Read from unmapped memory space, addr=" + h(addr, 8) /*+ " at " + h(instruction_pointer, 8)*/, LOG_IO);
+        }
+
+        //dbg_assert(memory_map_registered[addr >>> MMAP_BLOCK_SIZE]);
+    };
+
+    this.dump_all = function(start, end)
+    {
+        start = start || 0;
+        end = end || 0x100000;
+        
+
+        // textarea method: (slow)
+        //var result_string = "";
+
+        //for(var i = start; i < start + end; i++)
+        //{
+        //    result_string += String.fromCharCode(int8array[i]);
+        //}
+
+        //dump_text(btoa(result_string));
+
+        // save as method:
+        dump_file(buffer.slice(start, end), "memory.bin");
+
+    };
+    
+    this.dump = function(addr, length)
+    {
+        length = length || 4 * 0x10;
+        var line, byt;
+        
+        for(var i = 0; i < length >> 4; i++)
+        {
+            line = h(addr + (i << 4), 5) + "   ";
+
+            for(var j = 0; j < 0x10; j++)
+            {
+                byt = this.read8(addr + (i << 4) + j);
+                line += h(byt, 2) + " ";
+            }
+
+            line += "  ";
+
+            for(j = 0; j < 0x10; j++)
+            {
+                byt = this.read8(addr + (i << 4) + j);
+                line += (byt < 33 || byt > 126) ? "." : String.fromCharCode(byt);
+            }
+
+            dbg_log(line);
+        }
+    };
+
+    this.print_memory_map = function()
+    {
+        var width = 0x80,
+            height = 0x10,
+            block_size = memory_size / width / height | 0,
+            row;
+
+        for(var i = 0; i < height; i++)
+        {
+            row = "0x" + h(i * width * block_size, 8) + " | ";
+
+            for(var j = 0; j < width; j++)
+            {
+                var used = this.mem32s[(i * width + j) * block_size] > 0;
+
+                row += used ? "X" : " ";
+            }
+
+            dbg_log(row);
+        }
+    };
+
+
+    var 
+        /** 
+         * Arbritary value, the minimum number of bytes that can be mapped
+         * by one device. This might be spec'd somewhere ...
+         *
+         * Written as a power of 2.
+         *
+         * @const 
+         */ 
+        MMAP_BLOCK_SIZE = 14,
+
+        /** @const */
+        MMAP_BYTEWISE = 1,
+        MMAP_DWORDWISE = 4,
+
+        
+        // this only supports a 32 bit address space
+        memory_map_registered = new Int8Array(1 << 32 - MMAP_BLOCK_SIZE),
+
+        memory_map_read = [],
+        memory_map_write = [];
+
+    for(var i = 0; i < (1 << 32 - MMAP_BLOCK_SIZE); i++)
+    {
+        // avoid sparse arrays
+        memory_map_read[i] = memory_map_write[i] = undefined;
+    }
+
+    /**
+     * @param addr {number}
+     * @param size {number}
+     * @param is_dword {boolean} true if the memory is addressed in dwords, otherwise byte
+     *
+     */
+    this.mmap_register = function(addr, size, is_dword, read_func, write_func)
+    {
+        dbg_log("mmap_register32 " + h(addr, 8) + ": " + h(size, 8), LOG_IO);
+        dbg_assert((addr & (1 << MMAP_BLOCK_SIZE) - 1) === 0);
+        dbg_assert(size >= (1 << MMAP_BLOCK_SIZE) && (size & (1 << MMAP_BLOCK_SIZE) - 1) === 0);
+
+        var aligned_addr = addr >>> MMAP_BLOCK_SIZE,
+            unit_size = is_dword ? MMAP_DWORDWISE : MMAP_BYTEWISE;
+
+        for(; size > 0; aligned_addr++)
+        {
+            memory_map_registered[aligned_addr] = unit_size;
+
+            memory_map_read[aligned_addr] = function(read_addr)
+            {
+                return read_func(read_addr - addr | 0);
+            };
+            memory_map_write[aligned_addr] = function(write_addr, value)
+            {
+                write_func(write_addr - addr | 0, value);
+            }            
+
+            size -= 1 << MMAP_BLOCK_SIZE;
+        }
+    };
+
+    function mmap_read8(addr)
+    {
+        var aligned_addr = addr >>> MMAP_BLOCK_SIZE,
+            registered = memory_map_read[aligned_addr];
+
+        //dbg_log("mmap_read8 " + h(addr, 8), LOG_IO);
+
+        if(memory_map_registered[aligned_addr] === MMAP_BYTEWISE)
+        {
+            return registered(addr);
+        }
+        else 
+        {
+            return mmap_read32(addr & ~3) >> 8 * (addr & 3) & 0xFF;
+        }
+    };
+
+    function mmap_write8(addr, value)
+    {
+        var aligned_addr = addr >>> MMAP_BLOCK_SIZE,
+            registered = memory_map_write[addr >>> MMAP_BLOCK_SIZE];
+
+        //dbg_log("mmap_write8 " + h(addr, 8) + ": " + h(value, 2), LOG_IO);
+
+        if(memory_map_registered[aligned_addr] === MMAP_BYTEWISE)
+        {
+            registered(addr, value);
+        }
+        else
+        {
+            // impossible without reading. Maybe this should do nothing
+            dbg_assert(false);
+        }
+    };
+
+    function mmap_read32(addr)
+    {
+        var registered = memory_map_read[addr >>> MMAP_BLOCK_SIZE];
+
+        //dbg_log("mmap_read32 " + h(addr, 8), LOG_IO);
+        //dbg_assert((addr & 3) === 0);
+        dbg_assert(registered);
+
+        if((addr & 3) === 0 && 
+                memory_map_registered[addr >>> MMAP_BLOCK_SIZE] === MMAP_DWORDWISE)
+        {
+            return registered(addr);
+        }
+        else
+        {
+            return mmap_read8(addr) | mmap_read8(addr + 1) << 8 |
+                mmap_read8(addr + 2) << 16 | mmap_read8(addr + 3) << 24;
+        }
+    };
+
+    function mmap_write32(addr, value)
+    {
+        var registered = memory_map_write[addr >>> MMAP_BLOCK_SIZE];
+
+        //dbg_log("mmap_write32 " + h(addr, 8) + ": " + h(value, 8), LOG_IO);
+        //dbg_assert((addr & 3) === 0);
+        dbg_assert(registered);
+
+        if((addr & 3) === 0 && 
+                memory_map_registered[addr >>> MMAP_BLOCK_SIZE] === MMAP_DWORDWISE)
+        {
+            registered(addr, value);
+        }
+        else
+        {
+            mmap_write8(addr, value & 0xFF);
+            mmap_write8(addr + 1, value >> 8 & 0xFF);
+            mmap_write8(addr + 2, value >> 16 & 0xFF);
+            mmap_write8(addr + 3, value >> 24 & 0xFF);
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     */
+    this.read8s = function(addr)
+    {
+        debug_read(addr, 1);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            return mmap_read8(addr) << 24 >> 24;
+        }
+        else
+        {
+            return int8sarray[addr];
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     */
+    this.read8 = function(addr)
+    {
+        debug_read(addr, 1);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            return mmap_read8(addr);
+        }
+        else
+        {
+            return int8array[addr];
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     */
+    this.read16 = function(addr)
+    {
+        debug_read(addr, 2);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            return mmap_read8(addr) | mmap_read8(addr + 1) << 8;
+        }
+        else
+        {
+            return int8array[addr] | int8array[addr + 1] << 8;
+        }
+    };
+
+    /**
+     * @param addr {number}
+     */
+    this.read_aligned16 = function(addr)
+    {
+        debug_read(addr, 2);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            return mmap_read8(addr) | mmap_read8(addr + 1) << 8;
+        }
+        else
+        {
+            return int16array[addr >> 1];
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     */
+    this.read32s = function(addr)
+    {
+        debug_read(addr, 4);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            return mmap_read32(addr) | 0;
+        }
+        else
+        {
+            return int8array[addr] | int8array[addr + 1] << 8 | 
+                int8array[addr + 2] << 16 | int8array[addr + 3] << 24;
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     */
+    this.read_aligned32 = function(addr)
+    {
+        debug_read(addr, 4);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            return mmap_read32(addr) | 0;
+        }
+        else
+        {
+            return int32sarray[addr >> 2];
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     * @param value {number}
+     */
+    this.write8 = function(addr, value)
+    {
+        debug_write(addr, 1, value);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            mmap_write8(addr, value);
+        }
+        else
+        {
+            int8array[addr] = value;
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     * @param value {number}
+     */
+    this.write16 = function(addr, value)
+    {
+        debug_write(addr, 2, value);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            mmap_write8(addr, value & 0xff);
+            mmap_write8(addr + 1, value >> 8 & 0xff);
+        }
+        else
+        {
+            int8array[addr] = value;
+            int8array[addr + 1] = value >> 8;
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     * @param value {number}
+     */
+    this.write_aligned16 = function(addr, value)
+    {
+        debug_write(addr, 2, value);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            mmap_write8(addr, value & 0xff);
+            mmap_write8(addr + 1, value >> 8 & 0xff);
+        }
+        else
+        {
+            int16array[addr >> 1] = value;
+        }
+    };
+    
+    /**
+     * @param addr {number}
+     * @param value {number}
+     */
+    this.write32 = function(addr, value)
+    {
+        debug_write(addr, 4, value);
+
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            mmap_write32(addr, value);
+        }
+        else
+        {
+            int8array[addr] = value;
+            int8array[addr + 1] = value >> 8;
+            int8array[addr + 2] = value >> 16;
+            int8array[addr + 3] = value >> 24;
+        }
+    };
+
+    this.write_aligned32 = function(addr, value)
+    {
+        if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
+        {
+            mmap_write32(addr, value);
+        }
+        else
+        {
+            int32sarray[addr >> 2] = value;
+        }
+    };
+
+    /**
+     * @param offset {number}
+     * @param blob {Array.<number>}
+     */
+    this.write_blob = function(blob, offset)
+    {
+        dbg_assert(blob && blob.length);
+        int8array.set(blob, offset);
+    };
+
+    /**
+     * zero byte terminated string
+     */
+    this.read_string = function(addr)
+    {
+        var str = "",
+            data_byte;
+
+        while(data_byte = this.read8(addr))
+        {
+            str += String.fromCharCode(data_byte);
+            addr++;
+        }
+
+        return str;
+    };
+
+    this.write_string = function(str, addr)
+    {
+        for(var i = 0; i < str.length; i++)
+        {
+            this.write8(addr + i, str.charCodeAt(i));
+        }
+    };
+}

+ 458 - 0
src/misc_instr.macro.js

@@ -0,0 +1,458 @@
+/*
+ * Some miscellaneous instructions:
+ *
+ * jmpcc16, jmpcc32, jmp16
+ * loop, loope, loopne, jcxz
+ * test_cc
+ *
+ * mov, push, pop
+ * pusha, popa
+ * xchg, lss
+ * lea
+ * enter
+ * bswap
+ *
+ * Gets #included by cpu.macro.js
+ */
+"use strict";
+
+function jmp_rel16(rel16)
+{
+    var current_cs = get_seg(reg_cs);
+
+    // limit ip to 16 bit
+    // ugly
+    instruction_pointer -= current_cs;
+    instruction_pointer = (instruction_pointer + rel16) & 0xFFFF;
+    instruction_pointer = instruction_pointer + current_cs | 0;
+}
+
+function jmpcc16(condition)
+{
+    if(condition)
+    {
+        jmp_rel16(read_imm16());
+    }
+    else
+    {
+        instruction_pointer += 2;
+    }
+}
+
+
+function jmpcc32(condition)
+{
+    if(condition)
+    {
+        // don't write `instruction_pointer += read_imm32s()`
+        var imm32s = read_imm32s();
+        instruction_pointer = instruction_pointer + imm32s | 0;
+    }
+    else
+    {
+        instruction_pointer = instruction_pointer + 4 | 0;
+    }
+}
+
+function loopne()
+{
+    if(--regv[reg_vcx] && !getzf())
+    {
+        var imm8s = read_imm8s();
+        instruction_pointer = instruction_pointer + imm8s | 0;
+    }
+    else
+    {
+        instruction_pointer++;
+    }
+}
+
+function loope()
+{
+    if(--regv[reg_vcx] && getzf())
+    {
+        var imm8s = read_imm8s();
+        instruction_pointer = instruction_pointer + imm8s | 0;
+    }
+    else
+    {
+        instruction_pointer++;
+    }
+}
+
+function loop()
+{
+    if(--regv[reg_vcx])
+    {
+        var imm8s = read_imm8s();
+        instruction_pointer = instruction_pointer + imm8s | 0;
+    }
+    else
+    {
+        instruction_pointer++;
+    }
+}
+
+function jcxz()
+{
+    var imm8s = read_imm8s();
+
+    if(regv[reg_vcx] === 0)
+    {
+        instruction_pointer = instruction_pointer + imm8s | 0;
+    }
+}
+
+var test_o = getof,
+    test_b = getcf,
+    test_z = getzf,
+    test_s = getsf,
+    test_p = getpf;
+
+function test_be()
+{
+    return getcf() || getzf();
+}
+
+function test_l()
+{
+    return !getsf() !== !getof();
+}
+
+function test_le()
+{
+    return getzf() || !getsf() !== !getof();
+}
+
+/** 
+ * @return {number}
+ * @const
+ */
+function getcf()
+{
+    if(flags_changed & 1)
+    {
+        if(last_op_size === OPSIZE_32)
+        {
+            // cannot bit test above 2^32-1
+            return last_result > 0xffffffff | last_result < 0;
+            //return ((last_op1 ^ last_result) & (last_op2 ^ last_result)) >>> 31;
+        }
+        else
+        {
+            return last_result >> last_op_size & 1;
+        }
+
+        //return last_result >= (1 << last_op_size) | last_result < 0;
+    }
+    else
+    {
+        return flags & 1;
+    }
+}
+
+/** @return {number} */
+function getpf()
+{
+    if(flags_changed & flag_parity)
+    {
+        // inverted lookup table
+        return 0x9669 << 2 >> ((last_result ^ last_result >> 4) & 0xF) & flag_parity;
+    }
+    else
+    {
+        return flags & flag_parity;
+    }
+}
+
+/** @return {number} */
+function getaf()
+{
+    if(flags_changed & flag_adjust)
+    {
+        return (last_op1 ^ last_op2 ^ last_result ^ (last_op2 < 0) << 4) & flag_adjust;
+    }
+    else
+    {
+        return flags & flag_adjust;
+    }
+}
+
+/** @return {number} */
+function getzf()
+{
+    if(flags_changed & flag_zero)
+    {
+        return (~last_result & last_result - 1) >> last_op_size - 7 & flag_zero;
+    }
+    else
+    {
+        return flags & flag_zero;
+    }
+}
+
+/** @return {number} */
+function getsf()
+{
+    if(flags_changed & flag_sign)
+    {
+        return last_result >> last_op_size - 8 & flag_sign;
+    }
+    else
+    {
+        return flags & flag_sign;
+    }
+}
+
+/** @return {number} */
+function getof()
+{
+    if(flags_changed & flag_overflow)
+    {
+        return (((last_op1 ^ last_result) & (last_op2 ^ last_result)) >> last_op_size - 1) << 11 & flag_overflow;
+    }
+    else
+    {
+        return flags & flag_overflow;
+    }
+}
+
+
+function push16(imm16)
+{
+    var sp = get_esp_write(-2);
+
+    stack_reg[reg_vsp] -= 2;
+    memory.write16(sp, imm16);
+}
+
+function push32(imm32)
+{
+    var sp = get_esp_write(-4);
+
+    stack_reg[reg_vsp] -= 4;
+    memory.write32(sp, imm32);
+}
+
+function pop16()
+{
+    var sp = get_esp_read(0);
+
+    stack_reg[reg_vsp] += 2;
+    return memory.read16(sp);
+}
+
+function pop32s()
+{
+    var sp = get_esp_read(0);
+
+    stack_reg[reg_vsp] += 4;
+    return memory.read32s(sp);
+}
+
+function pusha16()
+{
+    var temp = reg16[reg_sp];
+
+    // make sure we don't get a pagefault after having 
+    // pushed several registers already
+    translate_address_write(temp - 15);
+
+    push16(reg16[reg_ax]);
+    push16(reg16[reg_cx]);
+    push16(reg16[reg_dx]);
+    push16(reg16[reg_bx]);
+    push16(temp);
+    push16(reg16[reg_bp]);
+    push16(reg16[reg_si]);
+    push16(reg16[reg_di]);
+}
+
+function pusha32()
+{
+    var temp = reg32s[reg_esp];
+
+    translate_address_write(temp - 31);
+
+    push32(reg32s[reg_eax]);
+    push32(reg32s[reg_ecx]);
+    push32(reg32s[reg_edx]);
+    push32(reg32s[reg_ebx]);
+    push32(temp);
+    push32(reg32s[reg_ebp]);
+    push32(reg32s[reg_esi]);
+    push32(reg32s[reg_edi]);
+}
+
+function popa16()
+{
+    translate_address_read(stack_reg[reg_vsp] + 15);
+
+    reg16[reg_di] = pop16();
+    reg16[reg_si] = pop16();
+    reg16[reg_bp] = pop16();
+    stack_reg[reg_vsp] += 2;
+    reg16[reg_bx] = pop16();
+    reg16[reg_dx] = pop16();
+    reg16[reg_cx] = pop16();
+    reg16[reg_ax] = pop16();
+}
+
+function popa32()
+{
+    translate_address_read(stack_reg[reg_vsp] + 31);
+
+    reg32[reg_edi] = pop32s();
+    reg32[reg_esi] = pop32s();
+    reg32[reg_ebp] = pop32s();
+    stack_reg[reg_vsp] += 4;
+    reg32[reg_ebx] = pop32s();
+    reg32[reg_edx] = pop32s();
+    reg32[reg_ecx] = pop32s();
+    reg32[reg_eax] = pop32s();
+}
+
+function xchg8(memory_data, modrm_byte)
+{
+    var mod = modrm_byte >> 1 & 0xC | modrm_byte >> 5 & 1,
+        tmp = reg8[mod];
+
+    reg8[mod] = memory_data;
+
+    return tmp;
+}
+
+function xchg16(memory_data, modrm_byte)
+{
+    var mod = modrm_byte >> 2 & 14,
+        tmp = reg16[mod];
+
+    reg16[mod] = memory_data;
+
+    return tmp;
+}
+
+function xchg16r(operand)
+{
+    var temp = reg16[reg_ax];
+    reg16[reg_ax] = reg16[operand];
+    reg16[operand] = temp;
+}
+
+function xchg32(memory_data, modrm_byte)
+{
+    var mod = modrm_byte >> 3 & 7,
+        tmp = reg32s[mod];
+
+    reg32[mod] = memory_data;
+
+    return tmp;
+}
+
+function xchg32r(operand)
+{
+    var temp = reg32s[reg_eax];
+    reg32[reg_eax] = reg32s[operand];
+    reg32[operand] = temp;
+}
+
+function lss16(seg, addr, mod)
+{
+    var new_reg = safe_read16(addr),
+        new_seg = safe_read16(addr + 2);
+
+    switch_seg(seg, new_seg);
+
+    reg16[mod] = new_reg;
+}
+
+function lss32(seg, addr, mod)
+{
+    var new_reg = safe_read32s(addr),
+        new_seg = safe_read16(addr + 4);
+
+    switch_seg(seg, new_seg);
+
+    reg32[mod] = new_reg;
+}
+
+function lea16()
+{
+    var modrm_byte = read_imm8(),
+        mod = modrm_byte >> 3 & 7;
+
+    // override prefix, so modrm16 does not return the segment part
+    segment_prefix = reg_noseg; 
+
+    reg16[mod << 1] = modrm_resolve(modrm_byte);
+
+    segment_prefix = -1;
+}
+
+function lea32()
+{
+    var modrm_byte = read_imm8(),
+        mod = modrm_byte >> 3 & 7;
+
+    segment_prefix = reg_noseg; 
+
+    reg32[mod] = modrm_resolve(modrm_byte);
+
+    segment_prefix = -1;
+}
+
+function enter16()
+{
+    var size = read_imm16(),
+        nesting_level = read_imm8(),
+        frame_temp;
+    
+    push16(reg16[reg_bp]);
+    frame_temp = reg16[reg_sp];
+
+    if(nesting_level > 0)
+    {
+        for(var i = 1; i < nesting_level; i++)
+        {
+            reg16[reg_bp] -= 2;
+            push16(reg16[reg_bp]);
+        }
+        push16(frame_temp);
+    }
+    reg16[reg_bp] = frame_temp;
+    reg16[reg_sp] = frame_temp - size;
+
+    dbg_assert(!page_fault);
+}
+
+function enter32()
+{
+    var size = read_imm16(),
+        nesting_level = read_imm8() & 31,
+        frame_temp;
+
+    push32(reg32s[reg_ebp]);
+    frame_temp = reg32s[reg_esp];
+
+    if(nesting_level > 0)
+    {
+        for(var i = 1; i < nesting_level; i++)
+        {
+            reg32[reg_ebp] -= 4;
+            push32(reg32s[reg_ebp]);
+        }
+        push32(frame_temp);
+    }
+    reg32[reg_ebp] = frame_temp;
+    reg32[reg_esp] -= size;
+
+    dbg_assert(!page_fault);
+}
+
+function bswap(reg)
+{
+    var temp = reg32s[reg];
+
+    reg32[reg] = temp >>> 24 | temp << 24 | (temp >> 8 & 0xFF00) | (temp << 8 & 0xFF0000);
+}
+

+ 176 - 0
src/modrm.macro.js

@@ -0,0 +1,176 @@
+/*
+ * This file contains functions to decode the modrm and sib bytes
+ *
+ * These functions return a virtual address
+ *
+ * Gets #included by cpu.macro.js
+ */
+"use strict";
+
+var modrm_resolve16,
+    modrm_resolve32;
+
+(function() {
+
+var modrm_table16 = Array(0xC0),
+    modrm_table32 = Array(0xC0),
+    sib_table = Array(0x100);
+
+#define ds get_seg_prefix(reg_ds)
+#define ss get_seg_prefix(reg_ss)
+
+#define eax reg32s[reg_eax]
+#define ecx reg32s[reg_ecx]
+#define edx reg32s[reg_edx]
+#define ebx reg32s[reg_ebx]
+#define esp reg32s[reg_esp]
+#define ebp reg32s[reg_ebp]
+#define esi reg32s[reg_esi]
+#define edi reg32s[reg_edi]
+
+#define imm32 read_imm32s()
+#define imm16 read_imm16()
+#define imm8 read_imm8()
+
+#define entry16(row, seg, value)\
+    entry16_(0x00 | row, seg + ((value) & 0xFFFF))\
+    entry16_(0x40 | row, seg + ((value) + read_imm8s() & 0xFFFF))\
+    entry16_(0x80 | row, seg + ((value) + read_imm16() & 0xFFFF))\
+
+#define entry16_(n, offset)\
+    modrm_table16[n] = function() { return offset | 0; };
+
+
+entry16(0, ds, reg16[reg_bx] + reg16[reg_si])
+entry16(1, ds, reg16[reg_bx] + reg16[reg_di])
+entry16(2, ss, reg16[reg_bp] + reg16[reg_si])
+entry16(3, ss, reg16[reg_bp] + reg16[reg_di])
+entry16(4, ds, reg16[reg_si])
+entry16(5, ds, reg16[reg_di])
+entry16(6, ss, reg16[reg_bp])
+entry16(7, ds, reg16[reg_bx])
+
+#define entry32(row, value)\
+    entry32_(0x00 | row, (value))\
+    entry32_(0x40 | row, (value) + read_imm8s())\
+    entry32_(0x80 | row, (value) + read_imm32s())\
+
+#define entry32_(n, offset)\
+    modrm_table32[n] = function() { return offset | 0; };
+
+
+entry32(0, ds + eax);
+entry32(1, ds + ecx);
+entry32(2, ds + edx);
+entry32(3, ds + ebx);
+entry32(4, getsib(false));
+entry32(5, ss + ebp);
+entry32(6, ds + esi);
+entry32(7, ds + edi);
+
+
+// special cases
+modrm_table16[0x00 | 6] = function() { return ds + read_imm16() | 0; }
+
+modrm_table32[0x00 | 5] = function() { return ds + read_imm32s() | 0; };
+
+modrm_table32[0x00 | 4] = function() { return getsib(false) | 0; };
+modrm_table32[0x40 | 4] = function() { return getsib(true) + read_imm8s() | 0; };
+modrm_table32[0x80 | 4] = function() { return getsib(true) + read_imm32s() | 0; };
+
+
+for(var low = 0; low < 8; low++)
+{
+    for(var high = 0; high < 3; high++)
+    {
+        for(var i = 1; i < 8; i++)
+        {
+            var x = low | high << 6;
+
+            modrm_table32[x | i << 3] = modrm_table32[x];
+            modrm_table16[x | i << 3] = modrm_table16[x];
+        }
+    }
+}
+
+#define entry_sib(n, reg1)\
+    entry_sib2(0x00 | n << 3, reg1)\
+    entry_sib2(0x40 | n << 3, reg1 << 1)\
+    entry_sib2(0x80 | n << 3, reg1 << 2)\
+    entry_sib2(0xC0 | n << 3, reg1 << 3)
+
+#define entry_sib2(n, offset)\
+    entry_sib3(n | 0, (offset) + ds + eax)\
+    entry_sib3(n | 1, (offset) + ds + ecx)\
+    entry_sib3(n | 2, (offset) + ds + edx)\
+    entry_sib3(n | 3, (offset) + ds + ebx)\
+    entry_sib3(n | 4, (offset) + ss + esp)\
+    entry_sib3(n | 5, (offset) + (mod ? ss + ebp : ds + imm32))\
+    entry_sib3(n | 6, (offset) + ds + esi)\
+    entry_sib3(n | 7, (offset) + ds + edi)
+
+#define entry_sib3(n, offset)\
+    sib_table[n] = function(mod) { return offset | 0; };
+
+entry_sib(0, eax);
+entry_sib(1, ecx);
+entry_sib(2, edx);
+entry_sib(3, ebx);
+entry_sib(4, 0);
+entry_sib(5, ebp);
+entry_sib(6, esi);
+entry_sib(7, edi);
+
+
+/**
+ * @param {number} modrm_byte
+ * @return {number}
+ */
+modrm_resolve16 = function(modrm_byte)
+{
+    return modrm_table16[modrm_byte]();
+}
+
+/**
+ * @param {number} modrm_byte
+ * @return {number}
+ */
+modrm_resolve32 = function(modrm_byte)
+{
+    return modrm_table32[modrm_byte]();
+}
+
+/**
+ * @param {boolean} mod
+ * @return {number}
+ */
+function getsib(mod)
+{
+    return sib_table[read_imm8()](mod);
+}
+
+#undef ds 
+#undef ss 
+
+#undef eax 
+#undef ecx 
+#undef edx 
+#undef ebx 
+#undef esp 
+#undef ebp 
+#undef esi 
+#undef edi 
+
+#undef imm32 
+#undef imm16
+#undef imm8
+
+#undef entry16
+#undef entry16_
+#undef entry32_
+
+#undef entry_sib
+#undef entry_sib2
+#undef entry_sib3
+
+})();

BIN
src/node/ascii.ttf


+ 35 - 0
src/node/keyboard_sdl.js

@@ -0,0 +1,35 @@
+"use strict";
+
+function NodeKeyboardSDL(sdl)
+{
+    var send_code;
+
+    sdl.events.on("KEYDOWN", onkeydown);
+    sdl.events.on("KEYUP", onkeyup);
+
+    this.enabled = true;
+
+    this.destroy = function()
+    {
+
+    };
+
+    this.init = function(send_code_fn)
+    {
+        send_code = send_code_fn;
+    };
+
+    function onkeydown(e)
+    {
+        //console.log("d", e);
+        send_code(e.scancode - 8);
+    }
+
+    function onkeyup(e)
+    {
+        //console.log("u", e);
+        send_code(e.scancode - 8 | 0x80);
+    }
+}
+
+

+ 60 - 0
src/node/keyboard_tty.js

@@ -0,0 +1,60 @@
+"use strict";
+
+function NodeKeyboardTTY()
+{
+    var stdin = process.stdin;
+    var send_code;
+
+    var charmap = [
+        // TODO: Fill this in or get it from somewhere
+    ];
+
+    //stdin.setRawMode(true);
+    stdin.resume();
+
+
+    stdin.setEncoding('utf8');
+
+    this.enabled = true;
+    this.destroy = function()
+    {
+
+    };
+    this.init = function(send_code_fn)
+    {
+        send_code = send_code_fn;
+    };
+
+    stdin.on("data", function(c)
+    {
+        if(c === '\u0003')
+        {
+            process.exit();
+        }
+
+        var str = "";
+
+        for(var i = 0; i < c.length; i++)
+        {
+            str += c.charCodeAt(i);   
+        }
+
+        //dbg_log(str);
+        dbg_log("2 " + JSON.stringify(arguments));
+    });
+
+    stdin.on("keypress", function(c)
+    {
+        if(c === '\u0003')
+        {
+            process.exit();
+        }
+
+        dbg_log("keypress: " + JSON.stringify(arguments));
+
+        var code = charmap[c.charCodeAt(0)];
+
+        send_code(code);
+        //send_code(code | 0x80);
+    });
+}

+ 141 - 0
src/node/main.js

@@ -0,0 +1,141 @@
+"use strict";
+
+
+
+var path = __dirname + "/../",
+    bios_path = path + "../bios/",
+    image_path = path + "../images/";
+
+
+// otherwise tty ouput is used
+var USE_SDL = true,
+    FONT_FILE = path + "node/ascii.ttf";
+
+(function()
+{
+    var tick_fn;
+
+    global.set_tick = function(fn)
+    {
+        tick_fn = fn;   
+    };
+
+    global.next_tick = function()
+    {
+        setImmediate(tick_fn);
+    };
+})();
+
+
+global.log = function(str)
+{
+    console.log(str);
+};
+ 
+var fs = require('fs'),
+    vm = require('vm'),
+    
+    include = function(path) 
+    {
+        // ugh ...
+        var code = fs.readFileSync(path);
+        vm.runInThisContext(code, path);
+    }.bind(this);
+
+include(path + "const.js");
+include(path + "cpu.js");
+include(path + "main.js");
+include(path + "floppy.js");
+include(path + "memory.js");
+include(path + "io.js");
+include(path + "pci.js");
+include(path + "disk.js");
+include(path + "dma.js");
+include(path + "pit.js");
+include(path + "vga.js");
+include(path + "ps2.js");
+include(path + "pic.js");
+include(path + "uart.js");
+include(path + "rtc.js");
+
+
+DEBUG = true;
+
+
+function read_array_buffer(file)
+{
+    var buffer = fs.readFileSync(file),
+        ab = new ArrayBuffer(buffer.length),
+        arr = new Uint8Array(ab);
+
+    for (var i = 0; i < buffer.length; i++) 
+    {
+        arr[i] = buffer[i];
+    }
+
+    return ab;
+}
+
+
+var settings = {
+        load_devices: true,
+    },
+    argv = process.argv;
+
+
+if(USE_SDL)
+{
+    var sdl = require("node-sdl");
+
+    include(path + "node/keyboard_sdl.js");
+    include(path + "node/screen_sdl.js");
+
+    settings.screen_adapter = new NodeScreenSDL(sdl, FONT_FILE);
+    settings.keyboard_adapter = new NodeKeyboardSDL(sdl);
+}
+else
+{
+    require('tty').setRawMode(true);
+
+    include(path + "node/keyboard_tty.js");
+    include(path + "node/screen_tty.js");
+
+    settings.screen_adapter = new NodeScreenTTY();
+    settings.keyboard_adapter = new NodeKeyboardTTY();
+}
+
+
+// just a prototype of a loader
+
+if(argv && argv.length === 4 && (argv[2] === "cdrom" || argv[2] === "fda"))
+{
+    var disk = new SyncBuffer(read_array_buffer(argv[3]));
+
+    if(argv[2] === "cdrom")
+    {
+        settings.cdrom_disk = disk;
+    }
+    else if(argv[2] === "fda")
+    {
+        settings.floppy_disk = disk;
+    }
+
+
+    settings.bios = read_array_buffer(bios_path + "seabios.bin");
+    settings.vga_bios = read_array_buffer(bios_path + "vgabios.bin");
+
+    settings.screen_adapter
+
+    var cpu = new v86();
+
+    cpu.init(settings);
+    cpu.run();
+}
+else
+{
+    console.log("Usage: node main.js [cdrom|fda] disk.img");
+
+    process.exit();
+}
+
+

+ 5 - 0
src/node/node_modules/node-sdl/.gitignore

@@ -0,0 +1,5 @@
+.svn
+*.o
+.lock-wscript
+build
+node_modules

+ 3 - 0
src/node/node_modules/node-sdl/.npmignore

@@ -0,0 +1,3 @@
+build
+examples
+examples/*

+ 72 - 0
src/node/node_modules/node-sdl/Makefile

@@ -0,0 +1,72 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := nodesdl
+DEFS_Default :=
+
+# Flags passed to all source files.
+CFLAGS_Default := \
+	-I/usr/include/SDL \
+	-D_GNU_SOURCE=1 \
+	-D_REENTRANT
+
+# Flags passed to only C files.
+CFLAGS_C_Default :=
+
+# Flags passed to only C++ files.
+CFLAGS_CC_Default :=
+
+INCS_Default :=
+
+OBJS := \
+	$(obj).target/$(TARGET)/node/node_modules/node-sdl/src/helpers.o \
+	$(obj).target/$(TARGET)/node/node_modules/node-sdl/src/nodesdl.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Default := \
+	-L/usr/lib \
+	-lSDL \
+	-lpthread \
+	-lSDL_ttf \
+	-lSDL_image
+
+LIBS :=
+
+$(obj).target/node/node_modules/node-sdl/libnodesdl.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/node/node_modules/node-sdl/libnodesdl.a: LIBS := $(LIBS)
+$(obj).target/node/node_modules/node-sdl/libnodesdl.a: TOOLSET := $(TOOLSET)
+$(obj).target/node/node_modules/node-sdl/libnodesdl.a: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,alink_thin)
+
+all_deps += $(obj).target/node/node_modules/node-sdl/libnodesdl.a
+# Add target alias
+.PHONY: nodesdl
+nodesdl: $(obj).target/node/node_modules/node-sdl/libnodesdl.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: nodesdl
+

+ 429 - 0
src/node/node_modules/node-sdl/README.md.orig

@@ -0,0 +1,429 @@
+# node-sdl ( Simple DirectMedia Layer bindings for node.js )
+
+## 0. Installation
+
+Installation of the node-sdl package is straight-forward: first clone the
+package using git, then build the C++ portion of the package with the
+node-waf command.
+
+This package depends on the SDL libraries being present on the target system.
+The following command was required to install these libraries on a "stock"
+Ubuntu 11.04 install:
+
+<pre>    sudo apt-get install libsdl1.2-dev libsdl-image1.2-dev libsdl-ttf2.0-dev</pre>
+
+Now that your library dependencies are satisfied, check out the source from
+github:
+
+<pre>    git clone https://github.com/creationix/node-sdl.git</pre>
+
+Second, build the package:
+
+<pre>    cd node-sdl
+    node-waf configure build</pre>
+
+You can test if the package was properly built by running one or more of the
+example programs:
+
+<pre>    cd examples
+    node img.js</pre>
+
+## 1. Usage
+
+### 1.1. Initialization and Shutdown
+
+Begin by requiring the node-sdl package and calling the init() function:
+
+<pre>    var SDL = require( 'sdl' );
+    SDL.init( SDL.INIT.VIDEO )</pre>
+
+The init() function takes a numeric parameter telling the library what
+subsystems to initialize. The node-sdl package defines the following
+constants:
+
+<pre>    SDL.INIT.TIMER - initializes timers (not currently supported)
+    SDL.INIT.AUDIO - initialize audio subsystem (not currently supported)
+    SDL.INIT.VIDEO - initialize video subsystem
+    SDL.INIT.CDROM - initialize CD playback subsystem (not currently supported)
+    SDL.INIT.JOYSTICK - initialize joystick support
+    SDL.INIT.EVERYTHING - all of the above
+    SDL.INIT.NOPARACHUTE - don't catch fatal signals
+</pre>
+
+Two or more of these parameters may be selected by or-ing them together:
+
+<pre>    SDL.init( SDL.INIT.VIDEO | SDL.INIT.JOYSTICK );</pre>
+
+The QUIT event signals the closure of a SDL managed window, so adding a
+function that exits the application when it is received may be useful:
+
+<pre>    SDL.events.on( 'QUIT', function( evt ) { process.exit( 0 ); } );</pre>
+
+Exiting the application when the user presses Control-C or the Escape key
+can be achieved by adding a listener to the KEYDOWN event:
+
+<pre>    SDL.events.on( 'KEYDOWN', function ( evt ) {
+      if( ( ( evt.sym === 99 ) && ( evt.mod === 64 ) ) ||
+          ( ( evt.sym === 27 ) && ( evt.mod === 0  ) ) ) {
+        process.exit( 0 );
+      }
+    } );</pre>
+
+### 1.2. Video Functions
+
+To create a window under SDL control, use the setVideoMode() function to 
+create a "surface".
+
+<pre>    var screen = SDL.setVideoMode( 640, 480, 32, SDL.SURFACE.SWSURFACE );</pre>
+
+The setVideoMode() function takes four parameters: surface width, surface
+height, bit depth and surface flags. The flags parameter selects options for
+the video buffer:
+
+<pre>    SDL.SURFACE.SWSURFACE - video buffer created in system memory
+    SDL.SURFACE.HWSURFACE - video buffer created in video memory
+    SDL.SURFACE.ASYNCBLIT - enable async updates of display surface
+    SDL.SURFACE.ANYFORMAT - don't emulate unavailable BPPs with a shadow surface
+    SDL.SURFACE.HWPALETTE - give SDL exclusive palette access (not supported)
+    SDL.SURFACE.DOUBLEBUF - enable hardware double buffering. (only works with
+                            SDL.SURFACE.HWSURFACE)
+    SDL.SURFACE.FULLSCREEN - use fullscreen mode
+    SDL.SURFACE.OPENGL    - create an OpenGL rendering context (not supported)
+    SDL.SURFACE.RESIZABLE - create a resizable window
+    SDL.SURFACE.HWACCEL   - use hardware accelerated blitter
+    SDL.SURFACE.SRCCOLORKEY - use color key blitter
+    SDL.SURFACE.RLEACCEL  - color key blitting is accelerated with RLE
+    SDL.SURFACE.SRCALPHA  - surface blit uses alpha blending
+    SDL.SURFACE.PREALLOC  - surface uses preallocated memory
+</pre>
+
+Like other numeric constants, they may be combined with the or operator:
+
+<pre>    var screen = SDL.setVideoMode( 640, 480, 32, SDL.SURFACE.HWSURFACE | SDL.SURFACE.HWACCEL );</pre>
+
+The surface created with the setVideoMode() call represents the contents of
+the displayed window. It's common practice to create a buffer surface to hold
+video contents in preparation for drawing on the screen. To create a buffer,
+use the createRGBSurface() call.
+
+<pre>    var surface = SDL.createRGBSurface( SDL.SURFACE.SWSURFACE, 24, 24 );</pre>
+
+The first parameter describes the type of surface to create, and the remaining
+parameters are x and y sizes.
+
+After you're done using a surface, you *should* free it. The freeSurface()
+function takes a surface (like one returned from the createRGBSurface()
+function above) and frees memory associated with it:
+
+<pre>    SDL.freeSurface( surface );</pre>
+
+The displayFormat() function copies a surface into a new surface suitable
+for blitting into the frame buffer. It takes a surface as it's first (and only)
+parameter and returns a new surface conformable with the system's frame buffer.
+This call is extremely useful in conjunction with the SDL.IMG.load() call:
+
+<pre>    var tempSheet = SDL.IMG.load( __dirname + "/sprites.png" );
+var sheet = SDL.displayFormat( tempSheet );
+SDL.freeSurface( tempSheet );</pre>
+
+SDL surfaces may have an Alpha value associated with them. This is a value from
+0 to 255 and sets the transparency of the surface's contents when blitted into
+another surface (like the frame buffer).
+
+<pre>    SDL.setAlpha( sheet, SDL.SURFACE.SRCALPHA | SRC.SURFACE.RLEACCEL, 192 );</pre>
+
+Options to the setAlpha() function include:
+
+<pre>    SDL.SURFACE.SRCALPHA - specifies that alpha blending should be used
+    SLD.SURFACE.RLEACCEL - specifies that RLE acceleration should be used for blitting</pre>
+
+You can set a specific color to be transparent (i.e. - the color key) using the
+setColorKey() function. After setting this value, when the surface's contents
+are blitted to another surface, pixels with the color key value won't be copied.
+
+<pre>    SDL.setColorKey( sheet, SDL.SURFACE.SRCCOLORKEY, 0x01010100 );</pre>
+
+The first parameter is the surface whose color key you're setting. The second
+is a set of flags that may be or'd together. The third is a 32 bit integer
+representing the value of the color key you want to use. Values for the flags
+include:
+
+<pre>    SDL.SURFACE.SRCCOLORKEY - means you're setting the surface's color key
+    SDL.SURFACE.RLEACCEL    - you want to enable RLE accleration
+    0                       - means you want to clear the surface's color key
+</pre>
+
+It can sometimes be tricky to get the precise color key value if you're using
+multiple surface geometries. Fortunately, you can use the mapRGB() function
+to return a color value, modified to account for a surface's color geometry.
+In other words, do this when you want to set the color key:
+
+<pre>    var colorKey = [ 255, 0, 0 ]; // setting the color key to red
+    SDL.setColorKey( sheet,
+                     SDL.SURFACE.SRCCOLORKEY | SDL.SURFACE.RLEACCEL,
+                     SDL.mapRGB( sheet.format,
+                                 colorKey[0],
+                                 colorKey[1],
+                                 colorKey[2] ) );</pre>
+
+To fill a rectangle with a particular color, use the fillRect() function.
+
+<pre>    SDL.fillRect( surface, [0, 0, 24, 24], 0xFF8080AF );</pre>
+
+To blit (copy) a portion of one surface into anotehr, use the blitSurface()
+function. It takes as it's parameters: the source surface, a rectangle
+describing the origin and extent of the pixels to be copied, the destination
+surface, and a point in the destination you're copying pixels to.
+
+So the following example copies an 8x16 rectangle from position (10,25) in the
+spriteSource surface into position (128,15) in the screen surface:
+
+<pre>    SDL.blitSurface( spriteSource, [10, 25, 8, 16], screen, [128, 15] );</pre>
+
+After making changes to a surface, you use the flip() function to instruct
+the system to make the changes apparent. In systems that support hardware
+double-buffering, this call "does the right thing" and waits for a vertical
+retrace to flip between video screens. On systems with a software surface, it
+simply makes sure that the contents of the surface are made visible.
+
+It's very useful to call this command after you make updates to the screen. For
+example:
+
+<pre>    var screen = SDL.setVideoMode( 640, 480, 32, SDL.SURFACE.SWSURFACE );
+    SDL.fillRect( surface, [0, 0, 24, 24], 0xFF8080AF );
+    SDL.flip( screen );
+</pre>
+
+### 1.3. Image Related Functions
+
+This package uses a supplimentary image library intended to make it easy for
+node-sdl applications to load and use JPG, PNG or TIFF images. Before using
+Image functions, you should initalize them with the image init() function:
+
+<pre>    SDL.IMG.init( 0 );</pre>
+
+To load an image into memory, use the image load() function. It takes a file
+path as a parameter and returns a reference to it. The following line loads
+a PNG file called "foo.png" into the variable foo.
+
+<pre>    var foo = SDL.IMG.load( __dirname + '/foo.png' );</pre>
+
+The foo variable can now be used as a surface blit calls (see below.)
+
+After you are finished using the image functions, be sure to use the image
+quit() function:
+
+<pre>    SDL.IMG.quit();</pre>
+
+### 1.4. Joystick Functions
+
+If you are developing an application that uses joysticks, you'll need to pass
+the SDL.INIT.JOYSTICK option along to the SDL.init() call:
+
+<pre>    SDL.init( SDL.INIT.VIDEO | SDL.INIT.JOYSTICK );</pre>
+
+Now that your app knows you want to use joysticks, you can detect the number of
+joysticks present with the numJoysticks() function. The following code checks
+to see if there's at least one joystick and complains if there's not:
+
+<pre>    var numPlayers = SDL.numJoysticks();
+    if( numPlayers &lt; 1 ) {
+        console.log( 'Blargh! At least one joystick is required!' );
+        process.exit( 2 );
+    }</pre>
+
+On systems with multiple joysticks, it might be useful to offer a player a
+selection of which joystick to use. The system assigns a human readable name
+for a joystick which the app can query with the joystickName() function. The
+following code prints out the name of each joystick:
+
+<pre>    SDL.init( SDL.INIT.VIDEO | SDL.INIT.JOYSTICK );
+    var stickCount = SDL.numJoysticks();
+
+    for( var i = 0; i &lt; stickCount; i ++ ) {
+        console.log( 'joystick ' + i + ': ' + SDL.joystickName( i ) );
+    }
+
+    // etc</pre>
+
+Now you must explicitly open each joystick you want to receive inputs from. Do
+this with the joystickOpen() function. This function takes an integer as a
+parameter and represents the index of the joystick you want to open. Here is
+some code that opens joystick number zero:
+
+<pre>    SDL.joystickOpen( 0 );</pre>
+
+After the joystick is opened, it will start to generate events. You can register
+event handlers with the SDL.events.on() function. Joystick related events are
+described in the events section below.
+
+### 1.5. Window Manager Functions
+
+node-sdl is capable of setting window manager related info with the SDL.WM.*
+functions.
+
+To set the title of a SDL window, use the setCaption() function. This fragment
+sets the window's title to "Window Title" and (if supported by your window
+manager) sets the name of the minimized icon to "Icon Title"
+
+<pre>    SDL.WM.setCaption( 'Window Title', 'Icon Title' );</pre>
+
+To set the application's icon, use the setIcon() function. It expects an image
+to be passed as it's parameter, so it's common practice to use the image load()
+function. The following example loads an icon from the file 'eight.png' and
+uses it as the app's icon:
+
+<pre>    SDL.WM.setIcon( SDL.IMG.load( __dirname + '/eight.png' ) );</pre>
+
+## 2. Events
+
+node-sdl uses javascript events to communicate certain conditions. The
+events.on() function is used to set handlers for these events. Event handlers
+are passed an object describing the event as a parameter.
+
+### 2.1. Quit
+
+As described above, the QUIT event is called when the user closes a SDL window.
+The proper response is to free buffers, and exit:
+
+<pre>    SDL.events.on( 'QUIT', function ( evt ) {
+        SDL.IMG.quit();
+       process.exit( 0 );
+    } );</pre>
+
+### 2.2. KEYDOWN & KEYUP
+
+The KEYDOWN and KEYUP events signal the app that the user has pressed (or
+released) a key. The event passed to the handler includes the following
+properties:
+
+<pre>    scancode - the scancode of the key pressed
+    sym      - the symbol of the key pressed
+    mod      - key modifier</pre>
+
+Key scancodes are hardware and locale dependent; it's recommended they be
+left alone unless you really are targeting a specific piece of hardware. Key
+symbols are numbers representing keyboard glyphs. Key modifiers represent
+shift, meta, alt and control keys. As you might expect, it's possible for
+multiple modifiers to be pressed simultaneously, so the mod value is a bit
+field with the following definitions:
+
+<pre>
+0x0000 - No modifiers pressed
+0x0001 - Left Shift
+0x0002 - Right Shift
+0x0040 - Left Control Key
+0x0080 - Right Control Key
+0x0100 - Left Alt Key
+0x0200 - Right Alt Key
+0x0400 - Left Meta Key (for hardware that has a meta key)
+0x0800 - Right Meta Key (for hardware that has a meta key)
+0x1000 - Num Lock on
+0x2000 - Caps Lock on
+0x4000 - Mode Key Pressed (bonus points if you can find hardware with a mode key)
+</pre>
+
+It should probably be noted that SDL keysyms are not exactly ASCII. Most
+importantly, the system will not return a capital letter ASCII code when the
+user hits a letter key and the shift key. Instead, you must manually check
+for the shift key being pressed, check the modifier bits and adjust the key
+code accordingly.
+
+The following code converts the modifier and symbol to an ascii value:
+
+<pre>    SDL.events.on( 'KEYDOWN', function( evt ) {
+        var ascii = evt.sym;
+        
+        if( ( ascii &lt; 123 ) && ( ascii &gt; 96 ) ) {
+            if( 0 != ( evt.mod && 0x2003 ) ) {
+                ascii -= 32;
+            }
+        }
+    
+        console.log( 'ascii: ' + ascii );
+    } );</pre>
+
+### 2.3. MOUSEMOTION
+
+When the user moves a mouse over an SDL screen, the system will generate
+MOUSEMOTION events. If you create a handler for these events, every time the
+mouse moves, you'll receive an event with the following properties:
+
+<pre>    state - button state (as described above)
+    x     - x position of the mouse pointer
+    y     - y position of the mouse pointer
+    xrel  - relative motion of the mouse pointer along the x axis
+    yrel  - relative motion of the mouse pointer along the y axis</pre>
+
+The button state is a bit field with the following values:
+
+<pre>    0x0000 - no mouse button pressed
+    0x0001 - left mouse button pressed
+    0x0002 - middle mouse button pressed
+    0x0004 - right mouse button pressed</pre>
+
+Take mouse chords with a grain of salt, some systems may be configured to 
+emulate a 3 button mouse. In these systems, pressing the left and right button
+together will generate a middle button press (code 0x0002) instead of the
+mouse chord you might be expecting (code 0x0005).
+
+### 2.4. MOUSEBUTTONDOWN & MOUSEBUTTONUP
+
+The MOUSEBUTTONUP and MOUSEBUTTONDOWN events report more data and have
+slightly different semantics than the button state in the MOUSEMOTION event.
+Handlers for these events are passed an object with the following properties:
+
+<pre>    button - mouse button clicked
+    x      - x position of the mouse
+    y      - y position of the mouse</pre>
+
+The button property IS NOT a bit field, but an integer. Instead of detecting
+mouse chords, it reports multiple button clicks. Here is the list of mouse
+buttons supported:
+
+<pre>    1 - left button
+    2 - middle button
+    3 - right button
+    4 - scroll wheel up
+    5 - scroll wheel down</pre>
+
+### 2.5. JOYAXISMOTION (Joystick Axis Motion)
+
+The JOYAXISMOTION event reports movement of the joystick device along one of
+its axes. Handlers for this event are passed an object with the following
+properties:
+
+<pre>    which - which joystick generated the event
+    axis  - which axis (x or y) the event is reporting movement upon
+    value - a value from -32768 to 32767 describing the logical position of the joystick</pre>
+
+### 2.6. JOYBALLMOTION (Joystick Trackball Motion)
+
+If a user's joystick is equipped with a trackball, it may generate these events
+when motion along the trackball is detected. Handlers assigned to listen for
+these events will receive an object with the following properties:
+
+<pre>    which - which joystick generated the event
+    ball  - which trackball generated the event
+    xrel  - relative trackball motion along the x axis
+    yrel  - relative trackball motion along the y axis</pre>
+
+### 2.7. JOYHATMOTION (Joystick Hat Motion)
+
+If a user's joystick is equipped with a hat, it may generate these events when
+hat motion is detected. Handlers for this event will be passed an object with
+the following properties:
+
+<pre>    which - which joystick generated the event
+    hat   - which hat on the joystick generated the event
+    value - the position of the hat</pre>
+
+### 2.8. JOYBUTTONDOWN & JOYBUTTONUP
+
+If a user's joystick is equipped with buttons, it may generate these events when
+a button press is detected. Handlers for these events will be passed an object
+with the following properties:
+
+<pre>    which  - which joystick generated the event
+    button - which button was pressed</pre>
+

+ 4 - 0
src/node/node_modules/node-sdl/Readme

@@ -0,0 +1,4 @@
+This is a fork from node-sdl (https://github.com/creationix/node-sdl) with a
+couple of modifications.
+
+The build process is currently broken. Will be fixed at a later point.

+ 21 - 0
src/node/node_modules/node-sdl/binding.gyp

@@ -0,0 +1,21 @@
+{
+  'targets': [
+    {
+      # have to specify 'liblib' here since gyp will remove the first one :\
+      'target_name': 'nodesdl',
+      'type': 'shared_library',
+      'sources': [
+        'src/helpers.cc',
+        'src/nodesdl.cc',
+      ],
+      'ldflags': [
+        '<!@(sdl-config --libs)',
+        "-lSDL_ttf",
+        "-lSDL_image"
+      ],
+      'cflags': [
+        '<!@(sdl-config --cflags)'
+      ],
+    }
+  ]
+}

+ 83 - 0
src/node/node_modules/node-sdl/nodesdl.target.mk

@@ -0,0 +1,83 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := nodesdl
+DEFS_Default :=
+
+# Flags passed to all source files.
+CFLAGS_Default := \
+	-I/usr/include/SDL \
+	-D_GNU_SOURCE=1 \
+	-D_REENTRANT
+
+# Flags passed to only C files.
+CFLAGS_C_Default :=
+
+# Flags passed to only C++ files.
+CFLAGS_CC_Default :=
+
+INCS_Default :=
+
+OBJS := \
+	$(obj).target/$(TARGET)/node/node_modules/node-sdl/src/helpers.o \
+	$(obj).target/$(TARGET)/node/node_modules/node-sdl/src/nodesdl.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Default := \
+	-L/usr/lib \
+	-lSDL \
+	-lpthread \
+	-lSDL_ttf \
+	-lSDL_image
+
+LIBS :=
+
+$(obj).target/node/node_modules/node-sdl/libnodesdl.so: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/node/node_modules/node-sdl/libnodesdl.so: LIBS := $(LIBS)
+$(obj).target/node/node_modules/node-sdl/libnodesdl.so: LD_INPUTS := $(OBJS)
+$(obj).target/node/node_modules/node-sdl/libnodesdl.so: TOOLSET := $(TOOLSET)
+$(obj).target/node/node_modules/node-sdl/libnodesdl.so: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,solink)
+
+all_deps += $(obj).target/node/node_modules/node-sdl/libnodesdl.so
+# Add target alias
+.PHONY: nodesdl
+nodesdl: $(builddir)/libnodesdl.so
+
+# Copy this to the shared library output path.
+$(builddir)/libnodesdl.so: TOOLSET := $(TOOLSET)
+$(builddir)/libnodesdl.so: $(obj).target/node/node_modules/node-sdl/libnodesdl.so FORCE_DO_CMD
+	$(call do_cmd,copy)
+
+all_deps += $(builddir)/libnodesdl.so
+# Short alias for building this shared library.
+.PHONY: libnodesdl.so
+libnodesdl.so: $(obj).target/node/node_modules/node-sdl/libnodesdl.so $(builddir)/libnodesdl.so
+
+# Add shared library to "all" target.
+.PHONY: all
+all: $(builddir)/libnodesdl.so
+

+ 16 - 0
src/node/node_modules/node-sdl/package.json

@@ -0,0 +1,16 @@
+{
+  "author": "Tim Caswell <tim@creationix.com> (http://creationix.com/)",
+  "name": "sdl",
+  "description": "SDL bindings for node",
+  "version": "0.1.8",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/creationix/node-sdl.git"
+  },
+  "main": "sdl.js",
+  "engines": {
+    "node": ">=0.4.4"
+  },
+  "dependencies": {},
+  "devDependencies": {}
+}

+ 25 - 0
src/node/node_modules/node-sdl/sdl.js

@@ -0,0 +1,25 @@
+var SDL = module.exports = require('./build/Release/nodesdl.node');
+
+// Easy event emitter based event loop.  Started automatically when the first
+// listener is added.
+var events;
+Object.defineProperty(SDL, 'events', {
+  get: function () {
+    if (events) return events;
+    events = new (require('events').EventEmitter);
+    var now = Date.now();
+    setInterval(function () {
+      var after = Date.now();
+      var delta = after - now;
+      now = after;
+      var data;
+      while (data = SDL.pollEvent()) {
+        events.emit('event', data);
+        events.emit(data.type, data);
+      }
+      events.emit('tick', delta);
+    }, 16);
+    return events;
+  }
+});
+

+ 437 - 0
src/node/node_modules/node-sdl/src/helpers.cc

@@ -0,0 +1,437 @@
+#include <v8.h>
+#include <node.h>
+#include <node_buffer.h>
+#include <SDL.h>
+#include <SDL_ttf.h>
+
+#include "helpers.h"
+
+namespace sdl {
+
+// Helper for formatting error exceptions
+Handle<Value> ThrowSDLException(const char* name) {
+  return ThrowException(MakeSDLException(name));
+}
+
+Local<Value> MakeSDLException(const char* name) {
+  return Exception::Error(String::Concat(
+    String::Concat(String::New(name), String::New(": ")),
+    String::New(SDL_GetError())
+  ));
+}
+
+// Wrap/Unwrap Surface
+
+static Persistent<ObjectTemplate> surface_template_;
+
+Handle<Value> GetSurfaceFlags(Local<String> name, const AccessorInfo& info) {
+  SDL_Surface* surface = UnwrapSurface(info.Holder());
+  return Number::New(surface->flags);
+}
+Handle<Value> GetSurfaceFormat(Local<String> name, const AccessorInfo& info) {
+  HandleScope scope;
+  SDL_Surface* surface = UnwrapSurface(info.Holder());
+  return scope.Close(WrapPixelFormat(surface->format));
+}
+Handle<Value> GetSurfaceWidth(Local<String> name, const AccessorInfo& info) {
+  SDL_Surface* surface = UnwrapSurface(info.Holder());
+  return Number::New(surface->w);
+}
+Handle<Value> GetSurfaceHeight(Local<String> name, const AccessorInfo& info) {
+  SDL_Surface* surface = UnwrapSurface(info.Holder());
+  return Number::New(surface->h);
+}
+Handle<Value> GetSurfacePitch(Local<String> name, const AccessorInfo& info) {
+  SDL_Surface* surface = UnwrapSurface(info.Holder());
+  return Number::New(surface->pitch);
+}
+Handle<Value> GetSurfaceRect(Local<String> name, const AccessorInfo& info) {
+  HandleScope scope;
+  SDL_Surface* surface = UnwrapSurface(info.Holder());
+  return scope.Close(WrapRect(&surface->clip_rect));
+}
+
+Handle<ObjectTemplate> MakeSurfaceTemplate() {
+  HandleScope handle_scope;
+
+  Handle<ObjectTemplate> result = ObjectTemplate::New();
+  result->SetInternalFieldCount(1);
+
+  // Add accessors for some of the fields of the surface.
+  result->SetAccessor(String::NewSymbol("flags"), GetSurfaceFlags);
+  result->SetAccessor(String::NewSymbol("format"), GetSurfaceFormat);
+  result->SetAccessor(String::NewSymbol("w"), GetSurfaceWidth);
+  result->SetAccessor(String::NewSymbol("h"), GetSurfaceHeight);
+  result->SetAccessor(String::NewSymbol("pitch"), GetSurfacePitch);
+  result->SetAccessor(String::NewSymbol("clip_rect"), GetSurfaceRect);
+
+  // Again, return the result through the current handle scope.
+  return handle_scope.Close(result);
+}
+
+Handle<Object> WrapSurface(SDL_Surface* surface) {
+  // Handle scope for temporary handles.
+  HandleScope handle_scope;
+
+  // Fetch the template for creating JavaScript http request wrappers.
+  // It only has to be created once, which we do on demand.
+  if (surface_template_.IsEmpty()) {
+    Handle<ObjectTemplate> raw_template = MakeSurfaceTemplate();
+    surface_template_ = Persistent<ObjectTemplate>::New(raw_template);
+  }
+  Handle<ObjectTemplate> templ = surface_template_;
+
+  // Create an empty http request wrapper.
+  Handle<Object> result = templ->NewInstance();
+
+  // Wrap the raw C++ pointer in an External so it can be referenced
+  // from within JavaScript.
+  Handle<External> request_ptr = External::New(surface);
+
+  // Store the request pointer in the JavaScript wrapper.
+  result->SetInternalField(0, request_ptr);
+
+  // Return the result through the current handle scope.  Since each
+  // of these handles will go away when the handle scope is deleted
+  // we need to call Close to let one, the result, escape into the
+  // outer handle scope.
+  return handle_scope.Close(result);
+}
+
+SDL_Surface* UnwrapSurface(Handle<Object> obj) {
+  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+  void* ptr = field->Value();
+  return static_cast<SDL_Surface*>(ptr);
+}
+
+// Wrap/Unwrap Rect
+
+static Persistent<ObjectTemplate> rect_template_;
+
+Handle<Value> GetRectX(Local<String> name, const AccessorInfo& info) {
+  SDL_Rect* rect = UnwrapRect(info.Holder());
+  return Number::New(rect->x);
+}
+Handle<Value> GetRectY(Local<String> name, const AccessorInfo& info) {
+  SDL_Rect* rect = UnwrapRect(info.Holder());
+  return Number::New(rect->y);
+}
+Handle<Value> GetRectW(Local<String> name, const AccessorInfo& info) {
+  SDL_Rect* rect = UnwrapRect(info.Holder());
+  return Number::New(rect->w);
+}
+Handle<Value> GetRectH(Local<String> name, const AccessorInfo& info) {
+  SDL_Rect* rect = UnwrapRect(info.Holder());
+  return Number::New(rect->h);
+}
+
+Handle<ObjectTemplate> MakeRectTemplate() {
+  HandleScope handle_scope;
+
+  Handle<ObjectTemplate> result = ObjectTemplate::New();
+  result->SetInternalFieldCount(1);
+
+  // Add accessors for some of the fields of the rect.
+  result->SetAccessor(String::NewSymbol("x"), GetRectX);
+  result->SetAccessor(String::NewSymbol("y"), GetRectY);
+  result->SetAccessor(String::NewSymbol("w"), GetRectW);
+  result->SetAccessor(String::NewSymbol("h"), GetRectH);
+
+  // Again, return the result through the current handle scope.
+  return handle_scope.Close(result);
+}
+
+Handle<Object> WrapRect(SDL_Rect* rect) {
+  // Handle scope for temporary handles.
+  HandleScope handle_scope;
+
+  // Fetch the template for creating JavaScript http request wrappers.
+  // It only has to be created once, which we do on demand.
+  if (rect_template_.IsEmpty()) {
+    Handle<ObjectTemplate> raw_template = MakeRectTemplate();
+    rect_template_ = Persistent<ObjectTemplate>::New(raw_template);
+  }
+  Handle<ObjectTemplate> templ = rect_template_;
+
+  // Create an empty http request wrapper.
+  Handle<Object> result = templ->NewInstance();
+
+  // Wrap the raw C++ pointer in an External so it can be referenced
+  // from within JavaScript.
+  Handle<External> request_ptr = External::New(rect);
+
+  // Store the request pointer in the JavaScript wrapper.
+  result->SetInternalField(0, request_ptr);
+
+  // Return the result through the current handle scope.  Since each
+  // of these handles will go away when the handle scope is deleted
+  // we need to call Close to let one, the result, escape into the
+  // outer handle scope.
+  return handle_scope.Close(result);
+}
+
+SDL_Rect* UnwrapRect(Handle<Object> obj) {
+  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+  void* ptr = field->Value();
+  return static_cast<SDL_Rect*>(ptr);
+}
+
+// Wrap/Unwrap PixelFormat
+
+static Persistent<ObjectTemplate> pixelformat_template_;
+
+Handle<Value> GetFormatBits(Local<String> name, const AccessorInfo& info) {
+  SDL_PixelFormat* format = UnwrapPixelFormat(info.Holder());
+  return Number::New(format->BitsPerPixel);
+}
+Handle<Value> GetFormatBytes(Local<String> name, const AccessorInfo& info) {
+  SDL_PixelFormat* format = UnwrapPixelFormat(info.Holder());
+  return Number::New(format->BytesPerPixel);
+}
+Handle<Value> GetFormatColorkey(Local<String> name, const AccessorInfo& info) {
+  SDL_PixelFormat* format = UnwrapPixelFormat(info.Holder());
+  return Number::New(format->colorkey);
+}
+Handle<Value> GetFormatAlpha(Local<String> name, const AccessorInfo& info) {
+  SDL_PixelFormat* format = UnwrapPixelFormat(info.Holder());
+  return Number::New(format->alpha);
+}
+
+Handle<ObjectTemplate> MakePixelFormatTemplate() {
+  HandleScope handle_scope;
+
+  Handle<ObjectTemplate> result = ObjectTemplate::New();
+  result->SetInternalFieldCount(1);
+
+  // Add accessors for some of the fields of the pixelformat.
+  result->SetAccessor(String::NewSymbol("bitsPerPixel"), GetFormatBits);
+  result->SetAccessor(String::NewSymbol("bytesPerPixel"), GetFormatBytes);
+  result->SetAccessor(String::NewSymbol("colorkey"), GetFormatColorkey);
+  result->SetAccessor(String::NewSymbol("alpha"), GetFormatAlpha);
+
+  // Again, return the result through the current handle scope.
+  return handle_scope.Close(result);
+}
+
+Handle<Object> WrapPixelFormat(SDL_PixelFormat* pixelformat) {
+  // Handle scope for temporary handles.
+  HandleScope handle_scope;
+
+  // Fetch the template for creating JavaScript http request wrappers.
+  // It only has to be created once, which we do on demand.
+  if (pixelformat_template_.IsEmpty()) {
+    Handle<ObjectTemplate> raw_template = MakePixelFormatTemplate();
+    pixelformat_template_ = Persistent<ObjectTemplate>::New(raw_template);
+  }
+  Handle<ObjectTemplate> templ = pixelformat_template_;
+
+  // Create an empty http request wrapper.
+  Handle<Object> result = templ->NewInstance();
+
+  // Wrap the raw C++ pointer in an External so it can be referenced
+  // from within JavaScript.
+  Handle<External> request_ptr = External::New(pixelformat);
+
+  // Store the request pointer in the JavaScript wrapper.
+  result->SetInternalField(0, request_ptr);
+
+  // Return the result through the current handle scope.  Since each
+  // of these handles will go away when the handle scope is deleted
+  // we need to call Close to let one, the result, escape into the
+  // outer handle scope.
+  return handle_scope.Close(result);
+}
+
+SDL_PixelFormat* UnwrapPixelFormat(Handle<Object> obj) {
+  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+  void* ptr = field->Value();
+  return static_cast<SDL_PixelFormat*>(ptr);
+}
+
+// Wrap/Unwrap Joystick
+
+static Persistent<ObjectTemplate> joystick_template_;
+
+//Handle<Value> GetJoystickFlags(Local<String> name, const AccessorInfo& info) {
+//  SDL_Joystick* joystick = UnwrapJoystick(info.Holder());
+//  return Number::New(joystick->flags);
+//}
+//Handle<Value> GetJoystickFormat(Local<String> name, const AccessorInfo& info) {
+//  HandleScope scope;
+//  SDL_Joystick* joystick = UnwrapJoystick(info.Holder());
+//  return scope.Close(WrapPixelFormat(joystick->format));
+//}
+//Handle<Value> GetJoystickWidth(Local<String> name, const AccessorInfo& info) {
+//  SDL_Joystick* joystick = UnwrapJoystick(info.Holder());
+//  return Number::New(joystick->w);
+//}
+//Handle<Value> GetJoystickHeight(Local<String> name, const AccessorInfo& info) {
+//  SDL_Joystick* joystick = UnwrapJoystick(info.Holder());
+//  return Number::New(joystick->h);
+//}
+//Handle<Value> GetJoystickPitch(Local<String> name, const AccessorInfo& info) {
+//  SDL_Joystick* joystick = UnwrapJoystick(info.Holder());
+//  return Number::New(joystick->pitch);
+//}
+//Handle<Value> GetJoystickRect(Local<String> name, const AccessorInfo& info) {
+//  HandleScope scope;
+//  SDL_Joystick* joystick = UnwrapJoystick(info.Holder());
+//  return scope.Close(WrapRect(&joystick->clip_rect));
+//}
+
+Handle<ObjectTemplate> MakeJoystickTemplate() {
+  HandleScope handle_scope;
+
+  Handle<ObjectTemplate> result = ObjectTemplate::New();
+  result->SetInternalFieldCount(1);
+
+  // Add accessors for some of the fields of the joystick.
+//  result->SetAccessor(String::NewSymbol("flags"), GetJoystickFlags);
+//  result->SetAccessor(String::NewSymbol("format"), GetJoystickFormat);
+//  result->SetAccessor(String::NewSymbol("w"), GetJoystickWidth);
+//  result->SetAccessor(String::NewSymbol("h"), GetJoystickHeight);
+//  result->SetAccessor(String::NewSymbol("pitch"), GetJoystickPitch);
+//  result->SetAccessor(String::NewSymbol("clip_rect"), GetJoystickRect);
+
+  // Again, return the result through the current handle scope.
+  return handle_scope.Close(result);
+}
+
+Handle<Object> WrapJoystick(SDL_Joystick* joystick) {
+  // Handle scope for temporary handles.
+  HandleScope handle_scope;
+
+  // Fetch the template for creating JavaScript http request wrappers.
+  // It only has to be created once, which we do on demand.
+  if (joystick_template_.IsEmpty()) {
+    Handle<ObjectTemplate> raw_template = MakeJoystickTemplate();
+    joystick_template_ = Persistent<ObjectTemplate>::New(raw_template);
+  }
+  Handle<ObjectTemplate> templ = joystick_template_;
+
+  // Create an empty http request wrapper.
+  Handle<Object> result = templ->NewInstance();
+
+  // Wrap the raw C++ pointer in an External so it can be referenced
+  // from within JavaScript.
+  Handle<External> request_ptr = External::New(joystick);
+
+  // Store the request pointer in the JavaScript wrapper.
+  result->SetInternalField(0, request_ptr);
+
+  // Return the result through the current handle scope.  Since each
+  // of these handles will go away when the handle scope is deleted
+  // we need to call Close to let one, the result, escape into the
+  // outer handle scope.
+  return handle_scope.Close(result);
+}
+
+SDL_Joystick* UnwrapJoystick(Handle<Object> obj) {
+  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+  void* ptr = field->Value();
+  return static_cast<SDL_Joystick*>(ptr);
+}
+
+// Wrap/Unwrap Font
+
+static Persistent<ObjectTemplate> font_template_;
+
+//Handle<Value> GetFontFlags(Local<String> name, const AccessorInfo& info) {
+//  TTF_Font* font = UnwrapFont(info.Holder());
+//  return Number::New(font->flags);
+//}
+//Handle<Value> GetFontFormat(Local<String> name, const AccessorInfo& info) {
+//  HandleScope scope;
+//  TTF_Font* font = UnwrapFont(info.Holder());
+//  return scope.Close(WrapPixelFormat(font->format));
+//}
+//Handle<Value> GetFontWidth(Local<String> name, const AccessorInfo& info) {
+//  TTF_Font* font = UnwrapFont(info.Holder());
+//  return Number::New(font->w);
+//}
+//Handle<Value> GetFontHeight(Local<String> name, const AccessorInfo& info) {
+//  TTF_Font* font = UnwrapFont(info.Holder());
+//  return Number::New(font->h);
+//}
+//Handle<Value> GetFontPitch(Local<String> name, const AccessorInfo& info) {
+//  TTF_Font* font = UnwrapFont(info.Holder());
+//  return Number::New(font->pitch);
+//}
+//Handle<Value> GetFontRect(Local<String> name, const AccessorInfo& info) {
+//  HandleScope scope;
+//  TTF_Font* font = UnwrapFont(info.Holder());
+//  return scope.Close(WrapRect(&font->clip_rect));
+//}
+
+Handle<ObjectTemplate> MakeFontTemplate() {
+  HandleScope handle_scope;
+
+  Handle<ObjectTemplate> result = ObjectTemplate::New();
+  result->SetInternalFieldCount(1);
+
+  // Add accessors for some of the fields of the font.
+//  result->SetAccessor(String::NewSymbol("flags"), GetFontFlags);
+//  result->SetAccessor(String::NewSymbol("format"), GetFontFormat);
+//  result->SetAccessor(String::NewSymbol("w"), GetFontWidth);
+//  result->SetAccessor(String::NewSymbol("h"), GetFontHeight);
+//  result->SetAccessor(String::NewSymbol("pitch"), GetFontPitch);
+//  result->SetAccessor(String::NewSymbol("clip_rect"), GetFontRect);
+
+  // Again, return the result through the current handle scope.
+  return handle_scope.Close(result);
+}
+
+Handle<Object> WrapFont(TTF_Font* font) {
+  // Handle scope for temporary handles.
+  HandleScope handle_scope;
+
+  // Fetch the template for creating JavaScript http request wrappers.
+  // It only has to be created once, which we do on demand.
+  if (font_template_.IsEmpty()) {
+    Handle<ObjectTemplate> raw_template = MakeFontTemplate();
+    font_template_ = Persistent<ObjectTemplate>::New(raw_template);
+  }
+  Handle<ObjectTemplate> templ = font_template_;
+
+  // Create an empty http request wrapper.
+  Handle<Object> result = templ->NewInstance();
+
+  // Wrap the raw C++ pointer in an External so it can be referenced
+  // from within JavaScript.
+  Handle<External> request_ptr = External::New(font);
+
+  // Store the request pointer in the JavaScript wrapper.
+  result->SetInternalField(0, request_ptr);
+
+  // Return the result through the current handle scope.  Since each
+  // of these handles will go away when the handle scope is deleted
+  // we need to call Close to let one, the result, escape into the
+  // outer handle scope.
+  return handle_scope.Close(result);
+}
+
+TTF_Font* UnwrapFont(Handle<Object> obj) {
+  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+  void* ptr = field->Value();
+  return static_cast<TTF_Font*>(ptr);
+}
+
+
+//char* BufferData(Buffer *b) {
+//  return Buffer::Data(b->handle_);
+//}
+
+//size_t BufferLength(Buffer *b) {
+//  return Buffer::Length(b->handle_);
+//}
+
+char* BufferData(Local<Object> buf_obj) {
+  return Buffer::Data(buf_obj);
+}
+
+size_t BufferLength(Local<Object> buf_obj) {
+  return Buffer::Length(buf_obj);
+}
+
+} // node_sdl
+

+ 42 - 0
src/node/node_modules/node-sdl/src/helpers.h

@@ -0,0 +1,42 @@
+#ifndef HELPERS_H_
+#define HELPERS_H_
+
+#include <v8.h>
+#include <node.h>
+#include <node_buffer.h>
+
+using namespace node;
+using namespace v8;
+
+namespace sdl {
+
+  // Error reporting helpers
+  Handle<Value> ThrowSDLException(const char* name);
+  Local<Value> MakeSDLException(const char* name);
+
+  // Wrapper and Unwrappers
+  Handle<Object> WrapSurface(SDL_Surface* surface);
+  SDL_Surface* UnwrapSurface(Handle<Object> obj);
+
+  Handle<Object> WrapRect(SDL_Rect* rect);
+  SDL_Rect* UnwrapRect(Handle<Object> obj);
+
+  Handle<Object> WrapPixelFormat(SDL_PixelFormat* pixelformat);
+  SDL_PixelFormat* UnwrapPixelFormat(Handle<Object> obj);
+
+  Handle<Object> WrapJoystick(SDL_Joystick* joystick);
+  SDL_Joystick* UnwrapJoystick(Handle<Object> obj);
+
+  Handle<Object> WrapFont(TTF_Font* font);
+  TTF_Font* UnwrapFont(Handle<Object> obj);
+
+  
+  // Helpers to work with buffers
+  //char* BufferData(Buffer *b);
+  //size_t BufferLength(Buffer *b);
+  char* BufferData(Local<Object> buf_obj);
+  size_t BufferLength(Local<Object> buf_obj);
+
+} // sdl
+
+#endif  // HELPERS_H_

+ 1078 - 0
src/node/node_modules/node-sdl/src/nodesdl.cc

@@ -0,0 +1,1078 @@
+#ifdef __APPLE__
+#include <objc/objc.h>
+#include <objc/objc-runtime.h>
+#endif
+
+#include "SDL.h"
+#include "nodesdl.h"
+#include <v8.h>
+
+using namespace v8;
+
+extern "C" void
+init(Handle<Object> target)
+{
+#ifdef __APPLE__
+  // on the mac it is necessary to create to call [NSApplication sharedApplication]
+  // before we can create a rendering window
+  objc_msgSend(objc_lookUpClass("NSApplication"), sel_getUid("sharedApplication"));
+#endif
+    
+  NODE_SET_METHOD(target, "init", sdl::Init);
+  NODE_SET_METHOD(target, "initSubSystem", sdl::InitSubSystem);
+  NODE_SET_METHOD(target, "quit", sdl::Quit);
+  NODE_SET_METHOD(target, "quitSubSystem", sdl::QuitSubSystem);
+  NODE_SET_METHOD(target, "wasInit", sdl::WasInit);
+  NODE_SET_METHOD(target, "clearError", sdl::ClearError);
+  NODE_SET_METHOD(target, "getError", sdl::GetError);
+  NODE_SET_METHOD(target, "setError", sdl::SetError);
+  NODE_SET_METHOD(target, "waitEvent", sdl::WaitEvent);
+  NODE_SET_METHOD(target, "pollEvent", sdl::PollEvent);
+  NODE_SET_METHOD(target, "setVideoMode", sdl::SetVideoMode);
+  NODE_SET_METHOD(target, "videoModeOK", sdl::VideoModeOK);
+  NODE_SET_METHOD(target, "numJoysticks", sdl::NumJoysticks);
+  NODE_SET_METHOD(target, "joystickOpen", sdl::JoystickOpen);
+  NODE_SET_METHOD(target, "joystickOpened", sdl::JoystickOpened);
+  NODE_SET_METHOD(target, "joystickName", sdl::JoystickName);
+  NODE_SET_METHOD(target, "joystickNumAxes", sdl::JoystickNumAxes);
+  NODE_SET_METHOD(target, "joystickNumButtons", sdl::JoystickNumButtons);
+  NODE_SET_METHOD(target, "joystickNumBalls", sdl::JoystickNumBalls);
+  NODE_SET_METHOD(target, "joystickNumHats", sdl::JoystickNumHats);
+  NODE_SET_METHOD(target, "joystickClose", sdl::JoystickClose);
+  NODE_SET_METHOD(target, "joystickUpdate", sdl::JoystickUpdate);
+  NODE_SET_METHOD(target, "joystickEventState", sdl::JoystickEventState);
+  NODE_SET_METHOD(target, "flip", sdl::Flip);
+  NODE_SET_METHOD(target, "putImageData", sdl::PutImageData);
+  NODE_SET_METHOD(target, "fillRect", sdl::FillRect);
+  NODE_SET_METHOD(target, "updateRect", sdl::UpdateRect);
+  NODE_SET_METHOD(target, "createRGBSurface", sdl::CreateRGBSurface);
+  NODE_SET_METHOD(target, "blitSurface", sdl::BlitSurface);
+  NODE_SET_METHOD(target, "freeSurface", sdl::FreeSurface);
+  NODE_SET_METHOD(target, "setColorKey", sdl::SetColorKey);
+  NODE_SET_METHOD(target, "displayFormat", sdl::DisplayFormat);
+  NODE_SET_METHOD(target, "displayFormatAlpha", sdl::DisplayFormatAlpha);
+  NODE_SET_METHOD(target, "setAlpha", sdl::SetAlpha);
+  NODE_SET_METHOD(target, "mapRGB", sdl::MapRGB);
+  NODE_SET_METHOD(target, "mapRGBA", sdl::MapRGBA);
+  NODE_SET_METHOD(target, "getRGB", sdl::GetRGB);
+  NODE_SET_METHOD(target, "getRGBA", sdl::GetRGBA);
+  NODE_SET_METHOD(target, "setClipRect",sdl::SetClipRect);
+
+  Local<Object> INIT = Object::New();
+  target->Set(String::New("INIT"), INIT);
+  INIT->Set(String::New("TIMER"), Number::New(SDL_INIT_TIMER));
+  INIT->Set(String::New("AUDIO"), Number::New(SDL_INIT_AUDIO));
+  INIT->Set(String::New("VIDEO"), Number::New(SDL_INIT_VIDEO));
+  INIT->Set(String::New("JOYSTICK"), Number::New(SDL_INIT_JOYSTICK));
+  INIT->Set(String::New("EVERYTHING"), Number::New(SDL_INIT_EVERYTHING));
+  INIT->Set(String::New("NOPARACHUTE"), Number::New(SDL_INIT_NOPARACHUTE));
+
+  Local<Object> SURFACE = Object::New();
+  target->Set(String::New("SURFACE"), SURFACE);
+  SURFACE->Set(String::New("ANYFORMAT"), Number::New(SDL_ANYFORMAT));
+  SURFACE->Set(String::New("ASYNCBLIT"), Number::New(SDL_ASYNCBLIT));
+  SURFACE->Set(String::New("DOUBLEBUF"), Number::New(SDL_DOUBLEBUF));
+  SURFACE->Set(String::New("HWACCEL"), Number::New(SDL_HWACCEL));
+  SURFACE->Set(String::New("HWPALETTE"), Number::New(SDL_HWPALETTE));
+  SURFACE->Set(String::New("HWSURFACE"), Number::New(SDL_HWSURFACE));
+  SURFACE->Set(String::New("FULLSCREEN"), Number::New(SDL_FULLSCREEN));
+  SURFACE->Set(String::New("OPENGL"), Number::New(SDL_OPENGL));
+  SURFACE->Set(String::New("RESIZABLE"), Number::New(SDL_RESIZABLE));
+  SURFACE->Set(String::New("RLEACCEL"), Number::New(SDL_RLEACCEL));
+  SURFACE->Set(String::New("SRCALPHA"), Number::New(SDL_SRCALPHA));
+  SURFACE->Set(String::New("SRCCOLORKEY"), Number::New(SDL_SRCCOLORKEY));
+  SURFACE->Set(String::New("SWSURFACE"), Number::New(SDL_SWSURFACE));
+  SURFACE->Set(String::New("PREALLOC"), Number::New(SDL_PREALLOC));
+
+  Local<Object> TTF = Object::New();
+  target->Set(String::New("TTF"), TTF);
+  NODE_SET_METHOD(TTF, "init", sdl::TTF::Init);
+  NODE_SET_METHOD(TTF, "openFont", sdl::TTF::OpenFont);
+  NODE_SET_METHOD(TTF, "renderTextBlended", sdl::TTF::RenderTextBlended);
+  NODE_SET_METHOD(TTF, "renderTextShaded", sdl::TTF::RenderTextShaded);
+
+  Local<Object> IMG = Object::New();
+  target->Set(String::New("IMG"), IMG);
+
+  NODE_SET_METHOD(IMG, "load", sdl::IMG::Load);
+
+  Local<Object> WM = Object::New();
+  target->Set(String::New("WM"), WM);
+
+  NODE_SET_METHOD(WM, "setCaption", sdl::WM::SetCaption);
+  NODE_SET_METHOD(WM, "setIcon", sdl::WM::SetIcon);
+
+  Local<Object> GL = Object::New();
+  target->Set(String::New("GL"), GL);
+
+
+  NODE_SET_METHOD(GL, "setAttribute", sdl::GL::SetAttribute);
+  NODE_SET_METHOD(GL, "getAttribute", sdl::GL::GetAttribute);
+  NODE_SET_METHOD(GL, "swapBuffers", sdl::GL::SwapBuffers);
+
+  GL->Set(String::New("RED_SIZE"), Number::New(SDL_GL_RED_SIZE));
+  GL->Set(String::New("GREEN_SIZE"), Number::New(SDL_GL_GREEN_SIZE));
+  GL->Set(String::New("BLUE_SIZE"), Number::New(SDL_GL_BLUE_SIZE));
+  GL->Set(String::New("ALPHA_SIZE"), Number::New(SDL_GL_ALPHA_SIZE));
+  GL->Set(String::New("DOUBLEBUFFER"), Number::New(SDL_GL_DOUBLEBUFFER));
+  GL->Set(String::New("BUFFER_SIZE"), Number::New(SDL_GL_BUFFER_SIZE));
+  GL->Set(String::New("DEPTH_SIZE"), Number::New(SDL_GL_DEPTH_SIZE));
+  GL->Set(String::New("STENCIL_SIZE"), Number::New(SDL_GL_STENCIL_SIZE));
+  GL->Set(String::New("ACCUM_RED_SIZE"), Number::New(SDL_GL_ACCUM_RED_SIZE));
+  GL->Set(String::New("ACCUM_GREEN_SIZE"), Number::New(SDL_GL_ACCUM_GREEN_SIZE));
+  GL->Set(String::New("ACCUM_BLUE_SIZE"), Number::New(SDL_GL_ACCUM_BLUE_SIZE));
+  GL->Set(String::New("ACCUM_ALPHA_SIZE"), Number::New(SDL_GL_ACCUM_ALPHA_SIZE));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+Handle<Value> sdl::GL::SetAttribute(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 2 && args[0]->IsNumber() && args[1]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected SetAttribute(Number, Number)")));
+  }
+
+  int attr = args[0]->Int32Value();
+  int value = args[1]->Int32Value();
+
+  if (SDL_GL_SetAttribute((SDL_GLattr)attr, value)) return ThrowSDLException(__func__);
+  return Undefined();
+}
+
+Handle<Value> sdl::GL::GetAttribute(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected GetAttribute(Number)")));
+  }
+
+  int attr = args[0]->Int32Value();
+  int value;
+
+  if (SDL_GL_GetAttribute((SDL_GLattr)attr, &value)) return ThrowSDLException(__func__);
+
+  return Number::New(value);
+}
+
+Handle<Value> sdl::GL::SwapBuffers(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected SwapBuffers()")));
+  }
+
+  SDL_GL_SwapBuffers();
+  return Undefined();
+}
+
+Handle<Value> sdl::Init(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected Init(Number)")));
+  }
+
+  if (SDL_Init(args[0]->Int32Value()) < 0) return ThrowSDLException(__func__);
+
+  return Undefined();
+}
+
+Handle<Value> sdl::InitSubSystem(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected InitSubSystem(Number)")));
+  }
+
+  if (SDL_InitSubSystem(args[0]->Int32Value()) < 0) return ThrowSDLException(__func__);
+
+  return Undefined();
+}
+
+Handle<Value> sdl::Quit(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected Quit()")));
+  }
+
+  SDL_Quit();
+
+  return Undefined();
+}
+
+Handle<Value> sdl::QuitSubSystem(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected QuitSubSystem(Number)")));
+  }
+
+  SDL_QuitSubSystem(args[0]->Int32Value());
+
+  return Undefined();
+}
+
+Handle<Value> sdl::WasInit(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected WasInit(Number)")));
+  }
+
+  return Number::New(SDL_WasInit(args[0]->Int32Value()));
+}
+
+Handle<Value> sdl::ClearError(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected ClearError()")));
+  }
+
+  SDL_ClearError();
+
+  return Undefined();
+}
+
+Handle<Value> sdl::GetError(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected GetError()")));
+  }
+
+  return String::New(SDL_GetError());
+}
+
+Handle<Value> sdl::SetError(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsString())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected SetError(String)")));
+  }
+
+  String::Utf8Value message(args[1]);
+
+  SDL_SetError(*message);
+
+  return Undefined();
+}
+
+/*
+static void sdl::EIO_WaitEvent(eio_req *req) {
+  sdl::closure_t *closure = (sdl::closure_t *) req->data;
+  closure->status = SDL_WaitEvent(NULL);
+}
+
+static int sdl::EIO_OnEvent(eio_req *req) {
+  HandleScope scope;
+
+  sdl::closure_t *closure = (sdl::closure_t *) req->data;
+  ev_unref(EV_DEFAULT_UC);
+
+  Handle<Value> argv[1];
+  if (closure->status == 0) {
+    argv[0] = MakeSDLException("WaitEvent");
+  } else {
+    argv[0] = Undefined();
+  }
+
+  closure->fn->Call(Context::GetCurrent()->Global(), 1, argv);
+
+  closure->fn.Dispose();
+  free(closure);
+  return 0;
+}*/
+
+static Handle<Value> sdl::WaitEvent(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsFunction())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected WaitEvent(Function)")));
+  }
+
+  closure_t *closure = (closure_t*) malloc(sizeof(closure_t));
+  closure->fn = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
+  //eio_custom(EIO_WaitEvent, EIO_PRI_DEFAULT, EIO_OnEvent, closure);
+  //ev_ref(EV_DEFAULT_UC);
+  return Undefined();
+}
+
+
+
+Handle<Value> sdl::PollEvent(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected PollEvent()")));
+  }
+
+  SDL_Event event;
+  if (!SDL_PollEvent(&event)) {
+    return Undefined();
+  }
+
+  Local<Object> evt = Object::New();
+
+  switch (event.type) {
+    case SDL_ACTIVEEVENT:
+      evt->Set(String::New("type"), String::New("ACTIVEEVENT"));
+      evt->Set(String::New("gain"), Boolean::New(event.active.gain));
+      evt->Set(String::New("state"), Number::New(event.active.state));
+      break;
+    case SDL_KEYDOWN:
+    case SDL_KEYUP:
+      evt->Set(String::New("type"), String::New(event.type == SDL_KEYDOWN ? "KEYDOWN" : "KEYUP"));
+      evt->Set(String::New("scancode"), Number::New(event.key.keysym.scancode));
+      evt->Set(String::New("sym"), Number::New(event.key.keysym.sym));
+      evt->Set(String::New("mod"), Number::New(event.key.keysym.mod));
+      break;
+    case SDL_MOUSEMOTION:
+      evt->Set(String::New("type"), String::New("MOUSEMOTION"));
+      evt->Set(String::New("state"), Number::New(event.motion.state));
+      evt->Set(String::New("which"), Number::New(event.motion.which));
+      evt->Set(String::New("x"), Number::New(event.motion.x));
+      evt->Set(String::New("y"), Number::New(event.motion.y));
+      evt->Set(String::New("xrel"), Number::New(event.motion.xrel));
+      evt->Set(String::New("yrel"), Number::New(event.motion.yrel));
+      break;
+    case SDL_MOUSEBUTTONDOWN:
+    case SDL_MOUSEBUTTONUP:
+      evt->Set(String::New("type"), String::New(event.type == SDL_MOUSEBUTTONDOWN ? "MOUSEBUTTONDOWN" : "MOUSEBUTTONUP"));
+      evt->Set(String::New("button"), Number::New(event.button.button));
+      evt->Set(String::New("which"), Number::New(event.button.which));
+      evt->Set(String::New("x"), Number::New(event.button.x));
+      evt->Set(String::New("y"), Number::New(event.button.y));
+      break;
+    case SDL_JOYAXISMOTION:
+      evt->Set(String::New("type"), String::New("JOYAXISMOTION"));
+      evt->Set(String::New("which"), Number::New(event.jaxis.which));
+      evt->Set(String::New("axis"), Number::New(event.jaxis.axis));
+      evt->Set(String::New("value"), Number::New(event.jaxis.value));
+      break;
+    case SDL_JOYBALLMOTION:
+      evt->Set(String::New("type"), String::New("JOYBALLMOTION"));
+      evt->Set(String::New("which"), Number::New(event.jball.which));
+      evt->Set(String::New("ball"), Number::New(event.jball.ball));
+      evt->Set(String::New("xrel"), Number::New(event.jball.xrel));
+      evt->Set(String::New("yrel"), Number::New(event.jball.yrel));
+      break;
+    case SDL_JOYHATMOTION:
+      evt->Set(String::New("type"), String::New("JOYHATMOTION"));
+      evt->Set(String::New("which"), Number::New(event.jhat.which));
+      evt->Set(String::New("hat"), Number::New(event.jhat.hat));
+      evt->Set(String::New("value"), Number::New(event.jhat.value));
+      break;
+    case SDL_JOYBUTTONDOWN:
+    case SDL_JOYBUTTONUP:
+      evt->Set(String::New("type"), String::New(event.type == SDL_JOYBUTTONDOWN ? "JOYBUTTONDOWN" : "JOYBUTTONUP"));
+      evt->Set(String::New("which"), Number::New(event.jbutton.which));
+      evt->Set(String::New("button"), Number::New(event.jbutton.button));
+      break;
+    case SDL_QUIT:
+      evt->Set(String::New("type"), String::New("QUIT"));
+      break;
+    default:
+      evt->Set(String::New("type"), String::New("UNKNOWN"));
+      evt->Set(String::New("typeCode"), Number::New(event.type));
+      break;
+  }
+
+  return scope.Close(evt);
+}
+
+static Handle<Value> sdl::SetVideoMode(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 4 && args[0]->IsNumber() && args[1]->IsNumber() && args[2]->IsNumber() && args[3]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected SetVideoMode(Number, Number, Number, Number)")));
+  }
+
+  int width = (args[0]->Int32Value());
+  int height = (args[1]->Int32Value());
+  int bpp = (args[2]->Int32Value());
+  int flags = (args[3]->Int32Value());
+
+  SDL_Surface* screen = SDL_SetVideoMode(width, height, bpp, flags);
+  if (screen == NULL) return ThrowSDLException(__func__);
+  return scope.Close(WrapSurface(screen));
+}
+
+static Handle<Value> sdl::PutImageData(const Arguments& args) {
+
+  HandleScope scope;
+
+  if (!(args.Length() == 2 && args[0]->IsObject() && args[1]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected PutImageData(Surface, Object)")));
+  }
+
+  // I'm a bad C++ coder.
+  // Don't judge me :(
+
+  Local<Object> obj = args[1]->ToObject();
+  //if (obj->GetIndexedPropertiesExternalArrayDataType() != kExternalFloatArray) return;
+  int len = obj->GetIndexedPropertiesExternalArrayDataLength();
+  int* data = static_cast<int*>(obj->GetIndexedPropertiesExternalArrayData());
+
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+
+  if( SDL_MUSTLOCK(surface) )
+      SDL_LockSurface(surface);
+
+  if(len > surface->w * surface->h)
+  {
+    len = surface->w * surface->h;
+  }
+
+  uint32_t * pixel = (uint32_t*)surface->pixels;
+  //pixel += (y * surface->pitch) + (x * sizeof(Uint32));
+
+  for(int i = 0; i < len; i++)
+  {
+      *pixel = *data;
+      pixel++;
+      data++;
+  }
+
+  if( SDL_MUSTLOCK(surface) )
+      SDL_UnlockSurface(surface);
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::VideoModeOK(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 4 && args[0]->IsNumber() && args[1]->IsNumber() && args[2]->IsNumber() && args[3]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected VideoModeOK(Number, Number, Number, Number)")));
+  }
+
+  int width = (args[0]->Int32Value());
+  int height = (args[1]->Int32Value());
+  int bpp = (args[2]->Int32Value());
+  int flags = (args[3]->Int32Value());
+
+  return Number::New(SDL_VideoModeOK(width, height, bpp, flags));
+}
+
+static Handle<Value> sdl::NumJoysticks(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected NumJoysticks()")));
+  }
+
+  return Number::New(SDL_NumJoysticks());
+}
+
+static Handle<Value> sdl::JoystickOpen(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickOpen(Number)")));
+  }
+
+  SDL_Joystick* joystick = SDL_JoystickOpen(args[0]->Int32Value());
+  if (!joystick) return ThrowSDLException(__func__);
+  return scope.Close(WrapJoystick(joystick));
+}
+
+static Handle<Value> sdl::JoystickOpened(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickOpened(Number)")));
+  }
+
+  return Number::New(SDL_JoystickOpened(args[0]->Int32Value()));
+}
+
+
+static Handle<Value> sdl::JoystickName(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickName(Number)")));
+  }
+
+  return String::New(SDL_JoystickName(args[0]->Int32Value()));
+}
+
+static Handle<Value> sdl::JoystickNumAxes(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickNumAxes(Joystick)")));
+  }
+
+  return Number::New(SDL_JoystickNumAxes(UnwrapJoystick(args[0]->ToObject())));
+}
+
+static Handle<Value> sdl::JoystickNumButtons(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickNumButtons(Joystick)")));
+  }
+
+  return Number::New(SDL_JoystickNumButtons(UnwrapJoystick(args[0]->ToObject())));
+}
+
+static Handle<Value> sdl::JoystickNumBalls(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickNumBalls(Joystick)")));
+  }
+
+  return Number::New(SDL_JoystickNumBalls(UnwrapJoystick(args[0]->ToObject())));
+}
+
+static Handle<Value> sdl::JoystickNumHats(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickNumHats(Joystick)")));
+  }
+
+  return Number::New(SDL_JoystickNumHats(UnwrapJoystick(args[0]->ToObject())));
+}
+
+static Handle<Value> sdl::JoystickClose(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickClose(Joystick)")));
+  }
+
+  SDL_JoystickClose(UnwrapJoystick(args[0]->ToObject()));
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::JoystickUpdate(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickUpdate()")));
+  }
+
+  SDL_JoystickUpdate();
+  return Undefined();
+}
+
+static Handle<Value> sdl::JoystickEventState(const Arguments& args) {
+  HandleScope scope;
+
+  int state;
+  if (args.Length() == 0) {
+    state = SDL_QUERY;
+  } else {
+    if (!(args.Length() == 1 && args[0]->IsBoolean())) {
+      return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected JoystickEventState([Boolean])")));
+    }
+    state = args[0]->BooleanValue() ? SDL_ENABLE : SDL_IGNORE;
+  }
+  return Boolean::New(SDL_JoystickEventState(state));
+}
+
+
+static Handle<Value> sdl::Flip(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected Flip(Surface)")));
+  }
+
+  SDL_Flip(UnwrapSurface(args[0]->ToObject()));
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::FillRect(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 3
+      && args[0]->IsObject()
+      && (args[1]->IsObject() || args[1]->IsNull())
+      && args[2]->IsNumber()
+  )) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected FillRect(Surface, Rect, Number)")));
+  }
+
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+  SDL_Rect* rect;
+  if (args[1]->IsNull()) {
+    rect = NULL;
+  } else if (args[1]->IsArray()) {
+    SDL_Rect r;
+    Handle<Object> arr = args[1]->ToObject();
+    r.x = arr->Get(String::New("0"))->Int32Value();
+    r.y = arr->Get(String::New("1"))->Int32Value();
+    r.w = arr->Get(String::New("2"))->Int32Value();
+    r.h = arr->Get(String::New("3"))->Int32Value();
+    rect = &r;
+  } else {
+    rect = UnwrapRect(args[1]->ToObject());
+  }
+  int color = args[2]->Int32Value();
+
+  if (SDL_FillRect (surface, rect, color) < 0) return ThrowSDLException(__func__);
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::UpdateRect(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 2
+      && args[0]->IsObject()
+      && (args[1]->IsObject() || args[1]->IsNull())
+  )) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected UpdateRect(Surface, Rect)")));
+  }
+
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+  SDL_Rect* rect;
+  if (args[1]->IsNull()) {
+    rect = NULL;
+  } else if (args[1]->IsArray()) {
+    SDL_Rect r;
+    Handle<Object> arr = args[1]->ToObject();
+    r.x = arr->Get(String::New("0"))->Int32Value();
+    r.y = arr->Get(String::New("1"))->Int32Value();
+    r.w = arr->Get(String::New("2"))->Int32Value();
+    r.h = arr->Get(String::New("3"))->Int32Value();
+    rect = &r;
+  } else {
+    rect = UnwrapRect(args[1]->ToObject());
+  }
+
+  SDL_UpdateRect(surface, rect->x, rect->y, rect->w, rect->h);
+
+  return Undefined();
+}
+
+
+static Handle<Value> sdl::CreateRGBSurface(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 3 && args[0]->IsNumber() && args[1]->IsNumber() && args[2]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected CreateRGBSurface(Number, Number, Number)")));
+  }
+
+  int flags = args[0]->Int32Value();
+  int width = args[1]->Int32Value();
+  int height = args[2]->Int32Value();
+
+  SDL_Surface *surface;
+  int rmask, gmask, bmask, amask;
+
+  /* SDL interprets each pixel as a 32-bit number, so our masks must depend
+     on the endianness (byte order) of the machine */
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+  rmask = 0xff000000;
+  gmask = 0x00ff0000;
+  bmask = 0x0000ff00;
+  amask = 0x000000ff;
+#else
+  rmask = 0x000000ff;
+  gmask = 0x0000ff00;
+  bmask = 0x00ff0000;
+  amask = 0xff000000;
+#endif
+
+  surface = SDL_CreateRGBSurface(flags, width, height, 32, rmask, gmask, bmask, amask);
+  if (surface == NULL) return ThrowSDLException(__func__);
+  return scope.Close(WrapSurface(surface));
+}
+
+static Handle<Value> sdl::BlitSurface(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 4
+        && args[0]->IsObject()
+        && (args[1]->IsObject() || args[1]->IsNull())
+        && args[2]->IsObject()
+        && (args[3]->IsObject() || args[3]->IsNull())
+  )) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected BlitSurface(Surface, Rect, Surface, Rect)")));
+  }
+
+  SDL_Surface* src = UnwrapSurface(args[0]->ToObject());
+  SDL_Surface* dst = UnwrapSurface(args[2]->ToObject());
+
+  SDL_Rect* srcrect;
+  if (args[1]->IsNull()) {
+    srcrect = NULL;
+  } else if (args[1]->IsArray()) {
+    Handle<Object> arr1 = args[1]->ToObject();
+    srcrect = new SDL_Rect();
+    srcrect->x = arr1->Get(String::New("0"))->Int32Value();
+    srcrect->y = arr1->Get(String::New("1"))->Int32Value();
+    srcrect->w = arr1->Get(String::New("2"))->Int32Value();
+    srcrect->h = arr1->Get(String::New("3"))->Int32Value();
+  } else {
+    srcrect = UnwrapRect(args[1]->ToObject());
+  }
+
+  SDL_Rect* dstrect;
+  if (args[3]->IsNull()) {
+    dstrect = NULL;
+  } else if (args[3]->IsArray()) {
+    Handle<Object> arr2 = args[3]->ToObject();
+    dstrect = new SDL_Rect();
+    dstrect->x = arr2->Get(String::New("0"))->Int32Value();
+    dstrect->y = arr2->Get(String::New("1"))->Int32Value();
+    dstrect->w = arr2->Get(String::New("2"))->Int32Value();
+    dstrect->h = arr2->Get(String::New("3"))->Int32Value();
+  } else {
+    dstrect = UnwrapRect(args[3]->ToObject());
+  }
+
+//  if (srcrect) printf("srcrect = {x: %d, y: %d, w: %d, h: %d}\n", srcrect->x, srcrect->y, srcrect->w, srcrect->h);
+//  else printf("srcrect = null\n");
+//  if (dstrect) printf("dstrect = {x: %d, y: %d, w: %d, h: %d}\n", dstrect->x, dstrect->y, dstrect->w, dstrect->h);
+//  else printf("dstrect = null\n");
+
+
+  if (SDL_BlitSurface(src, srcrect, dst, dstrect) < 0) return ThrowSDLException(__func__);
+  return Undefined();
+}
+
+static Handle<Value> sdl::FreeSurface(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected FreeSurface(Surface)")));
+  }
+
+  // TODO: find a way to do this automatically by using GC hooks.  This is dangerous in JS land
+  SDL_FreeSurface(UnwrapSurface(args[0]->ToObject()));
+  args[0]->ToObject()->Set(String::New("DEAD"), Boolean::New(true));
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::SetColorKey(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 3 && args[0]->IsObject() && args[1]->IsNumber() && args[2]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected SetColorKey(Surface, Number, Number)")));
+  }
+
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+  int flag = args[1]->Int32Value();
+  int key = args[2]->Int32Value();
+
+  if (SDL_SetColorKey(surface, flag, key) < 0) return ThrowSDLException(__func__);
+
+  return Undefined();
+
+}
+
+static Handle<Value> sdl::DisplayFormat(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected DisplayFormat(Surface)")));
+  }
+
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+
+  return scope.Close(WrapSurface(SDL_DisplayFormat(surface)));
+}
+
+static Handle<Value> sdl::DisplayFormatAlpha(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected DisplayFormatAlpha(Surface)")));
+  }
+
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+
+  return scope.Close(WrapSurface(SDL_DisplayFormatAlpha(surface)));
+}
+
+static Handle<Value> sdl::SetAlpha(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 3 && args[0]->IsObject() && args[1]->IsNumber() && args[2]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected SetAlpha(Surface, Number, Number)")));
+  }
+
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+  int flags = args[1]->Int32Value();
+  int alpha = args[2]->Int32Value();
+
+  if (SDL_SetAlpha(surface, flags, alpha) < 0) return ThrowSDLException(__func__);
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::MapRGB(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 4 && args[0]->IsObject() && args[1]->IsNumber() && args[2]->IsNumber() && args[3]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected MapRGB(PixelFormat, Number, Number, Number)")));
+  }
+
+  SDL_PixelFormat* fmt = UnwrapPixelFormat(args[0]->ToObject());
+  int r = args[1]->Int32Value();
+  int g = args[2]->Int32Value();
+  int b = args[3]->Int32Value();
+
+  return Number::New(SDL_MapRGB(fmt, r, g, b));
+}
+
+static Handle<Value> sdl::MapRGBA(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 5 && args[0]->IsObject() && args[1]->IsNumber() && args[2]->IsNumber() && args[3]->IsNumber() && args[4]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected MapRGBA(PixelFormat, Number, Number, Number, Number)")));
+  }
+
+  SDL_PixelFormat* fmt = UnwrapPixelFormat(args[0]->ToObject());
+  int r = args[1]->Int32Value();
+  int g = args[2]->Int32Value();
+  int b = args[3]->Int32Value();
+  int a = args[4]->Int32Value();
+
+  return Number::New(SDL_MapRGBA(fmt, r, g, b, a));
+}
+
+static Handle<Value> sdl::GetRGB(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 2 && args[0]->IsNumber() && args[1]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected GetRGB(Number, PixelFormat)")));
+  }
+
+  int pixel = args[0]->Int32Value();
+  SDL_PixelFormat* fmt = UnwrapPixelFormat(args[1]->ToObject());
+  ::Uint8 r, g, b;
+
+  SDL_GetRGB(pixel, fmt, &r, &g, &b);
+
+  Local<Object> rgb = Object::New();
+  rgb->Set(String::New("r"), Number::New(r));
+  rgb->Set(String::New("g"), Number::New(g));
+  rgb->Set(String::New("b"), Number::New(b));
+
+  return scope.Close(rgb);
+}
+
+static Handle<Value> sdl::GetRGBA(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 2 && args[0]->IsNumber() && args[1]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected GetRGBA(Number, PixelFormat)")));
+  }
+
+  int pixel = args[0]->Int32Value();
+  SDL_PixelFormat* fmt = UnwrapPixelFormat(args[1]->ToObject());
+  ::Uint8 r, g, b, a;
+
+  SDL_GetRGBA(pixel, fmt, &r, &g, &b, &a);
+
+  Local<Object> rgba = Object::New();
+  rgba->Set(String::New("r"), Number::New(r));
+  rgba->Set(String::New("g"), Number::New(g));
+  rgba->Set(String::New("b"), Number::New(b));
+  rgba->Set(String::New("a"), Number::New(a));
+
+  return scope.Close(rgba);
+}
+
+
+static Handle<Value> sdl::SetClipRect(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 2 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected SetClipRect(SDL_Surface, SDL_Rect)")));
+  }    
+  
+  SDL_Surface* surface = UnwrapSurface(args[0]->ToObject());
+  SDL_Rect* rect;
+  if (args[1]->IsNull()) {
+    rect = NULL;
+  } else if (args[1]->IsArray()) {
+    SDL_Rect r;
+    Handle<Object> arr = args[1]->ToObject();
+    r.x = arr->Get(String::New("0"))->Int32Value();
+    r.y = arr->Get(String::New("1"))->Int32Value();
+    r.w = arr->Get(String::New("2"))->Int32Value();
+    r.h = arr->Get(String::New("3"))->Int32Value();
+    rect = &r;
+  } else {
+    rect = UnwrapRect(args[1]->ToObject());
+  }
+  if (SDL_SetClipRect (surface, rect ) < 0) return ThrowSDLException(__func__);
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::TTF::Init(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 0)) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected TTF::Init()")));
+  }
+
+  if (TTF_Init() < 0) {
+    return ThrowException(Exception::Error(String::Concat(
+      String::New("TTF::Init: "),
+      String::New(TTF_GetError())
+    )));
+  }
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::TTF::OpenFont(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 2 && args[0]->IsString() && args[1]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected TTF::OpenFont(String, Number)")));
+  }
+
+  String::Utf8Value file(args[0]);
+  int ptsize = (args[1]->Int32Value());
+
+  TTF_Font* font = TTF_OpenFont(*file, ptsize);
+  if (font == NULL) {
+    return ThrowException(Exception::Error(String::Concat(
+      String::New("TTF::OpenFont: "),
+      String::New(TTF_GetError())
+    )));
+  }
+  return scope.Close(WrapFont(font));
+}
+
+static Handle<Value> sdl::TTF::RenderTextBlended(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 3 && args[0]->IsObject() && args[1]->IsString() && args[2]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected TTF::RenderTextBlended(Font, String, Number)")));
+  }
+
+  SDL_PixelFormat* vfmt = SDL_GetVideoInfo()->vfmt;
+  TTF_Font* font = UnwrapFont(args[0]->ToObject());
+  String::Utf8Value text(args[1]);
+  int colorCode = args[2]->Int32Value();
+
+  Uint8 r, g, b;
+  SDL_GetRGB(colorCode, vfmt, &r, &g, &b);
+
+  SDL_Color color;
+  color.r = r;
+  color.g = g;
+  color.b = b;
+
+  SDL_Surface *resulting_text;
+  resulting_text = TTF_RenderText_Blended(font, *text, color);
+  if (!resulting_text) {
+    return ThrowException(Exception::Error(String::Concat(
+      String::New("TTF::RenderTextBlended: "),
+      String::New(TTF_GetError())
+    )));
+  }
+  return scope.Close(WrapSurface(resulting_text));
+}
+
+static Handle<Value> sdl::TTF::RenderTextShaded(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 4 && args[0]->IsObject() && args[1]->IsString() && args[2]->IsNumber() && args[3]->IsNumber())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected TTF::RenderTextShaded(Font, String, Number, Number)")));
+  }
+
+  SDL_PixelFormat* vfmt = SDL_GetVideoInfo()->vfmt;
+  TTF_Font* font = UnwrapFont(args[0]->ToObject());
+  String::Utf8Value text(args[1]);
+  int colorCodeFg = args[2]->Int32Value();
+  int colorCodeBg = args[3]->Int32Value();
+
+  Uint8 r, g, b;
+  SDL_GetRGB(colorCodeFg, vfmt, &r, &g, &b);
+
+  SDL_Color colorFg;
+  colorFg.r = r;
+  colorFg.g = g;
+  colorFg.b = b;
+
+
+  SDL_GetRGB(colorCodeBg, vfmt, &r, &g, &b);
+
+  SDL_Color colorBg;
+  colorBg.r = r;
+  colorBg.g = g;
+  colorBg.b = b;
+
+  SDL_Surface *resulting_text;
+  resulting_text = TTF_RenderText_Shaded(font, *text, colorFg, colorBg);
+  if (!resulting_text) {
+    return ThrowException(Exception::Error(String::Concat(
+      String::New("TTF::RenderTextShaded: "),
+      String::New(TTF_GetError())
+    )));
+  }
+  return scope.Close(WrapSurface(resulting_text));
+}
+
+// TODO: make an async version so this can be used in loops or parallel load images
+static Handle<Value> sdl::IMG::Load(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsString())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected IMG::Load(String)")));
+  }
+
+  String::Utf8Value file(args[0]);
+
+  SDL_Surface *image;
+  image=IMG_Load(*file);
+  if(!image) {
+    return ThrowException(Exception::Error(String::Concat(
+      String::New("IMG::Load: "),
+      String::New(IMG_GetError())
+    )));
+  }
+
+  return scope.Close(WrapSurface(image));
+}
+
+static Handle<Value> sdl::WM::SetCaption(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 2 && args[0]->IsString() && args[1]->IsString())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected WM::SetCaption(String, String)")));
+  }
+
+  String::Utf8Value title(args[0]);
+  String::Utf8Value icon(args[0]);
+
+  SDL_WM_SetCaption(*title, *icon);
+
+  return Undefined();
+}
+
+static Handle<Value> sdl::WM::SetIcon(const Arguments& args) {
+  HandleScope scope;
+
+  if (!(args.Length() == 1 && args[0]->IsObject())) {
+    return ThrowException(Exception::TypeError(String::New("Invalid arguments: Expected WM::SetIcon(Surface)")));
+  }
+
+  SDL_Surface* icon = UnwrapSurface(args[0]->ToObject());
+  int colorkey = SDL_MapRGB(icon->format, 255, 0, 255);
+  SDL_SetColorKey(icon, SDL_SRCCOLORKEY, colorkey);
+  SDL_WM_SetIcon(icon, NULL);
+
+  return Undefined();
+}
+
+NODE_MODULE(nodesdl, init)

+ 93 - 0
src/node/node_modules/node-sdl/src/nodesdl.h

@@ -0,0 +1,93 @@
+#ifndef NODE_SDL_H_
+#define NODE_SDL_H_
+
+#include <node.h>
+#include <v8.h>
+#include <SDL.h>
+#include <SDL_ttf.h>
+#include <SDL_image.h>
+
+
+#include "helpers.h"
+
+using namespace v8;
+
+namespace sdl {
+
+  static Handle<Value> Init(const Arguments& args);
+  static Handle<Value> InitSubSystem(const Arguments& args);
+  static Handle<Value> Quit(const Arguments& args);
+  static Handle<Value> QuitSubSystem(const Arguments& args);
+  static Handle<Value> WasInit(const Arguments& args);
+  static Handle<Value> ClearError(const Arguments& args);
+  static Handle<Value> GetError(const Arguments& args);
+  static Handle<Value> SetError(const Arguments& args);
+  static Handle<Value> WaitEvent(const Arguments& args);
+  static Handle<Value> PollEvent(const Arguments& args);
+  static Handle<Value> SetVideoMode(const Arguments& args);
+  static Handle<Value> VideoModeOK(const Arguments& args);
+  static Handle<Value> NumJoysticks(const Arguments& args);
+  static Handle<Value> JoystickOpen(const Arguments& args);
+  static Handle<Value> JoystickOpened(const Arguments& args);
+  static Handle<Value> JoystickName(const Arguments& args);
+  static Handle<Value> JoystickNumAxes(const Arguments& args);
+  static Handle<Value> JoystickNumButtons(const Arguments& args);
+  static Handle<Value> JoystickNumBalls(const Arguments& args);
+  static Handle<Value> JoystickNumHats(const Arguments& args);
+  static Handle<Value> JoystickClose(const Arguments& args);
+  static Handle<Value> JoystickUpdate(const Arguments& args);
+  static Handle<Value> JoystickEventState(const Arguments& args);
+  static Handle<Value> Flip(const Arguments& args);
+  static Handle<Value> PutImageData(const Arguments& args);
+  static Handle<Value> FillRect(const Arguments& args);
+  static Handle<Value> UpdateRect(const Arguments& args);
+  static Handle<Value> CreateRGBSurface(const Arguments& args);
+  static Handle<Value> BlitSurface(const Arguments& args);
+  static Handle<Value> FreeSurface(const Arguments& args);
+  static Handle<Value> SetColorKey(const Arguments& args);
+  static Handle<Value> DisplayFormat(const Arguments& args);
+  static Handle<Value> DisplayFormatAlpha(const Arguments& args);
+  static Handle<Value> SetAlpha(const Arguments& args);
+  static Handle<Value> MapRGB(const Arguments& args);
+  static Handle<Value> MapRGBA(const Arguments& args);
+  static Handle<Value> GetRGB(const Arguments& args);
+  static Handle<Value> GetRGBA(const Arguments& args);
+  static Handle<Value> SetClipRect(const Arguments& args);
+
+  namespace TTF {
+    static Handle<Value> Init(const Arguments& args);
+    static Handle<Value> OpenFont(const Arguments& args);
+    static Handle<Value> RenderTextBlended(const Arguments& args);
+    static Handle<Value> RenderTextShaded(const Arguments& args);
+  }
+
+  namespace IMG {
+    static Handle<Value> Load(const Arguments& args);
+  }
+
+  namespace WM {
+    static Handle<Value> SetCaption(const Arguments& args);
+    static Handle<Value> SetIcon(const Arguments& args);
+  }
+
+  namespace GL {
+    static Handle<Value> SetAttribute (const Arguments& args);
+    static Handle<Value> GetAttribute (const Arguments& args);
+    static Handle<Value> SwapBuffers (const Arguments& args);
+  }
+
+
+
+
+
+
+  typedef struct {
+    Persistent<Function> fn;
+    int status;
+  } closure_t;
+  //static void EIO_WaitEvent(eio_req *req);
+  //static int  EIO_OnEvent(eio_req *req);
+
+}
+
+#endif

+ 124 - 0
src/node/screen_sdl.js

@@ -0,0 +1,124 @@
+"use strict";
+
+function NodeScreenSDL(sdl, font_file)
+{
+    var ROW_HEIGHT = 16,
+
+        // about right for ascii.ttf
+        CHAR_WIDTH = 10,
+
+        /** @type {number} */
+        cursor_scanline_start,
+
+        /** @type {number} */
+        cursor_scanline_end,
+
+        current_cursor_address,
+
+        graphic_buffer,
+        graphic_buffer8,
+        graphic_buffer32;
+
+
+    sdl.init(sdl.INIT.VIDEO);
+
+    var ttf = sdl.TTF,
+        screen;
+
+    ttf.init();
+
+    var font = ttf.openFont(font_file, 16);
+
+    sdl.events.on("QUIT", function()
+    {
+        process.exit();
+    });
+
+    this.put_char = function(row, col, chr, bg_color, fg_color)
+    {
+        if((chr & 0xff) === 0)
+        {
+            // required, otherwise sdl throws up
+            return;
+        }
+
+        var str = String.fromCharCode(chr & 0xff),
+            s = ttf.renderTextShaded(font, str, fg_color, bg_color);
+
+        sdl.blitSurface(s, null, screen, [col * CHAR_WIDTH, row * ROW_HEIGHT]);
+        sdl.freeSurface(s);
+    };
+
+    this.put_pixel_linear = function(offset, color_part)
+    {
+        graphic_buffer8[offset] = color_part;
+    };
+
+    this.put_pixel = function(x, y, color)
+    {
+        throw "TODO";
+    };
+
+    this.timer_text = function()
+    {
+        sdl.flip(screen);
+    };
+
+    this.timer_graphical = function()
+    {
+        sdl.putImageData(screen, graphic_buffer32);
+        sdl.flip(screen);
+    };
+
+    this.destroy = function()
+    {
+
+    };
+
+    this.set_mode = function(graphical)
+    {
+        // switch between graphical and text mode
+    };
+
+    this.clear_screen = function()
+    {
+
+    };
+
+    this.set_size_text = function(cols, rows)
+    {
+        screen = sdl.setVideoMode(CHAR_WIDTH * cols, ROW_HEIGHT * rows, 32, 0);
+
+        //dbg_log(screen, LOG_VGA);
+        //dbg_log(screen.pixels, LOG_VGA);
+    };
+
+    this.set_size_graphical = function(width, height)
+    {
+        screen = sdl.setVideoMode(width, height, 32, 0);
+
+        graphic_buffer = new ArrayBuffer(width * height * 4);
+        graphic_buffer8 = new Uint8Array(graphic_buffer);
+        graphic_buffer32 = new Int32Array(graphic_buffer);
+    };
+
+    this.update_cursor = function(cursor_row, cursor_col)
+    {
+        refresh_cursor();
+    };
+
+    this.update_cursor_scanline = function(start, end)
+    {
+        cursor_scanline_start = start;
+        cursor_scanline_end = end;
+
+        refresh_cursor();
+    };
+
+    function refresh_cursor()
+    {
+        // TODO
+    }
+
+}
+

+ 90 - 0
src/node/screen_tty.js

@@ -0,0 +1,90 @@
+"use strict";
+
+/*
+ * Very simple prototype, mostly incomplete
+ */
+function NodeScreenTTY()
+{
+    var stdout = process.stdout,
+        cursor_row = 0,
+        cursor_col = 0;
+
+    clear();
+
+    function clear()
+    {
+        stdout.write("\x1b[2J");
+    }
+
+    function set_cursor_pos(row, col)
+    {
+        stdout.write("\x1b[" + row + ";" + col + "H");
+    }
+
+    function hide_cursor()
+    {
+        stdout.write("\x1b[?25l");
+    }
+
+    function show_cursor()
+    {
+        stdout.write("\x1b[?25h");
+    }
+
+    this.timer_text = function()
+    {
+
+    };
+
+    this.destroy = function()
+    {
+
+    };
+
+    this.set_mode = function(is_graphical)
+    {
+        if(is_graphical)
+        {
+            console.log("Graphical Mode is not supported for NodeScreenTTY");
+        }
+    };
+
+    this.clear_screen = function()
+    {
+
+    };
+
+    this.set_size_text = function(cols, rows)
+    {
+
+    };
+
+    this.set_size_graphical = function(width, height)
+    {
+
+    };
+
+    this.update_cursor = function(row, col)
+    {
+        cursor_row = row;
+        cursor_col = col;
+    };
+
+    this.update_cursor_scanline = function(start, end)
+    {
+
+    };
+
+    this.put_char = function(row, col, chr, bg_color, fg_color)
+    {
+        var str = String.fromCharCode(chr);
+
+        hide_cursor();
+        set_cursor_pos(row, col + 1);
+
+        stdout.write(str);
+
+        set_cursor_pos(cursor_row, cursor_col + 2);
+        show_cursor();
+    };
+}

+ 173 - 0
src/pci.js

@@ -0,0 +1,173 @@
+"use strict";
+
+var 
+/** @const */  PCI_VENDOR_ID =		0x00	/* 16 bits */
+/** @const */ ,PCI_DEVICE_ID =		0x02	/* 16 bits */
+/** @const */ ,PCI_COMMAND =		0x04	/* 16 bits */
+/** @const */ ,PCI_BASE_ADDRESS_0 =	0x10	/* 32 bits */
+/** @const */ ,PCI_BASE_ADDRESS_1 =	0x14	/* 32 bits [htype 0,1 only] */
+/** @const */ ,PCI_BASE_ADDRESS_2 =	0x18	/* 32 bits [htype 0 only] */
+/** @const */ ,PCI_BASE_ADDRESS_3 =	0x1c	/* 32 bits */
+/** @const */ ,PCI_BASE_ADDRESS_4 =	0x20	/* 32 bits */
+/** @const */ ,PCI_BASE_ADDRESS_5 =	0x24	/* 32 bits */
+/** @const */ ,PCI_INTERRUPT_LINE =	0x3c	/* 8 bits */
+/** @const */ ,PCI_CLASS_REVISION =	0x08;	/* High 24 bits are class, low 8 revision */
+
+/** @constructor */
+function PCI(dev)
+{
+    var
+        io = dev.io,
+        pci_data = 0,
+        pci_counter = 0,
+        pci_response = -1,
+        pci_status = -1,
+        self = this;
+
+    // TODO: Change the format of this
+    this.devices = {};
+
+    /*
+    io.register_write(0xCF9, function(value)
+    {
+        dbg_log("PCI reboot: " + h(value, 2), LOG_PCI);
+
+        // PCI reboot
+        if(value & 6)
+        {
+            cpu_restart();
+        }
+    });*/
+
+    io.register_read(0xCFC, function()
+    {
+        return pci_response & 0xFF;
+    });
+
+    io.register_read(0xCFD, function()
+    {
+        return pci_response >> 8 & 0xFF;
+    });
+    io.register_read(0xCFE, function()
+    {
+        return pci_response >> 16 & 0xFF;
+    });
+    io.register_read(0xCFF, function()
+    {
+        return pci_response >> 24 & 0xFF;
+    });
+
+    io.register_read(0xCF8, function()
+    {
+        return pci_status & 0xFF;
+    });
+    io.register_read(0xCF9, function()
+    {
+        return pci_status >> 8 & 0xFF;
+    });
+    io.register_read(0xCFA, function()
+    {
+        return pci_status >> 16 & 0xFF;
+    });
+    io.register_read(0xCFB, function()
+    {
+        return pci_status >> 24 & 0xFF;
+    });
+
+    io.register_write(0xCF8, function(out_byte)
+    {
+        pci_data = pci_data & ~0xFF | out_byte;
+    });
+    io.register_write(0xCF9, function(out_byte)
+    {
+        pci_data = pci_data & ~0xFF00 | out_byte << 8;
+    });
+    io.register_write(0xCFA, function(out_byte)
+    {
+        pci_data = pci_data & ~0xFF0000 | out_byte << 16;
+    });
+    io.register_write(0xCFB, function(out_byte)
+    {
+        pci_data = pci_data & 0xFFFFFF | out_byte << 24;
+        pci_query(pci_data);
+    });
+
+    function pci_query(dword)
+    {
+        var dbg_line = "PCI: ";
+        
+        // Bit | .31                     .0
+        // Fmt | EBBBBBBBBDDDDDFFFRRRRRR00
+
+        var bdf = (dword & 0x7FFFFFFF) >> 8,
+            addr = dword & 0xFC,
+            devfn = bdf & 0xFF,
+            bus = bdf >> 8,
+            dev = bdf >> 3 & 0x1F,
+            fn = bdf & 7,
+            enabled = dword >> 31 & 1;
+
+        dbg_line += " enabled=" + (enabled);
+        dbg_line += " bdf=" + h(bdf);
+        dbg_line += " addr=" + h(addr);
+
+        dbg_log(dbg_line + " " + h(dword >>> 0, 8), LOG_PCI);
+
+        if(dword === (0x80000000 | 0))
+        {
+            pci_status = 0x80000000;
+        }
+        else if(self.devices[bdf])
+        {
+            var device = self.devices[bdf];
+
+            pci_status = 0x80000000;
+
+            if(addr === PCI_VENDOR_ID)
+            {
+                pci_response = device.vendor_id;
+            }
+            else if(addr === PCI_CLASS_REVISION)
+            {
+                pci_response = device.class_revision;
+            }
+            else if(addr === PCI_BASE_ADDRESS_5)
+            {
+                pci_response = device.iobase;
+            }
+            else if(addr === PCI_INTERRUPT_LINE)
+            {
+                pci_response = device.irq;
+            }
+            else
+            {
+                dbg_log("unimplemented addr " + h(addr) + " for device " + h(bdf), LOG_PCI);
+                pci_response = 0;
+            }
+        }
+        else
+        {
+            pci_response = 0;
+            pci_status = 0;
+        }
+    }
+
+    this.register_device = function(device, device_id)
+    {
+        dbg_assert(!this.devices[device_id]);
+        this.devices[device_id] = device;
+    };
+
+    // ~% lspci -x   
+    // 00:00.0 Host bridge: Intel Corporation 4 Series Chipset DRAM Controller (rev 02)
+    // 00: 86 80 20 2e 06 00 90 20 02 00 00 06 00 00 00 00
+    // 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+    // 20: 00 00 00 00 00 00 00 00 00 00 00 00 43 10 d3 82
+    // 30: 00 00 00 00 e0 00 00 00 00 00 00 00 00 00 00 00
+    this.register_device({
+        irq: 0,
+        iobase: 0,
+        vendor_id: 0x8680202e,
+        class_revision: 0x06009020,
+    }, 0);
+}

+ 280 - 0
src/pic.js

@@ -0,0 +1,280 @@
+"use strict";
+
+/** 
+ * Programmable Interrupt Controller
+ * http://stanislavs.org/helppc/8259.html
+ *
+ * @constructor
+ * @param {PIC=} master
+ */
+function PIC(dev, call_interrupt_vector, handle_irqs, master)
+{
+    var 
+        io = dev.io,
+
+        /** 
+         * all irqs off
+         * @type {number}
+         */
+        irq_mask = 0,
+
+        /**
+         * @type {number}
+         *
+         * Bogus default value (both master and slave mapped to 0).
+         * Will be initialized by the BIOS
+         */
+        irq_map = 0,
+
+        /**
+         * in-service register
+         * Holds interrupts that are currently being serviced
+         * @type {number}
+         */
+        isr = 0,
+
+        /**
+         * interrupt request register
+         * Holds interrupts that have been requested
+         * @type {number}
+         */
+        irr = 0,
+
+        is_master = master === undefined,
+
+        slave,
+
+        me = this;
+
+    if(is_master)
+    {
+        slave = new PIC(dev, call_interrupt_vector, handle_irqs, this);
+
+        this.handle_irqs = function()
+        {
+            var enabled_irr = irr & irq_mask;
+
+            if(!enabled_irr)
+            {
+                return false
+            }
+
+            var irq = enabled_irr & -enabled_irr;
+
+            if(isr && (isr & -isr) <= irq)
+            {
+                // wait for eoi of higher or same priority interrupt
+                return false;
+            }
+
+            irr &= ~irq;
+            isr |= irq;
+
+            if(irq === 4)
+            {
+                // this should always return true
+                return slave.handle_irqs();
+            }
+
+            call_interrupt_vector(irq_map + log2_table[irq], false, false);
+
+            return true;
+        };
+    }
+    else
+    {
+        // is slave
+        this.handle_irqs = function()
+        {
+            var enabled_irr = irr & irq_mask;
+
+            if(!enabled_irr)
+            {
+                return false;
+            }
+
+            var irq = enabled_irr & -enabled_irr;
+
+            if(isr && (isr & -isr) <= irq)
+            {
+                // wait for eoi of higher or same priority interrupt
+                return false;
+            }
+
+            irr &= ~irq;
+            isr |= irq;
+
+            call_interrupt_vector(irq_map + log2_table[irq], false, false);
+
+            if(irr)
+            {
+                // tell the master we have one more
+                master.push_irq(2);
+            }
+
+            return true;
+        };
+    }
+
+    this.dump = function()
+    {
+        dbg_log("mask: " + h(irq_mask & 0xFF), LOG_PIC);
+        dbg_log("base: " + h(irq_map), LOG_PIC);
+        dbg_log("requested: " + h(irr), LOG_PIC);
+        dbg_log("serviced: " + h(isr), LOG_PIC);
+
+        if(is_master)
+        {
+            slave.dump();
+        }
+    };
+
+
+    var expect_icw4,
+        state = 0,
+        read_irr = 1,
+        io_base,
+        auto_eoi;
+
+
+    if(is_master)
+    {
+        io_base = 0x20;
+    }
+    else
+    {
+        io_base = 0xA0;
+    }
+
+    io.register_write(io_base, port20_write);
+    io.register_read(io_base, port20_read);
+
+    io.register_write(io_base | 1, port21_write);
+    io.register_read(io_base | 1, port21_read);
+
+    function port20_write(data_byte)
+    {
+        //dbg_log("20 write: " + h(data_byte), LOG_PIC);
+        if(data_byte & 0x10) // xxxx1xxx
+        {
+            // icw1
+            dbg_log("icw1 = " + h(data_byte), LOG_PIC);
+            expect_icw4 = data_byte & 1;
+            state = 1;
+        }
+        else if(data_byte & 8) // xxx01xxx
+        {
+            // ocw3
+            dbg_log("ocw3: " + h(data_byte), LOG_PIC);
+            read_irr = data_byte & 1;
+        }
+        else // xxx00xxx
+        {
+            // ocw2
+            // end of interrupt
+            //dbg_log("ocw2: " + h(data_byte), LOG_PIC);
+
+            var eoi_type = data_byte >> 5;
+
+            if(eoi_type === 1)
+            {
+                // non-specific eoi
+                isr = 0;
+            }
+            else if(eoi_type === 3)
+            {
+                // specific eoi
+                isr &= ~(1 << (data_byte & 7));
+            }
+            else
+            {
+                dbg_log("Unknown eoi: " + h(data_byte), LOG_PIC);
+            }
+        }
+    };
+
+    function port20_read()
+    {
+        if(read_irr)
+        {
+            return irr;
+        }
+        else
+        {
+            return isr;
+        }
+    }
+
+    function port21_write(data_byte)
+    {
+        //dbg_log("21 write: " + h(data_byte), LOG_PIC);
+        if(state === 0)
+        {
+            if(expect_icw4)
+            {
+                // icw4
+                expect_icw4 = false;
+                auto_eoi = data_byte & 2;
+                dbg_log("icw4: " + h(data_byte), LOG_PIC);
+            }
+            else
+            {
+                // ocw1
+                irq_mask = ~data_byte;
+                //dbg_log("interrupt mask: " + (irq_mask & 0xFFFF).toString(2) + " / map " + h(irq_map), LOG_PIC);
+            }
+        }
+        else if(state === 1)
+        {
+            // icw2
+            irq_map = data_byte;
+            dbg_log("interrupts are mapped to " + h(irq_map) +
+                    " (" + (is_master ? "master" : "slave") + ")", LOG_PIC);
+            state++;
+        }
+        else if(state === 2)
+        {
+            // icw3
+            state = 0;
+            dbg_log("icw3: " + h(data_byte), LOG_PIC);
+        }
+    };
+
+    function port21_read()
+    {
+        //dbg_log("21h read (" + h(irq_map) + ")", LOG_PIC);
+        return ~irq_mask;
+    };
+
+    if(is_master)
+    {
+        this.push_irq = function(irq_number)
+        {
+            dbg_assert(irq_number >= 0 && irq_number < 16);
+
+            if(irq_number >= 8)
+            {
+                slave.push_irq(irq_number - 8);
+                irq_number = 2;
+            }
+
+            irr |= 1 << irq_number;
+
+            handle_irqs();
+        };
+    }
+    else
+    {
+        this.push_irq = function(irq_number)
+        {
+            dbg_assert(irq_number >= 0 && irq_number < 8);
+
+            irr |= 1 << irq_number;
+        };
+    }
+
+    this.get_isr = function()
+    {
+        return isr;
+    };
+}

+ 303 - 0
src/pit.js

@@ -0,0 +1,303 @@
+"use strict";
+
+
+/**
+ * @constructor
+ *
+ * Programmable Interval Timer
+ */
+function PIT(dev)
+{
+    var 
+        io = dev.io,
+        pic = dev.pic,
+        
+        next_tick = Date.now(),
+
+        me = this,
+
+        /** 
+         * @const 
+         * In kHz
+         */
+        OSCILLATOR_FREQ = 1193.1816666, // 1.193182 MHz
+
+
+        counter_next_low = new Uint8Array(3),
+        counter_enabled = new Uint8Array(3),
+        counter_mode = new Uint8Array(3),
+        counter_read_mode = new Uint8Array(3),
+
+        // 2 = latch low, 1 = latch high, 0 = no latch
+        counter_latch = new Uint8Array(3), 
+        counter_latch_value = new Uint16Array(3),
+
+        counter_reload = new Uint16Array(3),
+        counter_current = new Uint16Array(3),
+
+        // only counter2 output can be read
+        counter2_out = 0;
+
+
+    // TODO:
+    // - counter2 can be controlled by an input
+        
+
+    this.get_timer2 = function()
+    {
+        //dbg_log("timer2 read", LOG_PIT);
+        return counter2_out;
+    };
+
+    var parity = 0;
+
+    io.register_read(0x61, function()
+    {
+        // > xxx1 xxxx  0=RAM parity error enable
+        // >            PS/2: Read:  This bit tiggles for each refresh request.
+        // 
+        // tiggles??
+        
+        parity ^= 0x10;
+        return parity | counter2_out << 5;
+    });
+
+
+    this.timer = function(time)
+    {
+        var current,
+            mode,
+            steps = (time - next_tick) * OSCILLATOR_FREQ >>> 0;
+
+        if(!steps)
+        {
+            return;
+        }
+        dbg_assert(steps >= 0);
+
+        next_tick += steps / OSCILLATOR_FREQ;
+
+        // counter 0 produces interrupts
+        if(counter_enabled[0])
+        {
+            current = counter_current[0] -= steps;
+
+            if(current <= 0)
+            {
+                pic.push_irq(0);
+                mode = counter_mode[0];
+
+                if(mode === 0)
+                {
+                    counter_enabled[0] = 0;
+                    counter_current[0] = 0;
+                }
+                else if(mode === 3 || mode === 2)
+                {
+                    counter_current[0] = counter_reload[0] + current % counter_reload[0];
+                }
+            }
+        }
+
+        // counter 2 has an output bit
+        if(counter_enabled[2])
+        {
+            current = counter_current[2] -= steps;
+
+            if(current <= 0)
+            {
+                mode = counter_mode[2];
+
+                if(mode === 0)
+                {
+                    counter2_out = 1;
+                    counter_enabled[2] = 0;
+                    counter_current[2] = 0;
+                }
+                else if(mode === 2)
+                {
+                    counter2_out = 1;
+                    counter_current[2] = counter_reload[2] + current % counter_reload[2];
+                }
+                else if(mode === 3)
+                {
+                    counter2_out ^= 1;
+                    counter_current[2] = counter_reload[2] + current % counter_reload[2];
+                }
+            }
+            // cannot really happen, because the counter gets changed by big numbers
+            //else if(current === 1)
+            //{
+            //    if(counter_mode[2] === 2)
+            //    {
+            //        counter2_out = 0;
+            //    }
+            //}
+        }
+    }
+
+    io.register_read(0x40, function() { return counter_read(0); });
+    io.register_read(0x41, function() { return counter_read(1); });
+    io.register_read(0x42, function() { return counter_read(2); });
+            
+    function counter_read(i) 
+    { 
+        var latch = counter_latch[i];
+
+        if(latch)
+        {
+            counter_latch[i]--;
+
+            if(latch === 2)
+            {
+                return counter_latch_value[i] & 0xFF;
+            }
+            else
+            {
+                return counter_latch_value[i] >> 8;
+            }
+        }
+        else
+        {
+            var next_low = counter_next_low[i];
+
+            if(counter_mode[i] === 3)
+            {
+                counter_next_low[i] ^= 1;
+            }
+
+            if(next_low)
+            {
+                return counter_current[i] & 0xFF;
+            }
+            else
+            {
+                return counter_current[i] >> 8;
+            }
+        }
+    }
+
+    io.register_write(0x40, function(value) { counter_write(0, value); });
+    io.register_write(0x41, function(value) { counter_write(1, value); });
+    io.register_write(0x42, function(value) { counter_write(2, value); });
+            
+    function counter_write(i, value) 
+    { 
+        if(counter_next_low[i])
+        {
+            counter_reload[i] = counter_reload[i] & ~0xFF | value;
+        }
+        else
+        {
+            counter_reload[i] = counter_reload[i] & 0xFF | value << 8;
+        }
+
+        if(counter_read_mode[i] !== 3 || !counter_next_low[i])
+        {
+            if(!counter_reload[i])
+            {
+                counter_reload[i] = 0xFFFF;
+            }
+
+            // depends on the mode, should actually 
+            // happen on the first tick
+            counter_current[i] = counter_reload[i];
+
+            counter_enabled[i] = true;
+
+            dbg_log("counter" + i + " reload=" + h(counter_reload[i]) + 
+                " tick=" + (counter_reload[i] || 0x10000) / OSCILLATOR_FREQ + "ms", LOG_PIT);
+        }
+
+        if(counter_read_mode[i] === 3)
+        {
+            counter_next_low[i] ^= 1;
+        }
+    }
+
+    io.register_write(0x43, port43_write);
+
+    function port43_write(reg_byte)
+    {
+        var mode = reg_byte >> 1 & 7,
+            binary_mode = reg_byte & 1,
+            i = reg_byte >> 6 & 3,
+            read_mode = reg_byte >> 4 & 3,
+            next_low;
+
+        if(i === 1)
+        {
+            dbg_log("Unimplemented timer1", LOG_PIT);
+        }
+
+        if(i === 3)
+        {
+            dbg_log("Unimplemented read back", LOG_PIT);
+            return;
+        }
+
+        if(read_mode === 0)
+        {
+            // latch
+            counter_latch[i] = 2;
+            counter_latch_value[i] = counter_current[i];
+
+            return;
+        }
+
+        if(mode >= 6)
+        {
+            // 6 and 7 are aliased to 2 and 3
+            mode &= ~4;
+        }
+
+        dbg_log("Control: mode=" + mode + " ctr=" + i + 
+                " read_mode=" + read_mode + " bcd=" + binary_mode, LOG_PIT);
+
+        if(read_mode === 1)
+        {
+            // lsb
+            counter_next_low[i] = 1;
+        }
+        else if(read_mode === 2)
+        {
+            // msb
+            counter_next_low[i] = 0;
+        }
+        else
+        {
+            // first lsb then msb
+            counter_next_low[i] = 1;
+        }
+
+
+        if(mode === 0)
+        {
+        }
+        else if(mode === 3 || mode === 2)
+        {
+            // what is the difference 
+        }
+        else
+        {
+            dbg_log("Unimplemented counter mode: " + h(mode), LOG_PIT);
+        }
+
+        counter_mode[i] = mode;
+        counter_read_mode[i] = read_mode;
+
+        if(i === 2)
+        {
+            if(mode === 0)
+            {
+                counter2_out = 0;
+            }
+            else
+            {
+                // correct for mode 2 and 3
+                counter2_out = 1;
+            }
+        }
+    };
+}
+

+ 432 - 0
src/ps2.js

@@ -0,0 +1,432 @@
+"use strict";
+
+/**
+ * @constructor
+ */
+function PS2(dev, keyboard, mouse)
+{
+    var 
+        io = dev.io,
+        pic = dev.pic,
+
+        me = this,
+
+        /** @type {boolean} */
+        enable_mouse_stream = false,
+        /** @type {boolean} */
+        enable_mouse = false,
+
+        /** @type {boolean} */
+        have_mouse = false,
+
+        /** @type {number} */
+        mouse_delta_x = 0,
+        /** @type {number} */
+        mouse_delta_y = 0,
+        /** @type {number} */
+        mouse_clicks = 0,
+
+        /** @type {boolean} */
+        have_keyboard = false,
+
+        /** @type {boolean} */
+        next_is_mouse_command = false,
+
+        /** @type {boolean} */
+        next_read_sample = false,
+
+        /** @type {boolean} */
+        next_read_led = false,
+
+        /** 
+         * @type {Array.<number>} 
+         */
+        kbd_buffer = [],
+
+        /** @type {number} */
+        sample_rate = 100,
+
+        /** @type {number} */
+        last_mouse_packet = -1,
+
+        /** 
+         * @type {Array.<number>} 
+         */
+        mouse_buffer = [];
+
+
+    if(keyboard)
+    {
+        have_keyboard = true;
+        keyboard.init(kbd_send_code);
+    }
+
+    if(mouse)
+    {
+        have_mouse = true;
+        mouse.init(mouse_send_click, mouse_send_delta);
+
+        // TODO: Mouse Wheel
+        // http://www.computer-engineering.org/ps2mouse/
+    }
+
+
+    function mouse_irq()
+    {
+        pic.push_irq(12);
+    }
+
+    function kbd_irq()
+    {
+        pic.push_irq(1);
+    }
+
+
+    function kbd_send_code(code)
+    {
+        //console.log(h(code));
+        kbd_buffer.push(code);
+        kbd_irq();
+    }
+    this.kbd_send_code = kbd_send_code;
+
+    function mouse_send_delta(delta_x, delta_y)
+    {
+        if(have_mouse && enable_mouse)
+        {
+            mouse_delta_x += delta_x;
+            mouse_delta_y += delta_y;
+
+            if(enable_mouse_stream)
+            {
+                var now = Date.now();
+
+                if(now - last_mouse_packet < 1000 / sample_rate)
+                {
+                    // TODO: set timeout
+                    return;
+                }
+
+                last_mouse_packet = now;
+
+                send_mouse_packet();
+            }
+        }
+    }
+
+    function mouse_send_click(left, middle, right)
+    {
+        if(have_mouse && enable_mouse)
+        {
+            mouse_clicks = left | right << 1 | middle << 2;
+
+            if(enable_mouse_stream)
+            {
+                send_mouse_packet();
+            }
+        }
+    }
+
+    function send_mouse_packet()
+    {
+        var info_byte = 
+                (mouse_delta_y < 0) << 5 |
+                (mouse_delta_x < 0) << 4 |
+                1 << 3 | 
+                mouse_clicks;
+
+        mouse_buffer.push(
+            info_byte, 
+            mouse_delta_x & 0xFF,
+            mouse_delta_y & 0xFF
+        );
+
+        if(mouse_buffer.length > 15)
+        {
+            var off = mouse_buffer.length % 3;
+            mouse_buffer = mouse_buffer.slice(0, off).concat(mouse_buffer.slice(off + 3));
+        }
+
+        mouse_delta_x = 0;
+        mouse_delta_y = 0;
+
+        mouse_irq();
+    }
+
+    this.destroy = function()
+    {
+        if(have_keyboard)
+        {
+            keyboard.destroy();
+        }
+
+        if(have_mouse)
+        {
+            mouse.destroy();
+        }
+    };
+        
+
+    var command_register = 0,
+        read_output_register = false,
+        read_command_register = false;
+
+
+    io.register_read(0x60, port60_read);
+    io.register_read(0x64, port64_read);
+
+    io.register_write(0x60, port60_write);
+    io.register_write(0x64, port64_write);
+
+    function port60_read()
+    {
+        //log("port 60 read: " + (buffer[0] || "(none)"));
+
+        if(!kbd_buffer.length && !mouse_buffer.length)
+        {
+            // should not happen
+            dbg_log("Port 60 read: Empty", LOG_PS2);
+            return 0xFF;
+        }
+
+        var do_mouse_buffer;
+
+        if(kbd_buffer.length && mouse_buffer.length)
+        {
+            // tough decision, let's ask the PIC
+            do_mouse_buffer = (pic.get_isr() & 2) === 0;
+        }
+        else if(kbd_buffer.length)
+        {
+            do_mouse_buffer = false;
+        }
+        else
+        {
+            do_mouse_buffer = true;
+        }
+
+
+        if(do_mouse_buffer)
+        {
+            dbg_log("Port 60 read (mouse): " + h(mouse_buffer[0]), LOG_PS2);
+
+            if(mouse_buffer.length > 1)
+            {
+                mouse_irq();
+            }
+
+            return mouse_buffer.shift();
+        }
+        else
+        {
+            dbg_log("Port 60 read (kbd)  : " + h(kbd_buffer[0]), LOG_PS2);
+
+            if(kbd_buffer.length > 1)
+            {
+                kbd_irq();
+            }
+
+            return kbd_buffer.shift();
+        }
+    };
+
+    function port64_read()
+    {
+        // status port 
+        //dbg_log("port 64 read", LOG_PS2);
+
+        var status_byte = 0x10;
+
+        if(mouse_buffer.length || kbd_buffer.length)
+        {
+            status_byte |= 1;
+        }
+        if(mouse_buffer.length)
+        {
+            status_byte |= 0x20;
+        }
+
+        return status_byte;
+    };
+
+    function port60_write(write_byte)
+    {
+        if(read_command_register)
+        {
+            command_register = write_byte;
+            read_command_register = false;
+
+            dbg_log("Keyboard command register = " + h(command_register), LOG_PS2);
+        }
+        else if(read_output_register)
+        {
+            read_output_register = false;
+            mouse_buffer = [write_byte];
+            mouse_irq();
+        }
+        else if(next_read_sample)
+        {
+            next_read_sample = false;
+            mouse_buffer = [0xFA];
+
+            sample_rate = write_byte;
+            mouse_irq();
+        }
+        else if(next_read_led)
+        {
+            // nope
+            next_read_led = false;
+        }
+        else if(next_is_mouse_command)
+        {
+            dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2); 
+
+            if(!have_mouse)
+            {
+                return;
+            }
+
+            // send ack
+            mouse_buffer = [0xFA];
+
+            if(write_byte === 0xFF)
+            {
+                // reset, send completion code
+                mouse_buffer.push(0xAA, 0x00);
+
+                enable_mouse = true;
+                mouse.enabled = true;
+            }
+            else if(write_byte === 0xF2)
+            {
+                //  MouseID Byte
+                mouse_buffer.push(0, 0);
+            }
+            else if(write_byte === 0xF3)
+            {
+                // sample rate
+                next_read_sample = true;
+            }
+            else if(write_byte === 0xF4)
+            {
+                // enable streaming
+
+                enable_mouse_stream = true;
+                enable_mouse = true;
+
+                mouse.enabled = true;
+            }
+            else if(write_byte === 0xF5)
+            {
+                // disable streaming
+                enable_mouse_stream = true;
+            }
+            else if(write_byte === 0xF6)
+            {
+                // reset defaults 
+                enable_mouse_stream = false;
+                sample_rate = 100;
+
+                // ... resolution, scaling
+            }
+            else if(write_byte === 0xEB)
+            {
+                // request single packet
+                dbg_log("unimplemented request single packet");
+            }
+            else 
+            {
+                dbg_log("new mouse command: " + h(write_byte), LOG_PS2);
+            }
+
+            mouse_irq();
+        }
+        else 
+        {
+            dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2); 
+
+            // send ack
+            kbd_buffer.push(0xFA);
+
+            if(write_byte === 0xFF)
+            {
+                kbd_buffer.push(0xAA, 0x00);
+            }
+            else if(write_byte === 0xF2)
+            {
+                // identify
+                kbd_buffer.push(0xAB, 83);
+            }
+            else if(write_byte === 0xF4)
+            {
+                // enable scanning
+            }
+            else if(write_byte === 0xF5)
+            {
+                // disable scanning
+            }
+            else if(write_byte === 0xED)
+            {
+                next_read_led = true;
+            }
+            
+            kbd_irq();
+        }
+    };
+
+    function port64_write(write_byte)
+    {
+        dbg_log("port 64 write: " + h(write_byte), LOG_PS2);
+
+        if(write_byte === 0xFE)
+        {
+            dbg_log("CPU reboot via PS2");
+            dev.reboot();
+        }
+        else if(write_byte === 0x20)
+        {
+            kbd_buffer.push(command_register);
+            kbd_irq();
+        }
+        else if(write_byte === 0x60)
+        {
+            read_command_register = true;
+        }
+        else if(write_byte === 0xD3)
+        {
+            read_output_register = true;
+        }
+        else if(write_byte === 0xD4)
+        {
+            next_is_mouse_command = true;
+        }
+        else if(write_byte === 0xA9)
+        {
+            // test second ps/2 port
+            kbd_buffer = [0];
+            kbd_irq();
+        }
+        else if(write_byte === 0xAA)
+        {
+            kbd_buffer = [0x55];
+            kbd_irq();
+        }
+        else if(write_byte === 0xAB)
+        {
+            kbd_buffer = [0];
+            kbd_irq();
+        }
+        /*else if(write_byte === 0xAE)
+        {
+            // not sure if right ...
+            kbd_buffer =[];
+        }*/
+        else
+        {
+            dbg_log("port 64: New command byte: " + h(write_byte), LOG_PS2);
+        }
+    };
+}
+
+
+

+ 188 - 0
src/rtc.js

@@ -0,0 +1,188 @@
+/**
+ * RTC (real time clock) and CMOS
+ * @constructor
+ */
+function RTC(dev, diskette_type)
+{
+    var 
+        io = dev.io,
+        pic = dev.pic,
+
+        cmos_index = 0,
+        me = this,
+        
+        // used for cmos entries
+        rtc_time = Date.now(),
+        last_update = rtc_time,
+
+        // used for periodic interrupt
+        next_interrupt,
+
+        periodic_interrupt = false,
+
+        // corresponds to default value for cmos_a
+        periodic_interrupt_time = 1000 / 1024;
+
+
+    var cmos_a = 0x26,
+        cmos_b = 2;
+
+    this.nmi_disabled = 0;
+
+    this.timer = function(time)
+    {
+        if(periodic_interrupt)
+        {
+            while(next_interrupt < time)
+            {
+                next_interrupt += periodic_interrupt_time;
+
+                pic.push_irq(8);
+            }
+        }
+
+        rtc_time += time - last_update;
+        last_update = time;
+    };
+
+    io.register_write(0x70, function(out_byte)
+    {
+        cmos_index = out_byte & 0x7F;
+        me.nmi_disabled = out_byte >> 7;
+    });
+
+    io.register_write(0x71, cmos_write);
+    io.register_read(0x71, cmos_read);
+
+    function encode_time(t)
+    {
+        if(cmos_b & 4)
+        {
+            // binary mode
+            return t;
+        }
+        else
+        {
+            return Math.bcd_pack(t);
+        }
+    }
+    
+
+    // TODO
+    // - interrupt on update
+    // - countdown
+    // - letting bios/os set values
+    // (none of these are used by seabios or the OSes we're 
+    // currently testing)
+    function cmos_read()
+    {
+        var index = cmos_index;
+
+        cmos_index = 0xD;
+
+        switch(index)
+        {
+            case 0:
+                return encode_time(new Date(rtc_time).getUTCSeconds());
+            case 2:
+                return encode_time(new Date(rtc_time).getUTCMinutes());
+            case 4:
+                // TODO: 12 hour mode
+                return encode_time(new Date(rtc_time).getUTCHours());
+            case 7:
+                return encode_time(new Date(rtc_time).getUTCDate());
+            case 8:
+                return encode_time(new Date(rtc_time).getUTCMonth() + 1);
+            case 9:
+                return encode_time(new Date(rtc_time).getUTCFullYear() % 100);
+
+            case 0xA:
+                return cmos_a;
+            case 0xB:
+                //dbg_log("cmos read from index " + h(index));
+                return cmos_b;
+
+            case 0xE:
+                // post info
+                return 0;
+            case 0xC:
+                //dbg_log("cmos read from index " + h(index));
+                // TODO:
+                // It is important to know that upon a IRQ 8, Status Register C
+                // will contain a bitmask telling which interrupt happened.
+                // What is important is that if register C is not read after an
+                // IRQ 8, then the interrupt will not happen again. 
+
+                //dbg_log("cmos Ch read");
+                return 0;
+                // Missing IRQF flag
+                //return cmos_b & 0x70;
+
+            case 0xF:
+                return 0;
+
+            case 0x10:
+                // floppy type
+                return diskette_type;
+
+            case 0x14:
+                // equipment
+                return 0x2D;
+
+            case 0x32:
+                return encode_time(new Date(rtc_time).getUTCFullYear() / 100 | 0);
+
+            case 0x34:
+                return (memory_size - 16 * 1024 * 1024) >> 16 & 0xff;
+            case 0x35:
+                return (memory_size - 16 * 1024 * 1024) >> 24 & 0xff;
+
+            case 0x38:
+                // used by seabios to determine the boot order
+                // bootflag 1, high nibble, lowest priority
+                return 0x30; // hd
+            case 0x3D:
+                // bootflag 2, both nibbles, high and middle priority
+                return 0x21; // floppy first, cd second
+
+            case 0x5B:
+            case 0x5C:
+            case 0x5D:
+                // memory above 4GB
+                return 0;
+        }
+
+        dbg_log("cmos read from index " + h(index), LOG_RTC);
+
+        return 0xFF;
+    }
+
+    function cmos_write(data_byte)
+    {
+        switch(cmos_index)
+        {
+            case 0xA:
+                cmos_a = data_byte & 0x7F;
+                periodic_interrupt_time = 1000 / (32768 >> (cmos_a & 0xF) - 1);
+
+                dbg_log("Periodic interrupt, a=" + h(cmos_a, 2) + " t=" + periodic_interrupt_time , LOG_RTC);
+                break;
+            case 0xB:
+                cmos_b = data_byte;
+                if(cmos_b & 0x40)
+                {
+                    next_interrupt = Date.now();
+                }
+
+                if(cmos_b & 0x20) dbg_log("Unimplemented: alarm interrupt");
+                if(cmos_b & 0x10) dbg_log("Unimplemented: updated interrupt");
+
+                dbg_log("cmos b=" + h(cmos_b, 2), LOG_RTC);
+                break;
+            default:
+                dbg_log("cmos write index " + h(cmos_index) + ": " + h(data_byte), LOG_RTC);
+        }
+
+        periodic_interrupt = (cmos_b & 0x40) === 0x40 && (cmos_a & 0xF) > 0;
+    }
+}

+ 281 - 0
src/string.macro.js

@@ -0,0 +1,281 @@
+"use strict";
+
+/*
+ * string operations
+ *
+ *       cmp  si  di
+ * movs   0    1   1    A4
+ * cmps   1    1   1    A6
+ * stos   0    0   1    AA
+ * lods   0    1   0    AC
+ * scas   1    0   1    AE
+ * ins    0    0   1
+ * outs   0    1   0
+ */
+
+#define string_instruction(s, use_cmp, use_di, use_si, fn, aligned_fn)\
+    var src, dest, data_src, data_dest;\
+    var size = flags & flag_direction ? -(s >> 3) : s >> 3;\
+    var ds, es;\
+    if(use_cmp && !use_si) data_src = reg ## s[reg_eax];\
+    if(use_di) es = get_seg(reg_es), dest = es + regv[reg_vdi];\
+    if(use_si) ds = get_seg_prefix(reg_ds), src = ds + regv[reg_vsi];\
+    if(repeat_string_prefix) {\
+        if(regv[reg_vcx] === 0) return;\
+        var aligned = s > 8 && (!use_di || (dest & (s >> 3) - 1) === 0) && (!use_si || (src & (s >> 3) - 1) === 0);\
+        do {\
+            if(aligned) {\
+                aligned_fn;\
+            } else {\
+                fn;\
+            }\
+            if(use_di) dest += size, regv[reg_vdi] += size;\
+            if(use_si) src += size, regv[reg_vsi] += size;\
+        } while(--regv[reg_vcx] && (!use_cmp || (data_src === data_dest) === repeat_string_type));\
+    } else {\
+        fn;\
+        if(use_di) regv[reg_vdi] += size;\
+        if(use_si) regv[reg_vsi] += size;\
+    }\
+    if(use_cmp) cmp ## s(data_src, data_dest);\
+
+
+function movsb()
+{
+    string_instruction(8, false, true, true, 
+        {
+            safe_write8(dest, safe_read8(src));
+        }, {});
+}
+
+function movsw()
+{
+    string_instruction(16, false, true, true, 
+        {
+            safe_write16(dest, safe_read16(src));
+        }, {
+            var phys_src = translate_address_read(src);
+            var phys_dest = translate_address_write(dest);
+
+            memory.write_aligned16(phys_dest, memory.read_aligned16(phys_src));
+        });
+}
+
+function movsd()
+{
+    string_instruction(32, false, true, true, 
+        {
+            safe_write32(dest, safe_read32s(src));
+        }, {
+            var phys_src = translate_address_read(src);
+            var phys_dest = translate_address_write(dest);
+
+            memory.write_aligned32(phys_dest, memory.read_aligned32(phys_src));
+        });
+}
+
+function cmpsb()
+{
+    string_instruction(8, true, true, true,
+        {
+            data_dest = safe_read8(dest);
+            data_src = safe_read8(src);
+        }, {});
+}
+
+
+function cmpsw()
+{
+    string_instruction(16, true, true, true,
+        {
+            data_dest = safe_read16(dest);
+            data_src = safe_read16(src);
+        }, {
+            data_dest = memory.read_aligned16(translate_address_read(dest));
+            data_src = memory.read_aligned16(translate_address_read(src));
+        });
+}
+
+function cmpsd()
+{
+    string_instruction(32, true, true, true,
+        {
+            data_dest = safe_read32(dest);
+            data_src = safe_read32(src);
+        }, {
+            data_dest = memory.read_aligned32(translate_address_read(dest)) >>> 0;
+            data_src = memory.read_aligned32(translate_address_read(src)) >>> 0;
+        });
+}
+
+
+function stosb()
+{
+    var data = reg8[reg_al];
+
+    string_instruction(8, false, true, false,
+        {
+            safe_write8(dest, data);
+        }, {});
+}
+
+
+function stosw()
+{
+    var data = reg16[reg_ax];
+
+    string_instruction(16, false, true, false,
+        {
+            safe_write16(dest, data);
+        }, {
+            memory.write_aligned16(translate_address_write(dest), data);
+        });
+}
+
+
+function stosd()
+{
+    //dbg_log("stosd " + ((reg32[reg_edi] & 3) ? "mis" : "") + "aligned", LOG_CPU);
+    var data = reg32[reg_eax];
+
+    string_instruction(32, false, true, false,
+        {
+            safe_write32(dest, data);
+        }, {
+            memory.write_aligned32(translate_address_write(dest), data);
+        });
+}
+
+
+function lodsb()
+{
+    string_instruction(8, false, false, true,
+        {
+            reg8[reg_al] = safe_read8(src);
+        }, {});
+}
+
+
+function lodsw()
+{
+    string_instruction(16, false, false, true,
+        {
+            reg16[reg_ax] = safe_read16(src);
+        }, {
+            reg16[reg_ax] = safe_read16(src);
+        });
+}
+
+
+function lodsd()
+{
+    string_instruction(32, false, false, true,
+        {
+            reg32[reg_eax] = safe_read32s(src);
+        }, {
+            reg32[reg_eax] = safe_read32s(src);
+        });
+}
+
+
+function scasb()
+{
+    string_instruction(8, true, true, false,
+        {
+            data_dest = safe_read8(dest);
+        }, {});
+}
+
+
+function scasw()
+{
+    string_instruction(16, true, true, false,
+        {
+            data_dest = safe_read16(dest);
+        }, {
+            data_dest = memory.read_aligned16(translate_address_read(dest));
+        });
+}
+
+function scasd()
+{
+    string_instruction(32, true, true, false,
+        {
+            data_dest = safe_read32(dest);
+        }, {
+            data_dest = memory.read_aligned32(translate_address_read(dest)) >>> 0;
+        });
+}
+
+function insb()
+{
+    var port = reg16[reg_dx];
+
+    string_instruction(8, false, true, false, 
+        {
+            safe_write8(dest, in8(port));
+        }, {
+        });
+}
+
+function insw()
+{
+    var port = reg16[reg_dx];
+
+    string_instruction(8, false, true, false, 
+        {
+            safe_write16(dest, in16(port));
+        }, {
+            var phys_dest = translate_address_write(dest);
+            memory.write_aligned16(phys_dest, in16(port));
+        });
+}
+
+function insd()
+{
+    var port = reg16[reg_dx];
+
+    string_instruction(32, false, true, false, 
+        {
+            safe_write32(dest, in32(port));
+        }, {
+            var phys_dest = translate_address_write(dest);
+            memory.write_aligned32(phys_dest, in32(port));
+        });
+}
+
+function outsb()
+{
+    var port = reg16[reg_dx];
+
+    string_instruction(8, false, false, true, 
+        {
+            out8(port, safe_read8(src));
+        }, {
+            out8(port, safe_read8(src));
+        });
+}
+
+function outsw()
+{
+    var port = reg16[reg_dx];
+
+    string_instruction(16, false, false, true, 
+        {
+            out16(port, safe_read16(src));
+        }, {
+            out16(port, safe_read16(src));
+        });
+}
+
+function outsd()
+{
+    var port = reg16[reg_dx];
+
+    string_instruction(32, false, false, true, 
+        {
+            out32(port, safe_read32s(src));
+        }, {
+            out32(port, safe_read32s(src));
+        });
+}

+ 26 - 0
src/uart.js

@@ -0,0 +1,26 @@
+/** 
+ * No full implementation, just dumping serial output
+ * to console
+ *
+ * @constructor 
+ */
+function UART(dev)
+{
+    var 
+        io = dev.io,
+        line = "";
+
+    io.register_write(0x3F8, function(out_byte) 
+    {
+        if(out_byte === 0x0A)
+        {
+            log(line);
+            dbg_log(line, LOG_SERIAL);
+            line = "";
+        }
+        else
+        {
+            line += String.fromCharCode(out_byte);
+        }
+    });
+}

File diff suppressed because it is too large
+ 4 - 0
src/v86_all.js.map


+ 1023 - 0
src/vga.js

@@ -0,0 +1,1023 @@
+"use strict";
+
+
+/**
+ * @constructor
+ */
+function VGAScreen(dev, adapter)
+{
+    var
+        io = dev.io,
+        memory = dev.memory,
+
+        /** 
+         * TODO: Make this configurable
+         * @const 
+         */
+        SVGA_MEMORY_SIZE = 128 * 64 * 1024,
+
+        /** @const */
+        MAX_XRES = 1280,
+
+        /** @const */
+        MAX_YRES = 1024,
+
+        /** @const */
+        MAX_BPP = 32,
+        
+        /** @type {number} */
+        cursor_address = 0,
+
+        /** @type {number} */
+        cursor_scanline_start = 0xE,
+
+        /** @type {number} */
+        cursor_scanline_end = 0xF,
+
+        /** @type {VGAScreen} */
+        screen = this,
+
+        /**
+         * Number of columns in text mode
+         * @type {number} 
+         */
+        max_cols,
+
+        /** 
+         * Number of rows in text mode
+         * @type {number} 
+         */
+        max_rows,
+
+        /**
+         * Width in pixels in graphical mode
+         * @type {number}
+         */
+        screen_width,
+
+        /**
+         * Height in pixels in graphical mode
+         * @type {number}
+         */
+        screen_height,
+
+        /**
+         * video memory start address
+         * @type {number}
+         */
+        start_address = 0,
+
+        /** @type {boolean} */
+        graphical_mode_is_linear = true,
+
+        /** @type {boolean} */
+        graphical_mode = false,
+
+        /** @type {boolean} */
+        do_complete_redraw = false,
+
+        /* 
+         * VGA palette containing 256 colors for video mode 13 etc.
+         * Needs to be initialised by the BIOS
+         */
+        vga256_palette = new Int32Array(256),
+
+        // 4 times 64k
+        // Could be shared with SVGA memory
+        vga_memory = new Uint8Array(4 * 64 * 1024),
+
+        plane0 = new Uint8Array(vga_memory.buffer, 0 * 64 * 1024, 64 * 1024),
+        plane1 = new Uint8Array(vga_memory.buffer, 1 * 64 * 1024, 64 * 1024),
+        plane2 = new Uint8Array(vga_memory.buffer, 2 * 64 * 1024, 64 * 1024),
+        plane3 = new Uint8Array(vga_memory.buffer, 3 * 64 * 1024, 64 * 1024),
+
+        // VGA latches
+        latch0 = 0,
+        latch1 = 0,
+        latch2 = 0,
+        latch3 = 0,
+
+        
+        svga_memory = new Uint8Array(SVGA_MEMORY_SIZE),
+        svga_enabled = false,
+
+        /** @type {number} */
+        svga_width = 0,
+
+        /** @type {number} */
+        svga_height = 0,
+
+        /** @type {number} */
+        svga_bpp = 0;
+
+
+    function init()
+    {
+        screen.set_size_text(80, 25);
+        screen.update_cursor_scanline();
+
+        memory.mmap_register(0xA0000, 0x20000, false, vga_memory_read, vga_memory_write);
+
+        memory.mmap_register(0xE0000000, SVGA_MEMORY_SIZE, false, svga_memory_read, svga_memory_write);
+    }
+
+    function vga_memory_read(addr)
+    {
+        if(!graphical_mode || graphical_mode_is_linear)
+        {
+            return vga_memory[addr];
+        }
+
+
+        // planar mode
+        addr &= 0xFFFF;
+
+        latch0 = plane0[addr];
+        latch1 = plane1[addr];
+        latch2 = plane2[addr];
+        latch3 = plane3[addr];
+
+        return vga_memory[plane_read << 16 | addr];
+    }
+
+    function vga_memory_write(addr, value)
+    {
+        if(graphical_mode)
+        {
+            if(graphical_mode_is_linear)
+            {
+                vga_memory_write_graphical_linear(addr, value);
+            }
+            else
+            {
+                vga_memory_write_graphical_planar(addr, value);
+            }
+        }
+        else
+        {
+            vga_memory_write_text_mode(addr, value);
+        }
+    }
+
+    function vga_memory_write_graphical_linear(addr, value)
+    {
+        var offset = addr << 2,
+            color = vga256_palette[value];
+
+        adapter.put_pixel_linear(offset | 2, color >> 16 & 0xFF);
+        adapter.put_pixel_linear(offset | 1, color >> 8 & 0xFF);
+        adapter.put_pixel_linear(offset, color & 0xFF);
+
+        vga_memory[addr] = value;
+    }
+
+    function vga_memory_write_graphical_planar(addr, value)
+    {
+        if(addr > 0xFFFF)
+        {
+            return;
+        }
+
+        // TODO:
+        // Replace 4 byte operations with single double word operations
+
+        var write,
+            plane0_byte,
+            plane1_byte,
+            plane2_byte,
+            plane3_byte;
+
+        // not implemented
+        dbg_assert((planar_rotate_reg & 7) === 0);
+        dbg_assert(planar_mode < 3);
+        
+        if(planar_mode === 0)
+        {
+            plane0_byte = plane1_byte = plane2_byte = plane3_byte = value;
+        }
+        else if(planar_mode === 2)
+        {
+            if(plane_write_bm & 1)
+            {
+                write = value & 1 ? 0xFF : 0;
+                plane0_byte = latch0 & ~planar_bitmap | write & planar_bitmap;
+            }
+            if(plane_write_bm & 2)
+            {
+                write = value & 2 ? 0xFF : 0;
+                plane1_byte = latch1 & ~planar_bitmap | write & planar_bitmap;
+            }
+            if(plane_write_bm & 4)
+            {
+                write = value & 4 ? 0xFF : 0;
+                plane2_byte = latch2 & ~planar_bitmap | write & planar_bitmap;
+            }
+            if(plane_write_bm & 8)
+            {
+                write = value & 8 ? 0xFF : 0;
+                plane3_byte = latch3 & ~planar_bitmap | write & planar_bitmap;
+            }
+        }
+
+        if(planar_mode === 0 || planar_mode === 2)
+        {
+            switch(planar_rotate_reg & 0x18)
+            {
+                case 0x08:
+                    plane0_byte &= latch0;
+                    plane1_byte &= latch1;
+                    plane2_byte &= latch2;
+                    plane3_byte &= latch3;
+                    break;
+                case 0x10:
+                    plane0_byte |= latch0;
+                    plane1_byte |= latch1;
+                    plane2_byte |= latch2;
+                    plane3_byte |= latch3;
+                    break;
+                case 0x18:
+                    plane0_byte ^= latch0;
+                    plane1_byte ^= latch1;
+                    plane2_byte ^= latch2;
+                    plane3_byte ^= latch3;
+                    break;
+            }
+
+            if(plane_write_bm & 1)
+            {
+                plane0_byte = latch0 & ~planar_bitmap | plane0_byte & planar_bitmap;
+            }
+            if(plane_write_bm & 2)
+            {
+                plane1_byte = latch1 & ~planar_bitmap | plane1_byte & planar_bitmap;
+            }
+            if(plane_write_bm & 4)
+            {
+                plane2_byte = latch2 & ~planar_bitmap | plane2_byte & planar_bitmap;
+            }
+            if(plane_write_bm & 8)
+            {
+                plane3_byte = latch3 & ~planar_bitmap | plane3_byte & planar_bitmap;
+            }
+        }
+        else if(planar_mode === 1)
+        {
+            plane0_byte = latch0;
+            plane1_byte = latch1;
+            plane2_byte = latch2;
+            plane3_byte = latch3;
+        }
+
+        if(plane_write_bm & 1)
+        {
+            plane0[addr] = plane0_byte;
+        }
+        else
+        {
+            plane0_byte = plane0[addr];
+        }
+        if(plane_write_bm & 2)
+        {
+            plane1[addr] = plane1_byte;
+        }
+        else
+        {
+            plane1_byte = plane1[addr];
+        }
+        if(plane_write_bm & 4)
+        {
+            plane2[addr] = plane2_byte;
+        }
+        else
+        {
+            plane2_byte = plane2[addr];
+        }
+        if(plane_write_bm & 8)
+        {
+            plane3[addr] = plane3_byte;
+        }
+        else
+        {
+            plane3_byte = plane3[addr];
+        }
+
+        if(addr >= (screen_width * screen_height << 3))
+        {
+            return;
+        }
+
+        // Shift these, so that the bits for the color are in 
+        // the correct position in the while loop
+        plane1_byte <<= 1;
+        plane2_byte <<= 2;
+        plane3_byte <<= 3;
+
+        // 8 pixels per byte, we start at high (addr << 3 | 7)
+        // << 2 because we're using put_pixel_linear
+        var offset = (addr << 3 | 7) << 2;
+
+        for(var i = 0; i < 8; i++)
+        {
+            var color_index = 
+                    plane0_byte >> i & 1 |
+                    plane1_byte >> i & 2 |
+                    plane2_byte >> i & 4 |
+                    plane3_byte >> i & 8,
+                color = vga256_palette[dac_map[color_index]];
+
+            adapter.put_pixel_linear(offset | 2, color >> 16);
+            adapter.put_pixel_linear(offset | 1, color >> 8 & 0xFF);
+            adapter.put_pixel_linear(offset, color & 0xFF);
+
+            offset -= 4;
+        }
+    }
+
+    function text_mode_redraw()
+    {
+        var addr = 0x18000 | start_address << 1,
+            chr,
+            color;
+
+        for(var row = 0; row < max_rows; row++)
+        {
+            for(var col = 0; col < max_cols; col++)
+            {
+                chr = vga_memory[addr];
+                color = vga_memory[addr | 1];
+
+                adapter.put_char(row, col, chr, 
+                    vga256_palette[color >> 4 & 0xF], vga256_palette[color & 0xF]);
+
+                addr += 2;
+            }
+        }
+    }
+
+    function graphical_linear_redraw()
+    {
+        // TODO
+    }
+
+    function graphical_planar_redraw()
+    {
+        var addr = 0,
+            color;
+
+        for(var y = 0; y < screen_height; y++)
+        {
+            for(var x = 0; x < screen_width; x += 8)
+            {
+                for(var i = 0; i < 8; i++)
+                {
+                    color = 
+                        plane0[addr] >> i & 1 |
+                        plane1[addr] >> i << 1 & 2 |
+                        plane2[addr] >> i << 2 & 4 |
+                        plane3[addr] >> i << 3 & 8;
+
+                    adapter.put_pixel(x + 7 - i, y, vga256_palette[dac_map[color]]);
+                }
+
+                addr++;
+            }
+        }
+    }
+
+    function vga_memory_write_text_mode(addr, value)
+    {
+        if(addr < 0x18000)
+        {
+            return;
+        }
+
+        var memory_start = (addr - 0x18000 >> 1) - start_address,
+            row = memory_start / max_cols | 0,
+            col = memory_start % max_cols,
+            chr,
+            color;
+
+        if(addr & 1)
+        {
+            color = value;
+            chr = vga_memory[addr & ~1];
+        }
+        else
+        {
+            chr = value;
+            color = vga_memory[addr | 1];
+        }
+
+        adapter.put_char(row, col, chr, 
+                vga256_palette[color >> 4 & 0xF], vga256_palette[color & 0xF]);
+
+        vga_memory[addr] = value;
+    }
+
+    function update_cursor()
+    {
+        var row = (cursor_address - start_address) / max_cols | 0,
+            col = (cursor_address - start_address) % max_cols;
+
+        row = Math.min(max_rows - 1, row);
+
+        adapter.update_cursor(row, col);
+    }
+
+
+    function svga_memory_read(addr)
+    {
+        return svga_memory[addr];
+    }
+
+    function svga_memory_write(addr, value)
+    {
+        svga_memory[addr] = value;
+
+        switch(svga_bpp)
+        {
+            case 32:
+                if((addr & 3) === 3)
+                {
+                    // 4th byte is meaningless
+                    return;
+                }
+
+                adapter.put_pixel_linear(addr, value);
+                break;
+
+            case 24:
+                addr = addr * (4/3) | 0;
+                adapter.put_pixel_linear(addr, value);
+                break;
+
+            case 16:
+                if(addr & 1)
+                {
+                    var prev = svga_memory[addr ^ 1],
+                        green = prev >> 5 & 0x07 | value << 3 & 0x38,
+                        blue = value >> 3 & 0x1F;
+
+                    blue = blue * 0xFF / 0x1F | 0;
+                    green = green * 0xFF / 0x3F | 0;
+
+                    addr <<= 1;
+
+                    adapter.put_pixel_linear(addr - 1, green);
+                    adapter.put_pixel_linear(addr - 2, blue);
+                }
+                else
+                {
+                    var red = value & 0x1F;
+                    red = red * 0xFF / 0x1F | 0;
+
+                    adapter.put_pixel_linear((addr << 1) + 2, red);
+                }
+                break;
+
+            default:
+                if(DEBUG)
+                {
+                    throw "SVGA: Unsupported BPP: " + svga_bpp;
+                }
+        }
+    }
+
+    this.timer = function(time)
+    {
+        if(do_complete_redraw)
+        {
+            do_complete_redraw = false;
+
+            if(graphical_mode)
+            {
+                if(graphical_mode_is_linear)
+                {
+                    graphical_linear_redraw();
+                }
+                else 
+                {
+                    graphical_planar_redraw();
+                }
+            }
+            else
+            {
+                text_mode_redraw();
+            }
+        }
+
+        if(graphical_mode || svga_enabled)
+        {
+            adapter.timer_graphical();
+        }
+        else
+        {
+            adapter.timer_text();
+        }
+    };
+
+    /**
+     * @param {number} cols_count 
+     * @param {number} rows_count 
+     */
+    this.set_size_text = function(cols_count, rows_count)
+    {
+        max_cols = cols_count;
+        max_rows = rows_count;
+
+        adapter.set_size_text(cols_count, rows_count);
+    };
+
+    this.set_size_graphical = function(width, height)
+    {
+        adapter.set_size_graphical(width, height);
+    }
+
+    this.update_cursor_scanline = function()
+    {
+        adapter.update_cursor_scanline(cursor_scanline_start, cursor_scanline_end);
+    };
+
+    this.clear_screen = function()
+    {
+        adapter.clear_screen();
+    };
+
+    this.set_video_mode = function(mode)
+    {
+        var is_graphical = false;
+
+        switch(mode)
+        {
+            case 0x03:
+                this.set_size_text(80, 25);
+                break;
+            case 0x10:
+                this.set_size_graphical(640, 350);
+                screen_width = 640;
+                screen_height = 350;
+                is_graphical = true;
+                graphical_mode_is_linear = false;
+                break;
+            case 0x12:
+                this.set_size_graphical(640, 480);
+                screen_width = 640;
+                screen_height = 480;
+                is_graphical = true;
+                graphical_mode_is_linear = false;
+                break;
+            case 0x13:
+                this.set_size_graphical(320, 200);
+                screen_width = 320;
+                screen_height = 200;
+                is_graphical = true;
+                graphical_mode_is_linear = true;
+                break;
+            default:
+        }
+
+        adapter.set_mode(is_graphical);
+
+        graphical_mode = is_graphical;
+
+        dbg_log("Current video mode: " + h(mode), LOG_VGA);
+    };
+
+    this.destroy = function()
+    {
+
+    };
+
+    var index_crtc = 0,
+        index_dac = 0,
+        index_attribute = 0;
+
+
+    // index for setting colors through port 3C9h
+    var dac_color_index = 0;
+
+    function port3C7_write(index)
+    {
+        // index for reading the DAC
+        dbg_log("3C7 write: " + h(index), LOG_VGA);
+    };
+    io.register_write(0x3C7, port3C7_write);
+
+    function port3C8_write(index)
+    {
+        dac_color_index = index * 3;
+    };
+    io.register_write(0x3C8, port3C8_write);
+
+
+    function port3C9_write(color_byte)
+    {
+        var index = dac_color_index / 3 | 0,
+            offset = dac_color_index % 3,
+            color = vga256_palette[index];
+
+        color_byte = color_byte << 2 & 0xFF | 3;
+
+        if(offset === 0)
+        {
+            color = color & ~0xFF0000 | color_byte << 16;
+        }
+        else if(offset === 1)
+        {
+            color = color & ~0xFF00 | color_byte << 8;
+        }
+        else
+        {
+            color = color & ~0xFF | color_byte;
+            dbg_log("dac set color, index=" + h(index) + " value=" + h(color), LOG_VGA);
+        }
+
+        vga256_palette[index] = color;
+
+        dac_color_index++;
+
+        do_complete_redraw = true;
+    }
+    io.register_write(0x3C9, port3C9_write);
+
+    function port3D4_write(register)
+    {
+        index_crtc = register;
+    };
+    io.register_write(0x3D4, port3D4_write);
+
+    function port3D5_write(value)
+    {
+        switch(index_crtc)
+        {
+            case 0x2:
+                screen.set_size_text(value, 25);
+                break;
+            case 0xA:
+                cursor_scanline_start = value;
+                screen.update_cursor_scanline();
+                break;
+            case 0xB:
+                cursor_scanline_end = value;
+                screen.update_cursor_scanline();
+                break;
+            case 0xC:
+                start_address = start_address & 0xff | value << 8;
+                do_complete_redraw = true;
+                break;
+            case 0xD:
+                start_address = start_address & 0xff00 | value;
+                do_complete_redraw = true;
+                //dbg_log("start addr: " + h(start_address, 4), LOG_VGA);
+                break;
+            case 0xE:
+                cursor_address = cursor_address & 0xFF | value << 8;
+                update_cursor();
+                break;
+            case 0xF:
+                cursor_address = cursor_address & 0xFF00 | value;
+                update_cursor();
+                break;
+            default:
+                dbg_log("3D5 / CRTC write " + h(index_crtc) + ": " + h(value), LOG_VGA);
+        }
+
+    };
+    io.register_write(0x3D5, port3D5_write);
+
+    function port3D5_read()
+    {
+        if(index_crtc === 0xA)
+        {
+            return cursor_scanline_start;
+        }
+        else if(index_crtc === 0xB)
+        {
+            return cursor_scanline_end;
+        }
+        else if(index_crtc === 0xE)
+        {
+            return cursor_address >> 8;
+        }
+        else if(index_crtc === 0xF)
+        {
+            return cursor_address & 0xFF;
+        }
+
+        dbg_log("3D5 read " + h(index_crtc), LOG_VGA);
+        return 0;
+    };
+    io.register_read(0x3D5, port3D5_read);
+
+    var miscellaneous_output_register = 0xff;
+
+    function port3CC_read()
+    {
+        return miscellaneous_output_register;
+    }
+    io.register_read(0x3CC, port3CC_read);
+
+    function port3C2_write(value)
+    {
+        dbg_log("3C2 / miscellaneous output register = " + h(value), LOG_VGA);
+        miscellaneous_output_register = value;
+
+        // cheat way to figure out which video mode is indended to be used
+        switch_video_mode(value);
+    }
+    io.register_write(0x3C2, port3C2_write);
+
+
+    function port3DA_read()
+    {
+        // status register
+        attribute_controller_index = -1;
+        return 0xff;
+    }
+    io.register_read(0x3DA, port3DA_read);
+
+
+    var attribute_controller_index = -1;
+
+    function port3C1_read()
+    {
+        attribute_controller_index = -1;
+
+        dbg_log("3C1 / attribute controller read " + h(attribute_controller_index), LOG_VGA);
+        return -1;
+    }
+    io.register_read(0x3C1, port3C1_read);
+
+    var dac_map = new Uint8Array(0x10);
+
+    function port3C0_write(value)
+    {
+        if(attribute_controller_index === -1)
+        {
+            attribute_controller_index = value;
+        }
+        else
+        {
+            if(attribute_controller_index < 0x10)
+            {
+                dac_map[attribute_controller_index] = value;
+            }
+            else
+            switch(attribute_controller_index)
+            {
+                default:
+                    dbg_log("3C0 / attribute controller write " + h(attribute_controller_index) + ": " + h(value), LOG_VGA);
+            }
+
+            attribute_controller_index = -1;
+
+        }
+    }
+    io.register_write(0x3C0, port3C0_write);
+
+    function port3C0_read()
+    {
+        dbg_log("3C0 read", LOG_VGA);
+        var result = attribute_controller_index;
+        attribute_controller_index = -1;
+        return result;
+    }
+    io.register_read(0x3C0, port3C0_read);
+
+    
+    var sequencer_index = -1;
+
+    function port3C4_write(value)
+    {
+        sequencer_index = value;
+    }
+    io.register_write(0x3C4, port3C4_write);
+
+
+    var 
+        // bitmap of planes 0-3
+        plane_write_bm = 0xF,
+        sequencer_memory_mode = 0
+        ; 
+
+    function port3C5_write(value)
+    {
+        switch(sequencer_index)
+        {
+            case 0x02:
+                //dbg_log("plane write mask: " + h(value), LOG_VGA);
+                plane_write_bm = value;
+                break;
+            case 0x04:
+                dbg_log("sequencer memory mode: " + h(value), LOG_VGA);
+                sequencer_memory_mode = value;
+                break;
+            default:
+                dbg_log("3C5 / sequencer write " + h(sequencer_index) + ": " + h(value), LOG_VGA);
+        }
+    }
+    io.register_write(0x3C5, port3C5_write);
+
+
+    function port3C5_read()
+    {
+        switch(sequencer_index)
+        {
+            case 0x06:
+                return 0x12;
+                break;
+            default:
+                dbg_log("3C5 / sequencer read " + h(sequencer_index), LOG_VGA);
+        }
+    }
+    io.register_read(0x3C5, port3C5_read);
+
+
+    var graphics_index = -1;
+
+    function port3CE_write(value)
+    {
+        graphics_index = value;
+    }
+    io.register_write(0x3CE, port3CE_write);
+
+    var plane_read = 0, // value 0-3, which plane to read
+        planar_mode = 0,
+        planar_rotate_reg = 0,
+        planar_bitmap = 0xFF;
+
+    function port3CF_write(value)
+    {
+        switch(graphics_index)
+        {
+            // TODO: Set/Reset bit
+            //case 0:
+            //case 1:
+                //break;
+            case 3:
+                planar_rotate_reg = value;
+                dbg_log("plane rotate: " + h(value), LOG_VGA);
+                break;
+            case 4:
+                plane_read = value;
+                dbg_assert(value < 4);
+                dbg_log("plane read: " + h(value), LOG_VGA);
+                break;
+            case 5:
+                planar_mode = value;
+                dbg_log("planar mode: " + h(value), LOG_VGA);
+                break;
+            case 8:
+                planar_bitmap = value;
+                //dbg_log("planar bitmap: " + h(value), LOG_VGA);
+                break;
+            default:
+                dbg_log("3CF / graphics write " + h(graphics_index) + ": " + h(value), LOG_VGA);
+        }
+    }
+    io.register_write(0x3CF, port3CF_write);
+
+
+    function switch_video_mode(mar)
+    {
+        // Cheap way to figure this out, using the Miscellaneous Output Register 
+        // See: http://wiki.osdev.org/VGA_Hardware#List_of_register_settings
+
+        if(mar === 0x67)
+        {
+            screen.set_video_mode(0x3);
+        }
+        else if(mar === 0xE3)
+        {
+            // also mode X
+            screen.set_video_mode(0x12);
+        }
+        else if(mar === 0x63)
+        {
+            screen.set_video_mode(0x13);
+        }
+        else if(mar === 0xA3)
+        {
+            screen.set_video_mode(0x10);
+        }
+        else
+        {
+            dbg_log("Unkown MAR value: " + h(mar, 2) + ", going back to text mode", LOG_VGA);
+            screen.set_video_mode(0x3);
+        }
+    }
+
+
+    // Bochs VBE Extensions
+    // http://wiki.osdev.org/Bochs_VBE_Extensions
+    var dispi_index = -1,
+        dispi_value = -1,
+        read_index = true;
+
+    function port1CE_write(value)
+    {
+        dispi_index = value;
+        read_index = true;
+    }
+    io.register_write(0x1CE, port1CE_write);
+
+    function port1CF_write(value)
+    {
+        if(read_index)
+        {
+            dispi_index |= value << 8;
+            read_index = false
+            return;
+        }
+
+        read_index = true;
+
+        dispi_value = value;
+        switch(dispi_index)
+        {
+            default:
+                dbg_log("1CF / dispi write low " + h(dispi_index) + ": " + h(value), LOG_VGA);
+        }
+    }
+    io.register_write(0x1CF, port1CF_write);
+
+    function port1D0_write(value)
+    {
+        dbg_log("1D0 / dispi write high " + h(dispi_index) + ": " + h(value), LOG_VGA);
+        dispi_value |= value << 8;
+
+        switch(dispi_index)
+        {
+            case 1:
+                svga_width = dispi_value;
+                break;
+            case 2:
+                svga_height = dispi_value;
+                break;
+            case 3:
+                svga_bpp = dispi_value;
+                break;
+            case 4:
+                // enable, options
+                svga_enabled = (dispi_value & 1) === 1;
+                break;
+            default:
+        }
+
+        dbg_log("SVGA: enabled=" + svga_enabled + ", " + svga_width + "x" + svga_height + "x" + svga_bpp, LOG_VGA);
+
+        if(svga_enabled)
+        {
+            screen.set_size_graphical(svga_width, svga_height);
+            adapter.set_mode(true);
+        }
+    }
+    io.register_write(0x1D0, port1D0_write);
+
+
+    function port1CF_read()
+    {
+        switch(dispi_index)
+        {
+            case 0:
+                // id
+                return 0xC0;
+            case 1:
+                return MAX_XRES;
+            case 2:
+                return MAX_YRES;
+            case 3:
+                return MAX_BPP;
+            case 0x0A:
+                // memory size in 64 kilobyte banks
+                return SVGA_MEMORY_SIZE / 64 / 1024;
+            default:
+        }
+        dbg_log("1CF / dispi read low " + h(dispi_index), LOG_VGA);
+    }
+    io.register_read(0x1CF, port1CF_read);
+
+    function port1D0_read()
+    {
+        switch(dispi_index)
+        {
+            case 0:
+                // id
+                return 0xB0;
+            case 1:
+                return MAX_XRES >> 8;
+            case 2:
+                return MAX_YRES >> 8;
+            case 3:
+                return MAX_BPP >> 8;
+            case 0x0A:
+                return SVGA_MEMORY_SIZE / 64 / 1024 >> 8;
+            default:
+        }
+        dbg_log("1D0 / dispi read high " + h(dispi_index), LOG_VGA);
+    }
+    io.register_read(0x1D0, port1D0_read);
+
+    init();
+}
+

+ 8 - 0
tests/perf/build.sh

@@ -0,0 +1,8 @@
+nasm test.asm -o test.bin
+
+echo "var file = [" > test-asm.js
+cat test.bin|xxd -i >> test-asm.js
+echo "]" >> test-asm.js
+
+echo "done."
+

+ 59 - 0
tests/perf/runtest.js

@@ -0,0 +1,59 @@
+// Run with d8, not node
+
+var path = "../../src/";
+
+load(path + "const.js");
+load(path + "io.js");
+load(path + "cpu.js");
+load(path + "main.js");
+load(path + "disk.js");
+load(path + "pci.js");
+load(path + "test_helpers.js");
+load(path + "memory.js");
+load(path + "dma.js");
+load(path + "pit.js");
+load(path + "pic.js");
+
+
+if(typeof console === "undefined")
+{
+    var console = {
+        log: print,
+    }
+}
+
+var log = print;
+
+DEBUG = false;
+
+var cpu = new v86();
+
+cpu.init({});
+
+// defines file
+load("test-asm.js");
+
+for(var i = 0; i < file.length; i++)
+{
+    cpu.memory.mem8[i] = file[i];
+}
+
+function run()
+{
+    for(var i = 0; i < count; i++)
+    {
+        cpu.cycle();
+    }
+}
+
+var count = 1e7;
+
+var start = Date.now();
+
+run();
+
+var end = Date.now(),
+    duration = (end - start) / 1e3;
+
+console.log("Finished in " + duration + " seconds, " + (count / duration / 1e6).toFixed(2) + " mips");
+

+ 12 - 0
tests/perf/test-asm.js

@@ -0,0 +1,12 @@
+var file = [
+  0x66, 0xbb, 0x00, 0x00, 0x10, 0x00, 0x66, 0xb8, 0xff, 0xff, 0xff, 0xff,
+  0xb8, 0xff, 0xff, 0xb0, 0xff, 0x67, 0x8a, 0x03, 0x66, 0x0f, 0xbf, 0xc0,
+  0x0f, 0xbe, 0xc0, 0x66, 0x0f, 0xb6, 0xc0, 0x66, 0x91, 0x67, 0x86, 0x03,
+  0x67, 0x87, 0x03, 0x67, 0xfe, 0x03, 0x66, 0x67, 0xff, 0x03, 0x40, 0xfe,
+  0xc0, 0x67, 0xfe, 0x0b, 0x66, 0x67, 0xff, 0x0b, 0x48, 0xfe, 0xc8, 0x66,
+  0xf7, 0xe1, 0xf7, 0xe1, 0xf6, 0xe1, 0x66, 0xf7, 0xe9, 0xf7, 0xe9, 0xf6,
+  0xe9, 0x66, 0x6b, 0xc0, 0x10, 0x66, 0x69, 0xc0, 0x00, 0x00, 0x00, 0x01,
+  0x6b, 0xc0, 0x10, 0x66, 0x0f, 0xaf, 0xc1, 0x0f, 0xaf, 0xc1, 0x66, 0x50,
+  0x50, 0x5f, 0x66, 0x5f, 0xfb, 0xfa, 0xfd, 0xfc, 0x60, 0x61, 0x66, 0x60,
+  0x66, 0x61, 0x9c, 0x9d, 0x66, 0x9c, 0x66, 0x9d, 0xeb, 0x8a
+]

+ 199 - 0
tests/perf/test.asm

@@ -0,0 +1,199 @@
+;[BITS 32]
+
+start:
+mov ebx, 100000h
+
+%if 0
+
+add al, [4*ebx+100h]
+add eax, ecx
+add [4*ebx+1000h], cl
+add al, 10h
+add eax, 1000h
+add cl, 10h
+add ecx, 1000h
+
+or  al, [4*ebx+100h]
+or  eax, ecx
+or  [4*ebx+1000h], cl
+or  al, 10h
+or  eax, 1000h
+or  cl, 10h
+or  ecx, 1000h
+
+adc al, [4*ebx+100h]
+adc eax, ecx
+adc [4*ebx+1000h], cl
+adc al, 10h
+adc eax, 1000h
+adc cl, 10h
+adc ecx, 1000h
+
+sbb al, [4*ebx+100h]
+sbb eax, ecx
+sbb [4*ebx+1000h], cl
+sbb al, 10h
+sbb eax, 1000h
+sbb cl, 10h
+sbb ecx, 1000h
+
+and al, [4*ebx+100h]
+and eax, ecx
+and [4*ebx+1000h], cl
+and al, 10h
+and eax, 1000h
+and cl, 10h
+and ecx, 1000h
+
+sub al, [4*ebx+100h]
+sub eax, ecx
+sub [4*ebx+1000h], cl
+sub al, 10h
+sub eax, 1000h
+sub cl, 10h
+sub ecx, 1000h
+
+xor al, [4*ebx+100h]
+xor eax, ecx
+xor [4*ebx+1000h], cl
+xor al, 10h
+xor eax, 1000h
+xor cl, 10h
+xor ecx, 1000h
+
+cmp al, [4*ebx+100h]
+cmp eax, ecx
+cmp [4*ebx+1000h], cl
+cmp al, 10h
+cmp eax, 1000h
+cmp cl, 10h
+cmp ecx, 1000h
+
+
+test al, [4*ebx+100h]
+test eax, ecx
+test [4*ebx+1000h], cl
+test al, 10h
+test eax, 1000h
+%endif
+
+
+%if 0
+add eax, 12345671h
+
+jo $+2
+jno $+2
+jp $+2
+jnp $+2
+jc $+2
+jnc $+2
+js $+2
+jns $+2
+jz $+2
+jnz $+2
+jl $+2
+jnl $+2
+jbe $+2
+jnbe $+2
+jle $+2
+jnle $+2
+%endif
+
+%if 0
+sal eax, 12
+sal cx, 7
+sal dl, 1
+sal dh, 0
+
+shl eax, 12
+shl cx, 7
+shl dl, 1
+shl dh, 0
+
+shr eax, 12
+shr cx, 7
+shr dl, 1
+shr dh, 0
+
+ror eax, 12
+ror cx, 7
+ror dl, 1
+ror dh, 0
+
+rol eax, 12
+rol cx, 7
+rol dl, 1
+rol dh, 0
+
+rcr eax, 12
+rcr cx, 7
+rcr dl, 1
+rcr dh, 0
+
+rcl eax, 12
+rcl cx, 7
+rcl dl, 1
+rcl dh, 0
+
+
+%endif
+
+mov eax, -1
+mov ax, -1
+mov al, -1
+mov al, [ebx]
+
+movsx eax, ax
+movsx ax, al
+movzx eax, al
+
+xchg eax, ecx
+xchg [ebx], al
+xchg [ebx], ax
+
+inc byte [ebx]
+inc dword [ebx]
+inc ax
+inc al
+dec byte [ebx]
+dec dword [ebx]
+dec ax
+dec al
+
+mul ecx
+mul cx
+mul cl
+
+imul ecx
+imul cx
+imul cl
+
+
+imul eax, 10h
+imul eax, 1000000h
+imul ax, 10h
+imul eax, ecx
+imul ax, cx
+
+push eax
+push ax
+pop di
+pop edi
+
+sti
+cli
+std
+cld
+
+pusha
+popa
+pushad
+popad
+
+pushf
+popf
+pushfd
+popfd
+
+
+jmp start

BIN
tests/perf/test.bin


+ 16 - 0
tests/qemu/LICENSE

@@ -0,0 +1,16 @@
+x86 CPU test
+
+Copyright (c) 2003 Fabrice Bellard
+
+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, see <http://www.gnu.org/licenses/>.

+ 17 - 0
tests/qemu/Makefile

@@ -0,0 +1,17 @@
+
+CC=gcc
+CC_I386=$(CC) -m32
+QEMU_INCLUDES += -I../..
+CFLAGS=-Wall -O2 -g -fno-strict-aliasing -static
+LDFLAGS=
+
+
+# i386/x86_64 emulation test (test various opcodes) */
+test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \
+           test-i386.h test-i386-shift.h test-i386-muldiv.h
+	$(CC_I386) $(QEMU_INCLUDES) $(CFLAGS) $(LDFLAGS) -o $@ \
+              $(<D)/test-i386.c $(<D)/test-i386-code16.S $(<D)/test-i386-vm86.S -lm
+
+
+clean:
+	rm -f test-i386

+ 8 - 0
tests/qemu/Reame

@@ -0,0 +1,8 @@
+How to run:
+
+- Build test-i386
+- Run test-i386 locally (requires Linux and an x86 machine)
+- Put the both on a disk (for instance, a cd image built with buildroot)
+- Run test-i386 in the emulator
+- Compare the result
+

+ 58 - 0
tests/qemu/compiler.h

@@ -0,0 +1,58 @@
+/* public domain */
+
+#ifndef COMPILER_H
+#define COMPILER_H
+
+#include "config-host.h"
+
+/*----------------------------------------------------------------------------
+| The macro QEMU_GNUC_PREREQ tests for minimum version of the GNU C compiler.
+| The code is a copy of SOFTFLOAT_GNUC_PREREQ, see softfloat-macros.h.
+*----------------------------------------------------------------------------*/
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define QEMU_GNUC_PREREQ(maj, min) \
+         ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define QEMU_GNUC_PREREQ(maj, min) 0
+#endif
+
+#define QEMU_NORETURN __attribute__ ((__noreturn__))
+
+#if QEMU_GNUC_PREREQ(3, 4)
+#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define QEMU_WARN_UNUSED_RESULT
+#endif
+
+#if defined(_WIN32)
+# define QEMU_PACKED __attribute__((gcc_struct, packed))
+#else
+# define QEMU_PACKED __attribute__((packed))
+#endif
+
+#define cat(x,y) x ## y
+#define cat2(x,y) cat(x,y)
+#define QEMU_BUILD_BUG_ON(x) \
+    typedef char cat2(qemu_build_bug_on__,__LINE__)[(x)?-1:1];
+
+#if defined __GNUC__
+# if !QEMU_GNUC_PREREQ(4, 4)
+   /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */
+#  define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2)))
+#  define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m)))
+# else
+   /* Use gnu_printf when supported (qemu uses standard format strings). */
+#  define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2)))
+#  define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m)))
+#  if defined(_WIN32)
+    /* Map __printf__ to __gnu_printf__ because we want standard format strings
+     * even when MinGW or GLib include files use __printf__. */
+#   define __printf__ __gnu_printf__
+#  endif
+# endif
+#else
+#define GCC_ATTR /**/
+#define GCC_FMT_ATTR(n, m)
+#endif
+
+#endif /* COMPILER_H */

+ 66 - 0
tests/qemu/config-host.h

@@ -0,0 +1,66 @@
+/* Automatically generated by create_config - do not modify */
+#define CONFIG_QEMU_CONFDIR "/usr/local/etc/qemu"
+#define CONFIG_QEMU_DATADIR "/usr/local/share/qemu"
+#define CONFIG_QEMU_DOCDIR "/usr/local/share/doc/qemu"
+#define CONFIG_QEMU_LOCALSTATEDIR "/usr/local/var"
+#define CONFIG_QEMU_HELPERDIR "/usr/local/libexec"
+#define HOST_I386 1
+#define CONFIG_POSIX 1
+#define CONFIG_LINUX 1
+#define CONFIG_SLIRP 1
+#define CONFIG_SMBD_COMMAND "/usr/sbin/smbd"
+#define CONFIG_AC97 1
+#define CONFIG_ES1370 1
+#define CONFIG_SB16 1
+#define CONFIG_HDA 1
+#define CONFIG_AUDIO_DRIVERS \
+    &oss_audio_driver,\
+
+#define CONFIG_OSS 1
+#define CONFIG_BDRV_WHITELIST \
+    NULL
+#define CONFIG_VNC 1
+#define CONFIG_VNC_TLS 1
+#define CONFIG_VNC_PNG 1
+#define CONFIG_FNMATCH 1
+#define QEMU_VERSION "1.3.1"
+#define QEMU_PKGVERSION ""
+#define CONFIG_SDL 1
+#define CONFIG_CURSES 1
+#define CONFIG_ATFILE 1
+#define CONFIG_UTIMENSAT 1
+#define CONFIG_PIPE2 1
+#define CONFIG_ACCEPT4 1
+#define CONFIG_SPLICE 1
+#define CONFIG_EVENTFD 1
+#define CONFIG_FALLOCATE 1
+#define CONFIG_SYNC_FILE_RANGE 1
+#define CONFIG_FIEMAP 1
+#define CONFIG_DUP3 1
+#define CONFIG_EPOLL 1
+#define CONFIG_EPOLL_CREATE1 1
+#define CONFIG_EPOLL_PWAIT 1
+#define CONFIG_INOTIFY 1
+#define CONFIG_INOTIFY1 1
+#define CONFIG_BYTESWAP_H 1
+#define CONFIG_CURL 1
+#define CONFIG_ATTR 1
+#define CONFIG_IOVEC 1
+#define CONFIG_PREADV 1
+#define CONFIG_SIGNALFD 1
+#define CONFIG_FDATASYNC 1
+#define CONFIG_MADVISE 1
+#define CONFIG_POSIX_MADVISE 1
+#define CONFIG_SIGEV_THREAD_ID 1
+#define CONFIG_SMARTCARD 1
+#define CONFIG_OPENGL 1
+#define CONFIG_UNAME_RELEASE ""
+#define CONFIG_ZERO_MALLOC 1
+#define CONFIG_UCONTEXT_COROUTINE 1
+#define CONFIG_OPEN_BY_HANDLE 1
+#define CONFIG_LINUX_MAGIC_H 1
+#define CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET 1
+#define CONFIG_HAS_ENVIRON 1
+#define CONFIG_TRACE_NOP 1
+#define CONFIG_TRACE_FILE trace
+#define CONFIG_TRACE_DEFAULT 1

+ 79 - 0
tests/qemu/test-i386-code16.S

@@ -0,0 +1,79 @@
+        .code16
+        .globl code16_start
+        .globl code16_end
+
+CS_SEG = 0xf
+
+code16_start:
+
+        .globl code16_func1
+
+        /* basic test */
+code16_func1 = . - code16_start
+        mov $1, %eax
+        data32 lret
+
+/* test push/pop in 16 bit mode */
+        .globl code16_func2
+code16_func2 = . - code16_start
+        xor %eax, %eax
+        mov $0x12345678, %ebx
+        movl %esp, %ecx
+        push %bx
+        subl %esp, %ecx
+        pop %ax
+        data32 lret
+
+/* test various jmp opcodes */
+        .globl code16_func3
+code16_func3 = . - code16_start
+        jmp 1f
+        nop
+1:
+        mov $4, %eax
+        mov $0x12345678, %ebx
+        xor %bx, %bx
+        jz 2f
+        add $2, %ax
+2:
+
+        call myfunc
+
+        lcall $CS_SEG, $(myfunc2 - code16_start)
+
+        ljmp $CS_SEG, $(myjmp1 - code16_start)
+myjmp1_next:
+
+        cs lcall *myfunc2_addr - code16_start
+
+        cs ljmp *myjmp2_addr - code16_start
+myjmp2_next:
+
+        data32 lret
+
+myfunc2_addr:
+        .short myfunc2 - code16_start
+        .short CS_SEG
+
+myjmp2_addr:
+        .short myjmp2 - code16_start
+        .short CS_SEG
+
+myjmp1:
+        add $8, %ax
+        jmp myjmp1_next
+
+myjmp2:
+        add $16, %ax
+        jmp myjmp2_next
+
+myfunc:
+        add $1, %ax
+        ret
+
+myfunc2:
+        add $4, %ax
+        lret
+
+
+code16_end:

+ 76 - 0
tests/qemu/test-i386-muldiv.h

@@ -0,0 +1,76 @@
+
+void glue(glue(test_, OP), b)(long op0, long op1)
+{
+    long res, s1, s0, flags;
+    s0 = op0;
+    s1 = op1;
+    res = s0;
+    flags = 0;
+    asm ("push %4\n\t"
+         "popf\n\t"
+         stringify(OP)"b %b2\n\t"
+         "pushf\n\t"
+         "pop %1\n\t"
+         : "=a" (res), "=g" (flags)
+         : "q" (s1), "0" (res), "1" (flags));
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+           stringify(OP) "b", s0, s1, res, flags & CC_MASK);
+}
+
+void glue(glue(test_, OP), w)(long op0h, long op0, long op1)
+{
+    long res, s1, flags, resh;
+    s1 = op1;
+    resh = op0h;
+    res = op0;
+    flags = 0;
+    asm ("push %5\n\t"
+         "popf\n\t"
+         stringify(OP) "w %w3\n\t"
+         "pushf\n\t"
+         "pop %1\n\t"
+         : "=a" (res), "=g" (flags), "=d" (resh)
+         : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+    printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+           stringify(OP) "w", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+
+void glue(glue(test_, OP), l)(long op0h, long op0, long op1)
+{
+    long res, s1, flags, resh;
+    s1 = op1;
+    resh = op0h;
+    res = op0;
+    flags = 0;
+    asm ("push %5\n\t"
+         "popf\n\t"
+         stringify(OP) "l %k3\n\t"
+         "pushf\n\t"
+         "pop %1\n\t"
+         : "=a" (res), "=g" (flags), "=d" (resh)
+         : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+    printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+           stringify(OP) "l", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+
+#if defined(__x86_64__)
+void glue(glue(test_, OP), q)(long op0h, long op0, long op1)
+{
+    long res, s1, flags, resh;
+    s1 = op1;
+    resh = op0h;
+    res = op0;
+    flags = 0;
+    asm ("push %5\n\t"
+         "popf\n\t"
+         stringify(OP) "q %3\n\t"
+         "pushf\n\t"
+         "pop %1\n\t"
+         : "=a" (res), "=g" (flags), "=d" (resh)
+         : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+    printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+           stringify(OP) "q", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+#endif
+
+#undef OP

+ 185 - 0
tests/qemu/test-i386-shift.h

@@ -0,0 +1,185 @@
+
+#define exec_op glue(exec_, OP)
+#define exec_opq glue(glue(exec_, OP), q)
+#define exec_opl glue(glue(exec_, OP), l)
+#define exec_opw glue(glue(exec_, OP), w)
+#define exec_opb glue(glue(exec_, OP), b)
+
+#ifndef OP_SHIFTD
+
+#ifdef OP_NOBYTE
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+    asm ("push %4\n\t"\
+         "popf\n\t"\
+         stringify(OP) size " %" rsize "2, %" rsize "0\n\t" \
+         "pushf\n\t"\
+         "pop %1\n\t"\
+         : "=g" (res), "=g" (flags)\
+         : "r" (s1), "0" (res), "1" (flags));
+#else
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+    asm ("push %4\n\t"\
+         "popf\n\t"\
+         stringify(OP) size " %%cl, %" rsize "0\n\t" \
+         "pushf\n\t"\
+         "pop %1\n\t"\
+         : "=q" (res), "=g" (flags)\
+         : "c" (s1), "0" (res), "1" (flags));
+#endif
+
+#if defined(__x86_64__)
+void exec_opq(long s2, long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECSHIFT("q", "", res, s1, s2, flags);
+    /* overflow is undefined if count != 1 */
+    if (s1 != 1)
+      flags &= ~CC_O;
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+           stringify(OP) "q", s0, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_opl(long s2, long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECSHIFT("l", "k", res, s1, s2, flags);
+    /* overflow is undefined if count != 1 */
+    if (s1 != 1)
+      flags &= ~CC_O;
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+           stringify(OP) "l", s0, s1, res, iflags, flags & CC_MASK);
+}
+
+void exec_opw(long s2, long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECSHIFT("w", "w", res, s1, s2, flags);
+    /* overflow is undefined if count != 1 */
+    if (s1 != 1)
+      flags &= ~CC_O;
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+           stringify(OP) "w", s0, s1, res, iflags, flags & CC_MASK);
+}
+
+#else
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+    asm ("push %4\n\t"\
+         "popf\n\t"\
+         stringify(OP) size " %%cl, %" rsize "5, %" rsize "0\n\t" \
+         "pushf\n\t"\
+         "pop %1\n\t"\
+         : "=g" (res), "=g" (flags)\
+         : "c" (s1), "0" (res), "1" (flags), "r" (s2));
+
+#if defined(__x86_64__)
+void exec_opq(long s2, long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECSHIFT("q", "", res, s1, s2, flags);
+    /* overflow is undefined if count != 1 */
+    if (s1 != 1)
+      flags &= ~CC_O;
+    printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+           stringify(OP) "q", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_opl(long s2, long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECSHIFT("l", "k", res, s1, s2, flags);
+    /* overflow is undefined if count != 1 */
+    if (s1 != 1)
+      flags &= ~CC_O;
+    printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+           stringify(OP) "l", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+
+void exec_opw(long s2, long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECSHIFT("w", "w", res, s1, s2, flags);
+    /* overflow is undefined if count != 1 */
+    if (s1 != 1)
+      flags &= ~CC_O;
+    printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+           stringify(OP) "w", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+
+#endif
+
+#ifndef OP_NOBYTE
+void exec_opb(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECSHIFT("b", "b", res, s1, 0, flags);
+    /* overflow is undefined if count != 1 */
+    if (s1 != 1)
+      flags &= ~CC_O;
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+           stringify(OP) "b", s0, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_op(long s2, long s0, long s1)
+{
+    s2 = i2l(s2);
+    s0 = i2l(s0);
+#if defined(__x86_64__)
+    exec_opq(s2, s0, s1, 0);
+#endif
+    exec_opl(s2, s0, s1, 0);
+#ifdef OP_SHIFTD
+    exec_opw(s2, s0, s1, 0);
+#else
+    exec_opw(s2, s0, s1, 0);
+#endif
+#ifndef OP_NOBYTE
+    exec_opb(s0, s1, 0);
+#endif
+#ifdef OP_CC
+#if defined(__x86_64__)
+    exec_opq(s2, s0, s1, CC_C);
+#endif
+    exec_opl(s2, s0, s1, CC_C);
+    exec_opw(s2, s0, s1, CC_C);
+    exec_opb(s0, s1, CC_C);
+#endif
+}
+
+void glue(test_, OP)(void)
+{
+    int i, n;
+#if defined(__x86_64__)
+    n = 64;
+#else
+    n = 32;
+#endif
+    for(i = 0; i < n; i++)
+        exec_op(0x21ad3d34, 0x12345678, i);
+    for(i = 0; i < n; i++)
+        exec_op(0x813f3421, 0x82345679, i);
+}
+
+void *glue(_test_, OP) __init_call = glue(test_, OP);
+
+#undef OP
+#undef OP_CC
+#undef OP_SHIFTD
+#undef OP_NOBYTE
+#undef EXECSHIFT

+ 103 - 0
tests/qemu/test-i386-vm86.S

@@ -0,0 +1,103 @@
+        .code16
+        .globl vm86_code_start
+        .globl vm86_code_end
+
+#define GET_OFFSET(x) ((x) - vm86_code_start + 0x100)
+
+vm86_code_start:
+        movw $GET_OFFSET(hello_world), %dx
+        movb $0x09, %ah
+        int $0x21
+
+        /* prepare int 0x90 vector */
+        xorw %ax, %ax
+        movw %ax, %es
+        es movw $GET_OFFSET(int90_test), 0x90 * 4
+        es movw %cs, 0x90 * 4 + 2
+
+        /* launch int 0x90 */
+
+        int $0x90
+
+        /* test IF support */
+        movw $GET_OFFSET(IF_msg), %dx
+        movb $0x09, %ah
+        int $0x21
+
+        pushf
+        popw %dx
+        movb $0xff, %ah
+        int $0x21
+
+        cli
+        pushf
+        popw %dx
+        movb $0xff, %ah
+        int $0x21
+
+        sti
+        pushfl
+        popl %edx
+        movb $0xff, %ah
+        int $0x21
+
+#if 0
+        movw $GET_OFFSET(IF_msg1), %dx
+        movb $0x09, %ah
+        int $0x21
+
+        pushf
+        movw %sp, %bx
+        andw $~0x200, (%bx)
+        popf
+#else
+        cli
+#endif
+
+        pushf
+        popw %dx
+        movb $0xff, %ah
+        int $0x21
+
+        pushfl
+        movw %sp, %bx
+        orw $0x200, (%bx)
+        popfl
+
+        pushfl
+        popl %edx
+        movb $0xff, %ah
+        int $0x21
+
+        movb $0x00, %ah
+        int $0x21
+
+int90_test:
+        pushf
+        pop %dx
+        movb $0xff, %ah
+        int $0x21
+
+        movw %sp, %bx
+        movw 4(%bx), %dx
+        movb $0xff, %ah
+        int $0x21
+
+        movw $GET_OFFSET(int90_msg), %dx
+        movb $0x09, %ah
+        int $0x21
+        iret
+
+int90_msg:
+        .string "INT90 started\n$"
+
+hello_world:
+        .string "Hello VM86 world\n$"
+
+IF_msg:
+        .string "VM86 IF test\n$"
+
+IF_msg1:
+        .string "If you see a diff here, your Linux kernel is buggy, please update to 2.4.20 kernel\n$"
+
+vm86_code_end:

+ 2774 - 0
tests/qemu/test-i386.c

@@ -0,0 +1,2774 @@
+/*
+ *  x86 CPU test
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ */
+#define _GNU_SOURCE
+#include "compiler.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+#include <sys/mman.h>
+
+#if !defined(__x86_64__)
+//#define TEST_VM86
+#define TEST_SEGS
+#endif
+//#define LINUX_VM86_IOPL_FIX
+//#define TEST_P4_FLAGS
+#ifdef __SSE__
+#define TEST_SSE
+#define TEST_CMOV  1
+#define TEST_FCOMI 1
+#else
+#undef TEST_SSE
+#define TEST_CMOV  1
+#define TEST_FCOMI 1
+#endif
+
+#if defined(__x86_64__)
+#define FMT64X "%016lx"
+#define FMTLX "%016lx"
+#define X86_64_ONLY(x) x
+#else
+#define FMT64X "%016" PRIx64
+#define FMTLX "%08lx"
+#define X86_64_ONLY(x)
+#endif
+
+#ifdef TEST_VM86
+#include <asm/vm86.h>
+#endif
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s)	tostring(s)
+#define tostring(s)	#s
+
+#define CC_C   	0x0001
+#define CC_P 	0x0004
+#define CC_A	0x0010
+#define CC_Z	0x0040
+#define CC_S    0x0080
+#define CC_O    0x0800
+
+#define __init_call	__attribute__ ((unused,__section__ ("initcall")))
+
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)
+
+#if defined(__x86_64__)
+static inline long i2l(long v)
+{
+    return v | ((v ^ 0xabcd) << 32);
+}
+#else
+static inline long i2l(long v)
+{
+    return v;
+}
+#endif
+
+#define OP add
+#include "test-i386.h"
+
+#define OP sub
+#include "test-i386.h"
+
+#define OP xor
+#include "test-i386.h"
+
+#define OP and
+#include "test-i386.h"
+
+#define OP or
+#include "test-i386.h"
+
+#define OP cmp
+#include "test-i386.h"
+
+#define OP adc
+#define OP_CC
+#include "test-i386.h"
+
+#define OP sbb
+#define OP_CC
+#include "test-i386.h"
+
+#define OP inc
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP dec
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP neg
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP not
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#undef CC_MASK
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O)
+
+#define OP shl
+#include "test-i386-shift.h"
+
+#define OP shr
+#include "test-i386-shift.h"
+
+#define OP sar
+#include "test-i386-shift.h"
+
+#define OP rol
+#include "test-i386-shift.h"
+
+#define OP ror
+#include "test-i386-shift.h"
+
+#define OP rcr
+#define OP_CC
+#include "test-i386-shift.h"
+
+#define OP rcl
+#define OP_CC
+#include "test-i386-shift.h"
+
+#define OP shld
+#define OP_SHIFTD
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP shrd
+#define OP_SHIFTD
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+/* XXX: should be more precise ? */
+#undef CC_MASK
+#define CC_MASK (CC_C)
+
+#define OP bt
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP bts
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP btr
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP btc
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+/* lea test (modrm support) */
+#define TEST_LEAQ(STR)\
+{\
+    asm("lea " STR ", %0"\
+        : "=r" (res)\
+        : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+    printf("lea %s = " FMTLX "\n", STR, res);\
+}
+
+#define TEST_LEA(STR)\
+{\
+    asm("lea " STR ", %0"\
+        : "=r" (res)\
+        : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+    printf("lea %s = " FMTLX "\n", STR, res);\
+}
+
+#define TEST_LEA16(STR)\
+{\
+    asm(".code16 ; .byte 0x67 ; leal " STR ", %0 ; .code32"\
+        : "=wq" (res)\
+        : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+    printf("lea %s = %08lx\n", STR, res);\
+}
+
+
+void test_lea(void)
+{
+    long eax, ebx, ecx, edx, esi, edi, res;
+    eax = i2l(0x0001);
+    ebx = i2l(0x0002);
+    ecx = i2l(0x0004);
+    edx = i2l(0x0008);
+    esi = i2l(0x0010);
+    edi = i2l(0x0020);
+
+    TEST_LEA("0x4000");
+
+    TEST_LEA("(%%eax)");
+    TEST_LEA("(%%ebx)");
+    TEST_LEA("(%%ecx)");
+    TEST_LEA("(%%edx)");
+    TEST_LEA("(%%esi)");
+    TEST_LEA("(%%edi)");
+
+    TEST_LEA("0x40(%%eax)");
+    TEST_LEA("0x40(%%ebx)");
+    TEST_LEA("0x40(%%ecx)");
+    TEST_LEA("0x40(%%edx)");
+    TEST_LEA("0x40(%%esi)");
+    TEST_LEA("0x40(%%edi)");
+
+    TEST_LEA("0x4000(%%eax)");
+    TEST_LEA("0x4000(%%ebx)");
+    TEST_LEA("0x4000(%%ecx)");
+    TEST_LEA("0x4000(%%edx)");
+    TEST_LEA("0x4000(%%esi)");
+    TEST_LEA("0x4000(%%edi)");
+
+    TEST_LEA("(%%eax, %%ecx)");
+    TEST_LEA("(%%ebx, %%edx)");
+    TEST_LEA("(%%ecx, %%ecx)");
+    TEST_LEA("(%%edx, %%ecx)");
+    TEST_LEA("(%%esi, %%ecx)");
+    TEST_LEA("(%%edi, %%ecx)");
+
+    TEST_LEA("0x40(%%eax, %%ecx)");
+    TEST_LEA("0x4000(%%ebx, %%edx)");
+
+    TEST_LEA("(%%ecx, %%ecx, 2)");
+    TEST_LEA("(%%edx, %%ecx, 4)");
+    TEST_LEA("(%%esi, %%ecx, 8)");
+
+    TEST_LEA("(,%%eax, 2)");
+    TEST_LEA("(,%%ebx, 4)");
+    TEST_LEA("(,%%ecx, 8)");
+
+    TEST_LEA("0x40(,%%eax, 2)");
+    TEST_LEA("0x40(,%%ebx, 4)");
+    TEST_LEA("0x40(,%%ecx, 8)");
+
+
+    TEST_LEA("-10(%%ecx, %%ecx, 2)");
+    TEST_LEA("-10(%%edx, %%ecx, 4)");
+    TEST_LEA("-10(%%esi, %%ecx, 8)");
+
+    TEST_LEA("0x4000(%%ecx, %%ecx, 2)");
+    TEST_LEA("0x4000(%%edx, %%ecx, 4)");
+    TEST_LEA("0x4000(%%esi, %%ecx, 8)");
+
+#if defined(__x86_64__)
+    TEST_LEAQ("0x4000");
+    TEST_LEAQ("0x4000(%%rip)");
+
+    TEST_LEAQ("(%%rax)");
+    TEST_LEAQ("(%%rbx)");
+    TEST_LEAQ("(%%rcx)");
+    TEST_LEAQ("(%%rdx)");
+    TEST_LEAQ("(%%rsi)");
+    TEST_LEAQ("(%%rdi)");
+
+    TEST_LEAQ("0x40(%%rax)");
+    TEST_LEAQ("0x40(%%rbx)");
+    TEST_LEAQ("0x40(%%rcx)");
+    TEST_LEAQ("0x40(%%rdx)");
+    TEST_LEAQ("0x40(%%rsi)");
+    TEST_LEAQ("0x40(%%rdi)");
+
+    TEST_LEAQ("0x4000(%%rax)");
+    TEST_LEAQ("0x4000(%%rbx)");
+    TEST_LEAQ("0x4000(%%rcx)");
+    TEST_LEAQ("0x4000(%%rdx)");
+    TEST_LEAQ("0x4000(%%rsi)");
+    TEST_LEAQ("0x4000(%%rdi)");
+
+    TEST_LEAQ("(%%rax, %%rcx)");
+    TEST_LEAQ("(%%rbx, %%rdx)");
+    TEST_LEAQ("(%%rcx, %%rcx)");
+    TEST_LEAQ("(%%rdx, %%rcx)");
+    TEST_LEAQ("(%%rsi, %%rcx)");
+    TEST_LEAQ("(%%rdi, %%rcx)");
+
+    TEST_LEAQ("0x40(%%rax, %%rcx)");
+    TEST_LEAQ("0x4000(%%rbx, %%rdx)");
+
+    TEST_LEAQ("(%%rcx, %%rcx, 2)");
+    TEST_LEAQ("(%%rdx, %%rcx, 4)");
+    TEST_LEAQ("(%%rsi, %%rcx, 8)");
+
+    TEST_LEAQ("(,%%rax, 2)");
+    TEST_LEAQ("(,%%rbx, 4)");
+    TEST_LEAQ("(,%%rcx, 8)");
+
+    TEST_LEAQ("0x40(,%%rax, 2)");
+    TEST_LEAQ("0x40(,%%rbx, 4)");
+    TEST_LEAQ("0x40(,%%rcx, 8)");
+
+
+    TEST_LEAQ("-10(%%rcx, %%rcx, 2)");
+    TEST_LEAQ("-10(%%rdx, %%rcx, 4)");
+    TEST_LEAQ("-10(%%rsi, %%rcx, 8)");
+
+    TEST_LEAQ("0x4000(%%rcx, %%rcx, 2)");
+    TEST_LEAQ("0x4000(%%rdx, %%rcx, 4)");
+    TEST_LEAQ("0x4000(%%rsi, %%rcx, 8)");
+#else
+    /* limited 16 bit addressing test */
+    //TEST_LEA16("0x4000");
+    //TEST_LEA16("(%%bx)");
+    //TEST_LEA16("(%%si)");
+    //TEST_LEA16("(%%di)");
+    //TEST_LEA16("0x40(%%bx)");
+    //TEST_LEA16("0x40(%%si)");
+    //TEST_LEA16("0x40(%%di)");
+    //TEST_LEA16("0x4000(%%bx)");
+    //TEST_LEA16("0x4000(%%si)");
+    //TEST_LEA16("(%%bx,%%si)");
+    //TEST_LEA16("(%%bx,%%di)");
+    //TEST_LEA16("0x40(%%bx,%%si)");
+    //TEST_LEA16("0x40(%%bx,%%di)");
+    //TEST_LEA16("0x4000(%%bx,%%si)");
+    //TEST_LEA16("0x4000(%%bx,%%di)");
+#endif
+}
+
+#define TEST_JCC(JCC, v1, v2)\
+{\
+    int res;\
+    asm("movl $1, %0\n\t"\
+        "cmpl %2, %1\n\t"\
+        "j" JCC " 1f\n\t"\
+        "movl $0, %0\n\t"\
+        "1:\n\t"\
+        : "=r" (res)\
+        : "r" (v1), "r" (v2));\
+    printf("%-10s %d\n", "j" JCC, res);\
+\
+    asm("movl $0, %0\n\t"\
+        "cmpl %2, %1\n\t"\
+        "set" JCC " %b0\n\t"\
+        : "=r" (res)\
+        : "r" (v1), "r" (v2));\
+    printf("%-10s %d\n", "set" JCC, res);\
+ if (TEST_CMOV) {\
+    long val = i2l(1);\
+    long res = i2l(0x12345678);\
+X86_64_ONLY(\
+    asm("cmpl %2, %1\n\t"\
+        "cmov" JCC "q %3, %0\n\t"\
+        : "=r" (res)\
+        : "r" (v1), "r" (v2), "m" (val), "0" (res));\
+        printf("%-10s R=" FMTLX "\n", "cmov" JCC "q", res);)\
+    asm("cmpl %2, %1\n\t"\
+        "cmov" JCC "l %k3, %k0\n\t"\
+        : "=r" (res)\
+        : "r" (v1), "r" (v2), "m" (val), "0" (res));\
+        printf("%-10s R=" FMTLX "\n", "cmov" JCC "l", res);\
+    asm("cmpl %2, %1\n\t"\
+        "cmov" JCC "w %w3, %w0\n\t"\
+        : "=r" (res)\
+        : "r" (v1), "r" (v2), "r" (1), "0" (res));\
+        printf("%-10s R=" FMTLX "\n", "cmov" JCC "w", res);\
+ } \
+}
+
+/* various jump tests */
+void test_jcc(void)
+{
+    TEST_JCC("ne", 1, 1);
+    TEST_JCC("ne", 1, 0);
+
+    TEST_JCC("e", 1, 1);
+    TEST_JCC("e", 1, 0);
+
+    TEST_JCC("l", 1, 1);
+    TEST_JCC("l", 1, 0);
+    TEST_JCC("l", 1, -1);
+
+    TEST_JCC("le", 1, 1);
+    TEST_JCC("le", 1, 0);
+    TEST_JCC("le", 1, -1);
+
+    TEST_JCC("ge", 1, 1);
+    TEST_JCC("ge", 1, 0);
+    TEST_JCC("ge", -1, 1);
+
+    TEST_JCC("g", 1, 1);
+    TEST_JCC("g", 1, 0);
+    TEST_JCC("g", 1, -1);
+
+    TEST_JCC("b", 1, 1);
+    TEST_JCC("b", 1, 0);
+    TEST_JCC("b", 1, -1);
+
+    TEST_JCC("be", 1, 1);
+    TEST_JCC("be", 1, 0);
+    TEST_JCC("be", 1, -1);
+
+    TEST_JCC("ae", 1, 1);
+    TEST_JCC("ae", 1, 0);
+    TEST_JCC("ae", 1, -1);
+
+    TEST_JCC("a", 1, 1);
+    TEST_JCC("a", 1, 0);
+    TEST_JCC("a", 1, -1);
+
+
+    TEST_JCC("p", 1, 1);
+    TEST_JCC("p", 1, 0);
+
+    TEST_JCC("np", 1, 1);
+    TEST_JCC("np", 1, 0);
+
+    TEST_JCC("o", 0x7fffffff, 0);
+    TEST_JCC("o", 0x7fffffff, -1);
+
+    TEST_JCC("no", 0x7fffffff, 0);
+    TEST_JCC("no", 0x7fffffff, -1);
+
+    TEST_JCC("s", 0, 1);
+    TEST_JCC("s", 0, -1);
+    TEST_JCC("s", 0, 0);
+
+    TEST_JCC("ns", 0, 1);
+    TEST_JCC("ns", 0, -1);
+    TEST_JCC("ns", 0, 0);
+}
+
+#define TEST_LOOP(insn) \
+{\
+    for(i = 0; i < sizeof(ecx_vals) / sizeof(long); i++) {\
+        ecx = ecx_vals[i];\
+        for(zf = 0; zf < 2; zf++) {\
+    asm("test %2, %2\n\t"\
+        "movl $1, %0\n\t"\
+          insn " 1f\n\t" \
+        "movl $0, %0\n\t"\
+        "1:\n\t"\
+        : "=a" (res)\
+        : "c" (ecx), "b" (!zf)); \
+    printf("%-10s ECX=" FMTLX " ZF=%ld r=%d\n", insn, ecx, zf, res);      \
+        }\
+   }\
+}
+
+void test_loop(void)
+{
+    long ecx, zf;
+    const long ecx_vals[] = {
+        0,
+        1,
+        0x10000,
+        0x10001,
+#if defined(__x86_64__)
+        0x100000000L,
+        0x100000001L,
+#endif
+    };
+    int i, res;
+
+#if !defined(__x86_64__)
+    TEST_LOOP("jcxz");
+    TEST_LOOP("loopw");
+    TEST_LOOP("loopzw");
+    TEST_LOOP("loopnzw");
+#endif
+
+    TEST_LOOP("jecxz");
+    TEST_LOOP("loopl");
+    TEST_LOOP("loopzl");
+    TEST_LOOP("loopnzl");
+}
+
+#undef CC_MASK
+#ifdef TEST_P4_FLAGS
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)
+#else
+#define CC_MASK (CC_O | CC_C)
+#endif
+
+#define OP mul
+#include "test-i386-muldiv.h"
+
+#define OP imul
+#include "test-i386-muldiv.h"
+
+void test_imulw2(long op0, long op1)
+{
+    long res, s1, s0, flags;
+    s0 = op0;
+    s1 = op1;
+    res = s0;
+    flags = 0;
+    asm volatile ("push %4\n\t"
+         "popf\n\t"
+         "imulw %w2, %w0\n\t"
+         "pushf\n\t"
+         "pop %1\n\t"
+         : "=q" (res), "=g" (flags)
+         : "q" (s1), "0" (res), "1" (flags));
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+           "imulw", s0, s1, res, flags & CC_MASK);
+}
+
+void test_imull2(long op0, long op1)
+{
+    long res, s1, s0, flags;
+    s0 = op0;
+    s1 = op1;
+    res = s0;
+    flags = 0;
+    asm volatile ("push %4\n\t"
+         "popf\n\t"
+         "imull %k2, %k0\n\t"
+         "pushf\n\t"
+         "pop %1\n\t"
+         : "=q" (res), "=g" (flags)
+         : "q" (s1), "0" (res), "1" (flags));
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+           "imull", s0, s1, res, flags & CC_MASK);
+}
+
+#if defined(__x86_64__)
+void test_imulq2(long op0, long op1)
+{
+    long res, s1, s0, flags;
+    s0 = op0;
+    s1 = op1;
+    res = s0;
+    flags = 0;
+    asm volatile ("push %4\n\t"
+         "popf\n\t"
+         "imulq %2, %0\n\t"
+         "pushf\n\t"
+         "pop %1\n\t"
+         : "=q" (res), "=g" (flags)
+         : "q" (s1), "0" (res), "1" (flags));
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+           "imulq", s0, s1, res, flags & CC_MASK);
+}
+#endif
+
+#define TEST_IMUL_IM(size, rsize, op0, op1)\
+{\
+    long res, flags, s1;\
+    flags = 0;\
+    res = 0;\
+    s1 = op1;\
+    asm volatile ("push %3\n\t"\
+         "popf\n\t"\
+         "imul" size " $" #op0 ", %" rsize "2, %" rsize "0\n\t" \
+         "pushf\n\t"\
+         "pop %1\n\t"\
+         : "=r" (res), "=g" (flags)\
+         : "r" (s1), "1" (flags), "0" (res));\
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",\
+           "imul" size " im", (long)op0, (long)op1, res, flags & CC_MASK);\
+}
+
+
+#undef CC_MASK
+#define CC_MASK (0)
+
+#define OP div
+#include "test-i386-muldiv.h"
+
+#define OP idiv
+#include "test-i386-muldiv.h"
+
+void test_mul(void)
+{
+    test_imulb(0x1234561d, 4);
+    test_imulb(3, -4);
+    test_imulb(0x80, 0x80);
+    test_imulb(0x10, 0x10);
+
+    test_imulw(0, 0x1234001d, 45);
+    test_imulw(0, 23, -45);
+    test_imulw(0, 0x8000, 0x8000);
+    test_imulw(0, 0x100, 0x100);
+
+    test_imull(0, 0x1234001d, 45);
+    test_imull(0, 23, -45);
+    test_imull(0, 0x80000000, 0x80000000);
+    test_imull(0, 0x10000, 0x10000);
+
+    test_mulb(0x1234561d, 4);
+    test_mulb(3, -4);
+    test_mulb(0x80, 0x80);
+    test_mulb(0x10, 0x10);
+
+    test_mulw(0, 0x1234001d, 45);
+    test_mulw(0, 23, -45);
+    test_mulw(0, 0x8000, 0x8000);
+    test_mulw(0, 0x100, 0x100);
+
+    test_mull(0, 0x1234001d, 45);
+    test_mull(0, 23, -45);
+    test_mull(0, 0x80000000, 0x80000000);
+    test_mull(0, 0x10000, 0x10000);
+
+    test_imulw2(0x1234001d, 45);
+    test_imulw2(23, -45);
+    test_imulw2(0x8000, 0x8000);
+    test_imulw2(0x100, 0x100);
+
+    test_imull2(0x1234001d, 45);
+    test_imull2(23, -45);
+    test_imull2(0x80000000, 0x80000000);
+    test_imull2(0x10000, 0x10000);
+
+    TEST_IMUL_IM("w", "w", 45, 0x1234);
+    TEST_IMUL_IM("w", "w", -45, 23);
+    TEST_IMUL_IM("w", "w", 0x8000, 0x80000000);
+    TEST_IMUL_IM("w", "w", 0x7fff, 0x1000);
+
+    TEST_IMUL_IM("l", "k", 45, 0x1234);
+    TEST_IMUL_IM("l", "k", -45, 23);
+    TEST_IMUL_IM("l", "k", 0x8000, 0x80000000);
+    TEST_IMUL_IM("l", "k", 0x7fff, 0x1000);
+
+    test_idivb(0x12341678, 0x127e);
+    test_idivb(0x43210123, -5);
+    test_idivb(0x12340004, -1);
+
+    test_idivw(0, 0x12345678, 12347);
+    test_idivw(0, -23223, -45);
+    test_idivw(0, 0x12348000, -1);
+    test_idivw(0x12343, 0x12345678, 0x81238567);
+
+    test_idivl(0, 0x12345678, 12347);
+    test_idivl(0, -233223, -45);
+    test_idivl(0, 0x80000000, -1);
+    test_idivl(0x12343, 0x12345678, 0x81234567);
+
+    test_divb(0x12341678, 0x127e);
+    test_divb(0x43210123, -5);
+    test_divb(0x12340004, -1);
+
+    test_divw(0, 0x12345678, 12347);
+    test_divw(0, -23223, -45);
+    test_divw(0, 0x12348000, -1);
+    test_divw(0x12343, 0x12345678, 0x81238567);
+
+    test_divl(0, 0x12345678, 12347);
+    test_divl(0, -233223, -45);
+    test_divl(0, 0x80000000, -1);
+    test_divl(0x12343, 0x12345678, 0x81234567);
+
+#if defined(__x86_64__)
+    test_imulq(0, 0x1234001d1234001d, 45);
+    test_imulq(0, 23, -45);
+    test_imulq(0, 0x8000000000000000, 0x8000000000000000);
+    test_imulq(0, 0x100000000, 0x100000000);
+
+    test_mulq(0, 0x1234001d1234001d, 45);
+    test_mulq(0, 23, -45);
+    test_mulq(0, 0x8000000000000000, 0x8000000000000000);
+    test_mulq(0, 0x100000000, 0x100000000);
+
+    test_imulq2(0x1234001d1234001d, 45);
+    test_imulq2(23, -45);
+    test_imulq2(0x8000000000000000, 0x8000000000000000);
+    test_imulq2(0x100000000, 0x100000000);
+
+    TEST_IMUL_IM("q", "", 45, 0x12341234);
+    TEST_IMUL_IM("q", "", -45, 23);
+    TEST_IMUL_IM("q", "", 0x8000, 0x8000000000000000);
+    TEST_IMUL_IM("q", "", 0x7fff, 0x10000000);
+
+    test_idivq(0, 0x12345678abcdef, 12347);
+    test_idivq(0, -233223, -45);
+    test_idivq(0, 0x8000000000000000, -1);
+    test_idivq(0x12343, 0x12345678, 0x81234567);
+
+    test_divq(0, 0x12345678abcdef, 12347);
+    test_divq(0, -233223, -45);
+    test_divq(0, 0x8000000000000000, -1);
+    test_divq(0x12343, 0x12345678, 0x81234567);
+#endif
+}
+
+#define TEST_BSX(op, size, op0)\
+{\
+    long res, val, resz;\
+    val = op0;\
+    asm("xor %1, %1\n"\
+        "mov $0x12345678, %0\n"\
+        #op " %" size "2, %" size "0 ; setz %b1" \
+        : "=&r" (res), "=&q" (resz)\
+        : "r" (val));\
+    printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\
+}
+
+void test_bsx(void)
+{
+    TEST_BSX(bsrw, "w", 0);
+    TEST_BSX(bsrw, "w", 0x12340128);
+    TEST_BSX(bsfw, "w", 0);
+    TEST_BSX(bsfw, "w", 0x12340128);
+    TEST_BSX(bsrl, "k", 0);
+    TEST_BSX(bsrl, "k", 0x00340128);
+    TEST_BSX(bsfl, "k", 0);
+    TEST_BSX(bsfl, "k", 0x00340128);
+#if defined(__x86_64__)
+    TEST_BSX(bsrq, "", 0);
+    TEST_BSX(bsrq, "", 0x003401281234);
+    TEST_BSX(bsfq, "", 0);
+    TEST_BSX(bsfq, "", 0x003401281234);
+#endif
+}
+
+/**********************************************/
+
+union float64u {
+    double d;
+    uint64_t l;
+};
+
+union float64u q_nan = { .l = 0xFFF8000000000000LL };
+union float64u s_nan = { .l = 0xFFF0000000000000LL };
+
+void test_fops(double a, double b)
+{
+    printf("a=%f b=%f a+b=%f\n", a, b, a + b);
+    printf("a=%f b=%f a-b=%f\n", a, b, a - b);
+    printf("a=%f b=%f a*b=%f\n", a, b, a * b);
+    printf("a=%f b=%f a/b=%f\n", a, b, a / b);
+    //printf("a=%f b=%f fmod(a, b)=%f\n", a, b, fmod(a, b));
+    printf("a=%f sqrt(a)=%f\n", a, sqrt(a));
+    printf("a=%f sin(a)=%f\n", a, sin(a));
+    printf("a=%f cos(a)=%f\n", a, cos(a));
+    printf("a=%f tan(a)=%f\n", a, tan(a));
+    printf("a=%f log(a)=%f\n", a, log(a));
+    printf("a=%f exp(a)=%f\n", a, exp(a));
+    printf("a=%f b=%f atan2(a, b)=%f\n", a, b, atan2(a, b));
+    /* just to test some op combining */
+    printf("a=%f asin(sin(a))=%f\n", a, asin(sin(a)));
+    printf("a=%f acos(cos(a))=%f\n", a, acos(cos(a)));
+    printf("a=%f atan(tan(a))=%f\n", a, atan(tan(a)));
+
+}
+
+void fpu_clear_exceptions(void)
+{
+    struct QEMU_PACKED {
+        uint16_t fpuc;
+        uint16_t dummy1;
+        uint16_t fpus;
+        uint16_t dummy2;
+        uint16_t fptag;
+        uint16_t dummy3;
+        uint32_t ignored[4];
+        long double fpregs[8];
+    } float_env32;
+
+    asm volatile ("fnstenv %0\n" : "=m" (float_env32));
+    float_env32.fpus &= ~0x7f;
+    asm volatile ("fldenv %0\n" : : "m" (float_env32));
+}
+
+/* XXX: display exception bits when supported */
+#define FPUS_EMASK 0x0000
+//#define FPUS_EMASK 0x007f
+
+void test_fcmp(double a, double b)
+{
+    long eflags, fpus;
+
+    fpu_clear_exceptions();
+    asm("fcom %2\n"
+        "fstsw %%ax\n"
+        : "=a" (fpus)
+        : "t" (a), "u" (b));
+    printf("fcom(%f %f)=%04lx\n",
+           a, b, fpus & (0x4500 | FPUS_EMASK));
+    fpu_clear_exceptions();
+    asm("fucom %2\n"
+        "fstsw %%ax\n"
+        : "=a" (fpus)
+        : "t" (a), "u" (b));
+    printf("fucom(%f %f)=%04lx\n",
+           a, b, fpus & (0x4500 | FPUS_EMASK));
+    if (TEST_FCOMI) {
+        /* test f(u)comi instruction */
+        fpu_clear_exceptions();
+        asm("fcomi %3, %2\n"
+            "fstsw %%ax\n"
+            "pushf\n"
+            "pop %0\n"
+            : "=r" (eflags), "=a" (fpus)
+            : "t" (a), "u" (b));
+        printf("fcomi(%f %f)=%04lx %02lx\n",
+               a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));
+        fpu_clear_exceptions();
+        asm("fucomi %3, %2\n"
+            "fstsw %%ax\n"
+            "pushf\n"
+            "pop %0\n"
+            : "=r" (eflags), "=a" (fpus)
+            : "t" (a), "u" (b));
+        printf("fucomi(%f %f)=%04lx %02lx\n",
+               a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));
+    }
+    fpu_clear_exceptions();
+    asm volatile("fxam\n"
+                 "fstsw %%ax\n"
+                 : "=a" (fpus)
+                 : "t" (a));
+    printf("fxam(%f)=%04lx\n", a, fpus & 0x4700);
+    fpu_clear_exceptions();
+}
+
+void test_fcvt(double a)
+{
+    float fa;
+    long double la;
+    int16_t fpuc;
+    int i;
+    int64_t lla;
+    int ia;
+    int16_t wa;
+    double ra;
+
+    fa = a;
+    la = a;
+    printf("(float)%f = %f\n", a, fa);
+    printf("(long double)%f = %Lf\n", a, la);
+    printf("a=" FMT64X "\n", *(uint64_t *)&a);
+    printf("la=" FMT64X " %04x\n", *(uint64_t *)&la,
+           *(unsigned short *)((char *)(&la) + 8));
+
+    /* test all roundings */
+    asm volatile ("fstcw %0" : "=m" (fpuc));
+    for(i=0;i<4;i++) {
+        uint16_t val16;
+        val16 = (fpuc & ~0x0c00) | (i << 10);
+        asm volatile ("fldcw %0" : : "m" (val16));
+        asm volatile ("fist %0" : "=m" (wa) : "t" (a));
+        asm volatile ("fistl %0" : "=m" (ia) : "t" (a));
+        asm volatile ("fistpll %0" : "=m" (lla) : "t" (a) : "st");
+        asm volatile ("frndint ; fstl %0" : "=m" (ra) : "t" (a));
+        asm volatile ("fldcw %0" : : "m" (fpuc));
+        printf("(short)a = %d\n", wa);
+        printf("(int)a = %d\n", ia);
+        printf("(int64_t)a = " FMT64X "\n", lla);
+        printf("rint(a) = %f\n", ra);
+    }
+}
+
+#define TEST(N) \
+    asm("fld" #N : "=t" (a)); \
+    printf("fld" #N "= %f\n", a);
+
+void test_fconst(void)
+{
+    double a;
+    TEST(1);
+    TEST(l2t);
+    TEST(l2e);
+    TEST(pi);
+    TEST(lg2);
+    TEST(ln2);
+    TEST(z);
+}
+
+void test_fbcd(double a)
+{
+    unsigned short bcd[5];
+    double b;
+
+    asm("fbstp %0" : "=m" (bcd[0]) : "t" (a) : "st");
+    asm("fbld %1" : "=t" (b) : "m" (bcd[0]));
+    printf("a=%f bcd=%04x%04x%04x%04x%04x b=%f\n",
+           a, bcd[4], bcd[3], bcd[2], bcd[1], bcd[0], b);
+}
+
+#define TEST_ENV(env, save, restore)\
+{\
+    memset((env), 0xaa, sizeof(*(env)));\
+    for(i=0;i<5;i++)\
+        asm volatile ("fldl %0" : : "m" (dtab[i]));\
+    asm volatile (save " %0\n" : : "m" (*(env)));\
+    asm volatile (restore " %0\n": : "m" (*(env)));\
+    for(i=0;i<5;i++)\
+        asm volatile ("fstpl %0" : "=m" (rtab[i]));\
+    for(i=0;i<5;i++)\
+        printf("res[%d]=%f\n", i, rtab[i]);\
+    printf("fpuc=%04x fpus=%04x fptag=%04x\n",\
+           (env)->fpuc,\
+           (env)->fpus & 0xff00,\
+           (env)->fptag);\
+}
+
+void test_fenv(void)
+{
+    struct QEMU_PACKED {
+        uint16_t fpuc;
+        uint16_t dummy1;
+        uint16_t fpus;
+        uint16_t dummy2;
+        uint16_t fptag;
+        uint16_t dummy3;
+        uint32_t ignored[4];
+        long double fpregs[8];
+    } float_env32;
+    struct QEMU_PACKED {
+        uint16_t fpuc;
+        uint16_t fpus;
+        uint16_t fptag;
+        uint16_t ignored[4];
+        long double fpregs[8];
+    } float_env16;
+    double dtab[8];
+    double rtab[8];
+    int i;
+
+    for(i=0;i<8;i++)
+        dtab[i] = i + 1;
+
+    //TEST_ENV(&float_env16, "data16 fnstenv", "data16 fldenv");
+    //TEST_ENV(&float_env16, "data16 fnsave", "data16 frstor");
+    TEST_ENV(&float_env32, "fnstenv", "fldenv");
+    TEST_ENV(&float_env32, "fnsave", "frstor");
+
+    /* test for ffree */
+    for(i=0;i<5;i++)
+        asm volatile ("fldl %0" : : "m" (dtab[i]));
+    asm volatile("ffree %st(2)");
+    asm volatile ("fnstenv %0\n" : : "m" (float_env32));
+    asm volatile ("fninit");
+    printf("fptag=%04x\n", float_env32.fptag);
+}
+
+
+#define TEST_FCMOV(a, b, eflags, CC)\
+{\
+    double res;\
+    asm("push %3\n"\
+        "popf\n"\
+        "fcmov" CC " %2, %0\n"\
+        : "=t" (res)\
+        : "0" (a), "u" (b), "g" (eflags));\
+    printf("fcmov%s eflags=0x%04lx-> %f\n", \
+           CC, (long)eflags, res);\
+}
+
+void test_fcmov(void)
+{
+    double a, b;
+    long eflags, i;
+
+    a = 1.0;
+    b = 2.0;
+    for(i = 0; i < 4; i++) {
+        eflags = 0;
+        if (i & 1)
+            eflags |= CC_C;
+        if (i & 2)
+            eflags |= CC_Z;
+        TEST_FCMOV(a, b, eflags, "b");
+        TEST_FCMOV(a, b, eflags, "e");
+        TEST_FCMOV(a, b, eflags, "be");
+        TEST_FCMOV(a, b, eflags, "nb");
+        TEST_FCMOV(a, b, eflags, "ne");
+        TEST_FCMOV(a, b, eflags, "nbe");
+    }
+    TEST_FCMOV(a, b, 0, "u");
+    TEST_FCMOV(a, b, CC_P, "u");
+    TEST_FCMOV(a, b, 0, "nu");
+    TEST_FCMOV(a, b, CC_P, "nu");
+}
+
+void test_floats(void)
+{
+    test_fops(2, 3);
+    test_fops(1.4, -5);
+    test_fcmp(2, -1);
+    test_fcmp(2, 2);
+    test_fcmp(2, 3);
+    test_fcmp(2, q_nan.d);
+    test_fcmp(q_nan.d, -1);
+    test_fcmp(-1.0/0.0, -1);
+    test_fcmp(1.0/0.0, -1);
+    test_fcvt(0.5);
+    test_fcvt(-0.5);
+    test_fcvt(1.0/7.0);
+    test_fcvt(-1.0/9.0);
+    test_fcvt(32768);
+    test_fcvt(-1e20);
+    test_fcvt(-1.0/0.0);
+    test_fcvt(1.0/0.0);
+    test_fcvt(q_nan.d);
+    test_fconst();
+    //test_fbcd(1234567890123456.0);
+    //test_fbcd(-123451234567890.0);
+    test_fenv();
+    if (TEST_CMOV) {
+        test_fcmov();
+    }
+}
+
+/**********************************************/
+#if !defined(__x86_64__)
+
+#define TEST_BCD(op, op0, cc_in, cc_mask)\
+{\
+    int res, flags;\
+    res = op0;\
+    flags = cc_in;\
+    asm ("push %3\n\t"\
+         "popf\n\t"\
+         #op "\n\t"\
+         "pushf\n\t"\
+         "pop %1\n\t"\
+        : "=a" (res), "=g" (flags)\
+        : "0" (res), "1" (flags));\
+    printf("%-10s A=%08x R=%08x CCIN=%04x CC=%04x\n",\
+           #op, op0, res, cc_in, flags & cc_mask);\
+}
+
+void test_bcd(void)
+{
+    TEST_BCD(daa, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(daa, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+
+    TEST_BCD(das, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+    TEST_BCD(das, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+
+    TEST_BCD(aaa, 0x12340205, CC_A, (CC_C | CC_A));
+    TEST_BCD(aaa, 0x12340306, CC_A, (CC_C | CC_A));
+    TEST_BCD(aaa, 0x1234040a, CC_A, (CC_C | CC_A));
+    TEST_BCD(aaa, 0x123405fa, CC_A, (CC_C | CC_A));
+    TEST_BCD(aaa, 0x12340205, 0, (CC_C | CC_A));
+    TEST_BCD(aaa, 0x12340306, 0, (CC_C | CC_A));
+    TEST_BCD(aaa, 0x1234040a, 0, (CC_C | CC_A));
+    TEST_BCD(aaa, 0x123405fa, 0, (CC_C | CC_A));
+
+    TEST_BCD(aas, 0x12340205, CC_A, (CC_C | CC_A));
+    TEST_BCD(aas, 0x12340306, CC_A, (CC_C | CC_A));
+    TEST_BCD(aas, 0x1234040a, CC_A, (CC_C | CC_A));
+    TEST_BCD(aas, 0x123405fa, CC_A, (CC_C | CC_A));
+    TEST_BCD(aas, 0x12340205, 0, (CC_C | CC_A));
+    TEST_BCD(aas, 0x12340306, 0, (CC_C | CC_A));
+    TEST_BCD(aas, 0x1234040a, 0, (CC_C | CC_A));
+    TEST_BCD(aas, 0x123405fa, 0, (CC_C | CC_A));
+
+    TEST_BCD(aam, 0x12340547, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));
+    TEST_BCD(aad, 0x12340407, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));
+}
+#endif
+
+#define TEST_XCHG(op, size, opconst)\
+{\
+    long op0, op1;\
+    op0 = i2l(0x12345678);\
+    op1 = i2l(0xfbca7654);\
+    asm(#op " %" size "0, %" size "1" \
+        : "=q" (op0), opconst (op1) \
+        : "0" (op0));\
+    printf("%-10s A=" FMTLX " B=" FMTLX "\n",\
+           #op, op0, op1);\
+}
+
+#define TEST_CMPXCHG(op, size, opconst, eax)\
+{\
+    long op0, op1, op2;\
+    op0 = i2l(0x12345678);\
+    op1 = i2l(0xfbca7654);\
+    op2 = i2l(eax);\
+    asm(#op " %" size "0, %" size "1" \
+        : "=q" (op0), opconst (op1) \
+        : "0" (op0), "a" (op2));\
+    printf("%-10s EAX=" FMTLX " A=" FMTLX " C=" FMTLX "\n",\
+           #op, op2, op0, op1);\
+}
+
+void test_xchg(void)
+{
+#if defined(__x86_64__)
+    TEST_XCHG(xchgq, "", "+q");
+#endif
+    TEST_XCHG(xchgl, "k", "+q");
+    TEST_XCHG(xchgw, "w", "+q");
+    TEST_XCHG(xchgb, "b", "+q");
+
+#if defined(__x86_64__)
+    TEST_XCHG(xchgq, "", "=m");
+#endif
+    TEST_XCHG(xchgl, "k", "+m");
+    TEST_XCHG(xchgw, "w", "+m");
+    TEST_XCHG(xchgb, "b", "+m");
+
+#if defined(__x86_64__)
+    TEST_XCHG(xaddq, "", "+q");
+#endif
+    TEST_XCHG(xaddl, "k", "+q");
+    TEST_XCHG(xaddw, "w", "+q");
+    TEST_XCHG(xaddb, "b", "+q");
+
+    {
+        int res;
+        res = 0x12345678;
+        asm("xaddl %1, %0" : "=r" (res) : "0" (res));
+        printf("xaddl same res=%08x\n", res);
+    }
+
+#if defined(__x86_64__)
+    TEST_XCHG(xaddq, "", "+m");
+#endif
+    TEST_XCHG(xaddl, "k", "+m");
+    TEST_XCHG(xaddw, "w", "+m");
+    TEST_XCHG(xaddb, "b", "+m");
+
+#if defined(__x86_64__)
+    TEST_CMPXCHG(cmpxchgq, "", "+q", 0xfbca7654);
+#endif
+    TEST_CMPXCHG(cmpxchgl, "k", "+q", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgw, "w", "+q", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgb, "b", "+q", 0xfbca7654);
+
+#if defined(__x86_64__)
+    TEST_CMPXCHG(cmpxchgq, "", "+q", 0xfffefdfc);
+#endif
+    TEST_CMPXCHG(cmpxchgl, "k", "+q", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgw, "w", "+q", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgb, "b", "+q", 0xfffefdfc);
+
+#if defined(__x86_64__)
+    TEST_CMPXCHG(cmpxchgq, "", "+m", 0xfbca7654);
+#endif
+    TEST_CMPXCHG(cmpxchgl, "k", "+m", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgw, "w", "+m", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgb, "b", "+m", 0xfbca7654);
+
+#if defined(__x86_64__)
+    TEST_CMPXCHG(cmpxchgq, "", "+m", 0xfffefdfc);
+#endif
+    TEST_CMPXCHG(cmpxchgl, "k", "+m", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgw, "w", "+m", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgb, "b", "+m", 0xfffefdfc);
+
+    {
+        uint64_t op0, op1, op2;
+        long eax, edx;
+        long i, eflags;
+
+        for(i = 0; i < 2; i++) {
+            op0 = 0x123456789abcdLL;
+            eax = i2l(op0 & 0xffffffff);
+            edx = i2l(op0 >> 32);
+            if (i == 0)
+                op1 = 0xfbca765423456LL;
+            else
+                op1 = op0;
+            op2 = 0x6532432432434LL;
+            asm("cmpxchg8b %2\n"
+                "pushf\n"
+                "pop %3\n"
+                : "=a" (eax), "=d" (edx), "=m" (op1), "=g" (eflags)
+                : "0" (eax), "1" (edx), "m" (op1), "b" ((int)op2), "c" ((int)(op2 >> 32)));
+            printf("cmpxchg8b: eax=" FMTLX " edx=" FMTLX " op1=" FMT64X " CC=%02lx\n",
+                   eax, edx, op1, eflags & CC_Z);
+        }
+    }
+}
+
+#ifdef TEST_SEGS
+/**********************************************/
+/* segmentation tests */
+
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <asm/ldt.h>
+#include <linux/version.h>
+
+static inline int modify_ldt(int func, void * ptr, unsigned long bytecount)
+{
+    return syscall(__NR_modify_ldt, func, ptr, bytecount);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66)
+#define modify_ldt_ldt_s user_desc
+#endif
+
+#define MK_SEL(n) (((n) << 3) | 7)
+
+uint8_t seg_data1[4096];
+uint8_t seg_data2[4096];
+
+#define TEST_LR(op, size, seg, mask)\
+{\
+    int res, res2;\
+    uint16_t mseg = seg;\
+    res = 0x12345678;\
+    asm (op " %" size "2, %" size "0\n" \
+         "movl $0, %1\n"\
+         "jnz 1f\n"\
+         "movl $1, %1\n"\
+         "1:\n"\
+         : "=r" (res), "=r" (res2) : "m" (mseg), "0" (res));\
+    printf(op ": Z=%d %08x\n", res2, res & ~(mask));\
+}
+
+#define TEST_ARPL(op, size, op1, op2)\
+{\
+    long a, b, c;                               \
+    a = (op1);                                  \
+    b = (op2);                                  \
+    asm volatile(op " %" size "3, %" size "0\n"\
+                 "movl $0,%1\n"\
+                 "jnz 1f\n"\
+                 "movl $1,%1\n"\
+                 "1:\n"\
+                 : "=r" (a), "=r" (c) : "0" (a), "r" (b));    \
+    printf(op size " A=" FMTLX " B=" FMTLX " R=" FMTLX " z=%ld\n",\
+           (long)(op1), (long)(op2), a, c);\
+}
+
+/* NOTE: we use Linux modify_ldt syscall */
+void test_segs(void)
+{
+    struct modify_ldt_ldt_s ldt;
+    long long ldt_table[3];
+    int res, res2;
+    char tmp;
+    struct {
+        uint32_t offset;
+        uint16_t seg;
+    } QEMU_PACKED segoff;
+
+    ldt.entry_number = 1;
+    ldt.base_addr = (unsigned long)&seg_data1;
+    ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;
+    ldt.seg_32bit = 1;
+    ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+    ldt.read_exec_only = 0;
+    ldt.limit_in_pages = 1;
+    ldt.seg_not_present = 0;
+    ldt.useable = 1;
+    modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+    ldt.entry_number = 2;
+    ldt.base_addr = (unsigned long)&seg_data2;
+    ldt.limit = (sizeof(seg_data2) + 0xfff) >> 12;
+    ldt.seg_32bit = 1;
+    ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+    ldt.read_exec_only = 0;
+    ldt.limit_in_pages = 1;
+    ldt.seg_not_present = 0;
+    ldt.useable = 1;
+    modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+    modify_ldt(0, &ldt_table, sizeof(ldt_table)); /* read ldt entries */
+#if 0
+    {
+        int i;
+        for(i=0;i<3;i++)
+            printf("%d: %016Lx\n", i, ldt_table[i]);
+    }
+#endif
+    /* do some tests with fs or gs */
+    asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1)));
+
+    seg_data1[1] = 0xaa;
+    seg_data2[1] = 0x55;
+
+    asm volatile ("fs movzbl 0x1, %0" : "=r" (res));
+    printf("FS[1] = %02x\n", res);
+
+    asm volatile ("pushl %%gs\n"
+                  "movl %1, %%gs\n"
+                  "gs movzbl 0x1, %0\n"
+                  "popl %%gs\n"
+                  : "=r" (res)
+                  : "r" (MK_SEL(2)));
+    printf("GS[1] = %02x\n", res);
+
+    /* tests with ds/ss (implicit segment case) */
+    tmp = 0xa5;
+    asm volatile ("pushl %%ebp\n\t"
+                  "pushl %%ds\n\t"
+                  "movl %2, %%ds\n\t"
+                  "movl %3, %%ebp\n\t"
+                  "movzbl 0x1, %0\n\t"
+                  "movzbl (%%ebp), %1\n\t"
+                  "popl %%ds\n\t"
+                  "popl %%ebp\n\t"
+                  : "=r" (res), "=r" (res2)
+                  : "r" (MK_SEL(1)), "r" (&tmp));
+    printf("DS[1] = %02x\n", res);
+    printf("SS[tmp] = %02x\n", res2);
+
+    segoff.seg = MK_SEL(2);
+    segoff.offset = 0xabcdef12;
+    asm volatile("lfs %2, %0\n\t"
+                 "movl %%fs, %1\n\t"
+                 : "=r" (res), "=g" (res2)
+                 : "m" (segoff));
+    printf("FS:reg = %04x:%08x\n", res2, res);
+
+#if 0
+    TEST_LR("larw", "w", MK_SEL(2), 0x0100);
+    TEST_LR("larl", "", MK_SEL(2), 0x0100);
+    TEST_LR("lslw", "w", MK_SEL(2), 0);
+    TEST_LR("lsll", "", MK_SEL(2), 0);
+
+    TEST_LR("larw", "w", 0xfff8, 0);
+    TEST_LR("larl", "", 0xfff8, 0);
+    TEST_LR("lslw", "w", 0xfff8, 0);
+    TEST_LR("lsll", "", 0xfff8, 0);
+#endif
+
+    TEST_ARPL("arpl", "w", 0x12345678 | 3, 0x762123c | 1);
+    TEST_ARPL("arpl", "w", 0x12345678 | 1, 0x762123c | 3);
+    TEST_ARPL("arpl", "w", 0x12345678 | 1, 0x762123c | 1);
+}
+
+/* 16 bit code test */
+extern char code16_start, code16_end;
+extern char code16_func1;
+extern char code16_func2;
+extern char code16_func3;
+
+void test_code16(void)
+{
+    struct modify_ldt_ldt_s ldt;
+    int res, res2;
+
+    /* build a code segment */
+    ldt.entry_number = 1;
+    ldt.base_addr = (unsigned long)&code16_start;
+    ldt.limit = &code16_end - &code16_start;
+    ldt.seg_32bit = 0;
+    ldt.contents = MODIFY_LDT_CONTENTS_CODE;
+    ldt.read_exec_only = 0;
+    ldt.limit_in_pages = 0;
+    ldt.seg_not_present = 0;
+    ldt.useable = 1;
+    modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+    /* call the first function */
+    asm volatile ("lcall %1, %2"
+                  : "=a" (res)
+                  : "i" (MK_SEL(1)), "i" (&code16_func1): "memory", "cc");
+    printf("func1() = 0x%08x\n", res);
+    asm volatile ("lcall %2, %3"
+                  : "=a" (res), "=c" (res2)
+                  : "i" (MK_SEL(1)), "i" (&code16_func2): "memory", "cc");
+    printf("func2() = 0x%08x spdec=%d\n", res, res2);
+    asm volatile ("lcall %1, %2"
+                  : "=a" (res)
+                  : "i" (MK_SEL(1)), "i" (&code16_func3): "memory", "cc");
+    printf("func3() = 0x%08x\n", res);
+}
+#endif
+
+#if defined(__x86_64__)
+asm(".globl func_lret\n"
+    "func_lret:\n"
+    "movl $0x87654641, %eax\n"
+    "lretq\n");
+#else
+asm(".globl func_lret\n"
+    "func_lret:\n"
+    "movl $0x87654321, %eax\n"
+    "lret\n"
+
+    ".globl func_iret\n"
+    "func_iret:\n"
+    "movl $0xabcd4321, %eax\n"
+    "iret\n");
+#endif
+
+extern char func_lret;
+extern char func_iret;
+
+void test_misc(void)
+{
+    char table[256];
+    long res, i;
+
+    for(i=0;i<256;i++) table[i] = 256 - i;
+    res = 0x12345678;
+    asm ("xlat" : "=a" (res) : "b" (table), "0" (res));
+    printf("xlat: EAX=" FMTLX "\n", res);
+
+#if defined(__x86_64__)
+#if 0
+    {
+        /* XXX: see if Intel Core2 and AMD64 behavior really
+           differ. Here we implemented the Intel way which is not
+           compatible yet with QEMU. */
+        static struct QEMU_PACKED {
+            uint64_t offset;
+            uint16_t seg;
+        } desc;
+        long cs_sel;
+
+        asm volatile ("mov %%cs, %0" : "=r" (cs_sel));
+
+        asm volatile ("push %1\n"
+                      "call func_lret\n"
+                      : "=a" (res)
+                      : "r" (cs_sel) : "memory", "cc");
+        printf("func_lret=" FMTLX "\n", res);
+
+        desc.offset = (long)&func_lret;
+        desc.seg = cs_sel;
+
+        asm volatile ("xor %%rax, %%rax\n"
+                      "rex64 lcall *(%%rcx)\n"
+                      : "=a" (res)
+                      : "c" (&desc)
+                      : "memory", "cc");
+        printf("func_lret2=" FMTLX "\n", res);
+
+        asm volatile ("push %2\n"
+                      "mov $ 1f, %%rax\n"
+                      "push %%rax\n"
+                      "rex64 ljmp *(%%rcx)\n"
+                      "1:\n"
+                      : "=a" (res)
+                      : "c" (&desc), "b" (cs_sel)
+                      : "memory", "cc");
+        printf("func_lret3=" FMTLX "\n", res);
+    }
+#endif
+#else
+    asm volatile ("push %%cs ; call %1"
+                  : "=a" (res)
+                  : "m" (func_lret): "memory", "cc");
+    printf("func_lret=" FMTLX "\n", res);
+
+    asm volatile ("pushf ; push %%cs ; call %1"
+                  : "=a" (res)
+                  : "m" (func_iret): "memory", "cc");
+    printf("func_iret=" FMTLX "\n", res);
+#endif
+
+#if defined(__x86_64__)
+    /* specific popl test */
+    asm volatile ("push $12345432 ; push $0x9abcdef ; pop (%%rsp) ; pop %0"
+                  : "=g" (res));
+    printf("popl esp=" FMTLX "\n", res);
+#else
+    /* specific popl test */
+    asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popl (%%esp) ; popl %0"
+                  : "=g" (res));
+    printf("popl esp=" FMTLX "\n", res);
+
+    /* specific popw test */
+    asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popw (%%esp) ; addl $2, %%esp ; popl %0"
+                  : "=g" (res));
+    printf("popw esp=" FMTLX "\n", res);
+#endif
+}
+
+uint8_t str_buffer[4096];
+
+#define TEST_STRING1(OP, size, DF, REP)\
+{\
+    long esi, edi, eax, ecx, eflags;\
+\
+    esi = (long)(str_buffer + sizeof(str_buffer) / 2);\
+    edi = (long)(str_buffer + sizeof(str_buffer) / 2) + 16;\
+    eax = i2l(0x12345678);\
+    ecx = 17;\
+\
+    asm volatile ("push $0\n\t"\
+                  "popf\n\t"\
+                  DF "\n\t"\
+                  REP #OP size "\n\t"\
+                  "cld\n\t"\
+                  "pushf\n\t"\
+                  "pop %4\n\t"\
+                  : "=S" (esi), "=D" (edi), "=a" (eax), "=c" (ecx), "=g" (eflags)\
+                  : "0" (esi), "1" (edi), "2" (eax), "3" (ecx));\
+    printf("%-10s ESI=" FMTLX " EDI=" FMTLX " EAX=" FMTLX " ECX=" FMTLX " EFL=%04x\n",\
+           REP #OP size, esi, edi, eax, ecx,\
+           (int)(eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)));\
+}
+
+#define TEST_STRING(OP, REP)\
+    TEST_STRING1(OP, "b", "", REP);\
+    TEST_STRING1(OP, "w", "", REP);\
+    TEST_STRING1(OP, "l", "", REP);\
+    X86_64_ONLY(TEST_STRING1(OP, "q", "", REP));\
+    TEST_STRING1(OP, "b", "std", REP);\
+    TEST_STRING1(OP, "w", "std", REP);\
+    TEST_STRING1(OP, "l", "std", REP);\
+    X86_64_ONLY(TEST_STRING1(OP, "q", "std", REP))
+
+void test_string(void)
+{
+    int i;
+    for(i = 0;i < sizeof(str_buffer); i++)
+        str_buffer[i] = i + 0x56;
+   TEST_STRING(stos, "");
+   TEST_STRING(stos, "rep ");
+   TEST_STRING(lods, ""); /* to verify stos */
+   TEST_STRING(lods, "rep ");
+   TEST_STRING(movs, "");
+   TEST_STRING(movs, "rep ");
+   TEST_STRING(lods, ""); /* to verify stos */
+
+   /* XXX: better tests */
+   TEST_STRING(scas, "");
+   TEST_STRING(scas, "repz ");
+   TEST_STRING(scas, "repnz ");
+   TEST_STRING(cmps, "");
+   TEST_STRING(cmps, "repz ");
+   TEST_STRING(cmps, "repnz ");
+}
+
+#ifdef TEST_VM86
+/* VM86 test */
+
+static inline void set_bit(uint8_t *a, unsigned int bit)
+{
+    a[bit / 8] |= (1 << (bit % 8));
+}
+
+static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
+{
+    return (uint8_t *)((seg << 4) + (reg & 0xffff));
+}
+
+static inline void pushw(struct vm86_regs *r, int val)
+{
+    r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
+    *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
+}
+
+static inline int vm86(int func, struct vm86plus_struct *v86)
+{
+    return syscall(__NR_vm86, func, v86);
+}
+
+extern char vm86_code_start;
+extern char vm86_code_end;
+
+#define VM86_CODE_CS 0x100
+#define VM86_CODE_IP 0x100
+
+void test_vm86(void)
+{
+    struct vm86plus_struct ctx;
+    struct vm86_regs *r;
+    uint8_t *vm86_mem;
+    int seg, ret;
+
+    vm86_mem = mmap((void *)0x00000000, 0x110000,
+                    PROT_WRITE | PROT_READ | PROT_EXEC,
+                    MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
+    if (vm86_mem == MAP_FAILED) {
+        printf("ERROR: could not map vm86 memory");
+        return;
+    }
+    memset(&ctx, 0, sizeof(ctx));
+
+    /* init basic registers */
+    r = &ctx.regs;
+    r->eip = VM86_CODE_IP;
+    r->esp = 0xfffe;
+    seg = VM86_CODE_CS;
+    r->cs = seg;
+    r->ss = seg;
+    r->ds = seg;
+    r->es = seg;
+    r->fs = seg;
+    r->gs = seg;
+    r->eflags = VIF_MASK;
+
+    /* move code to proper address. We use the same layout as a .com
+       dos program. */
+    memcpy(vm86_mem + (VM86_CODE_CS << 4) + VM86_CODE_IP,
+           &vm86_code_start, &vm86_code_end - &vm86_code_start);
+
+    /* mark int 0x21 as being emulated */
+    set_bit((uint8_t *)&ctx.int_revectored, 0x21);
+
+    for(;;) {
+        ret = vm86(VM86_ENTER, &ctx);
+        switch(VM86_TYPE(ret)) {
+        case VM86_INTx:
+            {
+                int int_num, ah, v;
+
+                int_num = VM86_ARG(ret);
+                if (int_num != 0x21)
+                    goto unknown_int;
+                ah = (r->eax >> 8) & 0xff;
+                switch(ah) {
+                case 0x00: /* exit */
+                    goto the_end;
+                case 0x02: /* write char */
+                    {
+                        uint8_t c = r->edx;
+                        putchar(c);
+                    }
+                    break;
+                case 0x09: /* write string */
+                    {
+                        uint8_t c, *ptr;
+                        ptr = seg_to_linear(r->ds, r->edx);
+                        for(;;) {
+                            c = *ptr++;
+                            if (c == '$')
+                                break;
+                            putchar(c);
+                        }
+                        r->eax = (r->eax & ~0xff) | '$';
+                    }
+                    break;
+                case 0xff: /* extension: write eflags number in edx */
+                    v = (int)r->edx;
+#ifndef LINUX_VM86_IOPL_FIX
+                    v &= ~0x3000;
+#endif
+                    printf("%08x\n", v);
+                    break;
+                default:
+                unknown_int:
+                    printf("unsupported int 0x%02x\n", int_num);
+                    goto the_end;
+                }
+            }
+            break;
+        case VM86_SIGNAL:
+            /* a signal came, we just ignore that */
+            break;
+        case VM86_STI:
+            break;
+        default:
+            printf("ERROR: unhandled vm86 return code (0x%x)\n", ret);
+            goto the_end;
+        }
+    }
+ the_end:
+    printf("VM86 end\n");
+    munmap(vm86_mem, 0x110000);
+}
+#endif
+
+/* exception tests */
+#if defined(__i386__) && !defined(REG_EAX)
+#define REG_EAX EAX
+#define REG_EBX EBX
+#define REG_ECX ECX
+#define REG_EDX EDX
+#define REG_ESI ESI
+#define REG_EDI EDI
+#define REG_EBP EBP
+#define REG_ESP ESP
+#define REG_EIP EIP
+#define REG_EFL EFL
+#define REG_TRAPNO TRAPNO
+#define REG_ERR ERR
+#endif
+
+#if defined(__x86_64__)
+#define REG_EIP REG_RIP
+#endif
+
+jmp_buf jmp_env;
+int v1;
+int tab[2];
+
+void sig_handler(int sig, siginfo_t *info, void *puc)
+{
+    struct ucontext *uc = puc;
+
+    printf("si_signo=%d si_errno=%d si_code=%d",
+           info->si_signo, info->si_errno, info->si_code);
+    printf(" si_addr=0x%08lx",
+           (unsigned long)info->si_addr);
+    printf("\n");
+
+    printf("trapno=" FMTLX " err=" FMTLX,
+           (long)uc->uc_mcontext.gregs[REG_TRAPNO],
+           (long)uc->uc_mcontext.gregs[REG_ERR]);
+    printf(" EIP=" FMTLX, (long)uc->uc_mcontext.gregs[REG_EIP]);
+    printf("\n");
+    longjmp(jmp_env, 1);
+}
+
+void test_exceptions(void)
+{
+    struct sigaction act;
+    volatile int val;
+
+    act.sa_sigaction = sig_handler;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = SA_SIGINFO | SA_NODEFER;
+    sigaction(SIGFPE, &act, NULL);
+    sigaction(SIGILL, &act, NULL);
+    sigaction(SIGSEGV, &act, NULL);
+    sigaction(SIGBUS, &act, NULL);
+    sigaction(SIGTRAP, &act, NULL);
+
+    /* test division by zero reporting */
+    printf("DIVZ exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        /* now divide by zero */
+        v1 = 0;
+        v1 = 2 / v1;
+    }
+
+#if 0
+#if !defined(__x86_64__)
+    printf("BOUND exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        /* bound exception */
+        tab[0] = 1;
+        tab[1] = 10;
+        asm volatile ("bound %0, %1" : : "r" (11), "m" (tab[0]));
+    }
+#endif
+#endif
+
+#ifdef TEST_SEGS
+    printf("segment exceptions:\n");
+    if (setjmp(jmp_env) == 0) {
+        /* load an invalid segment */
+        asm volatile ("movl %0, %%fs" : : "r" ((0x1234 << 3) | 1));
+    }
+    if (setjmp(jmp_env) == 0) {
+        /* null data segment is valid */
+        asm volatile ("movl %0, %%fs" : : "r" (3));
+        /* null stack segment */
+        asm volatile ("movl %0, %%ss" : : "r" (3));
+    }
+
+    {
+        struct modify_ldt_ldt_s ldt;
+        ldt.entry_number = 1;
+        ldt.base_addr = (unsigned long)&seg_data1;
+        ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;
+        ldt.seg_32bit = 1;
+        ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+        ldt.read_exec_only = 0;
+        ldt.limit_in_pages = 1;
+        ldt.seg_not_present = 1;
+        ldt.useable = 1;
+        modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+        if (setjmp(jmp_env) == 0) {
+            /* segment not present */
+            asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1)));
+        }
+    }
+#endif
+
+    /* test SEGV reporting */
+    printf("PF exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        val = 1;
+        /* we add a nop to test a weird PC retrieval case */
+        asm volatile ("nop");
+        /* now store in an invalid address */
+        *(char *)0x1234 = 1;
+    }
+
+    /* test SEGV reporting */
+    printf("PF exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        val = 1;
+        /* read from an invalid address */
+        v1 = *(char *)0x1234;
+    }
+
+    /* test illegal instruction reporting */
+    printf("UD2 exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        /* now execute an invalid instruction */
+        asm volatile("ud2");
+    }
+#if 0
+    printf("lock nop exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        /* now execute an invalid instruction */
+        asm volatile(".byte 0xf0, 0x90"); /* lock nop */
+    }
+#endif
+
+    printf("INT exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("int $0xfd");
+    }
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("int $0x01");
+    }
+    if (setjmp(jmp_env) == 0) {
+        asm volatile (".byte 0xcd, 0x03");
+    }
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("int $0x04");
+    }
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("int $0x05");
+    }
+
+    printf("INT3 exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("int3");
+    }
+
+    printf("CLI exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("cli");
+    }
+
+    printf("STI exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("cli");
+    }
+
+#if !defined(__x86_64__)
+    printf("INTO exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        /* overflow exception */
+        asm volatile ("addl $1, %0 ; into" : : "r" (0x7fffffff));
+    }
+#endif
+
+    printf("OUTB exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("outb %%al, %%dx" : : "d" (0x4321), "a" (0));
+    }
+
+    printf("INB exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("inb %%dx, %%al" : "=a" (val) : "d" (0x4321));
+    }
+
+    printf("REP OUTSB exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("rep outsb" : : "d" (0x4321), "S" (tab), "c" (1));
+    }
+
+    printf("REP INSB exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("rep insb" : : "d" (0x4321), "D" (tab), "c" (1));
+    }
+
+    printf("HLT exception:\n");
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("hlt");
+    }
+
+#if 0
+    printf("single step exception:\n");
+    val = 0;
+    if (setjmp(jmp_env) == 0) {
+        asm volatile ("pushf\n"
+                      "orl $0x00100, (%%esp)\n"
+                      "popf\n"
+                      "movl $0xabcd, %0\n"
+                      "movl $0x0, %0\n" : "=m" (val) : : "cc", "memory");
+    }
+    printf("val=0x%x\n", val);
+#endif
+}
+
+
+#if !defined(__x86_64__)
+/* specific precise single step test */
+void sig_trap_handler(int sig, siginfo_t *info, void *puc)
+{
+    struct ucontext *uc = puc;
+    printf("EIP=" FMTLX "\n", (long)uc->uc_mcontext.gregs[REG_EIP]);
+}
+
+const uint8_t sstep_buf1[4] = { 1, 2, 3, 4};
+uint8_t sstep_buf2[4];
+
+void test_single_step(void)
+{
+    struct sigaction act;
+    volatile int val;
+    int i;
+
+    val = 0;
+    act.sa_sigaction = sig_trap_handler;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = SA_SIGINFO;
+    sigaction(SIGTRAP, &act, NULL);
+    asm volatile ("pushf\n"
+                  "orl $0x00100, (%%esp)\n"
+                  "popf\n"
+                  "movl $0xabcd, %0\n"
+
+                  /* jmp test */
+                  "movl $3, %%ecx\n"
+                  "1:\n"
+                  "addl $1, %0\n"
+                  "decl %%ecx\n"
+                  "jnz 1b\n"
+
+                  /* movsb: the single step should stop at each movsb iteration */
+                  "movl $sstep_buf1, %%esi\n"
+                  "movl $sstep_buf2, %%edi\n"
+                  "movl $0, %%ecx\n"
+                  "rep movsb\n"
+                  "movl $3, %%ecx\n"
+                  "rep movsb\n"
+                  "movl $1, %%ecx\n"
+                  "rep movsb\n"
+
+                  /* cmpsb: the single step should stop at each cmpsb iteration */
+                  "movl $sstep_buf1, %%esi\n"
+                  "movl $sstep_buf2, %%edi\n"
+                  "movl $0, %%ecx\n"
+                  "rep cmpsb\n"
+                  "movl $4, %%ecx\n"
+                  "rep cmpsb\n"
+
+                  /* getpid() syscall: single step should skip one
+                     instruction */
+                  "movl $20, %%eax\n"
+                  "int $0x80\n"
+                  "movl $0, %%eax\n"
+
+                  /* when modifying SS, trace is not done on the next
+                     instruction */
+                  "movl %%ss, %%ecx\n"
+                  "movl %%ecx, %%ss\n"
+                  "addl $1, %0\n"
+                  "movl $1, %%eax\n"
+                  "movl %%ecx, %%ss\n"
+                  "jmp 1f\n"
+                  "addl $1, %0\n"
+                  "1:\n"
+                  "movl $1, %%eax\n"
+                  "pushl %%ecx\n"
+                  "popl %%ss\n"
+                  "addl $1, %0\n"
+                  "movl $1, %%eax\n"
+
+                  "pushf\n"
+                  "andl $~0x00100, (%%esp)\n"
+                  "popf\n"
+                  : "=m" (val)
+                  :
+                  : "cc", "memory", "eax", "ecx", "esi", "edi");
+    printf("val=%d\n", val);
+    for(i = 0; i < 4; i++)
+        printf("sstep_buf2[%d] = %d\n", i, sstep_buf2[i]);
+}
+
+/* self modifying code test */
+uint8_t code[] = {
+    0xb8, 0x1, 0x00, 0x00, 0x00, /* movl $1, %eax */
+    0xc3, /* ret */
+};
+
+asm(".section \".data\"\n"
+    "smc_code2:\n"
+    "movl 4(%esp), %eax\n"
+    "movl %eax, smc_patch_addr2 + 1\n"
+    "nop\n"
+    "nop\n"
+    "nop\n"
+    "nop\n"
+    "nop\n"
+    "nop\n"
+    "nop\n"
+    "nop\n"
+    "smc_patch_addr2:\n"
+    "movl $1, %eax\n"
+    "ret\n"
+    ".previous\n"
+    );
+
+typedef int FuncType(void);
+extern int smc_code2(int);
+void test_self_modifying_code(void)
+{
+    int i;
+    printf("self modifying code:\n");
+    printf("func1 = 0x%x\n", ((FuncType *)code)());
+    for(i = 2; i <= 4; i++) {
+        code[1] = i;
+        printf("func%d = 0x%x\n", i, ((FuncType *)code)());
+    }
+
+    /* more difficult test : the modified code is just after the
+       modifying instruction. It is forbidden in Intel specs, but it
+       is used by old DOS programs */
+    for(i = 2; i <= 4; i++) {
+        printf("smc_code2(%d) = %d\n", i, smc_code2(i));
+    }
+}
+#endif
+
+long enter_stack[4096];
+
+#if defined(__x86_64__)
+#define RSP "%%rsp"
+#define RBP "%%rbp"
+#else
+#define RSP "%%esp"
+#define RBP "%%ebp"
+#endif
+
+#if !defined(__x86_64__)
+/* causes an infinite loop, disable it for now.  */
+#define TEST_ENTER(size, stack_type, level)
+#else
+#define TEST_ENTER(size, stack_type, level)\
+{\
+    long esp_save, esp_val, ebp_val, ebp_save, i;\
+    stack_type *ptr, *stack_end, *stack_ptr;\
+    memset(enter_stack, 0, sizeof(enter_stack));\
+    stack_end = stack_ptr = (stack_type *)(enter_stack + 4096);\
+    ebp_val = (long)stack_ptr;\
+    for(i=1;i<=32;i++)\
+       *--stack_ptr = i;\
+    esp_val = (long)stack_ptr;\
+    asm("mov " RSP ", %[esp_save]\n"\
+        "mov " RBP ", %[ebp_save]\n"\
+        "mov %[esp_val], " RSP "\n"\
+        "mov %[ebp_val], " RBP "\n"\
+        "enter" size " $8, $" #level "\n"\
+        "mov " RSP ", %[esp_val]\n"\
+        "mov " RBP ", %[ebp_val]\n"\
+        "mov %[esp_save], " RSP "\n"\
+        "mov %[ebp_save], " RBP "\n"\
+        : [esp_save] "=r" (esp_save),\
+        [ebp_save] "=r" (ebp_save),\
+        [esp_val] "=r" (esp_val),\
+        [ebp_val] "=r" (ebp_val)\
+        :  "[esp_val]" (esp_val),\
+        "[ebp_val]" (ebp_val));\
+    printf("level=%d:\n", level);\
+    printf("esp_val=" FMTLX "\n", esp_val - (long)stack_end);\
+    printf("ebp_val=" FMTLX "\n", ebp_val - (long)stack_end);\
+    for(ptr = (stack_type *)esp_val; ptr < stack_end; ptr++)\
+        printf(FMTLX "\n", (long)ptr[0]);\
+}
+#endif
+
+static void test_enter(void)
+{
+#if defined(__x86_64__)
+    TEST_ENTER("q", uint64_t, 0);
+    TEST_ENTER("q", uint64_t, 1);
+    TEST_ENTER("q", uint64_t, 2);
+    TEST_ENTER("q", uint64_t, 31);
+#else
+    TEST_ENTER("l", uint32_t, 0);
+    TEST_ENTER("l", uint32_t, 1);
+    TEST_ENTER("l", uint32_t, 2);
+    TEST_ENTER("l", uint32_t, 31);
+#endif
+
+    TEST_ENTER("w", uint16_t, 0);
+    TEST_ENTER("w", uint16_t, 1);
+    TEST_ENTER("w", uint16_t, 2);
+    TEST_ENTER("w", uint16_t, 31);
+}
+
+#ifdef TEST_SSE
+
+typedef int __m64 __attribute__ ((__mode__ (__V2SI__)));
+typedef float __m128 __attribute__ ((__mode__(__V4SF__)));
+
+typedef union {
+    double d[2];
+    float s[4];
+    uint32_t l[4];
+    uint64_t q[2];
+    __m128 dq;
+} XMMReg;
+
+static uint64_t __attribute__((aligned(16))) test_values[4][2] = {
+    { 0x456723c698694873, 0xdc515cff944a58ec },
+    { 0x1f297ccd58bad7ab, 0x41f21efba9e3e146 },
+    { 0x007c62c2085427f8, 0x231be9e8cde7438d },
+    { 0x0f76255a085427f8, 0xc233e9e8c4c9439a },
+};
+
+#define SSE_OP(op)\
+{\
+    asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           b.q[1], b.q[0],\
+           r.q[1], r.q[0]);\
+}
+
+#define SSE_OP2(op)\
+{\
+    int i;\
+    for(i=0;i<2;i++) {\
+    a.q[0] = test_values[2*i][0];\
+    a.q[1] = test_values[2*i][1];\
+    b.q[0] = test_values[2*i+1][0];\
+    b.q[1] = test_values[2*i+1][1];\
+    SSE_OP(op);\
+    }\
+}
+
+#define MMX_OP2(op)\
+{\
+    int i;\
+    for(i=0;i<2;i++) {\
+    a.q[0] = test_values[2*i][0];\
+    b.q[0] = test_values[2*i+1][0];\
+    asm volatile (#op " %2, %0" : "=y" (r.q[0]) : "0" (a.q[0]), "y" (b.q[0]));\
+    printf("%-9s: a=" FMT64X " b=" FMT64X " r=" FMT64X "\n",\
+           #op,\
+           a.q[0],\
+           b.q[0],\
+           r.q[0]);\
+    }\
+    SSE_OP2(op);\
+}
+
+#define SHUF_OP(op, ib)\
+{\
+    a.q[0] = test_values[0][0];\
+    a.q[1] = test_values[0][1];\
+    b.q[0] = test_values[1][0];\
+    b.q[1] = test_values[1][1];\
+    asm volatile (#op " $" #ib ", %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           b.q[1], b.q[0],\
+           ib,\
+           r.q[1], r.q[0]);\
+}
+
+#define PSHUF_OP(op, ib)\
+{\
+    int i;\
+    for(i=0;i<2;i++) {\
+    a.q[0] = test_values[2*i][0];\
+    a.q[1] = test_values[2*i][1];\
+    asm volatile (#op " $" #ib ", %1, %0" : "=x" (r.dq) : "x" (a.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           ib,\
+           r.q[1], r.q[0]);\
+    }\
+}
+
+#define SHIFT_IM(op, ib)\
+{\
+    int i;\
+    for(i=0;i<2;i++) {\
+    a.q[0] = test_values[2*i][0];\
+    a.q[1] = test_values[2*i][1];\
+    asm volatile (#op " $" #ib ", %0" : "=x" (r.dq) : "0" (a.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           ib,\
+           r.q[1], r.q[0]);\
+    }\
+}
+
+#define SHIFT_OP(op, ib)\
+{\
+    int i;\
+    SHIFT_IM(op, ib);\
+    for(i=0;i<2;i++) {\
+    a.q[0] = test_values[2*i][0];\
+    a.q[1] = test_values[2*i][1];\
+    b.q[0] = ib;\
+    b.q[1] = 0;\
+    asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           b.q[1], b.q[0],\
+           r.q[1], r.q[0]);\
+    }\
+}
+
+#define MOVMSK(op)\
+{\
+    int i, reg;\
+    for(i=0;i<2;i++) {\
+    a.q[0] = test_values[2*i][0];\
+    a.q[1] = test_values[2*i][1];\
+    asm volatile (#op " %1, %0" : "=r" (reg) : "x" (a.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           reg);\
+    }\
+}
+
+#define SSE_OPS(a) \
+SSE_OP(a ## ps);\
+SSE_OP(a ## ss);
+
+#define SSE_OPD(a) \
+SSE_OP(a ## pd);\
+SSE_OP(a ## sd);
+
+#define SSE_COMI(op, field)\
+{\
+    unsigned int eflags;\
+    XMMReg a, b;\
+    a.field[0] = a1;\
+    b.field[0] = b1;\
+    asm volatile (#op " %2, %1\n"\
+        "pushf\n"\
+        "pop %0\n"\
+        : "=m" (eflags)\
+        : "x" (a.dq), "x" (b.dq));\
+    printf("%-9s: a=%f b=%f cc=%04x\n",\
+           #op, a1, b1,\
+           eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\
+}
+
+void test_sse_comi(double a1, double b1)
+{
+    SSE_COMI(ucomiss, s);
+    SSE_COMI(ucomisd, d);
+    SSE_COMI(comiss, s);
+    SSE_COMI(comisd, d);
+}
+
+#define CVT_OP_XMM(op)\
+{\
+    asm volatile (#op " %1, %0" : "=x" (r.dq) : "x" (a.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           r.q[1], r.q[0]);\
+}
+
+/* Force %xmm0 usage to avoid the case where both register index are 0
+   to test instruction decoding more extensively */
+#define CVT_OP_XMM2MMX(op)\
+{\
+    asm volatile (#op " %1, %0" : "=y" (r.q[0]) : "x" (a.dq) \
+                  : "%xmm0"); \
+    asm volatile("emms\n"); \
+    printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           r.q[0]);\
+}
+
+#define CVT_OP_MMX2XMM(op)\
+{\
+    asm volatile (#op " %1, %0" : "=x" (r.dq) : "y" (a.q[0]));\
+    asm volatile("emms\n"); \
+    printf("%-9s: a=" FMT64X " r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.q[0],\
+           r.q[1], r.q[0]);\
+}
+
+#define CVT_OP_REG2XMM(op)\
+{\
+    asm volatile (#op " %1, %0" : "=x" (r.dq) : "r" (a.l[0]));\
+    printf("%-9s: a=%08x r=" FMT64X "" FMT64X "\n",\
+           #op,\
+           a.l[0],\
+           r.q[1], r.q[0]);\
+}
+
+#define CVT_OP_XMM2REG(op)\
+{\
+    asm volatile (#op " %1, %0" : "=r" (r.l[0]) : "x" (a.dq));\
+    printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
+           #op,\
+           a.q[1], a.q[0],\
+           r.l[0]);\
+}
+
+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[8 * 16];
+    uint8_t dummy2[224];
+};
+
+static struct fpxstate fpx_state __attribute__((aligned(16)));
+static struct fpxstate fpx_state2 __attribute__((aligned(16)));
+
+void test_fxsave(void)
+{
+    struct fpxstate *fp = &fpx_state;
+    struct fpxstate *fp2 = &fpx_state2;
+    int i, nb_xmm;
+    XMMReg a, b;
+    a.q[0] = test_values[0][0];
+    a.q[1] = test_values[0][1];
+    b.q[0] = test_values[1][0];
+    b.q[1] = test_values[1][1];
+
+    asm("movdqa %2, %%xmm0\n"
+        "movdqa %3, %%xmm7\n"
+#if defined(__x86_64__)
+        "movdqa %2, %%xmm15\n"
+#endif
+        " fld1\n"
+        " fldpi\n"
+        " fldln2\n"
+        " fxsave %0\n"
+        " fxrstor %0\n"
+        " fxsave %1\n"
+        " fninit\n"
+        : "=m" (*(uint32_t *)fp2), "=m" (*(uint32_t *)fp)
+        : "m" (a), "m" (b));
+    printf("fpuc=%04x\n", fp->fpuc);
+    printf("fpus=%04x\n", fp->fpus);
+    printf("fptag=%04x\n", fp->fptag);
+    for(i = 0; i < 3; i++) {
+        printf("ST%d: " FMT64X " %04x\n",
+               i,
+               *(uint64_t *)&fp->fpregs1[i * 16],
+               *(uint16_t *)&fp->fpregs1[i * 16 + 8]);
+    }
+    printf("mxcsr=%08x\n", fp->mxcsr & 0x1f80);
+#if defined(__x86_64__)
+    nb_xmm = 16;
+#else
+    nb_xmm = 8;
+#endif
+    for(i = 0; i < nb_xmm; i++) {
+        printf("xmm%d: " FMT64X "" FMT64X "\n",
+               i,
+               *(uint64_t *)&fp->xmm_regs[i * 16],
+               *(uint64_t *)&fp->xmm_regs[i * 16 + 8]);
+    }
+}
+
+void test_sse(void)
+{
+    XMMReg r, a, b;
+    int i;
+
+    MMX_OP2(punpcklbw);
+    MMX_OP2(punpcklwd);
+    MMX_OP2(punpckldq);
+    MMX_OP2(packsswb);
+    MMX_OP2(pcmpgtb);
+    MMX_OP2(pcmpgtw);
+    MMX_OP2(pcmpgtd);
+    MMX_OP2(packuswb);
+    MMX_OP2(punpckhbw);
+    MMX_OP2(punpckhwd);
+    MMX_OP2(punpckhdq);
+    MMX_OP2(packssdw);
+    MMX_OP2(pcmpeqb);
+    MMX_OP2(pcmpeqw);
+    MMX_OP2(pcmpeqd);
+
+    MMX_OP2(paddq);
+    MMX_OP2(pmullw);
+    MMX_OP2(psubusb);
+    MMX_OP2(psubusw);
+    MMX_OP2(pminub);
+    MMX_OP2(pand);
+    MMX_OP2(paddusb);
+    MMX_OP2(paddusw);
+    MMX_OP2(pmaxub);
+    MMX_OP2(pandn);
+
+    MMX_OP2(pmulhuw);
+    MMX_OP2(pmulhw);
+
+    MMX_OP2(psubsb);
+    MMX_OP2(psubsw);
+    MMX_OP2(pminsw);
+    MMX_OP2(por);
+    MMX_OP2(paddsb);
+    MMX_OP2(paddsw);
+    MMX_OP2(pmaxsw);
+    MMX_OP2(pxor);
+    MMX_OP2(pmuludq);
+    MMX_OP2(pmaddwd);
+    MMX_OP2(psadbw);
+    MMX_OP2(psubb);
+    MMX_OP2(psubw);
+    MMX_OP2(psubd);
+    MMX_OP2(psubq);
+    MMX_OP2(paddb);
+    MMX_OP2(paddw);
+    MMX_OP2(paddd);
+
+    MMX_OP2(pavgb);
+    MMX_OP2(pavgw);
+
+    asm volatile ("pinsrw $1, %1, %0" : "=y" (r.q[0]) : "r" (0x12345678));
+    printf("%-9s: r=" FMT64X "\n", "pinsrw", r.q[0]);
+
+    asm volatile ("pinsrw $5, %1, %0" : "=x" (r.dq) : "r" (0x12345678));
+    printf("%-9s: r=" FMT64X "" FMT64X "\n", "pinsrw", r.q[1], r.q[0]);
+
+    a.q[0] = test_values[0][0];
+    a.q[1] = test_values[0][1];
+    asm volatile ("pextrw $1, %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
+    printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
+
+    asm volatile ("pextrw $5, %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
+    printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
+
+    asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
+    printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
+
+    asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
+    printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
+
+    {
+        r.q[0] = -1;
+        r.q[1] = -1;
+
+        a.q[0] = test_values[0][0];
+        a.q[1] = test_values[0][1];
+        b.q[0] = test_values[1][0];
+        b.q[1] = test_values[1][1];
+        asm volatile("maskmovq %1, %0" :
+                     : "y" (a.q[0]), "y" (b.q[0]), "D" (&r)
+                     : "memory");
+        printf("%-9s: r=" FMT64X " a=" FMT64X " b=" FMT64X "\n",
+               "maskmov",
+               r.q[0],
+               a.q[0],
+               b.q[0]);
+        asm volatile("maskmovdqu %1, %0" :
+                     : "x" (a.dq), "x" (b.dq), "D" (&r)
+                     : "memory");
+        printf("%-9s: r=" FMT64X "" FMT64X " a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X "\n",
+               "maskmov",
+               r.q[1], r.q[0],
+               a.q[1], a.q[0],
+               b.q[1], b.q[0]);
+    }
+
+    asm volatile ("emms");
+
+    SSE_OP2(punpcklqdq);
+    SSE_OP2(punpckhqdq);
+    SSE_OP2(andps);
+    SSE_OP2(andpd);
+    SSE_OP2(andnps);
+    SSE_OP2(andnpd);
+    SSE_OP2(orps);
+    SSE_OP2(orpd);
+    SSE_OP2(xorps);
+    SSE_OP2(xorpd);
+
+    SSE_OP2(unpcklps);
+    SSE_OP2(unpcklpd);
+    SSE_OP2(unpckhps);
+    SSE_OP2(unpckhpd);
+
+    SHUF_OP(shufps, 0x78);
+    SHUF_OP(shufpd, 0x02);
+
+    PSHUF_OP(pshufd, 0x78);
+    PSHUF_OP(pshuflw, 0x78);
+    PSHUF_OP(pshufhw, 0x78);
+
+    SHIFT_OP(psrlw, 7);
+    SHIFT_OP(psrlw, 16);
+    SHIFT_OP(psraw, 7);
+    SHIFT_OP(psraw, 16);
+    SHIFT_OP(psllw, 7);
+    SHIFT_OP(psllw, 16);
+
+    SHIFT_OP(psrld, 7);
+    SHIFT_OP(psrld, 32);
+    SHIFT_OP(psrad, 7);
+    SHIFT_OP(psrad, 32);
+    SHIFT_OP(pslld, 7);
+    SHIFT_OP(pslld, 32);
+
+    SHIFT_OP(psrlq, 7);
+    SHIFT_OP(psrlq, 32);
+    SHIFT_OP(psllq, 7);
+    SHIFT_OP(psllq, 32);
+
+    SHIFT_IM(psrldq, 16);
+    SHIFT_IM(psrldq, 7);
+    SHIFT_IM(pslldq, 16);
+    SHIFT_IM(pslldq, 7);
+
+    MOVMSK(movmskps);
+    MOVMSK(movmskpd);
+
+    /* FPU specific ops */
+
+    {
+        uint32_t mxcsr;
+        asm volatile("stmxcsr %0" : "=m" (mxcsr));
+        printf("mxcsr=%08x\n", mxcsr & 0x1f80);
+        asm volatile("ldmxcsr %0" : : "m" (mxcsr));
+    }
+
+    test_sse_comi(2, -1);
+    test_sse_comi(2, 2);
+    test_sse_comi(2, 3);
+    test_sse_comi(2, q_nan.d);
+    test_sse_comi(q_nan.d, -1);
+
+    for(i = 0; i < 2; i++) {
+        a.s[0] = 2.7;
+        a.s[1] = 3.4;
+        a.s[2] = 4;
+        a.s[3] = -6.3;
+        b.s[0] = 45.7;
+        b.s[1] = 353.4;
+        b.s[2] = 4;
+        b.s[3] = 56.3;
+        if (i == 1) {
+            a.s[0] = q_nan.d;
+            b.s[3] = q_nan.d;
+        }
+
+        SSE_OPS(add);
+        SSE_OPS(mul);
+        SSE_OPS(sub);
+        SSE_OPS(min);
+        SSE_OPS(div);
+        SSE_OPS(max);
+        SSE_OPS(sqrt);
+        SSE_OPS(cmpeq);
+        SSE_OPS(cmplt);
+        SSE_OPS(cmple);
+        SSE_OPS(cmpunord);
+        SSE_OPS(cmpneq);
+        SSE_OPS(cmpnlt);
+        SSE_OPS(cmpnle);
+        SSE_OPS(cmpord);
+
+
+        a.d[0] = 2.7;
+        a.d[1] = -3.4;
+        b.d[0] = 45.7;
+        b.d[1] = -53.4;
+        if (i == 1) {
+            a.d[0] = q_nan.d;
+            b.d[1] = q_nan.d;
+        }
+        SSE_OPD(add);
+        SSE_OPD(mul);
+        SSE_OPD(sub);
+        SSE_OPD(min);
+        SSE_OPD(div);
+        SSE_OPD(max);
+        SSE_OPD(sqrt);
+        SSE_OPD(cmpeq);
+        SSE_OPD(cmplt);
+        SSE_OPD(cmple);
+        SSE_OPD(cmpunord);
+        SSE_OPD(cmpneq);
+        SSE_OPD(cmpnlt);
+        SSE_OPD(cmpnle);
+        SSE_OPD(cmpord);
+    }
+
+    /* float to float/int */
+    a.s[0] = 2.7;
+    a.s[1] = 3.4;
+    a.s[2] = 4;
+    a.s[3] = -6.3;
+    CVT_OP_XMM(cvtps2pd);
+    CVT_OP_XMM(cvtss2sd);
+    CVT_OP_XMM2MMX(cvtps2pi);
+    CVT_OP_XMM2MMX(cvttps2pi);
+    CVT_OP_XMM2REG(cvtss2si);
+    CVT_OP_XMM2REG(cvttss2si);
+    CVT_OP_XMM(cvtps2dq);
+    CVT_OP_XMM(cvttps2dq);
+
+    a.d[0] = 2.6;
+    a.d[1] = -3.4;
+    CVT_OP_XMM(cvtpd2ps);
+    CVT_OP_XMM(cvtsd2ss);
+    CVT_OP_XMM2MMX(cvtpd2pi);
+    CVT_OP_XMM2MMX(cvttpd2pi);
+    CVT_OP_XMM2REG(cvtsd2si);
+    CVT_OP_XMM2REG(cvttsd2si);
+    CVT_OP_XMM(cvtpd2dq);
+    CVT_OP_XMM(cvttpd2dq);
+
+    /* sse/mmx moves */
+    CVT_OP_XMM2MMX(movdq2q);
+    CVT_OP_MMX2XMM(movq2dq);
+
+    /* int to float */
+    a.l[0] = -6;
+    a.l[1] = 2;
+    a.l[2] = 100;
+    a.l[3] = -60000;
+    CVT_OP_MMX2XMM(cvtpi2ps);
+    CVT_OP_MMX2XMM(cvtpi2pd);
+    CVT_OP_REG2XMM(cvtsi2ss);
+    CVT_OP_REG2XMM(cvtsi2sd);
+    CVT_OP_XMM(cvtdq2ps);
+    CVT_OP_XMM(cvtdq2pd);
+
+    /* XXX: test PNI insns */
+#if 0
+    SSE_OP2(movshdup);
+#endif
+    asm volatile ("emms");
+}
+
+#endif
+
+#define TEST_CONV_RAX(op)\
+{\
+    unsigned long a, r;\
+    a = i2l(0x8234a6f8);\
+    r = a;\
+    asm volatile(#op : "=a" (r) : "0" (r));\
+    printf("%-10s A=" FMTLX " R=" FMTLX "\n", #op, a, r);\
+}
+
+#define TEST_CONV_RAX_RDX(op)\
+{\
+    unsigned long a, d, r, rh;                   \
+    a = i2l(0x8234a6f8);\
+    d = i2l(0x8345a1f2);\
+    r = a;\
+    rh = d;\
+    asm volatile(#op : "=a" (r), "=d" (rh) : "0" (r), "1" (rh));   \
+    printf("%-10s A=" FMTLX " R=" FMTLX ":" FMTLX "\n", #op, a, r, rh);  \
+}
+
+void test_conv(void)
+{
+    TEST_CONV_RAX(cbw);
+    TEST_CONV_RAX(cwde);
+#if defined(__x86_64__)
+    TEST_CONV_RAX(cdqe);
+#endif
+
+    TEST_CONV_RAX_RDX(cwd);
+    TEST_CONV_RAX_RDX(cdq);
+#if defined(__x86_64__)
+    TEST_CONV_RAX_RDX(cqo);
+#endif
+
+    {
+        unsigned long a, r;
+        a = i2l(0x12345678);
+        asm volatile("bswapl %k0" : "=r" (r) : "0" (a));
+        printf("%-10s: A=" FMTLX " R=" FMTLX "\n", "bswapl", a, r);
+    }
+#if defined(__x86_64__)
+    {
+        unsigned long a, r;
+        a = i2l(0x12345678);
+        asm volatile("bswapq %0" : "=r" (r) : "0" (a));
+        printf("%-10s: A=" FMTLX " R=" FMTLX "\n", "bswapq", a, r);
+    }
+#endif
+}
+
+extern void *__start_initcall;
+extern void *__stop_initcall;
+
+
+int main(int argc, char **argv)
+{
+    void **ptr;
+    void (*func)(void);
+
+    ptr = &__start_initcall;
+    while (ptr != &__stop_initcall) {
+        func = *ptr++;
+        func();
+    }
+    test_bsx();
+    test_mul();
+    test_jcc();
+    test_loop();
+    test_floats();
+#if !defined(__x86_64__)
+    test_bcd();
+#endif
+    test_xchg();
+    test_string();
+    test_misc();
+    test_lea();
+#ifdef TEST_SEGS
+    test_segs();
+    //test_code16();
+#endif
+#ifdef TEST_VM86
+    //test_vm86();
+#endif
+#if !defined(__x86_64__)
+    test_self_modifying_code();
+#endif
+    test_enter();
+    test_conv();
+#ifdef TEST_SSE
+    test_sse();
+    test_fxsave();
+#endif
+    test_exceptions();
+    //test_single_step();
+    return 0;
+}

+ 152 - 0
tests/qemu/test-i386.h

@@ -0,0 +1,152 @@
+
+#define exec_op glue(exec_, OP)
+#define exec_opq glue(glue(exec_, OP), q)
+#define exec_opl glue(glue(exec_, OP), l)
+#define exec_opw glue(glue(exec_, OP), w)
+#define exec_opb glue(glue(exec_, OP), b)
+
+#define EXECOP2(size, rsize, res, s1, flags) \
+    asm ("push %4\n\t"\
+         "popf\n\t"\
+         stringify(OP) size " %" rsize "2, %" rsize "0\n\t" \
+         "pushf\n\t"\
+         "pop %1\n\t"\
+         : "=q" (res), "=g" (flags)\
+         : "q" (s1), "0" (res), "1" (flags)); \
+    printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \
+           stringify(OP) size, s0, s1, res, iflags, flags & CC_MASK);
+
+#define EXECOP1(size, rsize, res, flags) \
+    asm ("push %3\n\t"\
+         "popf\n\t"\
+         stringify(OP) size " %" rsize "0\n\t" \
+         "pushf\n\t"\
+         "pop %1\n\t"\
+         : "=q" (res), "=g" (flags)\
+         : "0" (res), "1" (flags)); \
+    printf("%-10s A=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \
+           stringify(OP) size, s0, res, iflags, flags & CC_MASK);
+
+#ifdef OP1
+#if defined(__x86_64__)
+void exec_opq(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP1("q", "", res, flags);
+}
+#endif
+
+void exec_opl(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP1("l", "k", res, flags);
+}
+
+void exec_opw(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP1("w", "w", res, flags);
+}
+
+void exec_opb(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP1("b", "b", res, flags);
+}
+#else
+#if defined(__x86_64__)
+void exec_opq(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP2("q", "", res, s1, flags);
+}
+#endif
+
+void exec_opl(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP2("l", "k", res, s1, flags);
+}
+
+void exec_opw(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP2("w", "w", res, s1, flags);
+}
+
+void exec_opb(long s0, long s1, long iflags)
+{
+    long res, flags;
+    res = s0;
+    flags = iflags;
+    EXECOP2("b", "b", res, s1, flags);
+}
+#endif
+
+void exec_op(long s0, long s1)
+{
+    s0 = i2l(s0);
+    s1 = i2l(s1);
+#if defined(__x86_64__)
+    exec_opq(s0, s1, 0);
+#endif
+    exec_opl(s0, s1, 0);
+    exec_opw(s0, s1, 0);
+    exec_opb(s0, s1, 0);
+#ifdef OP_CC
+#if defined(__x86_64__)
+    exec_opq(s0, s1, CC_C);
+#endif
+    exec_opl(s0, s1, CC_C);
+    exec_opw(s0, s1, CC_C);
+    exec_opb(s0, s1, CC_C);
+#endif
+}
+
+void glue(test_, OP)(void)
+{
+    exec_op(0x12345678, 0x812FADA);
+    exec_op(0x12341, 0x12341);
+    exec_op(0x12341, -0x12341);
+    exec_op(0xffffffff, 0);
+    exec_op(0xffffffff, -1);
+    exec_op(0xffffffff, 1);
+    exec_op(0xffffffff, 2);
+    exec_op(0x7fffffff, 0);
+    exec_op(0x7fffffff, 1);
+    exec_op(0x7fffffff, -1);
+    exec_op(0x80000000, -1);
+    exec_op(0x80000000, 1);
+    exec_op(0x80000000, -2);
+    exec_op(0x12347fff, 0);
+    exec_op(0x12347fff, 1);
+    exec_op(0x12347fff, -1);
+    exec_op(0x12348000, -1);
+    exec_op(0x12348000, 1);
+    exec_op(0x12348000, -2);
+    exec_op(0x12347f7f, 0);
+    exec_op(0x12347f7f, 1);
+    exec_op(0x12347f7f, -1);
+    exec_op(0x12348080, -1);
+    exec_op(0x12348080, 1);
+    exec_op(0x12348080, -2);
+}
+
+void *glue(_test_, OP) __init_call = glue(test_, OP);
+
+#undef OP
+#undef OP_CC

+ 60 - 0
v86.css

@@ -0,0 +1,60 @@
+#log {
+    font-family: DejaVu Sans Mono;
+    font-size: 13px;
+    height: 350px;
+    float: right;
+    width: 400px;
+    border: 0 none;
+    background-color: #111;
+    color: #fff;
+}
+#screen {
+    white-space: pre;
+    position: relative;
+    font-family: Liberation Mono, DejaVu Sans Mono, Courier New, monospace;
+    font-weight: bold;
+    font-size: 15px;
+    line-height: 1;
+    float: left;
+}
+#screen, #vga {
+    border: 1px solid #555;
+    border-width: 2px 1px;
+    display: none;
+    -webkit-transform-origin: top left;
+    -moz-transform-origin: top left;
+}
+#vga {
+    image-rendering: -moz-crisp-edges;          // FF 6.0+
+    image-rendering: -webkit-optimize-contrast; // Webkit
+                                                //  (Safari now, Chrome soon)
+    image-rendering: -o-crisp-edges;            // OS X & Windows Opera (12.02+)
+    image-rendering: optimize-contrast;         // Possible future browsers.
+    -ms-interpolation-mode: nearest-neighbor;   // IE
+}
+#screen > div > span {
+    height: 15px;
+}
+#cursor {
+    position: absolute;
+    background-color: #ccc;
+    width: 7px;
+    display: inline-block;
+}
+body {
+    background-color: #111;
+    color: #fff;
+    line-height: 1.5;
+}
+#info {
+    font-size: 80%;
+}
+a {
+    color: wheat;
+}
+.phone_keyboard {
+    background-color: #111;
+    border: 1px solid #555;
+    width: 100px;
+    height: 100px;
+}

+ 247 - 0
v86_all.js

@@ -0,0 +1,247 @@
+'use strict';var ea,oa;function Tb(f){f.origin===dc&&mc()}var mc,dc=location.protocol+"//"+location.hostname;oa=function(f){mc=f;window.removeEventListener("message",Tb,!1);window.addEventListener("message",Tb,!1)};ea=function(){window.postMessage(null,dc)};function nc(f){var t=document.getElementById("log");t.textContent+=f+"\n";t.scrollTop=1E9}
+function Lc(f,t,D){var C=new XMLHttpRequest;C.open("get",f,!0);C.responseType="arraybuffer";C.onload=function(){C.response&&t(C.response)};D&&(C.onprogress=function(f){D(f)});C.send(null)}function Mc(f){var t=f.requestPointerLock||f.mozRequestPointerLock||f.webkitRequestPointerLock;t&&t.call(f)}
+function Wc(f){var t=vd("loading");t.style.display="block";f.lengthComputable?(f=f.loaded/f.total*50|0,t.textContent="Loading: "+2*f+"% ["+String.a("#",f)+String.a(" ",50-f)+"]"):t.textContent="Loading ..."}function vd(f){return document.getElementById(f)}
+function Hd(f){var t=new Vd,D=new Wd;vd("boot_options").parentNode.removeChild(vd("boot_options"));vd("loading").style.display="none";vd("runtime_options").style.display="block";document.getElementsByClassName("phone_keyboard")[0].style.display="block";var C=!0;vd("run").onclick=function(){C?(B+=Date.now()-X,vd("run").value="Run",t.v()):(vd("run").value="Pause",t.m(),X=Date.now());C=!C;vd("run").blur()};var O=document.getElementById("running_time"),L=document.getElementById("speed"),X=Date.now(),
+B=0,G=0;setInterval(function(){if(C){var f=Date.now();B+=f-X;X=f;L.textContent=(t.i-G)/1E3|0;O.textContent=B/1E3|0;G=t.i}},1E3);vd("reset").onclick=function(){t.q();vd("reset").blur()};vd("get_floppy").onclick=function(){var f=t.a.Aa.buffer;f&&(f.Ba(function(f){f=new Blob([f]);var t;t=document.createElement("a");t.download="floppy.img";t.href=window.URL.createObjectURL(f);t.textContent="Download floppy.img";t.onclick=function(){t.parentNode.removeChild(t)};t.dataset.downloadurl=["application/octet-stream",
+t.download,t.href].join(":");document.body.appendChild(t)}),vd("get_floppy").blur())};vd("ctrlaltdel").onclick=function(){var f=t.a.Pa;f.G(29);f.G(56);f.G(83);f.G(157);f.G(184);f.G(211);vd("ctrlaltdel").blur()};vd("scale").onchange=function(){var f=parseFloat(this.value);f||0<f?D.a(f,f):this.value="1"};vd("fullscreen").onclick=function(){var f=document.getElementById("screen_container"),t=f.requestFullScreen||f.webkitRequestFullscreen||f.mozRequestFullScreen||f.msRequestFullScreen;t&&(t.call(f),document.getElementsByClassName("phone_keyboard")[0].focus());
+Mc(f)};f.Ua=D;f.Ha=new Xd;f.La=new Yd;t.w(f);t.m()}
+window.onload=function(){function f(f,C){if(f.files.length){var O=new FileReader;O.onload=function(f){f=new Zd(f.target.result);switch(C){case "floppy":t.K=f;break;case "hd":t.ja=f;break;case "cdrom":t.X=f}Hd(t)};O.readAsArrayBuffer(f.files[0])}}if("responseType"in new XMLHttpRequest){var t={Ja:!0};vd("lock_mouse").onclick=function(){Mc(document.body);vd("lock_mouse").blur()};Lc("bios/seabios.bin",function(f){t.P=f});Lc("bios/vgabios.bin",function(f){t.ea=f});vd("floppy_image").onchange=function(){f(this,
+"floppy")};vd("cd_image").onchange=function(){f(this,"cdrom")};vd("hd_image").onchange=function(){f(this,"hd")};vd("start_freedos").onclick=function(){Lc("images/freedos722.img",function(f){t.K=new Zd(f);Hd(t)},Wc);vd("start_freedos").blur()};vd("start_win101").onclick=function(){Lc("images/windows101.img",function(f){t.K=new Zd(f);Hd(t)},Wc);vd("start_win101").blur()};vd("start_linux").onclick=function(){Lc("images/linux.iso",function(f){t.X=new Zd(f);Hd(t)},Wc);vd("start_linux").blur()};vd("start_koli").onclick=
+function(){Lc("images/kolibri.img",function(f){t.K=new Zd(f);Hd(t)},Wc);vd("start_koli").blur()};vd("start_bsd").onclick=function(){Lc("images/openbsd.img",function(f){t.K=new Zd(f);Hd(t)},Wc);vd("start_bsd").blur()}}else nc("Your browser is not supported because it doesn't have XMLHttpRequest.responseType")};if("complete"===document.readyState)window.onload();function $d(){function f(){return 255}function t(){}for(var D=0,C=[],O=[],L=0;65536>L;L++)C[L]=f,O[L]=t;this.h=function(f,t){C[f]=t};this.g=function(f,t){O[f]=t};this.h(146,function(){return D});this.g(146,function(f){D=f});this.g(128,function(){});this.i=function(f,t){O[f](t)};this.a=function(f){return C[f]()}};function Vd(){function f(){if(oc)oc=ec=!1;else{ec=!0;try{C()}catch(b){if(233495534===b)wa=Nc=!1,ab=-1,Cb=Db,zb(),xa=Db,La(),f();else throw b;}}}function t(){wd.n();Xc.n();D(xd);throw 233495534;}function D(b){"undefined"!==typeof oa&&oa(f);xd=b;bb.memory=k=new be;Yc=new Uint8Array(8);yd=new Uint32Array(8);ae=new Uint32Array(8);da=new Int32Array(8);zd=new Int32Array(1048576);Ad=new Int32Array(1048576);Bd=new Int32Array(1048576);Cd=new Int32Array(1048576);Eb=new Uint8Array(1048576);pc=new Uint8Array(1048576);
+y=new Uint32Array(8);e=new Int32Array(y.buffer);g=new Uint16Array(y.buffer);Ga=new Int16Array(y.buffer);v=new Uint8Array(y.buffer);fc=new Int8Array(y.buffer);ra=new Uint16Array(8);Ma=!1;Dd=Ed=Zc=$c=qc=ad=rc=bd=0;Nc=!1;ya=Oc=cd=Ub=Na=0;J=!1;Fd=0;Cb=dd=xa=Db=!1;L();La();zb();$=g;aa=8;sc=10;Vb=ed=0;oc=ec=fd=!1;Pc=20;q=Ea=Qc;ab=-1;wa=!1;I=0;u=2;Y=ma=ba=K=0;if(b.P){for(var c=new Uint8Array(b.P),d=1048576-b.P.byteLength,h=0;h<b.P.byteLength;h++)k.m[d+h]=c[h];if(b.ea)for(c=new Uint8Array(b.ea),h=0;h<b.ea.byteLength;h++)k.m[786432+
+h]=c[h];E=1048560;ca(2,48);g[8]=256}else b.H?(E=65536,k.J(new Uint8Array(b.H.kb),1048576),k.J(new Uint8Array(b.H.ib),E),b.H.root&&(k.J(new Uint8Array(b.H.root),4194304),y[3]=b.H.root.byteLength),k.aa(b.H.ab),y[0]=67108864,y[1]=63488,ca(1,0),ca(2,0),ca(3,0),ca(0,0),ca(5,0),ca(4,0),Ma=dd=xa=Cb=Db=!0,La(),zb(),A=y,aa=4,sc=5,Na=1):(ca(2,48),g[8]=256,E=0);bb.a={};b.Ja&&(c={memory:k,Ta:t},c.j=bb.a.j=Ua=new $d,c.s=Gd=new de(c,ha,Da),c.M=new pe(c),c.xa=new qe(c),bb.a.jb=Xc=new re(c,b.Ua),bb.a.Pa=wd=new se(c,
+b.Ha,b.La),Ha=new Ce,new te(c),bb.a.Aa=ce=new ue(c,b.K),b.X&&(bb.a.$a=new ve(c,b.X)),b.ja&&(bb.a.gb=new we(c,b.ja,0)),b.Da&&(bb.a.hb=new we(c,b.Da,1)),ee=new xe(c),fe=new ye(c,ce.type))}function C(){var b,c=Date.now();Xc.i();for(var d=Pc;d--;){for(b=20001;b--;){Vb=E;var e=m();Ab[e]();ed++}b=Date.now();ee.a(b);fe.a(b)}bb.i+=20001*Pc;33<b-c?Pc--:Pc++;ea()}function O(){var b=0!==(Na&2147483648);Na=Ha.a?Na&-5:Na|4;b!==J&&(J=b,L())}function L(){var b=J?He:Ie;m=b.na;P=b.oa;U=b.la;M=b.ma;ka=b.ta;Rc=b.ua;
+Q=b.ra;S=b.sa;Va=b.ha;Id=b.ia;X()}function X(){Fb=-1;J?ya?(q=Wb,Ea=gd):(q=Ia,Ea=hb):Ea=q=Qc}function B(b,c){return k.d(b)|k.d(c)<<8}function G(b,c){var d=k.d(b)|k.d(c)<<24;return d=b&1?b&2?d|k.d(c-2)<<8|k.d(c-1)<<16:d|k.d(b+1)<<8|k.d(b+2)<<16:d|k.d(b+1)<<8|k.d(c-1)<<16}function n(b,c,d){k.e(b,d);k.e(c,d>>8)}function H(b,c,d){k.e(b,d);k.e(c,d>>24);b&1?b&2?(k.e(c-2,d>>8),k.e(c-1,d>>16)):(k.e(b+1,d>>8),k.e(b+2,d>>16)):(k.e(b+1,d>>8),k.e(c-1,d>>16))}function V(b,c){k.e(q(b),c)}function R(b,c){var d=q(b);
+4095===(b&4095)?n(d,q(b+1),c):k.a(d,c)}function N(b,c){var d=q(b);4093<=(b&4095)?H(d,q(b+3),c):k.f(d,c)}function F(){return Cb?h(3)+M():h(3)+U()}function na(){u=u&-2262|Ba()|ge()|Sc()|Gb()|hd()|Tc();K=0}function sa(b){return dd?da[2]+$[aa]+b:da[2]+($[aa]+b&65535)}function pa(){return E-da[1]}function ha(b,c,d){fd&&(E++,fd=!1);if(Ma){if((b<<3|7)>bd)throw ze("#GP handler");var g=rc+(b<<3)|0;J&&(g=hb(g));var h=k.b(g)|k.b(g+6)<<16,l=k.b(g+2),g=k.d(g+5),m=g>>5&3;if(0===(g&128))throw ze("#NP handler");
+c&&m<ya&&fa(b<<3|2);g&=31;if(14===g)b=!1;else if(15===g)b=!0;else{if(5===g)throw ze("call int to task gate");if(6===g)throw ze("16 bit interrupt gate");if(7===g)throw ze("16 bit trap gate");throw ze("#GP handler");}c=Bb(l);if(c.A)throw ze("#GP handler");if(-1===c)throw ze("#GP handler");if(!c.F||c.l>ya)throw ze("#GP handler");if(!c.B)throw ze("#NP handler");if(u&131072)throw ze("VM flag");if(!c.C&&c.l<ya){m=(c.l<<3)+4;if(m+5>Ed)throw ze("#TS handler");m+=Dd;J&&(m=hb(m));var g=k.c(m),m=k.b(m+4),q=
+Bb(m);if(q.A)throw ze("#TS handler");if(q.t!==c.l)throw ze("#TS handler");if(q.l!==c.l||!q.ca)throw ze("#TS handler");if(!q.B)throw ze("#TS handler");var q=e[4],p=ra[2];y[4]=g;ra[2]=m;ya=c.l;X();ia(p);ia(q)}na();ia(u);ia(ra[1]);ia(pa());!1!==d&&ia(d);ra[1]=l;E=da[1]+h|0;b?Da():u&=-513}else na(),la(u),la(ra[1]),la(pa()),u&=-513,ca(1,k.b((b<<2)+2)),E=da[1]+k.b(b<<2)|0}function za(b){ha(b,!1,!1);throw 233495534;}function qa(){E=Vb;za(0)}function z(){E=Vb;za(6)}function fa(b){E=Vb;ha(13,!1,b);throw 233495534;
+}function ja(b){ab=b;Ab[m()]();ab=-1}function h(b){return-1===ab?da[b]:da[ab]}function Ca(b,c){K&=-65;if((b&3)<(g[c]&3))return u|=64,b&-4|g[c]&3;u&=-65;return b}function Da(){Gd&&u&512&&!Nc&&Gd.a()}function Fa(b,c){$a()?Ua.i(b,c):fa(0)}function Xb(b,c){$a()?(Ua.i(b,c&255),Ua.i(b+1,c>>8&255)):fa(0)}function gc(b,c){$a()?(Ua.i(b,c&255),Ua.i(b+1,c>>8&255),Ua.i(b+2,c>>16&255),Ua.i(b+3,c>>24&255)):fa(0)}function hc(b){if($a())return Ua.a(b);fa(0)}function ic(b){if($a())return Ua.a(b)|Ua.a(b+1)<<8;fa(0)}
+function cb(b){if($a())return Ua.a(b)|Ua.a(b+1)<<8|Ua.a(b+2)<<16|Ua.a(b+3)<<24;fa(0)}function $a(){return!Ma||ya<=(u>>12&3)}function jc(b){u=0!==ya&&Ma?ya<=(u>>12&3)?b&-12289|u&12288:b&-12801|u&12800:b;K=0}function La(){xa?(Ab=s,Jd=w):(Ab=r,Jd=x)}function zb(){Cb?(l=he,A=y,ga=1,W=6,T=7):(l=ie,A=g,ga=2,W=12,T=14)}function Bb(b){var c=0===(b&4),d=b&-8,e;b={t:b&3,ga:c,A:!1,$:!0};c?(c=qc,e=ad):(c=Zc,e=$c);if(0===d)return b.A=!0,b;if(d>>3>e)return b.$=!1,b;c+=d;J&&(c=hb(c));b.W=k.b(c+2)|k.d(c+4)<<16|k.d(c+
+7)<<24;b.u=k.d(c+5);b.fa=k.d(c+6)>>4;b.V=k.b(c)|(k.d(c+6)&15)<<16;b.type=b.u&15;b.l=b.u>>5&3;b.L=0===(b.u&16);b.B=128===(b.u&128);b.F=8===(b.u&8);b.ca=2===(b.u&2);b.C=4===(b.u&4);b.size=4===(b.fa&4);b.fb=8===(b.fa&8);b.Sa=b.eb?(b.V<<12|4095)>>>0:b.V;b.Fa=b.ca&&!b.F;b.Ea=b.ca||!b.F;return b}function ca(b,c){1===b&&(Ma=1===(Na&1));if(Ma){var d=Bb(c);if(2===b){if(d.A){fa(0);return}if(!d.$||d.L||d.t!==ya||!d.Fa||d.l!==ya){fa(c&-4);return}if(!d.B)throw E=Vb,ha(12,!1,c&-4),233495534;(dd=d.size)?($=e,aa=
+4,sc=5):($=g,aa=8,sc=10)}else if(1===b){if(!d.F)throw ze("#GP handler");if(d.L)throw ze("load system segment descriptor, type = "+(d.u&15));if(d.C&&d.l!==d.t)throw ze("#GP handler");if(d.t!==ya)throw ze("privilege change");if(!d.C&&d.l<ya)throw ze("inter privilege interrupt");if(!d.C&&d.l!==ya)throw ze("#GP handler");xa=Cb=Db=d.size;La();zb()}else{if(d.A){ra[b]=c;Yc[b]=1;return}if(!d.$||d.L||!d.Ea||(!d.F||!d.C)&&d.t>d.l&&ya>d.l){fa(c&-4);return}if(!d.B)throw E=Vb,ha(11,!1,c&-4),233495534;}Yc[b]=0;
+yd[b]=d.Sa;ae[b]=0;da[b]=d.W;ra[b]=c}else ra[b]=c,Yc[b]=0,yd[b]=1048575,da[b]=c<<4}function tc(){Fb=-1;Eb.set(pc)}function Qc(b){return b}function Wb(b){var c=b>>>12;return Eb[c]&8?Ad[c]^b:db(b,1,1)|b&4095}function gd(b){var c=b>>>12;return Eb[c]&4?zd[c]^b:db(b,0,1)|b&4095}function Ia(b){var c=b>>>12;return Eb[c]&2?Cd[c]^b:db(b,1,0)|b&4095}function hb(b){var c=b>>>12;return Eb[c]&1?Bd[c]^b:db(b,0,0)|b&4095}function db(b,c,d){var e=b>>>12,g=(cd>>>2)+(e>>10),h=k.i[g],l=!0,m=!0,q=!0;h&1||(Ub=b,ib(c,
+d,0));0===(h&2)&&(l=!1,c&&(Ub=b,ib(c,d,1)));0===(h&4)&&(q=!1,d&&(Ub=b,ib(c,d,1)));0===(h&16)&&(m=!1);if(h&Fd)k.i[g]=h|32|c<<6,b=h&4290772992|e<<12&4190208,h=h&256;else{var u=((h&4294963200)>>>2)+(e&1023),p=k.i[u];p&1||(Ub=b,ib(c,d,0));0===(p&2)&&(l=!1,c&&(Ub=b,ib(c,d,1)));0===(p&4)&&(q=!1,d&&(Ub=b,ib(c,d,1)));0===(p&16)&&(m=!1);k.i[g]=h|32;k.i[u]=p|32|c<<6;b=p&4294963200;h=p&256}m&&(m=b^e<<12,p=0,q&&(zd[e]=m,p|=4,l&&(Ad[e]=m,p|=8)),Bd[e]=m,p|=1,l&&(Cd[e]=m,p|=2),Eb[e]|=p,h&&(pc[e]=p));return b}function ib(b,
+c,d){if(Nc)throw ze("Double fault");E=Vb;Nc=!0;ha(14,!1,c<<2|b<<1|d);throw 233495534;}function Z(b,c){var d=b&65535,e=c&65535;return d*e+d*(c&-65536)+(b&-65536)*e}function kc(b,c){ba=b;ma=c;I=ba+c|0;Y=8;K=2261;return I}function eb(b,c){ba=b;ma=c;I=ba+c|0;Y=16;K=2261;return I}function fb(b,c){ba=b;ma=c;I=ba+c;Y=32;K=2261;return I}function uc(b,c){ba=b;ma=c;I=ba+ma+Ba()|0;Y=8;K=2261;return I}function jb(b,c){ba=b;ma=c;I=ba+ma+Ba()|0;Y=16;K=2261;return I}function kb(b,c){ba=b;ma=c;I=ba+ma+Ba();Y=32;
+K=2261;return I}function lc(b,c){ba=b;ma=~c;I=ba-c;Y=8;K=2261}function Yb(b,c){ba=b;ma=~c;I=ba-c;Y=16;K=2261}function Zb(b,c){ba=b;ma=-c-1;I=ba-c;Y=32;K=2261}function vc(b,c){ba=b;ma=~c;I=ba-c|0;Y=8;K=2261;return I}function lb(b,c){ba=b;ma=~c;I=ba-c|0;Y=16;K=2261;return I}function mb(b,c){ba=b;ma=-c-1;I=ba-c;Y=32;K=2261;return I}function wc(b,c){ba=b;ma=~c;I=ba-c-Ba()|0;Y=8;K=2261;return I}function nb(b,c){ba=b;ma=~c;I=ba-c-Ba()|0;Y=16;K=2261;return I}function ob(b,c){ba=b;ma=-c-1;I=ba-c-Ba();Y=32;
+K=2261;return I}function je(b){u=u&-2|Ba();ba=b;ma=1;I=ba+1|0;Y=8;K=2260;return I}function pb(b){u=u&-2|Ba();ba=b;ma=1;I=ba+1|0;Y=16;K=2260;return I}function qb(b){u=u&-2|Ba();ba=b;ma=1;I=ba+1;Y=32;K=2260;return I}function ke(b){u=u&-2|Ba();ba=b;ma=-1;I=ba-1|0;Y=8;K=2260;return I}function rb(b){u=u&-2|Ba();ba=b;ma=-1;I=ba-1|0;Y=16;K=2260;return I}function sb(b){u=u&-2|Ba();ba=b;ma=-1;I=ba-1;Y=32;K=2260;return I}function le(b){I=-b;K=2261;Y=8;ba=0;ma=I-1;return I}function Kd(b){I=-b;K=2261;Y=16;ba=
+0;ma=I-1;return I}function Ld(b){I=-b;K=2261;Y=32;ba=0;ma=I-1;return I}function Md(b,c){var d=b*c;u=32767<d||-32768>d?u|2049:u&-2050;K=0;return d}function Nd(b,c){var d=Z(b,c);u=0===(b*c/4294967296|0)?u&-2050:u|2049;K=0;return d}function me(b,c){var d=v[c];v[c]=b;return kc(b,d)}function Od(b,c){var d=g[c];g[c]=b;return eb(b,d)}function Pd(b,c){var d=y[c];y[c]=b;return fb(b,d)}function xc(b,c){I=b&c;Y=8;u&=-2066;K=196;return I}function tb(b,c){I=b&c;Y=16;u&=-2066;K=196;return I}function ub(b,c){I=
+b&c;Y=32;u&=-2066;K=196;return I}function id(b,c){I=b&c;Y=8;u&=-2066;K=196}function jd(b,c){I=b&c;Y=16;u&=-2050;K=196}function kd(b,c){I=b&c;Y=32;u&=-2066;K=196}function yc(b,c){I=b|c;Y=8;u&=-2066;K=196;return I}function vb(b,c){I=b|c;Y=16;u&=-2066;K=196;return I}function wb(b,c){I=b|c;Y=32;u&=-2066;K=196;return I}function zc(b,c){I=b^c;Y=8;u&=-2066;K=196;return I}function xb(b,c){I=b^c;Y=16;u&=-2066;K=196;return I}function yb(b,c){I=b^c;Y=32;u&=-2066;K=196;return I}function Ac(b,c){if(!c)return b;
+c&=7;var d=b<<c|b>>8-c;K&=-2050;u=u&-2050|d&1|(d<<11^d<<4)&2048;return d}function Hb(b,c){if(!c)return b;c&=15;var d=b<<c|b>>16-c;K&=-2050;u=u&-2050|d&1|(d<<11^d>>4)&2048;return d}function Ib(b,c){if(!c)return b;var d=b<<c|b>>>32-c;K&=-2050;u=u&-2050|d&1|(d<<11^d>>20)&2048;return d}function Bc(b,c){c%=9;if(!c)return b;var d=b<<c|Ba()<<c-1|b>>9-c;K&=-2050;u=u&-2050|d>>8&1|(d<<3^d<<4)&2048;return d}function Jb(b,c){c%=17;if(!c)return b;var d=b<<c|Ba()<<c-1|b>>17-c;K&=-2050;u=u&-2050|d>>16&1|(d>>5^d>>
+4)&2048;return d}function Kb(b,c){if(!c)return b;var d=b<<c|Ba()<<c-1;1<c&&(d|=b>>>33-c);K&=-2050;u=u&-2050|b>>>32-c&1;u|=(u<<11^d>>20)&2048;return d}function Cc(b,c){c&=7;if(!c)return b;var d=b>>c|b<<8-c;K&=-2050;u=u&-2050|d>>7&1|(d<<4^d<<5)&2048;return d}function Lb(b,c){c&=15;if(!c)return b;var d=b>>c|b<<16-c;K&=-2050;u=u&-2050|d>>15&1|(d>>4^d>>3)&2048;return d}function Mb(b,c){if(!c)return b;var d=b>>>c|b<<32-c;K&=-2050;u=u&-2050|d>>31&1|(d>>20^d>>19)&2048;return d}function Dc(b,c){c%=9;if(!c)return b;
+var d=b>>c|Ba()<<8-c|b<<9-c;K&=-2050;u=u&-2050|d>>8&1|(d<<4^d<<5)&2048;return d}function Nb(b,c){c%=17;if(!c)return b;var d=b>>c|Ba()<<16-c|b<<17-c;K&=-2050;u=u&-2050|d>>16&1|(d>>4^d>>3)&2048;return d}function Ob(b,c){if(!c)return b;var d=b>>>c|Ba()<<32-c;1<c&&(d|=b<<33-c);K&=-2050;u=u&-2050|b>>c-1&1|(d>>20^d>>19)&2048;return d}function gb(b,c){if(0===c)return b;I=b<<c;Y=8;K=212;u=u&-2050|I>>8&1|(I<<3^I<<4)&2048;return I}function Ja(b,c){if(0===c)return b;I=b<<c;Y=16;K=212;u=u&-2050|I>>16&1|(I>>5^
+I>>4)&2048;return I}function Ka(b,c){if(0===c)return b;I=b<<c;Y=32;K=212;u=u&-2050|b>>>32-c&1;u|=(u&1^I>>31&1)<<11&2048;return I}function Ec(b,c){if(0===c)return b;I=b>>c;Y=8;K=212;u=u&-2050|b>>c-1&1|(b>>7&1)<<11&2048;return I}function Pb(b,c){if(0===c)return b;I=b>>c;Y=16;K=212;u=u&-2050|b>>c-1&1|b>>4&2048;return I}function Qb(b,c){if(0===c)return b;I=b>>>c;Y=32;K=212;u=u&-2050|b>>>c-1&1|b>>20&2048;return I}function Fc(b,c){if(0===c)return b;I=b>>c;Y=8;K=212;u=u&-2050|b>>c-1&1;return I}function Rb(b,
+c){if(0===c)return b;I=b>>c;Y=16;K=212;u=u&-2050|b>>c-1&1;return I}function Sb(b,c){if(0===c)return b;I=b>>c;Y=32;K=212;u=u&-2050|b>>>c-1&1;return I}function Gc(b,c,d){if(0===d)return b;16>=d?(I=b>>d|c<<16-d,u=u&-2|b>>d-1&1):(I=b<<32-d|c>>d-16,u=u&-2|c>>d-17&1);Y=16;K=212;u=u&-2049|(I^b)>>4&2048;return I}function Hc(b,c,d){if(0===d)return b;I=b>>>d|c<<32-d;Y=32;K=212;u=u&-2|b>>>d-1&1;u=u&-2049|(I^b)>>20&2048;return I}function Ic(b,c,d){if(0===d)return b;16>=d?(I=b<<d|c>>>16-d,u=u&-2|b>>>16-d&1):(I=
+b>>32-d|c<<d-16,u=u&-2|c>>>32-d&1);Y=16;K=212;u=u&-2049|(u&1^I>>15&1)<<11;return I}function Jc(b,c,d){if(0===d)return b;I=b<<d|c>>>32-d;Y=32;K=212;u=u&-2|b>>>32-d&1;u=u&-2049|(u&1^I>>31&1)<<11;return I}function ld(b,c){u=u&-2|b>>c&1;K=0}function md(b,c){u=u&-2|b>>c&1;K=0;return b^1<<c}function nd(b,c){u=u&-2|b>>c&1;K=0;return b|1<<c}function od(b,c){u=u&-2|b>>c&1;K=0;return b&~(1<<c)}function pd(b,c){var d=ka(b+(c>>3));u=u&-2|d>>(c&7)&1;K=0}function qd(b,c){var d=q(b+(c>>3)),e=k.d(d);c&=7;u=u&-2|
+e>>c&1;K=0;k.e(d,e^1<<c)}function rd(b,c){var d=q(b+(c>>3)),e=k.d(d);c&=7;u=u&-2|e>>c&1;K=0;k.e(d,e&~(1<<c))}function sd(b,c){var d=q(b+(c>>3)),e=k.d(d);c&=7;u=u&-2|e>>c&1;K=0;k.e(d,e|1<<c)}function Qd(b){var c=da[1];E-=c;E=E+b&65535;E=E+c|0}function Oa(b){b?Qd(U()):E+=2}function Pa(b){b?(b=M(),E=E+b|0):E=E+4|0}function Qa(){return Ba()||Gb()}function Wa(){return!hd()!==!Tc()}function Xa(){return Gb()||!hd()!==!Tc()}function Ba(){return K&1?32===Y?4294967295<I|0>I:I>>Y&1:u&1}function ge(){return K&
+4?154020>>((I^I>>4)&15)&4:u&4}function Sc(){return K&16?(ba^ma^I^(0>ma)<<4)&16:u&16}function Gb(){return K&64?(~I&I-1)>>Y-7&64:u&64}function hd(){return K&128?I>>Y-8&128:u&128}function Tc(){return K&2048?((ba^I)&(ma^I))>>Y-1<<11&2048:u&2048}function la(b){var c=Id(-2);$[aa]-=2;k.a(c,b)}function ia(b){var c=Id(-4);$[aa]-=4;k.f(c,b)}function Aa(){var b=Va(0);$[aa]+=2;return k.b(b)}function ua(){var b=Va(0);$[aa]+=4;return k.c(b)}function ne(b,c){var d=c>>1&12|c>>5&1,e=v[d];v[d]=b;return e}function Rd(b,
+c){var d=c>>2&14,e=g[d];g[d]=b;return e}function $b(b){var c=g[0];g[0]=g[b];g[b]=c}function Sd(b,c){var d=c>>3&7,g=e[d];y[d]=b;return g}function ac(b){var c=e[0];y[0]=e[b];y[b]=c}function Uc(b,c,d){var e=Q(c);c=Q(c+2);ca(b,c);g[d]=e}function Vc(b,c,d){var e=S(c);c=Q(c+4);ca(b,c);y[d]=e}function bc(b){var c=e[b];y[b]=c>>>24|c<<24|c>>8&65280|c<<8&16711680}function Ce(){function b(b){var c=r();Z&=-18177;c>b||(Z=b>c?Z|256:c===b?Z|16384:Z|17664)}function c(b){var c=n[F];K&=-70;u&=-70;c>b||(u=b>c?u|1:c===
+b?u|64:u|69)}function d(){J=895;M=sa=L=Z=0;E=255;F=0}function e(b){if(xa){R(b,J);R(b+4,Z&-14337|F<<11);for(var c=0,d,g=0;8>g;g++)if(d=n[g],E>>g&1)c|=3<<(g<<1);else if(0===d)c|=1<<(g<<1);else if(isNaN(d)||Infinity===d||-Infinity===d)c|=2<<(g<<1);R(b+8,c);N(b+12,L);R(b+16,V);R(b+18,M);N(b+20,sa);R(b+24,P)}else z()}function k(b){if(xa){J=Q(b);var c=Q(b+4);Z=c&-14337;F=c>>11&7;for(var c=Q(b+8),d=E=0;8>d;d++)E|=c>>d&c>>d+1&1<<d;L=S(b+12)>>>0;V=Q(b+16);M=Q(b+18);sa=S(b+20)>>>0;P=Q(b+24)}else z()}function h(b){var c=
+J>>10&3;return 0===c?(c=Math.round(b),0.5===c-b&&c&1&&c--,c):1===c||3===c&&0<b?Math.floor(b):Math.ceil(b)}function l(b){return 0<b?Math.floor(b):Math.ceil(b)}function m(b){F=F-1&7;E>>F&1?(Z&=-513,E&=~(1<<F),n[F]=b):(Z|=512,Z=Z|t|A,n[F]=NaN)}function p(){E|=1<<F;F=F+1&7}function s(b){b=b+F&7;return E>>b&1?(Z&=-513,Z=Z|t|A,NaN):n[b]}function r(){return E>>F&1?(Z&=-513,Z=Z|t|A,NaN):n[F]}function v(b){var c=Q(b+8),d=S(b)>>>0,e=S(b+4)>>>0;b=c>>15;c&=-32769;if(0===c)return 0;if(!(32767>c))return I[7]=127|
+b<<7,I[6]=240|e>>30<<3&8,I[5]=0,I[4]=0,D[0]=0,G[0];d+=4294967296*e;b&&(d=-d);return d*Math.pow(2,c-16383-63)}function w(b,c){G[0]=n[F+c&7];var d=I[7]&128,e=(I[7]&127)<<4|I[6]>>4,g,k;2047===e?(e=32767,g=0,k=2147483648|(D[1]&524288)<<11):0===e?k=g=0:(e+=15360,g=D[0]<<11,k=2147483648|(D[1]&1048575)<<11|D[0]>>>21);N(b,g);N(b+4,k);R(b+8,d<<8|e)}function x(b){D[0]=S(b);D[1]=S(b+4);return G[0]}function y(b){q(b+7);G[0]=s(0);N(b,D[0]);N(b+4,D[1])}function f(b){H[0]=S(b);return C[0]}this.a=1;var t=64,A=1,
+n=new Float64Array(8),B=new Uint8Array(n.buffer);new Uint32Array(n.buffer);var E=255,F=0,C=new Float32Array(1);new Uint8Array(C.buffer);var H=new Uint32Array(C.buffer),G=new Float64Array(1),I=new Uint8Array(G.buffer),D=new Uint32Array(G.buffer);new Uint8Array(10);var J=895,Z=0,L=0,V=0,M=0,sa=0,P=0,O=new Float64Array([1,Math.log(10)/Math.LN2,Math.LOG2E,Math.PI,Math.log(2)/Math.LN10,Math.LN2,0]);this.m=function(c){var d=c>>3&7;c=s(c&7);var e=r();switch(d){case 0:n[F]=e+c;break;case 1:n[F]=e*c;break;
+case 2:b(c);break;case 3:b(c);p();break;case 4:n[F]=e-c;break;case 5:n[F]=c-e;break;case 6:n[F]=e/c;break;case 7:n[F]=c/e;break;default:z()}};this.i=function(c,d){var e=c>>3&7,g=f(d),k=r();switch(e){case 0:n[F]=k+g;break;case 1:n[F]=k*g;break;case 2:b(g);break;case 3:b(g);p();break;case 4:n[F]=k-g;break;case 5:n[F]=g-k;break;case 6:n[F]=k/g;break;case 7:n[F]=g/k;break;default:z()}};this.v=function(b){var c=b&7;switch(b>>3&7){case 0:b=s(c);m(b);break;case 1:b=s(c);n[F+c&7]=r();n[F]=b;break;case 4:switch(c){case 0:n[F]=
+-r();break;case 1:n[F]=Math.abs(r());break;case 4:c=r();Z&=-18177;isNaN(c)?Z|=17664:0===c?Z|=16384:0>c&&(Z|=256);break;case 5:c=r();Z&=-18177;Z|=B[(F+0&7)<<3|7]>>7<<9;Z=E>>F&1?Z|16640:isNaN(c)?Z|256:0===c?Z|16384:Infinity===c||-Infinity===c?Z|1280:Z|1024;break;default:z()}break;case 5:m(O[c]);break;case 6:switch(c){case 0:n[F]=Math.pow(2,r())-1;break;case 1:n[F+1&7]=s(1)*Math.log(r())/Math.LN2;p();break;case 2:n[F]=Math.tan(r());m(1);break;case 3:n[F+1&7]=Math.atan2(s(1),r());p();break;case 5:n[F]=
+r()%s(1);break;default:z()}break;case 7:switch(c){case 0:n[F]=r()%s(1);break;case 2:n[F]=Math.sqrt(r());break;case 3:c=r();n[F]=Math.sin(c);m(Math.cos(c));break;case 4:n[F]=h(r());break;case 5:n[F]=r()*Math.pow(2,l(s(1)));break;case 6:n[F]=Math.sin(r());break;case 7:n[F]=Math.cos(r());break;default:z()}break;default:z()}};this.q=function(b,c){switch(b>>3&7){case 0:var d=f(c);m(d);break;case 2:C[0]=s(0);N(c,H[0]);break;case 3:C[0]=s(0);N(c,H[0]);p();break;case 4:k(c);break;case 5:J=Q(c);break;case 6:e(c);
+break;case 7:R(c,J);break;default:z()}};this.Q=function(c){var d=c&7;switch(c>>3&7){case 0:Ra()&&(n[F]=s(d),E&=~(1<<F));break;case 1:Sa()&&(n[F]=s(d),E&=~(1<<F));break;case 2:Qa()&&(n[F]=s(d),E&=~(1<<F));break;case 3:Ta()&&(n[F]=s(d),E&=~(1<<F));break;case 5:1===d?(c=s(1),b(c),p(),p()):z();break;default:z()}};this.D=function(c,d){var e=c>>3&7,g=S(d),k=r();switch(e){case 0:n[F]=k+g;break;case 1:n[F]=k*g;break;case 2:b(g);break;case 3:b(g);p();break;case 4:n[F]=k-g;break;case 5:n[F]=g-k;break;case 6:n[F]=
+k/g;break;case 7:n[F]=g/k;break;default:z()}};this.S=function(b){var e=b&7;switch(b>>3&7){case 0:Ra()||(n[F]=s(e),E&=~(1<<F));break;case 1:Sa()||(n[F]=s(e),E&=~(1<<F));break;case 2:Qa()||(n[F]=s(e),E&=~(1<<F));break;case 3:Ta()||(n[F]=s(e),E&=~(1<<F));break;case 4:227===b?d():228!==b&&z();break;case 5:b=s(e);c(b);break;case 6:c(s(e));break;default:z()}};this.R=function(b,c){switch(b>>3&7){case 0:var d=S(c);m(d);break;case 2:d=r();isNaN(d)||2147483647<d||-2147483648>d?(Z|=A,N(c,2147483648)):N(c,h(d));
+break;case 3:d=r();isNaN(d)||2147483647<d||-2147483648>d?(Z|=A,N(c,2147483648)):N(c,h(d));p();break;case 5:m(v(c));break;case 7:w(c,0);p();break;default:z()}};this.Ga=function(c){var d=c>>3&7,e=c&7;c=F+e&7;var e=s(e),g=r();switch(d){case 0:n[c]=e+g;break;case 1:n[c]=e*g;break;case 2:b(e);break;case 3:b(e);p();break;case 4:n[c]=g-e;break;case 5:n[c]=e-g;break;case 6:n[c]=g/e;break;case 7:n[c]=e/g;break;default:z()}};this.aa=function(c,d){var e=c>>3&7,g=x(d),k=r();switch(e){case 0:n[F]=k+g;break;case 1:n[F]=
+k*g;break;case 2:b(g);break;case 3:b(g);p();break;case 4:n[F]=k-g;break;case 5:n[F]=g-k;break;case 6:n[F]=k/g;break;case 7:n[F]=g/k;break;default:z()}};this.Ka=function(c){var d=c&7;switch(c>>3&7){case 0:E|=1<<(F+d&7);break;case 2:n[F+d&7]=r();break;case 3:0!==d&&(n[F+d&7]=r());p();break;case 4:c=s(d);b(c);break;case 5:c=s(d);b(c);p();break;default:z()}};this.Ia=function(b,c){switch(b>>3&7){case 0:var g=x(c);m(g);break;case 2:y(c);break;case 3:y(c);p();break;case 4:g=c;k(g);for(var g=g+28,h=0;8>h;h++)n[h]=
+v(g),g+=10;break;case 6:g=c;e(g);g+=28;for(h=0;8>h;h++)w(g,h-F&7),g+=10;d();break;case 7:R(c,Z&-14337|F<<11);break;default:z()}};this.Na=function(c){var d=c>>3&7;c=c&7;var e=F+c&7,g=s(c),k=r();switch(d){case 0:n[e]=g+k;break;case 1:n[e]=g*k;break;case 2:b(g);break;case 3:1===c?(b(n[e]),p()):z();break;case 4:n[e]=k-g;break;case 5:n[e]=g-k;break;case 6:n[e]=k/g;break;case 7:n[e]=g/k;break;default:z()}p()};this.Ma=function(c,d){var e=c>>3&7,g=Q(d)<<16>>16,k=r();switch(e){case 0:n[F]=k+g;break;case 1:n[F]=
+k*g;break;case 2:b(g);break;case 3:b(g);p();break;case 4:n[F]=k-g;break;case 5:n[F]=g-k;break;case 6:n[F]=k/g;break;case 7:n[F]=g/k;break;default:z()}};this.Ra=function(b){var d=b&7;switch(b>>3&7){case 4:224===b?g[0]=Z&-14337|F<<11:z();break;case 5:b=s(d);c(b);p();break;default:z()}};this.Oa=function(b,c){switch(b>>3&7){case 0:var d=Q(c)<<16>>16;m(d);break;case 2:d=r();isNaN(d)||32767<d||-32768>d?(Z|=A,R(c,32768)):R(c,h(d));break;case 3:d=r();isNaN(d)||32767<d||-32768>d?(Z|=A,R(c,32768)):R(c,h(d));
+p();break;case 5:var e=S(c)>>>0,d=S(c+4)>>>0,e=e+4294967296*d;d>>31&&(e-=1.8446744073709552E19);m(e);break;case 7:d=h(r());if(isNaN(d)||9223372036854775E3<d||-9223372036854775E3>d)d=9223372036854775E3,Z|=A;p();N(c,d);d/=4294967296;0>d&&-1<d&&(d=-1);N(c+4,d);break;default:z()}}}var bb=this;this.m=function(){ec||f()};this.v=function(){ec&&(oc=!0)};this.w=D;this.q=function(){var b=ec;oc=!0;ec=!1;setTimeout(function(){wd.n();Xc.n();D(xd);b&&f()},10)};this.a={};this.i=0;var Yc,da,yd,ae,zd,Ad,Bd,Cd,Eb,
+pc,Ma,bd,rc,ad,qc,$c,Zc,Ed,Dd,Nc,Na,Ub,cd,Oc,ya,J,Fd,Db,xa,dd,ed,Vb,fd,Xc,wd,ee,fe,ce,ec,oc,Pc,k,Ha,Gd,Ua,Ea,q,Cb,E,Fb,Kc,ab,wa,cc,I,u,K,ba,ma,Y,y,e,g,Ga,v,fc,ra,$,aa,sc,A,ga,W,T,m,P,U,M,ka,Rc,Q,S,Va,Id,Ab,Jd,l,xd;"undefined"!==typeof window&&(window.a=C);bb.D=function(){Ab[m()]()};var Ie={ha:sa,ia:sa,na:function(){return k.m[E++]},oa:function(){return k.Q[E++]},la:function(){var b=k.b(E);E=E+2|0;return b},ma:function(){var b=k.c(E);E=E+4|0;return b},ta:function(b){return k.d(b)},ua:function(b){return k.R(b)},
+ra:function(b){return k.b(b)},sa:function(b){return k.c(b)}},He={ha:function(b){return Ea(da[2]+$[aa]+b)},ia:function(b){return q(da[2]+$[aa]+b)},na:function(){E&-4096^Fb&&(Kc=Ea(E)^E,Fb=E&-4096);return k.m[Kc^E++]},oa:function(){E&-4096^Fb&&(Kc=Ea(E)^E,Fb=E&-4096);return k.Q[Kc^E++]},la:function(){if(4094<(E^Fb))return m()|m()<<8;var b=k.b(Kc^E);E=E+2|0;return b},ma:function(){if(4092<(E^Fb))return U()|U()<<16;var b=k.c(Kc^E);E=E+4|0;return b},ta:function(b){return k.d(Ea(b))},ua:function(b){return k.R(Ea(b))},
+ra:function(b){return 4095===(b&4095)?ka(b)|ka(b+1)<<8:k.b(Ea(b))},sa:function(b){return 4093<=(b&4095)?Q(b)|Q(b+2)<<16:k.c(Ea(b))}};"use strict";"use strict";var ie,he,va=Array(192),ta=Array(192),p=Array(256);va[0]=function(){return h(3)+(g[6]+g[12]&65535)|0};va[64]=function(){return h(3)+(g[6]+g[12]+P()&65535)|0};va[128]=function(){return h(3)+(g[6]+g[12]+U()&65535)|0};va[1]=function(){return h(3)+(g[6]+g[14]&65535)|0};va[65]=function(){return h(3)+(g[6]+g[14]+P()&65535)|0};va[129]=function(){return h(3)+
+(g[6]+g[14]+U()&65535)|0};va[2]=function(){return h(2)+(g[10]+g[12]&65535)|0};va[66]=function(){return h(2)+(g[10]+g[12]+P()&65535)|0};va[130]=function(){return h(2)+(g[10]+g[12]+U()&65535)|0};va[3]=function(){return h(2)+(g[10]+g[14]&65535)|0};va[67]=function(){return h(2)+(g[10]+g[14]+P()&65535)|0};va[131]=function(){return h(2)+(g[10]+g[14]+U()&65535)|0};va[4]=function(){return h(3)+(g[12]&65535)|0};va[68]=function(){return h(3)+(g[12]+P()&65535)|0};va[132]=function(){return h(3)+(g[12]+U()&65535)|
+0};va[5]=function(){return h(3)+(g[14]&65535)|0};va[69]=function(){return h(3)+(g[14]+P()&65535)|0};va[133]=function(){return h(3)+(g[14]+U()&65535)|0};va[6]=function(){return h(2)+(g[10]&65535)|0};va[70]=function(){return h(2)+(g[10]+P()&65535)|0};va[134]=function(){return h(2)+(g[10]+U()&65535)|0};va[7]=function(){return h(3)+(g[6]&65535)|0};va[71]=function(){return h(3)+(g[6]+P()&65535)|0};va[135]=function(){return h(3)+(g[6]+U()&65535)|0};ta[0]=function(){return h(3)+e[0]|0};ta[64]=function(){return h(3)+
+e[0]+P()|0};ta[128]=function(){return h(3)+e[0]+M()|0};ta[1]=function(){return h(3)+e[1]|0};ta[65]=function(){return h(3)+e[1]+P()|0};ta[129]=function(){return h(3)+e[1]+M()|0};ta[2]=function(){return h(3)+e[2]|0};ta[66]=function(){return h(3)+e[2]+P()|0};ta[130]=function(){return h(3)+e[2]+M()|0};ta[3]=function(){return h(3)+e[3]|0};ta[67]=function(){return h(3)+e[3]+P()|0};ta[131]=function(){return h(3)+e[3]+M()|0};ta[4]=function(){return p[m()](!1)|0};ta[68]=function(){return p[m()](!1)+P()|0};
+ta[132]=function(){return p[m()](!1)+M()|0};ta[5]=function(){return h(2)+e[5]|0};ta[69]=function(){return h(2)+e[5]+P()|0};ta[133]=function(){return h(2)+e[5]+M()|0};ta[6]=function(){return h(3)+e[6]|0};ta[70]=function(){return h(3)+e[6]+P()|0};ta[134]=function(){return h(3)+e[6]+M()|0};ta[7]=function(){return h(3)+e[7]|0};ta[71]=function(){return h(3)+e[7]+P()|0};ta[135]=function(){return h(3)+e[7]+M()|0};va[6]=function(){return h(3)+U()|0};ta[5]=function(){return h(3)+M()|0};ta[4]=function(){return p[m()](!1)|
+0};ta[68]=function(){return p[m()](!0)+P()|0};ta[132]=function(){return p[m()](!0)+M()|0};for(var Td=0;8>Td;Td++)for(var Ud=0;3>Ud;Ud++)for(var td=1;8>td;td++){var ud=Td|Ud<<6;ta[ud|td<<3]=ta[ud];va[ud|td<<3]=va[ud]}p[0]=function(){return e[0]+h(3)+e[0]|0};p[1]=function(){return e[0]+h(3)+e[1]|0};p[2]=function(){return e[0]+h(3)+e[2]|0};p[3]=function(){return e[0]+h(3)+e[3]|0};p[4]=function(){return e[0]+h(2)+e[4]|0};p[5]=function(b){return e[0]+(b?h(2)+e[5]:h(3)+M())|0};p[6]=function(){return e[0]+
+h(3)+e[6]|0};p[7]=function(){return e[0]+h(3)+e[7]|0};p[64]=function(){return(e[0]<<1)+h(3)+e[0]|0};p[65]=function(){return(e[0]<<1)+h(3)+e[1]|0};p[66]=function(){return(e[0]<<1)+h(3)+e[2]|0};p[67]=function(){return(e[0]<<1)+h(3)+e[3]|0};p[68]=function(){return(e[0]<<1)+h(2)+e[4]|0};p[69]=function(b){return(e[0]<<1)+(b?h(2)+e[5]:h(3)+M())|0};p[70]=function(){return(e[0]<<1)+h(3)+e[6]|0};p[71]=function(){return(e[0]<<1)+h(3)+e[7]|0};p[128]=function(){return(e[0]<<2)+h(3)+e[0]|0};p[129]=function(){return(e[0]<<
+2)+h(3)+e[1]|0};p[130]=function(){return(e[0]<<2)+h(3)+e[2]|0};p[131]=function(){return(e[0]<<2)+h(3)+e[3]|0};p[132]=function(){return(e[0]<<2)+h(2)+e[4]|0};p[133]=function(b){return(e[0]<<2)+(b?h(2)+e[5]:h(3)+M())|0};p[134]=function(){return(e[0]<<2)+h(3)+e[6]|0};p[135]=function(){return(e[0]<<2)+h(3)+e[7]|0};p[192]=function(){return(e[0]<<3)+h(3)+e[0]|0};p[193]=function(){return(e[0]<<3)+h(3)+e[1]|0};p[194]=function(){return(e[0]<<3)+h(3)+e[2]|0};p[195]=function(){return(e[0]<<3)+h(3)+e[3]|0};p[196]=
+function(){return(e[0]<<3)+h(2)+e[4]|0};p[197]=function(b){return(e[0]<<3)+(b?h(2)+e[5]:h(3)+M())|0};p[198]=function(){return(e[0]<<3)+h(3)+e[6]|0};p[199]=function(){return(e[0]<<3)+h(3)+e[7]|0};p[8]=function(){return e[1]+h(3)+e[0]|0};p[9]=function(){return e[1]+h(3)+e[1]|0};p[10]=function(){return e[1]+h(3)+e[2]|0};p[11]=function(){return e[1]+h(3)+e[3]|0};p[12]=function(){return e[1]+h(2)+e[4]|0};p[13]=function(b){return e[1]+(b?h(2)+e[5]:h(3)+M())|0};p[14]=function(){return e[1]+h(3)+e[6]|0};
+p[15]=function(){return e[1]+h(3)+e[7]|0};p[72]=function(){return(e[1]<<1)+h(3)+e[0]|0};p[73]=function(){return(e[1]<<1)+h(3)+e[1]|0};p[74]=function(){return(e[1]<<1)+h(3)+e[2]|0};p[75]=function(){return(e[1]<<1)+h(3)+e[3]|0};p[76]=function(){return(e[1]<<1)+h(2)+e[4]|0};p[77]=function(b){return(e[1]<<1)+(b?h(2)+e[5]:h(3)+M())|0};p[78]=function(){return(e[1]<<1)+h(3)+e[6]|0};p[79]=function(){return(e[1]<<1)+h(3)+e[7]|0};p[136]=function(){return(e[1]<<2)+h(3)+e[0]|0};p[137]=function(){return(e[1]<<
+2)+h(3)+e[1]|0};p[138]=function(){return(e[1]<<2)+h(3)+e[2]|0};p[139]=function(){return(e[1]<<2)+h(3)+e[3]|0};p[140]=function(){return(e[1]<<2)+h(2)+e[4]|0};p[141]=function(b){return(e[1]<<2)+(b?h(2)+e[5]:h(3)+M())|0};p[142]=function(){return(e[1]<<2)+h(3)+e[6]|0};p[143]=function(){return(e[1]<<2)+h(3)+e[7]|0};p[200]=function(){return(e[1]<<3)+h(3)+e[0]|0};p[201]=function(){return(e[1]<<3)+h(3)+e[1]|0};p[202]=function(){return(e[1]<<3)+h(3)+e[2]|0};p[203]=function(){return(e[1]<<3)+h(3)+e[3]|0};p[204]=
+function(){return(e[1]<<3)+h(2)+e[4]|0};p[205]=function(b){return(e[1]<<3)+(b?h(2)+e[5]:h(3)+M())|0};p[206]=function(){return(e[1]<<3)+h(3)+e[6]|0};p[207]=function(){return(e[1]<<3)+h(3)+e[7]|0};p[16]=function(){return e[2]+h(3)+e[0]|0};p[17]=function(){return e[2]+h(3)+e[1]|0};p[18]=function(){return e[2]+h(3)+e[2]|0};p[19]=function(){return e[2]+h(3)+e[3]|0};p[20]=function(){return e[2]+h(2)+e[4]|0};p[21]=function(b){return e[2]+(b?h(2)+e[5]:h(3)+M())|0};p[22]=function(){return e[2]+h(3)+e[6]|0};
+p[23]=function(){return e[2]+h(3)+e[7]|0};p[80]=function(){return(e[2]<<1)+h(3)+e[0]|0};p[81]=function(){return(e[2]<<1)+h(3)+e[1]|0};p[82]=function(){return(e[2]<<1)+h(3)+e[2]|0};p[83]=function(){return(e[2]<<1)+h(3)+e[3]|0};p[84]=function(){return(e[2]<<1)+h(2)+e[4]|0};p[85]=function(b){return(e[2]<<1)+(b?h(2)+e[5]:h(3)+M())|0};p[86]=function(){return(e[2]<<1)+h(3)+e[6]|0};p[87]=function(){return(e[2]<<1)+h(3)+e[7]|0};p[144]=function(){return(e[2]<<2)+h(3)+e[0]|0};p[145]=function(){return(e[2]<<
+2)+h(3)+e[1]|0};p[146]=function(){return(e[2]<<2)+h(3)+e[2]|0};p[147]=function(){return(e[2]<<2)+h(3)+e[3]|0};p[148]=function(){return(e[2]<<2)+h(2)+e[4]|0};p[149]=function(b){return(e[2]<<2)+(b?h(2)+e[5]:h(3)+M())|0};p[150]=function(){return(e[2]<<2)+h(3)+e[6]|0};p[151]=function(){return(e[2]<<2)+h(3)+e[7]|0};p[208]=function(){return(e[2]<<3)+h(3)+e[0]|0};p[209]=function(){return(e[2]<<3)+h(3)+e[1]|0};p[210]=function(){return(e[2]<<3)+h(3)+e[2]|0};p[211]=function(){return(e[2]<<3)+h(3)+e[3]|0};p[212]=
+function(){return(e[2]<<3)+h(2)+e[4]|0};p[213]=function(b){return(e[2]<<3)+(b?h(2)+e[5]:h(3)+M())|0};p[214]=function(){return(e[2]<<3)+h(3)+e[6]|0};p[215]=function(){return(e[2]<<3)+h(3)+e[7]|0};p[24]=function(){return e[3]+h(3)+e[0]|0};p[25]=function(){return e[3]+h(3)+e[1]|0};p[26]=function(){return e[3]+h(3)+e[2]|0};p[27]=function(){return e[3]+h(3)+e[3]|0};p[28]=function(){return e[3]+h(2)+e[4]|0};p[29]=function(b){return e[3]+(b?h(2)+e[5]:h(3)+M())|0};p[30]=function(){return e[3]+h(3)+e[6]|0};
+p[31]=function(){return e[3]+h(3)+e[7]|0};p[88]=function(){return(e[3]<<1)+h(3)+e[0]|0};p[89]=function(){return(e[3]<<1)+h(3)+e[1]|0};p[90]=function(){return(e[3]<<1)+h(3)+e[2]|0};p[91]=function(){return(e[3]<<1)+h(3)+e[3]|0};p[92]=function(){return(e[3]<<1)+h(2)+e[4]|0};p[93]=function(b){return(e[3]<<1)+(b?h(2)+e[5]:h(3)+M())|0};p[94]=function(){return(e[3]<<1)+h(3)+e[6]|0};p[95]=function(){return(e[3]<<1)+h(3)+e[7]|0};p[152]=function(){return(e[3]<<2)+h(3)+e[0]|0};p[153]=function(){return(e[3]<<
+2)+h(3)+e[1]|0};p[154]=function(){return(e[3]<<2)+h(3)+e[2]|0};p[155]=function(){return(e[3]<<2)+h(3)+e[3]|0};p[156]=function(){return(e[3]<<2)+h(2)+e[4]|0};p[157]=function(b){return(e[3]<<2)+(b?h(2)+e[5]:h(3)+M())|0};p[158]=function(){return(e[3]<<2)+h(3)+e[6]|0};p[159]=function(){return(e[3]<<2)+h(3)+e[7]|0};p[216]=function(){return(e[3]<<3)+h(3)+e[0]|0};p[217]=function(){return(e[3]<<3)+h(3)+e[1]|0};p[218]=function(){return(e[3]<<3)+h(3)+e[2]|0};p[219]=function(){return(e[3]<<3)+h(3)+e[3]|0};p[220]=
+function(){return(e[3]<<3)+h(2)+e[4]|0};p[221]=function(b){return(e[3]<<3)+(b?h(2)+e[5]:h(3)+M())|0};p[222]=function(){return(e[3]<<3)+h(3)+e[6]|0};p[223]=function(){return(e[3]<<3)+h(3)+e[7]|0};p[32]=function(){return 0+h(3)+e[0]|0};p[33]=function(){return 0+h(3)+e[1]|0};p[34]=function(){return 0+h(3)+e[2]|0};p[35]=function(){return 0+h(3)+e[3]|0};p[36]=function(){return 0+h(2)+e[4]|0};p[37]=function(b){return 0+(b?h(2)+e[5]:h(3)+M())|0};p[38]=function(){return 0+h(3)+e[6]|0};p[39]=function(){return 0+
+h(3)+e[7]|0};p[96]=function(){return 0+h(3)+e[0]|0};p[97]=function(){return 0+h(3)+e[1]|0};p[98]=function(){return 0+h(3)+e[2]|0};p[99]=function(){return 0+h(3)+e[3]|0};p[100]=function(){return 0+h(2)+e[4]|0};p[101]=function(b){return 0+(b?h(2)+e[5]:h(3)+M())|0};p[102]=function(){return 0+h(3)+e[6]|0};p[103]=function(){return 0+h(3)+e[7]|0};p[160]=function(){return 0+h(3)+e[0]|0};p[161]=function(){return 0+h(3)+e[1]|0};p[162]=function(){return 0+h(3)+e[2]|0};p[163]=function(){return 0+h(3)+e[3]|0};
+p[164]=function(){return 0+h(2)+e[4]|0};p[165]=function(b){return 0+(b?h(2)+e[5]:h(3)+M())|0};p[166]=function(){return 0+h(3)+e[6]|0};p[167]=function(){return 0+h(3)+e[7]|0};p[224]=function(){return 0+h(3)+e[0]|0};p[225]=function(){return 0+h(3)+e[1]|0};p[226]=function(){return 0+h(3)+e[2]|0};p[227]=function(){return 0+h(3)+e[3]|0};p[228]=function(){return 0+h(2)+e[4]|0};p[229]=function(b){return 0+(b?h(2)+e[5]:h(3)+M())|0};p[230]=function(){return 0+h(3)+e[6]|0};p[231]=function(){return 0+h(3)+e[7]|
+0};p[40]=function(){return e[5]+h(3)+e[0]|0};p[41]=function(){return e[5]+h(3)+e[1]|0};p[42]=function(){return e[5]+h(3)+e[2]|0};p[43]=function(){return e[5]+h(3)+e[3]|0};p[44]=function(){return e[5]+h(2)+e[4]|0};p[45]=function(b){return e[5]+(b?h(2)+e[5]:h(3)+M())|0};p[46]=function(){return e[5]+h(3)+e[6]|0};p[47]=function(){return e[5]+h(3)+e[7]|0};p[104]=function(){return(e[5]<<1)+h(3)+e[0]|0};p[105]=function(){return(e[5]<<1)+h(3)+e[1]|0};p[106]=function(){return(e[5]<<1)+h(3)+e[2]|0};p[107]=
+function(){return(e[5]<<1)+h(3)+e[3]|0};p[108]=function(){return(e[5]<<1)+h(2)+e[4]|0};p[109]=function(b){return(e[5]<<1)+(b?h(2)+e[5]:h(3)+M())|0};p[110]=function(){return(e[5]<<1)+h(3)+e[6]|0};p[111]=function(){return(e[5]<<1)+h(3)+e[7]|0};p[168]=function(){return(e[5]<<2)+h(3)+e[0]|0};p[169]=function(){return(e[5]<<2)+h(3)+e[1]|0};p[170]=function(){return(e[5]<<2)+h(3)+e[2]|0};p[171]=function(){return(e[5]<<2)+h(3)+e[3]|0};p[172]=function(){return(e[5]<<2)+h(2)+e[4]|0};p[173]=function(b){return(e[5]<<
+2)+(b?h(2)+e[5]:h(3)+M())|0};p[174]=function(){return(e[5]<<2)+h(3)+e[6]|0};p[175]=function(){return(e[5]<<2)+h(3)+e[7]|0};p[232]=function(){return(e[5]<<3)+h(3)+e[0]|0};p[233]=function(){return(e[5]<<3)+h(3)+e[1]|0};p[234]=function(){return(e[5]<<3)+h(3)+e[2]|0};p[235]=function(){return(e[5]<<3)+h(3)+e[3]|0};p[236]=function(){return(e[5]<<3)+h(2)+e[4]|0};p[237]=function(b){return(e[5]<<3)+(b?h(2)+e[5]:h(3)+M())|0};p[238]=function(){return(e[5]<<3)+h(3)+e[6]|0};p[239]=function(){return(e[5]<<3)+h(3)+
+e[7]|0};p[48]=function(){return e[6]+h(3)+e[0]|0};p[49]=function(){return e[6]+h(3)+e[1]|0};p[50]=function(){return e[6]+h(3)+e[2]|0};p[51]=function(){return e[6]+h(3)+e[3]|0};p[52]=function(){return e[6]+h(2)+e[4]|0};p[53]=function(b){return e[6]+(b?h(2)+e[5]:h(3)+M())|0};p[54]=function(){return e[6]+h(3)+e[6]|0};p[55]=function(){return e[6]+h(3)+e[7]|0};p[112]=function(){return(e[6]<<1)+h(3)+e[0]|0};p[113]=function(){return(e[6]<<1)+h(3)+e[1]|0};p[114]=function(){return(e[6]<<1)+h(3)+e[2]|0};p[115]=
+function(){return(e[6]<<1)+h(3)+e[3]|0};p[116]=function(){return(e[6]<<1)+h(2)+e[4]|0};p[117]=function(b){return(e[6]<<1)+(b?h(2)+e[5]:h(3)+M())|0};p[118]=function(){return(e[6]<<1)+h(3)+e[6]|0};p[119]=function(){return(e[6]<<1)+h(3)+e[7]|0};p[176]=function(){return(e[6]<<2)+h(3)+e[0]|0};p[177]=function(){return(e[6]<<2)+h(3)+e[1]|0};p[178]=function(){return(e[6]<<2)+h(3)+e[2]|0};p[179]=function(){return(e[6]<<2)+h(3)+e[3]|0};p[180]=function(){return(e[6]<<2)+h(2)+e[4]|0};p[181]=function(b){return(e[6]<<
+2)+(b?h(2)+e[5]:h(3)+M())|0};p[182]=function(){return(e[6]<<2)+h(3)+e[6]|0};p[183]=function(){return(e[6]<<2)+h(3)+e[7]|0};p[240]=function(){return(e[6]<<3)+h(3)+e[0]|0};p[241]=function(){return(e[6]<<3)+h(3)+e[1]|0};p[242]=function(){return(e[6]<<3)+h(3)+e[2]|0};p[243]=function(){return(e[6]<<3)+h(3)+e[3]|0};p[244]=function(){return(e[6]<<3)+h(2)+e[4]|0};p[245]=function(b){return(e[6]<<3)+(b?h(2)+e[5]:h(3)+M())|0};p[246]=function(){return(e[6]<<3)+h(3)+e[6]|0};p[247]=function(){return(e[6]<<3)+h(3)+
+e[7]|0};p[56]=function(){return e[7]+h(3)+e[0]|0};p[57]=function(){return e[7]+h(3)+e[1]|0};p[58]=function(){return e[7]+h(3)+e[2]|0};p[59]=function(){return e[7]+h(3)+e[3]|0};p[60]=function(){return e[7]+h(2)+e[4]|0};p[61]=function(b){return e[7]+(b?h(2)+e[5]:h(3)+M())|0};p[62]=function(){return e[7]+h(3)+e[6]|0};p[63]=function(){return e[7]+h(3)+e[7]|0};p[120]=function(){return(e[7]<<1)+h(3)+e[0]|0};p[121]=function(){return(e[7]<<1)+h(3)+e[1]|0};p[122]=function(){return(e[7]<<1)+h(3)+e[2]|0};p[123]=
+function(){return(e[7]<<1)+h(3)+e[3]|0};p[124]=function(){return(e[7]<<1)+h(2)+e[4]|0};p[125]=function(b){return(e[7]<<1)+(b?h(2)+e[5]:h(3)+M())|0};p[126]=function(){return(e[7]<<1)+h(3)+e[6]|0};p[127]=function(){return(e[7]<<1)+h(3)+e[7]|0};p[184]=function(){return(e[7]<<2)+h(3)+e[0]|0};p[185]=function(){return(e[7]<<2)+h(3)+e[1]|0};p[186]=function(){return(e[7]<<2)+h(3)+e[2]|0};p[187]=function(){return(e[7]<<2)+h(3)+e[3]|0};p[188]=function(){return(e[7]<<2)+h(2)+e[4]|0};p[189]=function(b){return(e[7]<<
+2)+(b?h(2)+e[5]:h(3)+M())|0};p[190]=function(){return(e[7]<<2)+h(3)+e[6]|0};p[191]=function(){return(e[7]<<2)+h(3)+e[7]|0};p[248]=function(){return(e[7]<<3)+h(3)+e[0]|0};p[249]=function(){return(e[7]<<3)+h(3)+e[1]|0};p[250]=function(){return(e[7]<<3)+h(3)+e[2]|0};p[251]=function(){return(e[7]<<3)+h(3)+e[3]|0};p[252]=function(){return(e[7]<<3)+h(2)+e[4]|0};p[253]=function(b){return(e[7]<<3)+(b?h(2)+e[5]:h(3)+M())|0};p[254]=function(){return(e[7]<<3)+h(3)+e[6]|0};p[255]=function(){return(e[7]<<3)+h(3)+
+e[7]|0};ie=function(b){return va[b]()};he=function(b){return ta[b]()};"use strict";var oe=new Uint8Array([32,0,1,26,2,23,27,0,3,16,24,30,28,11,0,13,4,7,17,0,25,22,31,15,29,10,12,6,0,21,14,9,5,20,8,19,18]);"use strict";var Ya=Tc,Ra=Ba,Sa=Gb,Za=hd,Ta=ge;"use strict";"use strict";"use strict";var r=[],s=[],x=[],w=[];r[0]=s[0]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,kc(c,v[b>>1&12|b>>5&1]))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=kc(c,v[b>>1&12|b>>5&1]))};r[1]=function(){var b=m(),c,
+d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,eb(c,g[b>>2&14]))):(c=k.b(d),k.a(d,eb(c,g[b>>2&14])))):(c=g[b<<1&14],g[b<<1&14]=eb(c,g[b>>2&14]))};s[1]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,fb(c,y[b>>3&7]))):(c=k.c(d)>>>0,k.f(d,fb(c,y[b>>3&7])))):(c=y[b&7],e[b&7]=fb(c,y[b>>3&7]))};r[2]=s[2]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];v[b>>1&12|b>>5&1]=kc(v[b>>1&12|b>>5&1],c)};r[3]=function(){var b=m(),c=192>
+b?Q(l(b)):g[b<<1&14];g[b>>2&14]=eb(g[b>>2&14],c)};s[3]=function(){var b=m(),c=192>b?S(l(b))>>>0:y[b&7];e[b>>3&7]=fb(y[b>>3&7],c)};r[4]=s[4]=function(){v[0]=kc(v[0],m())};r[5]=function(){g[0]=eb(g[0],U())};s[5]=function(){y[0]=fb(y[0],M()>>>0)};r[6]=function(){la(ra[0])};s[6]=function(){ia(ra[0])};r[7]=function(){ca(0,k.b(Va(0)));$[aa]+=2};s[7]=function(){ca(0,k.b(Va(0)));$[aa]+=4};r[8]=s[8]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,yc(c,v[b>>1&12|b>>5&1]))):(c=v[b<<2&12|b>>2&1],v[b<<
+2&12|b>>2&1]=yc(c,v[b>>1&12|b>>5&1]))};r[9]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,vb(c,g[b>>2&14]))):(c=k.b(d),k.a(d,vb(c,g[b>>2&14])))):(c=g[b<<1&14],g[b<<1&14]=vb(c,g[b>>2&14]))};s[9]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g),H(d,g,wb(c,e[b>>3&7]))):(c=k.c(d),k.f(d,wb(c,e[b>>3&7])))):(c=e[b&7],e[b&7]=wb(c,e[b>>3&7]))};r[10]=s[10]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];v[b>>1&12|b>>5&
+1]=yc(v[b>>1&12|b>>5&1],c)};r[11]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=vb(g[b>>2&14],c)};s[11]=function(){var b=m(),c=192>b?S(l(b)):e[b&7];e[b>>3&7]=wb(e[b>>3&7],c)};r[12]=s[12]=function(){v[0]=yc(v[0],m())};r[13]=function(){g[0]=vb(g[0],U())};s[13]=function(){y[0]=wb(e[0],M())};r[14]=function(){la(ra[1])};s[14]=function(){ia(ra[1])};r[15]=s[15]=function(){Jd[m()]()};r[16]=s[16]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,uc(c,v[b>>1&12|b>>5&1]))):(c=v[b<<2&12|b>>
+2&1],v[b<<2&12|b>>2&1]=uc(c,v[b>>1&12|b>>5&1]))};r[17]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,jb(c,g[b>>2&14]))):(c=k.b(d),k.a(d,jb(c,g[b>>2&14])))):(c=g[b<<1&14],g[b<<1&14]=jb(c,g[b>>2&14]))};s[17]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,kb(c,y[b>>3&7]))):(c=k.c(d)>>>0,k.f(d,kb(c,y[b>>3&7])))):(c=y[b&7],e[b&7]=kb(c,y[b>>3&7]))};r[18]=s[18]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>
+2&1];v[b>>1&12|b>>5&1]=uc(v[b>>1&12|b>>5&1],c)};r[19]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=jb(g[b>>2&14],c)};s[19]=function(){var b=m(),c=192>b?S(l(b))>>>0:y[b&7];e[b>>3&7]=kb(y[b>>3&7],c)};r[20]=s[20]=function(){v[0]=uc(v[0],m())};r[21]=function(){g[0]=jb(g[0],U())};s[21]=function(){y[0]=kb(y[0],M()>>>0)};r[22]=function(){la(ra[2])};s[22]=function(){ia(ra[2])};r[23]=function(){ca(2,k.b(Va(0)));$[aa]+=2};s[23]=function(){ca(2,k.b(Va(0)));$[aa]+=4};r[24]=s[24]=function(){var b=
+m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,wc(c,v[b>>1&12|b>>5&1]))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=wc(c,v[b>>1&12|b>>5&1]))};r[25]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,nb(c,g[b>>2&14]))):(c=k.b(d),k.a(d,nb(c,g[b>>2&14])))):(c=g[b<<1&14],g[b<<1&14]=nb(c,g[b>>2&14]))};s[25]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,ob(c,y[b>>3&7]))):(c=k.c(d)>>>0,k.f(d,ob(c,y[b>>3&7])))):(c=y[b&7],e[b&7]=
+ob(c,y[b>>3&7]))};r[26]=s[26]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];v[b>>1&12|b>>5&1]=wc(v[b>>1&12|b>>5&1],c)};r[27]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=nb(g[b>>2&14],c)};s[27]=function(){var b=m(),c=192>b?S(l(b))>>>0:y[b&7];e[b>>3&7]=ob(y[b>>3&7],c)};r[28]=s[28]=function(){v[0]=wc(v[0],m())};r[29]=function(){g[0]=nb(g[0],U())};s[29]=function(){y[0]=ob(y[0],M()>>>0)};r[30]=function(){la(ra[3])};s[30]=function(){ia(ra[3])};r[31]=function(){ca(3,k.b(Va(0)));
+$[aa]+=2};s[31]=function(){ca(3,k.b(Va(0)));$[aa]+=4};r[32]=s[32]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,xc(c,v[b>>1&12|b>>5&1]))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=xc(c,v[b>>1&12|b>>5&1]))};r[33]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,tb(c,g[b>>2&14]))):(c=k.b(d),k.a(d,tb(c,g[b>>2&14])))):(c=g[b<<1&14],g[b<<1&14]=tb(c,g[b>>2&14]))};s[33]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g),
+H(d,g,ub(c,e[b>>3&7]))):(c=k.c(d),k.f(d,ub(c,e[b>>3&7])))):(c=e[b&7],e[b&7]=ub(c,e[b>>3&7]))};r[34]=s[34]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];v[b>>1&12|b>>5&1]=xc(v[b>>1&12|b>>5&1],c)};r[35]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=tb(g[b>>2&14],c)};s[35]=function(){var b=m(),c=192>b?S(l(b)):e[b&7];e[b>>3&7]=ub(e[b>>3&7],c)};r[36]=s[36]=function(){v[0]=xc(v[0],m())};r[37]=function(){g[0]=tb(g[0],U())};s[37]=function(){y[0]=ub(e[0],M())};r[38]=s[38]=function(){ja(0)};
+r[39]=s[39]=function(){var b=v[0],c=Ba(),d=Sc();u&=-18;if(9<(b&15)||d)v[0]+=6,u|=16;if(153<b||c)v[0]+=96,u|=1;I=v[0];Y=8;ba=ma=0;K=196};r[40]=s[40]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,vc(c,v[b>>1&12|b>>5&1]))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=vc(c,v[b>>1&12|b>>5&1]))};r[41]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,lb(c,g[b>>2&14]))):(c=k.b(d),k.a(d,lb(c,g[b>>2&14])))):(c=g[b<<1&14],g[b<<1&14]=lb(c,g[b>>2&14]))};s[41]=function(){var b=
+m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,mb(c,y[b>>3&7]))):(c=k.c(d)>>>0,k.f(d,mb(c,y[b>>3&7])))):(c=y[b&7],e[b&7]=mb(c,y[b>>3&7]))};r[42]=s[42]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];v[b>>1&12|b>>5&1]=vc(v[b>>1&12|b>>5&1],c)};r[43]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=lb(g[b>>2&14],c)};s[43]=function(){var b=m(),c=192>b?S(l(b))>>>0:y[b&7];e[b>>3&7]=mb(y[b>>3&7],c)};r[44]=s[44]=function(){v[0]=vc(v[0],m())};r[45]=function(){g[0]=
+lb(g[0],U())};s[45]=function(){y[0]=mb(y[0],M()>>>0)};r[46]=s[46]=function(){ja(1)};r[47]=s[47]=function(){var b=v[0],c=Ba();u&=-2;9<(b&15)||Sc()?(v[0]-=6,u|=16,u=u&-2|c|v[0]>>7):u&=-17;if(153<b||c)v[0]-=96,u|=1;I=v[0];Y=8;ba=ma=0;K=196};r[48]=s[48]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,zc(c,v[b>>1&12|b>>5&1]))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=zc(c,v[b>>1&12|b>>5&1]))};r[49]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,xb(c,
+g[b>>2&14]))):(c=k.b(d),k.a(d,xb(c,g[b>>2&14])))):(c=g[b<<1&14],g[b<<1&14]=xb(c,g[b>>2&14]))};s[49]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g),H(d,g,yb(c,e[b>>3&7]))):(c=k.c(d),k.f(d,yb(c,e[b>>3&7])))):(c=e[b&7],e[b&7]=yb(c,e[b>>3&7]))};r[50]=s[50]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];v[b>>1&12|b>>5&1]=zc(v[b>>1&12|b>>5&1],c)};r[51]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=xb(g[b>>2&14],c)};s[51]=function(){var b=m(),c=
+192>b?S(l(b)):e[b&7];e[b>>3&7]=yb(e[b>>3&7],c)};r[52]=s[52]=function(){v[0]=zc(v[0],m())};r[53]=function(){g[0]=xb(g[0],U())};s[53]=function(){y[0]=yb(e[0],M())};r[54]=s[54]=function(){ja(2)};r[55]=s[55]=function(){9<(v[0]&15)||Sc()?(g[0]+=6,v[1]+=1,u|=17):u&=-18;v[0]&=15;K&=-18};r[56]=s[56]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];lc(c,v[b>>1&12|b>>5&1])};r[57]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];Yb(c,g[b>>2&14])};s[57]=function(){var b=m(),c=192>b?S(l(b))>>>0:y[b&7];Zb(c,
+y[b>>3&7])};r[58]=s[58]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];lc(v[b>>1&12|b>>5&1],c)};r[59]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];Yb(g[b>>2&14],c)};s[59]=function(){var b=m(),c=192>b?S(l(b))>>>0:y[b&7];Zb(y[b>>3&7],c)};r[60]=s[60]=function(){lc(v[0],m())};r[61]=function(){Yb(g[0],U())};s[61]=function(){Zb(y[0],M()>>>0)};r[62]=s[62]=function(){ja(3)};r[63]=s[63]=function(){9<(v[0]&15)||Sc()?(g[0]-=6,v[1]-=1,u|=17):u&=-18;v[0]&=15;K&=-18};r[64]=function(){g[0]=pb(g[0])};
+s[64]=function(){y[0]=qb(y[0])};r[65]=function(){g[2]=pb(g[2])};s[65]=function(){y[1]=qb(y[1])};r[66]=function(){g[4]=pb(g[4])};s[66]=function(){y[2]=qb(y[2])};r[67]=function(){g[6]=pb(g[6])};s[67]=function(){y[3]=qb(y[3])};r[68]=function(){g[8]=pb(g[8])};s[68]=function(){y[4]=qb(y[4])};r[69]=function(){g[10]=pb(g[10])};s[69]=function(){y[5]=qb(y[5])};r[70]=function(){g[12]=pb(g[12])};s[70]=function(){y[6]=qb(y[6])};r[71]=function(){g[14]=pb(g[14])};s[71]=function(){y[7]=qb(y[7])};r[72]=function(){g[0]=
+rb(g[0])};s[72]=function(){y[0]=sb(y[0])};r[73]=function(){g[2]=rb(g[2])};s[73]=function(){y[1]=sb(y[1])};r[74]=function(){g[4]=rb(g[4])};s[74]=function(){y[2]=sb(y[2])};r[75]=function(){g[6]=rb(g[6])};s[75]=function(){y[3]=sb(y[3])};r[76]=function(){g[8]=rb(g[8])};s[76]=function(){y[4]=sb(y[4])};r[77]=function(){g[10]=rb(g[10])};s[77]=function(){y[5]=sb(y[5])};r[78]=function(){g[12]=rb(g[12])};s[78]=function(){y[6]=sb(y[6])};r[79]=function(){g[14]=rb(g[14])};s[79]=function(){y[7]=sb(y[7])};r[80]=
+function(){la(g[0])};s[80]=function(){ia(e[0])};r[81]=function(){la(g[2])};s[81]=function(){ia(e[1])};r[82]=function(){la(g[4])};s[82]=function(){ia(e[2])};r[83]=function(){la(g[6])};s[83]=function(){ia(e[3])};r[84]=function(){la(g[8])};s[84]=function(){ia(e[4])};r[85]=function(){la(g[10])};s[85]=function(){ia(e[5])};r[86]=function(){la(g[12])};s[86]=function(){ia(e[6])};r[87]=function(){la(g[14])};s[87]=function(){ia(e[7])};r[88]=function(){g[0]=Aa()};s[88]=function(){y[0]=ua()};r[89]=function(){g[2]=
+Aa()};s[89]=function(){y[1]=ua()};r[90]=function(){g[4]=Aa()};s[90]=function(){y[2]=ua()};r[91]=function(){g[6]=Aa()};s[91]=function(){y[3]=ua()};r[92]=function(){g[8]=Aa()};s[92]=function(){y[4]=ua()};r[93]=function(){g[10]=Aa()};s[93]=function(){y[5]=ua()};r[94]=function(){g[12]=Aa()};s[94]=function(){y[6]=ua()};r[95]=function(){g[14]=Aa()};s[95]=function(){y[7]=ua()};r[96]=function(){var b=g[8];q(b-15);la(g[0]);la(g[2]);la(g[4]);la(g[6]);la(b);la(g[10]);la(g[12]);la(g[14])};s[96]=function(){var b=
+e[4];q(b-31);ia(e[0]);ia(e[1]);ia(e[2]);ia(e[3]);ia(b);ia(e[5]);ia(e[6]);ia(e[7])};r[97]=function(){Ea($[aa]+15);g[14]=Aa();g[12]=Aa();g[10]=Aa();$[aa]+=2;g[6]=Aa();g[4]=Aa();g[2]=Aa();g[0]=Aa()};s[97]=function(){Ea($[aa]+31);y[7]=ua();y[6]=ua();y[5]=ua();$[aa]+=4;y[3]=ua();y[2]=ua();y[1]=ua();y[0]=ua()};r[98]=s[98]=function(){throw ze("bound instruction");};r[99]=s[99]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,Ca(c,b>>2&14))):(c=k.b(d),k.a(d,Ca(c,
+b>>2&14)))):(c=g[b<<1&14],g[b<<1&14]=Ca(c,b>>2&14))};r[100]=s[100]=function(){ja(4)};r[101]=s[101]=function(){ja(5)};r[102]=s[102]=function(){xa=!Db;La();Ab[m()]();xa=Db;La()};r[103]=s[103]=function(){Cb=!Db;zb();Ab[m()]();Cb=Db;zb()};r[104]=function(){la(U())};s[104]=function(){ia(M())};r[105]=function(){var b=m(),c=192>b?Q(l(b))<<16>>16:Ga[b<<1&14];g[b>>2&14]=Md(U()<<16>>16,c)};s[105]=function(){var b=m(),c=192>b?S(l(b)):e[b&7];y[b>>3&7]=Nd(M(),c)};r[106]=function(){la(P())};s[106]=function(){ia(P())};
+r[107]=function(){var b=m(),c=192>b?Q(l(b))<<16>>16:Ga[b<<1&14];g[b>>2&14]=Md(P(),c)};s[107]=function(){var b=m(),c=192>b?S(l(b)):e[b&7];y[b>>3&7]=Nd(P(),c)};r[108]=s[108]=function(){var b=g[4],c,d=u&1024?-1:1;c=da[0]+A[T];if(wa){if(0!==A[ga]){do V(c,hc(b)),c+=d,A[T]+=d;while(--A[ga])}}else V(c,hc(b)),A[T]+=d};r[109]=function(){var b=g[4],c,d=u&1024?-1:1;c=da[0]+A[T];if(wa){if(0!==A[ga]){do R(c,ic(b)),c+=d,A[T]+=d;while(--A[ga])}}else R(c,ic(b)),A[T]+=d};s[109]=function(){var b=g[4],c,d=u&1024?-4:
+4;c=da[0]+A[T];if(wa){if(0!==A[ga]){var e=0===(c&3)&&!0;do{if(e){var h=q(c);k.D(h,cb(b))}else N(c,cb(b));c+=d;A[T]+=d}while(--A[ga])}}else N(c,cb(b)),A[T]+=d};r[110]=s[110]=function(){var b=g[4],c,d=u&1024?-1:1;c=h(3)+A[W];if(wa){if(0!==A[ga]){do Fa(b,ka(c)),c+=d,A[W]+=d;while(--A[ga])}}else Fa(b,ka(c)),A[W]+=d};r[111]=function(){var b=g[4],c,d=u&1024?-2:2;c=h(3)+A[W];if(wa){if(0!==A[ga]){do Xb(b,Q(c)),c+=d,A[W]+=d;while(--A[ga])}}else Xb(b,Q(c)),A[W]+=d};s[111]=function(){var b=g[4],c,d=u&1024?-4:
+4;c=h(3)+A[W];if(wa){if(0!==A[ga]){do gc(b,S(c)),c+=d,A[W]+=d;while(--A[ga])}}else gc(b,S(c)),A[W]+=d};r[112]=s[112]=function(){Ya()&&(E=E+P()|0);E++};r[113]=s[113]=function(){Ya()||(E=E+P()|0);E++};r[114]=s[114]=function(){Ra()&&(E=E+P()|0);E++};r[115]=s[115]=function(){Ra()||(E=E+P()|0);E++};r[116]=s[116]=function(){Sa()&&(E=E+P()|0);E++};r[117]=s[117]=function(){Sa()||(E=E+P()|0);E++};r[118]=s[118]=function(){Qa()&&(E=E+P()|0);E++};r[119]=s[119]=function(){Qa()||(E=E+P()|0);E++};r[120]=s[120]=
+function(){Za()&&(E=E+P()|0);E++};r[121]=s[121]=function(){Za()||(E=E+P()|0);E++};r[122]=s[122]=function(){Ta()&&(E=E+P()|0);E++};r[123]=s[123]=function(){Ta()||(E=E+P()|0);E++};r[124]=s[124]=function(){Wa()&&(E=E+P()|0);E++};r[125]=s[125]=function(){Wa()||(E=E+P()|0);E++};r[126]=s[126]=function(){Xa()&&(E=E+P()|0);E++};r[127]=s[127]=function(){Xa()||(E=E+P()|0);E++};r[128]=s[128]=function(){var b=m();switch(b>>3&7){case 0:var c;192>b?(b=q(l(b)),c=k.d(b),k.e(b,kc(c,m()))):(c=v[b<<2&12|b>>2&1],v[b<<
+2&12|b>>2&1]=kc(c,m()));break;case 1:192>b?(b=q(l(b)),c=k.d(b),k.e(b,yc(c,m()))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=yc(c,m()));break;case 2:192>b?(b=q(l(b)),c=k.d(b),k.e(b,uc(c,m()))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=uc(c,m()));break;case 3:192>b?(b=q(l(b)),c=k.d(b),k.e(b,wc(c,m()))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=wc(c,m()));break;case 4:192>b?(b=q(l(b)),c=k.d(b),k.e(b,xc(c,m()))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=xc(c,m()));break;case 5:192>b?(b=q(l(b)),c=k.d(b),k.e(b,vc(c,
+m()))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=vc(c,m()));break;case 6:192>b?(b=q(l(b)),c=k.d(b),k.e(b,zc(c,m()))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=zc(c,m()));break;case 7:c=192>b?ka(l(b)):v[b<<2&12|b>>2&1],lc(c,m())}};r[129]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,eb(c,U()))):(c=k.b(b),k.a(b,eb(c,U())))):(c=g[b<<1&14],g[b<<1&14]=eb(c,U()));break;case 1:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,
+vb(c,U()))):(c=k.b(b),k.a(b,vb(c,U())))):(c=g[b<<1&14],g[b<<1&14]=vb(c,U()));break;case 2:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,jb(c,U()))):(c=k.b(b),k.a(b,jb(c,U())))):(c=g[b<<1&14],g[b<<1&14]=jb(c,U()));break;case 3:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,nb(c,U()))):(c=k.b(b),k.a(b,nb(c,U())))):(c=g[b<<1&14],g[b<<1&14]=nb(c,U()));break;case 4:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,tb(c,U()))):(c=k.b(b),k.a(b,tb(c,U())))):
+(c=g[b<<1&14],g[b<<1&14]=tb(c,U()));break;case 5:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,lb(c,U()))):(c=k.b(b),k.a(b,lb(c,U())))):(c=g[b<<1&14],g[b<<1&14]=lb(c,U()));break;case 6:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,xb(c,U()))):(c=k.b(b),k.a(b,xb(c,U())))):(c=g[b<<1&14],g[b<<1&14]=xb(c,U()));break;case 7:c=192>b?Q(l(b)):g[b<<1&14],Yb(c,U())}};s[129]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+
+3),c=G(b,d)>>>0,H(b,d,fb(c,M()>>>0))):(c=k.c(b)>>>0,k.f(b,fb(c,M()>>>0)))):(c=y[b&7],e[b&7]=fb(c,M()>>>0));break;case 1:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d),H(b,d,wb(c,M()))):(c=k.c(b),k.f(b,wb(c,M())))):(c=e[b&7],e[b&7]=wb(c,M()));break;case 2:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,kb(c,M()>>>0))):(c=k.c(b)>>>0,k.f(b,kb(c,M()>>>0)))):(c=y[b&7],e[b&7]=kb(c,M()>>>0));break;case 3:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,ob(c,
+M()>>>0))):(c=k.c(b)>>>0,k.f(b,ob(c,M()>>>0)))):(c=y[b&7],e[b&7]=ob(c,M()>>>0));break;case 4:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d),H(b,d,ub(c,M()))):(c=k.c(b),k.f(b,ub(c,M())))):(c=e[b&7],e[b&7]=ub(c,M()));break;case 5:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,mb(c,M()>>>0))):(c=k.c(b)>>>0,k.f(b,mb(c,M()>>>0)))):(c=y[b&7],e[b&7]=mb(c,M()>>>0));break;case 6:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d),H(b,d,yb(c,M()))):(c=k.c(b),k.f(b,yb(c,
+M())))):(c=e[b&7],e[b&7]=yb(c,M()));break;case 7:192>b?c=S(l(b))>>>0:c=y[b&7],Zb(c,M()>>>0)}};r[130]=s[130]=function(){Ab[128]()};r[131]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,eb(c,P()&65535))):(c=k.b(b),k.a(b,eb(c,P()&65535)))):(c=g[b<<1&14],g[b<<1&14]=eb(c,P()&65535));break;case 1:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,vb(c,P()))):(c=k.b(b),k.a(b,vb(c,P())))):(c=g[b<<1&14],g[b<<1&14]=vb(c,P()));
+break;case 2:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,jb(c,P()&65535))):(c=k.b(b),k.a(b,jb(c,P()&65535)))):(c=g[b<<1&14],g[b<<1&14]=jb(c,P()&65535));break;case 3:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,nb(c,P()&65535))):(c=k.b(b),k.a(b,nb(c,P()&65535)))):(c=g[b<<1&14],g[b<<1&14]=nb(c,P()&65535));break;case 4:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,tb(c,P()))):(c=k.b(b),k.a(b,tb(c,P())))):(c=g[b<<1&14],g[b<<1&14]=tb(c,P()));
+break;case 5:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,lb(c,P()&65535))):(c=k.b(b),k.a(b,lb(c,P()&65535)))):(c=g[b<<1&14],g[b<<1&14]=lb(c,P()&65535));break;case 6:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,xb(c,P()))):(c=k.b(b),k.a(b,xb(c,P())))):(c=g[b<<1&14],g[b<<1&14]=xb(c,P()));break;case 7:c=192>b?Q(l(b)):g[b<<1&14],Yb(c,P()&65535)}};s[131]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>
+0,H(b,d,fb(c,P()>>>0))):(c=k.c(b)>>>0,k.f(b,fb(c,P()>>>0)))):(c=y[b&7],e[b&7]=fb(c,P()>>>0));break;case 1:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d),H(b,d,wb(c,P()))):(c=k.c(b),k.f(b,wb(c,P())))):(c=e[b&7],e[b&7]=wb(c,P()));break;case 2:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,kb(c,P()>>>0))):(c=k.c(b)>>>0,k.f(b,kb(c,P()>>>0)))):(c=y[b&7],e[b&7]=kb(c,P()>>>0));break;case 3:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,ob(c,P()>>>0))):
+(c=k.c(b)>>>0,k.f(b,ob(c,P()>>>0)))):(c=y[b&7],e[b&7]=ob(c,P()>>>0));break;case 4:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d),H(b,d,ub(c,P()))):(c=k.c(b),k.f(b,ub(c,P())))):(c=e[b&7],e[b&7]=ub(c,P()));break;case 5:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,mb(c,P()>>>0))):(c=k.c(b)>>>0,k.f(b,mb(c,P()>>>0)))):(c=y[b&7],e[b&7]=mb(c,P()>>>0));break;case 6:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d),H(b,d,yb(c,P()))):(c=k.c(b),k.f(b,yb(c,P())))):(c=
+e[b&7],e[b&7]=yb(c,P()));break;case 7:192>b?c=S(l(b))>>>0:c=y[b&7],Zb(c,P()>>>0)}};r[132]=s[132]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];id(c,v[b>>1&12|b>>5&1])};r[133]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];jd(c,g[b>>2&14])};s[133]=function(){var b=m(),c=192>b?S(l(b)):e[b&7];kd(c,e[b>>3&7])};r[134]=s[134]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,ne(c,b))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=ne(c,b))};r[135]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),
+J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,Rd(c,b))):(c=k.b(d),k.a(d,Rd(c,b)))):(c=g[b<<1&14],g[b<<1&14]=Rd(c,b))};s[135]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,Sd(c,b))):(c=k.c(d)>>>0,k.f(d,Sd(c,b)))):(c=y[b&7],e[b&7]=Sd(c,b))};r[136]=s[136]=function(){var b=m();192>b?V(l(b),v[b>>1&12|b>>5&1]):v[b<<2&12|b>>2&1]=v[b>>1&12|b>>5&1]};r[137]=function(){var b=m();192>b?R(l(b),g[b>>2&14]):g[b<<1&14]=g[b>>2&14]};s[137]=function(){var b=m();192>b?N(l(b),
+e[b>>3&7]):y[b&7]=e[b>>3&7]};r[138]=s[138]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];v[b>>1&12|b>>5&1]=c};r[139]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c};s[139]=function(){var b=m(),c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c};r[140]=function(){var b=m();192>b?R(l(b),ra[b>>3&7]):g[b<<1&14]=ra[b>>3&7]};s[140]=function(){var b=m();192>b?N(l(b),ra[b>>3&7]):y[b&7]=ra[b>>3&7]};r[141]=function(){var b=m();ab=6;g[(b>>3&7)<<1]=l(b);ab=-1};s[141]=function(){var b=m();ab=6;y[b>>3&
+7]=l(b);ab=-1};r[142]=s[142]=function(){var b=m(),c=b>>3&7,b=192>b?Q(l(b)):g[b<<1&14];ca(c,b)};r[143]=s[143]=function(){var b=m();if(xa){var c=Va(0);$[aa]+=4;192>b?N(l(b),k.c(c)):y[b&7]=k.c(c)}else c=Va(0),$[aa]+=2,192>b?R(l(b),k.b(c)):g[b<<1&14]=k.b(c)};r[144]=function(){$b(0)};s[144]=function(){ac(0)};r[145]=function(){$b(2)};s[145]=function(){ac(1)};r[146]=function(){$b(4)};s[146]=function(){ac(2)};r[147]=function(){$b(6)};s[147]=function(){ac(3)};r[148]=function(){$b(8)};s[148]=function(){ac(4)};
+r[149]=function(){$b(10)};s[149]=function(){ac(5)};r[150]=function(){$b(12)};s[150]=function(){ac(6)};r[151]=function(){$b(14)};s[151]=function(){ac(7)};r[144]=s[144]=function(){};r[152]=function(){g[0]=fc[0]};s[152]=function(){y[0]=Ga[0]};r[153]=function(){g[4]=Ga[0]>>15};s[153]=function(){y[2]=e[0]>>31};r[154]=function(){if(Ma)throw ze("16 bit callf in protected mode");var b=U(),c=U();la(ra[1]);la(pa());ca(1,c);E=da[1]+b|0};s[154]=function(){if(Ma)throw ze("callf");var b=M(),c=U();ia(ra[1]);ia(pa());
+ca(1,c);E=da[1]+b|0};r[155]=s[155]=function(){};r[156]=function(){na();la(u)};s[156]=function(){na();ia(u)};r[157]=function(){var b;b=Aa();jc(b);Da()};s[157]=function(){jc(ua());Da()};r[158]=s[158]=function(){u=u&-256|v[1];u=u&4161493|2;K=0};r[159]=s[159]=function(){na();v[1]=u};r[160]=s[160]=function(){var b=ka(F());v[0]=b};r[161]=function(){var b=Q(F());g[0]=b};s[161]=function(){var b=S(F());y[0]=b};r[162]=s[162]=function(){V(F(),v[0])};r[163]=function(){R(F(),g[0])};s[163]=function(){N(F(),e[0])};
+r[164]=s[164]=function(){var b,c,d=u&1024?-1:1;c=da[0]+A[T];b=h(3)+A[W];if(wa){if(0!==A[ga]){do V(c,ka(b)),c+=d,A[T]+=d,b+=d,A[W]+=d;while(--A[ga])}}else V(c,ka(b)),A[T]+=d,A[W]+=d};r[165]=function(){var b,c,d=u&1024?-2:2;c=da[0]+A[T];b=h(3)+A[W];if(wa){if(0!==A[ga]){var e=0===(c&1)&&0===(b&1);do{if(e){var g=Ea(b),l=q(c);k.S(l,k.q(g))}else R(c,Q(b));c+=d;A[T]+=d;b+=d;A[W]+=d}while(--A[ga])}}else R(c,Q(b)),A[T]+=d,A[W]+=d};s[165]=function(){var b,c,d=u&1024?-4:4;c=da[0]+A[T];b=h(3)+A[W];if(wa){if(0!==
+A[ga]){var e=0===(c&3)&&0===(b&3);do{if(e){var g=Ea(b),l=q(c);k.D(l,k.v(g))}else N(c,S(b));c+=d;A[T]+=d;b+=d;A[W]+=d}while(--A[ga])}}else N(c,S(b)),A[T]+=d,A[W]+=d};r[166]=s[166]=function(){a:{var b,c,d,e,g=u&1024?-1:1;c=da[0]+A[T];b=h(3)+A[W];if(wa){if(0===A[ga])break a;do e=ka(c),d=ka(b),c+=g,A[T]+=g,b+=g,A[W]+=g;while(--A[ga]&&d===e===cc)}else e=ka(c),d=ka(b),A[T]+=g,A[W]+=g;lc(d,e)}};r[167]=function(){a:{var b,c,d,e,g=u&1024?-2:2;c=da[0]+A[T];b=h(3)+A[W];if(wa){if(0===A[ga])break a;var l=0===
+(c&1)&&0===(b&1);do l?(e=k.q(Ea(c)),d=k.q(Ea(b))):(e=Q(c),d=Q(b)),c+=g,A[T]+=g,b+=g,A[W]+=g;while(--A[ga]&&d===e===cc)}else e=Q(c),d=Q(b),A[T]+=g,A[W]+=g;Yb(d,e)}};s[167]=function(){a:{var b,c,d,e,g=u&1024?-4:4;c=da[0]+A[T];b=h(3)+A[W];if(wa){if(0===A[ga])break a;var l=0===(c&3)&&0===(b&3);do l?(e=k.v(Ea(c))>>>0,d=k.v(Ea(b))>>>0):(e=S(c)>>>0,d=S(b)>>>0),c+=g,A[T]+=g,b+=g,A[W]+=g;while(--A[ga]&&d===e===cc)}else e=S(c)>>>0,d=S(b)>>>0,A[T]+=g,A[W]+=g;Zb(d,e)}};r[168]=s[168]=function(){id(v[0],m())};
+r[169]=function(){jd(g[0],U())};s[169]=function(){kd(e[0],M())};r[170]=s[170]=function(){var b=v[0],c,d=u&1024?-1:1;c=da[0]+A[T];if(wa){if(0!==A[ga]){do V(c,b),c+=d,A[T]+=d;while(--A[ga])}}else V(c,b),A[T]+=d};r[171]=function(){var b=g[0],c,d=u&1024?-2:2;c=da[0]+A[T];if(wa){if(0!==A[ga]){var e=0===(c&1)&&!0;do e?k.S(q(c),b):R(c,b),c+=d,A[T]+=d;while(--A[ga])}}else R(c,b),A[T]+=d};s[171]=function(){var b=y[0],c,d=u&1024?-4:4;c=da[0]+A[T];if(wa){if(0!==A[ga]){var e=0===(c&3)&&!0;do e?k.D(q(c),b):N(c,
+b),c+=d,A[T]+=d;while(--A[ga])}}else N(c,b),A[T]+=d};r[172]=s[172]=function(){var b,c=u&1024?-1:1;b=h(3)+A[W];if(wa){if(0!==A[ga]){do v[0]=ka(b),b+=c,A[W]+=c;while(--A[ga])}}else v[0]=ka(b),A[W]+=c};r[173]=function(){var b,c=u&1024?-2:2;b=h(3)+A[W];if(wa){if(0!==A[ga]){do g[0]=Q(b),b+=c,A[W]+=c;while(--A[ga])}}else g[0]=Q(b),A[W]+=c};s[173]=function(){var b,c=u&1024?-4:4;b=h(3)+A[W];if(wa){if(0!==A[ga]){do y[0]=S(b),b+=c,A[W]+=c;while(--A[ga])}}else y[0]=S(b),A[W]+=c};r[174]=s[174]=function(){a:{var b,
+c,d,e=u&1024?-1:1;c=v[0];b=da[0]+A[T];if(wa){if(0===A[ga])break a;do d=ka(b),b+=e,A[T]+=e;while(--A[ga]&&c===d===cc)}else d=ka(b),A[T]+=e;lc(c,d)}};r[175]=function(){a:{var b,c,d,e=u&1024?-2:2;c=g[0];b=da[0]+A[T];if(wa){if(0===A[ga])break a;var h=0===(b&1)&&!0;do d=h?k.q(Ea(b)):Q(b),b+=e,A[T]+=e;while(--A[ga]&&c===d===cc)}else d=Q(b),A[T]+=e;Yb(c,d)}};s[175]=function(){a:{var b,c,d,e=u&1024?-4:4;c=y[0];b=da[0]+A[T];if(wa){if(0===A[ga])break a;var g=0===(b&3)&&!0;do d=g?k.v(Ea(b))>>>0:S(b)>>>0,b+=
+e,A[T]+=e;while(--A[ga]&&c===d===cc)}else d=S(b)>>>0,A[T]+=e;Zb(c,d)}};r[176]=s[176]=function(){v[0]=m()};r[177]=s[177]=function(){v[4]=m()};r[178]=s[178]=function(){v[8]=m()};r[179]=s[179]=function(){v[12]=m()};r[180]=s[180]=function(){v[1]=m()};r[181]=s[181]=function(){v[5]=m()};r[182]=s[182]=function(){v[9]=m()};r[183]=s[183]=function(){v[13]=m()};r[184]=function(){g[0]=U()};s[184]=function(){e[0]=M()};r[185]=function(){g[2]=U()};s[185]=function(){e[1]=M()};r[186]=function(){g[4]=U()};s[186]=function(){e[2]=
+M()};r[187]=function(){g[6]=U()};s[187]=function(){e[3]=M()};r[188]=function(){g[8]=U()};s[188]=function(){e[4]=M()};r[189]=function(){g[10]=U()};s[189]=function(){e[5]=M()};r[190]=function(){g[12]=U()};s[190]=function(){e[6]=M()};r[191]=function(){g[14]=U()};s[191]=function(){e[7]=M()};r[192]=s[192]=function(){var b=m();switch(b>>3&7){case 0:var c;192>b?(b=q(l(b)),c=k.d(b),k.e(b,Ac(c,m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Ac(c,m()&31));break;case 1:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Cc(c,
+m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Cc(c,m()&31));break;case 2:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Bc(c,m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Bc(c,m()&31));break;case 3:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Dc(c,m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Dc(c,m()&31));break;case 4:192>b?(b=q(l(b)),c=k.d(b),k.e(b,gb(c,m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=gb(c,m()&31));break;case 5:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Ec(c,m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=
+Ec(c,m()&31));break;case 6:192>b?(b=q(l(b)),c=k.d(b),k.e(b,gb(c,m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=gb(c,m()&31));break;case 7:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Fc(c,m()&31))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Fc(c,m()&31))}};r[193]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Hb(c,m()&31))):(c=k.b(b),k.a(b,Hb(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Hb(c,m()&31));break;case 1:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?
+(d=q(c+1),c=B(b,d),n(b,d,Lb(c,m()&31))):(c=k.b(b),k.a(b,Lb(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Lb(c,m()&31));break;case 2:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Jb(c,m()&31))):(c=k.b(b),k.a(b,Jb(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Jb(c,m()&31));break;case 3:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Nb(c,m()&31))):(c=k.b(b),k.a(b,Nb(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Nb(c,m()&31));break;case 4:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),
+c=B(b,d),n(b,d,Ja(c,m()&31))):(c=k.b(b),k.a(b,Ja(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Ja(c,m()&31));break;case 5:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Pb(c,m()&31))):(c=k.b(b),k.a(b,Pb(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Pb(c,m()&31));break;case 6:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Ja(c,m()&31))):(c=k.b(b),k.a(b,Ja(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Ja(c,m()&31));break;case 7:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),
+n(b,d,Rb(c,m()&31))):(c=k.b(b),k.a(b,Rb(c,m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Rb(c,m()&31))}};s[193]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ib(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Ib(c,m()&31)))):(c=y[b&7],e[b&7]=Ib(c,m()&31));break;case 1:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Mb(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Mb(c,m()&31)))):(c=y[b&7],e[b&7]=Mb(c,m()&31));break;case 2:192>b?(c=l(b),b=q(c),J&&
+4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Kb(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Kb(c,m()&31)))):(c=y[b&7],e[b&7]=Kb(c,m()&31));break;case 3:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ob(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Ob(c,m()&31)))):(c=y[b&7],e[b&7]=Ob(c,m()&31));break;case 4:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ka(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Ka(c,m()&31)))):(c=y[b&7],e[b&7]=Ka(c,m()&31));break;case 5:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?
+(d=q(c+3),c=G(b,d)>>>0,H(b,d,Qb(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Qb(c,m()&31)))):(c=y[b&7],e[b&7]=Qb(c,m()&31));break;case 6:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ka(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Ka(c,m()&31)))):(c=y[b&7],e[b&7]=Ka(c,m()&31));break;case 7:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Sb(c,m()&31))):(c=k.c(b)>>>0,k.f(b,Sb(c,m()&31)))):(c=y[b&7],e[b&7]=Sb(c,m()&31))}};r[194]=function(){var b=U();E=da[1]+Aa()|0;y[4]+=b};s[194]=function(){var b=
+U();E=da[1]+ua()|0;y[4]+=b};r[195]=function(){E=da[1]+Aa()|0};s[195]=function(){E=da[1]+ua()|0};r[196]=s[196]=function(){var b=m();192<=b?za(6):xa?Vc(0,l(b),b>>3&7):Uc(0,l(b),b>>2&14)};r[197]=s[197]=function(){var b=m();192<=b?za(6):xa?Vc(3,l(b),b>>3&7):Uc(3,l(b),b>>2&14)};r[198]=s[198]=function(){var b=m();192>b?V(l(b),m()):v[b<<2&12|b>>2&1]=m()};r[199]=function(){var b=m();192>b?R(l(b),U()):g[b<<1&14]=U()};s[199]=function(){var b=m();192>b?N(l(b),M()):y[b&7]=M()};r[200]=function(){var b=U(),c=m(),
+d;la(g[10]);d=g[8];if(0<c){for(var e=1;e<c;e++)g[10]-=2,la(g[10]);la(d)}g[10]=d;g[8]=d-b};s[200]=function(){var b=U(),c=m()&31,d;ia(e[5]);d=e[4];if(0<c){for(var g=1;g<c;g++)y[5]-=4,ia(e[5]);ia(d)}y[5]=d;y[4]-=b};r[201]=function(){$[aa]=$[sc];g[10]=Aa()};s[201]=function(){$[aa]=$[sc];y[5]=ua()};r[202]=function(){if(Ma)throw ze("16 bit retf in protected mode");var b=U(),c=Aa();ca(1,Aa());E=da[1]+c|0;g[8]+=b};s[202]=function(){var b=U();if(Ma){var c=ua();ca(1,ua()&65535);E=da[1]+c|0;$[aa]+=b}else throw ze("32 bit retf in real mode");
+};r[203]=function(){if(Ma)throw ze("16 bit retf in protected mode");var b=Aa();ca(1,Aa());E=da[1]+b|0};s[203]=function(){var b=ua();ca(1,ua()&65535);E=da[1]+b|0};r[204]=s[204]=function(){ha(3,!0,!1)};r[205]=s[205]=function(){var b=m();ha(b,!0,!1)};r[206]=s[206]=function(){Tc()&&ha(4,!0,!1)};r[207]=function(){if(Ma)throw ze("16 bit iret in protected mode");var b=Aa();ca(1,Aa());var c=Aa();E=b+da[1]|0;u=c;K=0;Da()};s[207]=function(){if(!Ma)throw ze("32 bit iret in real mode");E=ua();ra[1]=ua();var b=
+ua(),c=Bb(ra[1]);if(c.A)throw ze("is null");if(!c.B)throw ze("not present");if(!c.F)throw ze("not exec");if(c.t<ya)throw ze("rpl < cpl");if(c.C&&c.l>c.t)throw ze("conforming and dpl > rpl");if(c.t>ya){var d=ua(),e=ua();y[4]=d;jc(b);ya=c.t;ca(2,e&65535);X()}else jc(b);Da()};r[208]=s[208]=function(){var b=m();switch(b>>3&7){case 0:var c;192>b?(b=q(l(b)),c=k.d(b),k.e(b,Ac(c,1))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Ac(c,1));break;case 1:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Cc(c,1))):(c=v[b<<2&12|b>>2&1],
+v[b<<2&12|b>>2&1]=Cc(c,1));break;case 2:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Bc(c,1))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Bc(c,1));break;case 3:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Dc(c,1))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Dc(c,1));break;case 4:192>b?(b=q(l(b)),c=k.d(b),k.e(b,gb(c,1))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=gb(c,1));break;case 5:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Ec(c,1))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Ec(c,1));break;case 6:192>b?(b=q(l(b)),c=k.d(b),k.e(b,gb(c,1))):(c=v[b<<
+2&12|b>>2&1],v[b<<2&12|b>>2&1]=gb(c,1));break;case 7:192>b?(b=q(l(b)),c=k.d(b),k.e(b,Fc(c,1))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Fc(c,1))}};r[209]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Hb(c,1))):(c=k.b(b),k.a(b,Hb(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Hb(c,1));break;case 1:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Lb(c,1))):(c=k.b(b),k.a(b,Lb(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Lb(c,1));break;case 2:192>
+b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Jb(c,1))):(c=k.b(b),k.a(b,Jb(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Jb(c,1));break;case 3:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Nb(c,1))):(c=k.b(b),k.a(b,Nb(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Nb(c,1));break;case 4:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Ja(c,1))):(c=k.b(b),k.a(b,Ja(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Ja(c,1));break;case 5:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),
+n(b,d,Pb(c,1))):(c=k.b(b),k.a(b,Pb(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Pb(c,1));break;case 6:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Ja(c,1))):(c=k.b(b),k.a(b,Ja(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Ja(c,1));break;case 7:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Rb(c,1))):(c=k.b(b),k.a(b,Rb(c,1)))):(c=g[b<<1&14],g[b<<1&14]=Rb(c,1))}};s[209]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,
+d,Ib(c,1))):(c=k.c(b)>>>0,k.f(b,Ib(c,1)))):(c=y[b&7],e[b&7]=Ib(c,1));break;case 1:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Mb(c,1))):(c=k.c(b)>>>0,k.f(b,Mb(c,1)))):(c=y[b&7],e[b&7]=Mb(c,1));break;case 2:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Kb(c,1))):(c=k.c(b)>>>0,k.f(b,Kb(c,1)))):(c=y[b&7],e[b&7]=Kb(c,1));break;case 3:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ob(c,1))):(c=k.c(b)>>>0,k.f(b,Ob(c,1)))):(c=y[b&7],e[b&
+7]=Ob(c,1));break;case 4:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ka(c,1))):(c=k.c(b)>>>0,k.f(b,Ka(c,1)))):(c=y[b&7],e[b&7]=Ka(c,1));break;case 5:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Qb(c,1))):(c=k.c(b)>>>0,k.f(b,Qb(c,1)))):(c=y[b&7],e[b&7]=Qb(c,1));break;case 6:192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ka(c,1))):(c=k.c(b)>>>0,k.f(b,Ka(c,1)))):(c=y[b&7],e[b&7]=Ka(c,1));break;case 7:192>b?(c=l(b),b=q(c),J&&4093<=(c&
+4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Sb(c,1))):(c=k.c(b)>>>0,k.f(b,Sb(c,1)))):(c=y[b&7],e[b&7]=Sb(c,1))}};r[210]=s[210]=function(){var b=m(),c=v[4]&31;switch(b>>3&7){case 0:var d;192>b?(b=q(l(b)),d=k.d(b),k.e(b,Ac(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Ac(d,c));break;case 1:192>b?(b=q(l(b)),d=k.d(b),k.e(b,Cc(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Cc(d,c));break;case 2:192>b?(b=q(l(b)),d=k.d(b),k.e(b,Bc(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Bc(d,c));break;case 3:192>b?(b=q(l(b)),
+d=k.d(b),k.e(b,Dc(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Dc(d,c));break;case 4:192>b?(b=q(l(b)),d=k.d(b),k.e(b,gb(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=gb(d,c));break;case 5:192>b?(b=q(l(b)),d=k.d(b),k.e(b,Ec(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Ec(d,c));break;case 6:192>b?(b=q(l(b)),d=k.d(b),k.e(b,gb(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=gb(d,c));break;case 7:192>b?(b=q(l(b)),d=k.d(b),k.e(b,Fc(d,c))):(d=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=Fc(d,c))}};r[211]=function(){var b=
+m(),c=v[4]&31;switch(b>>3&7){case 0:var d,e;192>b?(d=l(b),b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Hb(d,c))):(d=k.b(b),k.a(b,Hb(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Hb(d,c));break;case 1:192>b?(d=l(b),b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Lb(d,c))):(d=k.b(b),k.a(b,Lb(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Lb(d,c));break;case 2:192>b?(d=l(b),b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Jb(d,c))):(d=k.b(b),k.a(b,Jb(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Jb(d,c));break;case 3:192>b?(d=l(b),
+b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Nb(d,c))):(d=k.b(b),k.a(b,Nb(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Nb(d,c));break;case 4:192>b?(d=l(b),b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Ja(d,c))):(d=k.b(b),k.a(b,Ja(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Ja(d,c));break;case 5:192>b?(d=l(b),b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Pb(d,c))):(d=k.b(b),k.a(b,Pb(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Pb(d,c));break;case 6:192>b?(d=l(b),b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Ja(d,
+c))):(d=k.b(b),k.a(b,Ja(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Ja(d,c));break;case 7:192>b?(d=l(b),b=q(d),J&&4095===(d&4095)?(e=q(d+1),d=B(b,e),n(b,e,Rb(d,c))):(d=k.b(b),k.a(b,Rb(d,c)))):(d=g[b<<1&14],g[b<<1&14]=Rb(d,c))}};s[211]=function(){var b=m(),c=v[4]&31;switch(b>>3&7){case 0:var d,g;192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,Ib(d,c))):(d=k.c(b)>>>0,k.f(b,Ib(d,c)))):(d=y[b&7],e[b&7]=Ib(d,c));break;case 1:192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,
+Mb(d,c))):(d=k.c(b)>>>0,k.f(b,Mb(d,c)))):(d=y[b&7],e[b&7]=Mb(d,c));break;case 2:192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,Kb(d,c))):(d=k.c(b)>>>0,k.f(b,Kb(d,c)))):(d=y[b&7],e[b&7]=Kb(d,c));break;case 3:192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,Ob(d,c))):(d=k.c(b)>>>0,k.f(b,Ob(d,c)))):(d=y[b&7],e[b&7]=Ob(d,c));break;case 4:192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,Ka(d,c))):(d=k.c(b)>>>0,k.f(b,Ka(d,c)))):(d=y[b&7],e[b&7]=
+Ka(d,c));break;case 5:192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,Qb(d,c))):(d=k.c(b)>>>0,k.f(b,Qb(d,c)))):(d=y[b&7],e[b&7]=Qb(d,c));break;case 6:192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,Ka(d,c))):(d=k.c(b)>>>0,k.f(b,Ka(d,c)))):(d=y[b&7],e[b&7]=Ka(d,c));break;case 7:192>b?(d=l(b),b=q(d),J&&4093<=(d&4095)?(g=q(d+3),d=G(b,g)>>>0,H(b,g,Sb(d,c))):(d=k.c(b)>>>0,k.f(b,Sb(d,c)))):(d=y[b&7],e[b&7]=Sb(d,c))}};r[212]=s[212]=function(){var b=m();if(0===b)qa();
+else{var c=v[0];v[1]=c/b;v[0]=c%b;I=v[0];K=2261}};r[213]=s[213]=function(){var b=m();I=v[0]+v[1]*b;g[0]=I&255;Y=8;K=2261};r[214]=s[214]=function(){throw ze("salc instruction");};r[215]=s[215]=function(){v[0]=Cb?ka(h(3)+e[3]+v[0]):ka(h(3)+g[6]+v[0])};r[216]=s[216]=function(){var b=m();192>b?Ha.i(b,l(b)):Ha.m(b)};r[217]=s[217]=function(){var b=m();192>b?Ha.q(b,l(b)):Ha.v(b)};r[218]=s[218]=function(){var b=m();192>b?Ha.D(b,l(b)):Ha.Q(b)};r[219]=s[219]=function(){var b=m();192>b?Ha.R(b,l(b)):Ha.S(b)};
+r[220]=s[220]=function(){var b=m();192>b?Ha.aa(b,l(b)):Ha.Ga(b)};r[221]=s[221]=function(){var b=m();192>b?Ha.Ia(b,l(b)):Ha.Ka(b)};r[222]=s[222]=function(){var b=m();192>b?Ha.Ma(b,l(b)):Ha.Na(b)};r[223]=s[223]=function(){var b=m();192>b?Ha.Oa(b,l(b)):Ha.Ra(b)};r[224]=s[224]=function(){if(--A[ga]&&!Gb()){var b=P();E=E+b|0}else E++};r[225]=s[225]=function(){if(--A[ga]&&Gb()){var b=P();E=E+b|0}else E++};r[226]=s[226]=function(){if(--A[ga]){var b=P();E=E+b|0}else E++};r[227]=s[227]=function(){var b=P();
+0===A[ga]&&(E=E+b|0)};r[228]=s[228]=function(){v[0]=hc(m())};r[229]=function(){g[0]=ic(m())};s[229]=function(){y[0]=cb(m())};r[230]=s[230]=function(){Fa(m(),v[0])};r[231]=function(){Xb(m(),g[0])};s[231]=function(){gc(m(),e[0])};r[232]=function(){var b=U()<<16>>16;la(pa());Qd(b)};s[232]=function(){var b=M();ia(pa());E=E+b|0};r[233]=function(){var b=U()<<16>>16;Qd(b)};s[233]=function(){var b=M();E=E+b|0};r[234]=function(){var b=U();ca(1,U());E=b+da[1]|0};s[234]=function(){var b=M();ca(1,U());E=b+da[1]|
+0};r[235]=s[235]=function(){var b=P();E=E+b|0};r[236]=s[236]=function(){v[0]=hc(g[4])};r[237]=function(){g[0]=ic(g[4])};s[237]=function(){y[0]=cb(g[4])};r[238]=s[238]=function(){Fa(g[4],v[0])};r[239]=function(){Xb(g[4],g[0])};s[239]=function(){gc(g[4],e[0])};r[240]=s[240]=function(){};r[241]=s[241]=function(){throw ze("int1 instruction");};r[242]=s[242]=function(){wa=!0;cc=!1;Ab[m()]();wa=!1};r[243]=s[243]=function(){cc=wa=!0;Ab[m()]();wa=!1};r[244]=s[244]=function(){ya&&fa(0);if(0===(u&512))throw nc("cpu halted"),
+oc=!0,"HALT";E--;fd=!0};r[245]=s[245]=function(){u=(u|1)^Ba();K&=-2};r[246]=s[246]=function(){var b=m();switch(b>>3&7){case 0:var c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];id(c,m());break;case 1:c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];id(c,m());break;case 2:192>b?(b=q(l(b)),c=k.d(b),k.e(b,~c)):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=~c);break;case 3:192>b?(b=q(l(b)),c=k.d(b),k.e(b,le(c))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=le(c));break;case 4:c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];c=c*v[0];g[0]=c;u=256>c?u&-2050:
+u|2049;K=0;break;case 5:c=192>b?Rc(l(b)):fc[b<<2&12|b>>2&1];c=c*fc[0];g[0]=c;u=127<c||-128>c?u|2049:u&-2050;K=0;break;case 6:var c=192>b?ka(l(b)):v[b<<2&12|b>>2&1],b=g[0],d=b/c|0;255<d||0===c?qa():(v[0]=d,v[1]=b%c);break;case 7:c=192>b?Rc(l(b)):fc[b<<2&12|b>>2&1],b=Ga[0],d=b/c|0,127<d||-128>d||0===c?qa():(v[0]=d,v[1]=b%c)}};r[247]=function(){var b=m();switch(b>>3&7){case 0:var c=192>b?Q(l(b)):g[b<<1&14];jd(c,U());break;case 1:c=192>b?Q(l(b)):g[b<<1&14];jd(c,U());break;case 2:var d;192>b?(c=l(b),b=
+q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,~c)):(c=k.b(b),k.a(b,~c))):(c=g[b<<1&14],g[b<<1&14]=~c);break;case 3:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,Kd(c))):(c=k.b(b),k.a(b,Kd(c)))):(c=g[b<<1&14],g[b<<1&14]=Kd(c));break;case 4:c=192>b?Q(l(b)):g[b<<1&14];c=c*g[0];b=c>>>16;g[0]=c;g[4]=b;u=0===b?u&-2050:u|2049;K=0;break;case 5:192>b?c=Q(l(b))<<16>>16:c=Ga[b<<1&14];c=c*Ga[0];g[0]=c;g[4]=c>>16;u=32767<c||-32768>c?u|2049:u&-2050;K=0;break;case 6:c=192>b?Q(l(b)):g[b<<1&
+14];b=(g[0]|g[4]<<16)>>>0;d=b/c|0;65535<d||0===c?qa():(g[0]=d,g[4]=b%c);break;case 7:192>b?c=Q(l(b))<<16>>16:c=Ga[b<<1&14],b=g[0]|g[4]<<16,d=b/c|0,32767<d||-32768>d||0===c?qa():(g[0]=d,g[4]=b%c)}};s[247]=function(){var b=m();switch(b>>3&7){case 0:var c=192>b?S(l(b)):e[b&7];kd(c,M());break;case 1:c=192>b?S(l(b)):e[b&7];kd(c,M());break;case 2:var d;192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,~c)):(c=k.c(b)>>>0,k.f(b,~c))):(c=y[b&7],e[b&7]=~c);break;case 3:192>b?(c=l(b),b=q(c),
+J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,Ld(c))):(c=k.c(b)>>>0,k.f(b,Ld(c)))):(c=y[b&7],e[b&7]=Ld(c));break;case 4:192>b?c=S(l(b))>>>0:c=y[b&7];b=y[0];d=c*b/4294967296|0;y[0]=Z(c,b);y[2]=d;u=0===d?u&-2050:u|2049;K=0;break;case 5:b=c=192>b?S(l(b)):e[b&7];d=e[0];c=b*d/4294967296|0;b=Z(b,d);0===c&&0>b&&(c=-1);y[0]=b;y[2]=c;u=c===(2147483648>y[0]?0:-1)?u&-2050:u|2049;K=0;break;case 6:192>b?c=S(l(b))>>>0:c=y[b&7];d=y[0];var g=y[2],b=(4294967296*g%c+d%c)%c;d=d/c+4294967296*g/c;4294967295<d||0===c?
+qa():(y[0]=d,y[2]=b);break;case 7:c=192>b?S(l(b)):e[b&7],d=y[0],g=e[2],b=(4294967296*g%c+d%c)%c,d=d/c+4294967296*g/c,2147483647<d||-2147483648>d||0===c?qa():(y[0]=d,y[2]=b)}};r[248]=s[248]=function(){u&=-2;K&=-2};r[249]=s[249]=function(){u|=1;K&=-2};r[250]=s[250]=function(){$a()?u&=-513:fa(0)};r[251]=s[251]=function(){$a()?(u|=512,Da()):fa(0)};r[252]=s[252]=function(){u&=-1025};r[253]=s[253]=function(){u|=1024};r[254]=s[254]=function(){var b=m(),c=b&56;0===c?192>b?(b=q(l(b)),c=k.d(b),k.e(b,je(c))):
+(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=je(c)):8===c?192>b?(b=q(l(b)),c=k.d(b),k.e(b,ke(c))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=ke(c)):z()};r[255]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,pb(c))):(c=k.b(b),k.a(b,pb(c)))):(c=g[b<<1&14],g[b<<1&14]=pb(c));break;case 1:192>b?(c=l(b),b=q(c),J&&4095===(c&4095)?(d=q(c+1),c=B(b,d),n(b,d,rb(c))):(c=k.b(b),k.a(b,rb(c)))):(c=g[b<<1&14],g[b<<1&14]=rb(c));break;case 2:c=192>b?Q(l(b)):
+g[b<<1&14];la(pa());E=da[1]+c|0;break;case 3:192<=b&&za(6);c=l(b);la(ra[1]);la(pa());ca(1,Q(c+2));E=da[1]+Q(c)|0;break;case 4:c=192>b?Q(l(b)):g[b<<1&14];E=da[1]+c|0;break;case 5:192<=b&&za(6);c=l(b);ca(1,Q(c+2));E=da[1]+Q(c)|0;break;case 6:c=192>b?Q(l(b)):g[b<<1&14];la(c);break;case 7:z()}};s[255]=function(){var b=m();switch(b>>3&7){case 0:var c,d;192>b?(c=l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,qb(c))):(c=k.c(b)>>>0,k.f(b,qb(c)))):(c=y[b&7],e[b&7]=qb(c));break;case 1:192>b?(c=
+l(b),b=q(c),J&&4093<=(c&4095)?(d=q(c+3),c=G(b,d)>>>0,H(b,d,sb(c))):(c=k.c(b)>>>0,k.f(b,sb(c)))):(c=y[b&7],e[b&7]=sb(c));break;case 2:c=192>b?S(l(b)):e[b&7];ia(pa());E=da[1]+c|0;break;case 3:192<=b&&za(6);c=l(b);b=Q(c+4);c=S(c);ia(ra[1]);ia(pa());ca(1,b);E=da[1]+c|0;break;case 4:c=192>b?S(l(b)):e[b&7];E=da[1]+c|0;break;case 5:192<=b&&za(6);c=l(b);b=Q(c+4);c=S(c);ca(1,b);E=da[1]+c|0;break;case 6:c=192>b?S(l(b)):e[b&7];ia(c);break;case 7:z()}};x[0]=w[0]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];
+Ma||z();ya&&fa(0);switch(b>>3&7){case 2:b=Bb(c);if(b.A)Zc=$c=0;else{if(!b.ga)throw ze("LDTR can only be loaded from GDT");if(!b.B)throw ze("#GP handler");if(!b.L)throw ze("#GP handler");if(2!==b.type)throw ze("#GP handler");$c=b.V;Zc=b.W}break;case 3:b=Bb(c);if(!b.ga)throw ze("TR can only be loaded from GDT");if(b.A)throw ze("#GP handler");if(!b.B)throw ze("#GP handler");if(!b.L)throw ze("#GP handler");if(9!==b.type)throw ze("#GP handler");Ed=b.V;Dd=b.W;break;default:z()}};x[1]=w[1]=function(){var b=
+m();ya&&fa(0);var c=b>>3&7;if(4===c)192>b?R(l(b),Na):g[b<<1&14]=Na;else if(6===c)c=192>b?Q(l(b)):g[b<<1&14],Na=Na&-16|c&15,O();else switch(192<=b&&za(6),2!==c&&3!==c||!Ma||(ab=6),b=l(b),ab=-1,c){case 0:R(b,ad);N(b+2,qc);break;case 1:R(b,bd);N(b+2,rc);break;case 2:c=Q(b);b=S(b+2);ad=c;qc=b;xa||(qc&=16777215);break;case 3:c=Q(b);b=S(b+2);bd=c;rc=b;xa||(rc&=16777215);break;case 7:c=b>>>12;Eb[c]=0;pc[c]=0;Fb=-1;break;default:z()}};x[2]=w[2]=function(){m();z()};x[3]=w[3]=function(){m();z()};x[4]=w[4]=
+function(){z()};x[5]=w[5]=function(){z()};x[6]=w[6]=function(){ya?fa(0):Na&=-9};x[7]=w[7]=function(){z()};x[8]=w[8]=function(){z()};x[9]=w[9]=function(){ya&&fa(0)};x[10]=w[10]=function(){z()};x[11]=w[11]=function(){z()};x[12]=w[12]=function(){z()};x[13]=w[13]=function(){z()};x[14]=w[14]=function(){z()};x[15]=w[15]=function(){z()};x[16]=w[16]=function(){z()};x[17]=w[17]=function(){z()};x[18]=w[18]=function(){z()};x[19]=w[19]=function(){z()};x[20]=w[20]=function(){z()};x[21]=w[21]=function(){z()};x[22]=
+w[22]=function(){z()};x[23]=w[23]=function(){z()};x[24]=w[24]=function(){var b=m();xa?192>b&&S(l(b)):192>b&&Q(l(b))};x[25]=w[25]=function(){z()};x[26]=w[26]=function(){z()};x[27]=w[27]=function(){z()};x[28]=w[28]=function(){z()};x[29]=w[29]=function(){z()};x[30]=w[30]=function(){z()};x[31]=w[31]=function(){z()};x[32]=w[32]=function(){var b=m();ya&&fa(0);switch(b>>3&7){case 0:y[b&7]=Na;break;case 2:y[b&7]=Ub;break;case 3:y[b&7]=cd;break;case 4:y[b&7]=Oc;break;default:z()}};x[33]=w[33]=function(){m();
+ya&&fa(0)};x[34]=w[34]=function(){var b=m();ya&&fa(0);var c=y[b&7];switch(b>>3&7){case 0:if(-2147483648===(c&2147483649))throw ze("#GP handler");Na&2147483648&&!(c&2147483648)&&(pc=new Uint8Array(1048576),tc());Na=c;O();break;case 3:cd=c;tc();break;case 4:(Oc^c)&128&&(pc=new Uint8Array(1048576),tc());Oc=c;Fd=Oc&16?128:0;break;default:z()}};x[35]=w[35]=function(){m();ya&&fa(0)};x[36]=w[36]=function(){z()};x[37]=w[37]=function(){z()};x[38]=w[38]=function(){z()};x[39]=w[39]=function(){z()};x[40]=w[40]=
+function(){z()};x[41]=w[41]=function(){z()};x[42]=w[42]=function(){z()};x[43]=w[43]=function(){z()};x[44]=w[44]=function(){z()};x[45]=w[45]=function(){z()};x[46]=w[46]=function(){z()};x[47]=w[47]=function(){z()};x[48]=w[48]=function(){z()};x[49]=w[49]=function(){y[0]=ed;y[2]=ed/4294967296};x[50]=w[50]=function(){z()};x[51]=w[51]=function(){z()};x[52]=w[52]=function(){z()};x[53]=w[53]=function(){z()};x[54]=w[54]=function(){z()};x[55]=w[55]=function(){z()};x[56]=w[56]=function(){z()};x[57]=w[57]=function(){z()};
+x[58]=w[58]=function(){z()};x[59]=w[59]=function(){z()};x[60]=w[60]=function(){z()};x[61]=w[61]=function(){z()};x[62]=w[62]=function(){z()};x[63]=w[63]=function(){z()};x[64]=function(){var b=m();if(Ya()){var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}else 192>b&&l(b)};w[64]=function(){var b=m();if(Ya()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>b&&l(b)};x[65]=function(){var b=m();if(Ya())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[65]=function(){var b=m();if(Ya())192>b&&l(b);
+else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[66]=function(){var b=m();if(Ra()){var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}else 192>b&&l(b)};w[66]=function(){var b=m();if(Ra()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>b&&l(b)};x[67]=function(){var b=m();if(Ra())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[67]=function(){var b=m();if(Ra())192>b&&l(b);else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[68]=function(){var b=m();if(Sa()){var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&
+14]=c}else 192>b&&l(b)};w[68]=function(){var b=m();if(Sa()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>b&&l(b)};x[69]=function(){var b=m();if(Sa())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[69]=function(){var b=m();if(Sa())192>b&&l(b);else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[70]=function(){var b=m();if(Qa()){var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}else 192>b&&l(b)};w[70]=function(){var b=m();if(Qa()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>b&&l(b)};x[71]=
+function(){var b=m();if(Qa())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[71]=function(){var b=m();if(Qa())192>b&&l(b);else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[72]=function(){var b=m();if(Za()){var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}else 192>b&&l(b)};w[72]=function(){var b=m();if(Za()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>b&&l(b)};x[73]=function(){var b=m();if(Za())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[73]=function(){var b=m();
+if(Za())192>b&&l(b);else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[74]=function(){var b=m();if(Ta()){var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}else 192>b&&l(b)};w[74]=function(){var b=m();if(Ta()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>b&&l(b)};x[75]=function(){var b=m();if(Ta())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[75]=function(){var b=m();if(Ta())192>b&&l(b);else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[76]=function(){var b=m();if(Wa()){var c=192>b?Q(l(b)):
+g[b<<1&14];g[b>>2&14]=c}else 192>b&&l(b)};w[76]=function(){var b=m();if(Wa()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>b&&l(b)};x[77]=function(){var b=m();if(Wa())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[77]=function(){var b=m();if(Wa())192>b&&l(b);else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[78]=function(){var b=m();if(Xa()){var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}else 192>b&&l(b)};w[78]=function(){var b=m();if(Xa()){var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}else 192>
+b&&l(b)};x[79]=function(){var b=m();if(Xa())192>b&&l(b);else{var c=192>b?Q(l(b)):g[b<<1&14];g[b>>2&14]=c}};w[79]=function(){var b=m();if(Xa())192>b&&l(b);else{var c=192>b?S(l(b)):e[b&7];e[b>>3&7]=c}};x[80]=w[80]=function(){z()};x[81]=w[81]=function(){z()};x[82]=w[82]=function(){z()};x[83]=w[83]=function(){z()};x[84]=w[84]=function(){z()};x[85]=w[85]=function(){z()};x[86]=w[86]=function(){z()};x[87]=w[87]=function(){z()};x[88]=w[88]=function(){z()};x[89]=w[89]=function(){z()};x[90]=w[90]=function(){z()};
+x[91]=w[91]=function(){z()};x[92]=w[92]=function(){z()};x[93]=w[93]=function(){z()};x[94]=w[94]=function(){z()};x[95]=w[95]=function(){z()};x[96]=w[96]=function(){z()};x[97]=w[97]=function(){z()};x[98]=w[98]=function(){z()};x[99]=w[99]=function(){z()};x[100]=w[100]=function(){z()};x[101]=w[101]=function(){z()};x[102]=w[102]=function(){z()};x[103]=w[103]=function(){z()};x[104]=w[104]=function(){z()};x[105]=w[105]=function(){z()};x[106]=w[106]=function(){z()};x[107]=w[107]=function(){z()};x[108]=w[108]=
+function(){z()};x[109]=w[109]=function(){z()};x[110]=w[110]=function(){z()};x[111]=w[111]=function(){z()};x[112]=w[112]=function(){z()};x[113]=w[113]=function(){z()};x[114]=w[114]=function(){z()};x[115]=w[115]=function(){z()};x[116]=w[116]=function(){z()};x[117]=w[117]=function(){z()};x[118]=w[118]=function(){z()};x[119]=w[119]=function(){z()};x[120]=w[120]=function(){z()};x[121]=w[121]=function(){z()};x[122]=w[122]=function(){z()};x[123]=w[123]=function(){z()};x[124]=w[124]=function(){z()};x[125]=
+w[125]=function(){z()};x[126]=w[126]=function(){z()};x[127]=w[127]=function(){z()};x[128]=function(){Oa(Ya())};w[128]=function(){Pa(Ya())};x[129]=function(){Oa(!Ya())};w[129]=function(){Pa(!Ya())};x[130]=function(){Oa(Ra())};w[130]=function(){Pa(Ra())};x[131]=function(){Oa(!Ra())};w[131]=function(){Pa(!Ra())};x[132]=function(){Oa(Sa())};w[132]=function(){Pa(Sa())};x[133]=function(){Oa(!Sa())};w[133]=function(){Pa(!Sa())};x[134]=function(){Oa(Qa())};w[134]=function(){Pa(Qa())};x[135]=function(){Oa(!Qa())};
+w[135]=function(){Pa(!Qa())};x[136]=function(){Oa(Za())};w[136]=function(){Pa(Za())};x[137]=function(){Oa(!Za())};w[137]=function(){Pa(!Za())};x[138]=function(){Oa(Ta())};w[138]=function(){Pa(Ta())};x[139]=function(){Oa(!Ta())};w[139]=function(){Pa(!Ta())};x[140]=function(){Oa(Wa())};w[140]=function(){Pa(Wa())};x[141]=function(){Oa(!Wa())};w[141]=function(){Pa(!Wa())};x[142]=function(){Oa(Xa())};w[142]=function(){Pa(Xa())};x[143]=function(){Oa(!Xa())};w[143]=function(){Pa(!Xa())};x[144]=w[144]=function(){var b=
+m();192>b?V(l(b),!Ya()^1):v[b<<2&12|b>>2&1]=!Ya()^1};x[145]=w[145]=function(){var b=m();192>b?V(l(b),!!Ya()^1):v[b<<2&12|b>>2&1]=!!Ya()^1};x[146]=w[146]=function(){var b=m();192>b?V(l(b),!Ra()^1):v[b<<2&12|b>>2&1]=!Ra()^1};x[147]=w[147]=function(){var b=m();192>b?V(l(b),!!Ra()^1):v[b<<2&12|b>>2&1]=!!Ra()^1};x[148]=w[148]=function(){var b=m();192>b?V(l(b),!Sa()^1):v[b<<2&12|b>>2&1]=!Sa()^1};x[149]=w[149]=function(){var b=m();192>b?V(l(b),!!Sa()^1):v[b<<2&12|b>>2&1]=!!Sa()^1};x[150]=w[150]=function(){var b=
+m();192>b?V(l(b),!Qa()^1):v[b<<2&12|b>>2&1]=!Qa()^1};x[151]=w[151]=function(){var b=m();192>b?V(l(b),!!Qa()^1):v[b<<2&12|b>>2&1]=!!Qa()^1};x[152]=w[152]=function(){var b=m();192>b?V(l(b),!Za()^1):v[b<<2&12|b>>2&1]=!Za()^1};x[153]=w[153]=function(){var b=m();192>b?V(l(b),!!Za()^1):v[b<<2&12|b>>2&1]=!!Za()^1};x[154]=w[154]=function(){var b=m();192>b?V(l(b),!Ta()^1):v[b<<2&12|b>>2&1]=!Ta()^1};x[155]=w[155]=function(){var b=m();192>b?V(l(b),!!Ta()^1):v[b<<2&12|b>>2&1]=!!Ta()^1};x[156]=w[156]=function(){var b=
+m();192>b?V(l(b),!Wa()^1):v[b<<2&12|b>>2&1]=!Wa()^1};x[157]=w[157]=function(){var b=m();192>b?V(l(b),!!Wa()^1):v[b<<2&12|b>>2&1]=!!Wa()^1};x[158]=w[158]=function(){var b=m();192>b?V(l(b),!Xa()^1):v[b<<2&12|b>>2&1]=!Xa()^1};x[159]=w[159]=function(){var b=m();192>b?V(l(b),!!Xa()^1):v[b<<2&12|b>>2&1]=!!Xa()^1};x[160]=function(){la(ra[4])};w[160]=function(){ia(ra[4])};x[161]=function(){ca(4,k.b(Va(0)));$[aa]+=2};w[161]=function(){ca(4,k.b(Va(0)));$[aa]+=4};x[162]=w[162]=function(){var b=e[0];0===(b&2147483647)?
+(y[0]=2,0===b&&(y[3]=1970169159,y[2]=1231384169,y[1]=1818588270)):1===b?(y[0]=1299,y[3]=0,y[1]=0,y[2]=Ha.a|41240):2===b?(y[0]=1717260289,y[3]=0,y[1]=0,y[2]=8024064):-2138701824===b&&(y[0]=0,y[3]=0,y[1]=0,y[2]=0)};x[163]=w[163]=function(){var b=m();xa?192>b?pd(l(b),e[b>>3&7]):ld(y[b&7],y[b>>3&7]&31):192>b?pd(l(b),Ga[b>>2&14]):ld(g[b<<1&14],g[b>>2&14]&15)};x[164]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,Ic(c,g[b>>2&14],m()&31))):(c=k.b(d),k.a(d,Ic(c,
+g[b>>2&14],m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Ic(c,g[b>>2&14],m()&31))};w[164]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,Jc(c,y[b>>3&7],m()&31))):(c=k.c(d)>>>0,k.f(d,Jc(c,y[b>>3&7],m()&31)))):(c=y[b&7],e[b&7]=Jc(c,y[b>>3&7],m()&31))};x[165]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,Ic(c,g[b>>2&14],v[4]&31))):(c=k.b(d),k.a(d,Ic(c,g[b>>2&14],v[4]&31)))):(c=g[b<<1&14],g[b<<1&14]=Ic(c,g[b>>2&14],v[4]&
+31))};w[165]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,Jc(c,y[b>>3&7],v[4]&31))):(c=k.c(d)>>>0,k.f(d,Jc(c,y[b>>3&7],v[4]&31)))):(c=y[b&7],e[b&7]=Jc(c,y[b>>3&7],v[4]&31))};x[166]=w[166]=function(){z()};x[167]=w[167]=function(){z()};x[168]=function(){la(ra[5])};w[168]=function(){ia(ra[5])};x[169]=function(){ca(5,k.b(Va(0)));$[aa]+=2};w[169]=function(){ca(5,k.b(Va(0)));$[aa]+=4};x[170]=w[170]=function(){z()};x[171]=w[171]=function(){var b=m();xa?192>
+b?sd(l(b),e[b>>3&7]):y[b&7]=nd(e[b&7],e[b>>3&7]&31):192>b?sd(l(b),Ga[b>>2&14]):g[b<<1&14]=nd(g[b<<1&14],Ga[b>>2&14]&15)};x[172]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,Gc(c,g[b>>2&14],m()&31))):(c=k.b(d),k.a(d,Gc(c,g[b>>2&14],m()&31)))):(c=g[b<<1&14],g[b<<1&14]=Gc(c,g[b>>2&14],m()&31))};w[172]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,Hc(c,y[b>>3&7],m()&31))):(c=k.c(d)>>>0,k.f(d,Hc(c,y[b>>3&7],
+m()&31)))):(c=y[b&7],e[b&7]=Hc(c,y[b>>3&7],m()&31))};x[173]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,Gc(c,g[b>>2&14],v[4]&31))):(c=k.b(d),k.a(d,Gc(c,g[b>>2&14],v[4]&31)))):(c=g[b<<1&14],g[b<<1&14]=Gc(c,g[b>>2&14],v[4]&31))};w[173]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,Hc(c,y[b>>3&7],v[4]&31))):(c=k.c(d)>>>0,k.f(d,Hc(c,y[b>>3&7],v[4]&31)))):(c=y[b&7],e[b&7]=Hc(c,y[b>>3&7],v[4]&31))};x[174]=w[174]=
+function(){z()};x[175]=function(){var b=m(),c=192>b?Q(l(b))<<16>>16:Ga[b<<1&14];g[b>>2&14]=Md(Ga[b>>2&14],c)};w[175]=function(){var b=m(),c=192>b?S(l(b)):e[b&7];y[b>>3&7]=Nd(e[b>>3&7],c)};x[176]=w[176]=function(){var b=m();if(192>b)var c=l(b),d=ka(c);else d=v[b<<2&12|b>>2&1];lc(d,v[0]);Gb()?192>b?V(c,v[b>>1&12|b>>5&1]):v[b<<2&12|b>>2&1]=v[b>>1&12|b>>5&1]:v[0]=d};x[177]=w[177]=function(){var b=m();if(xa){if(192>b)var c=l(b),d=S(c)>>>0;else d=y[b&7];Zb(d,y[0]);Gb()?192>b?N(c,y[b>>3&7]):y[b&7]=y[b>>
+3&7]:y[0]=d}else 192>b?(c=l(b),d=Q(c)):d=g[b<<1&14],Yb(d,g[0]),Gb()?192>b?R(c,g[b>>2&14]):g[b<<1&14]=g[b>>2&14]:g[0]=d};x[178]=w[178]=function(){var b=m();192<=b?za(6):xa?Vc(2,l(b),b>>3&7):Uc(2,l(b),b>>2&14)};x[179]=w[179]=function(){var b=m();xa?192>b?rd(l(b),e[b>>3&7]):y[b&7]=od(e[b&7],e[b>>3&7]&31):192>b?rd(l(b),Ga[b>>2&14]):g[b<<1&14]=od(g[b<<1&14],Ga[b>>2&14]&15)};x[180]=w[180]=function(){var b=m();192<=b?za(6):xa?Vc(4,l(b),b>>3&7):Uc(4,l(b),b>>2&14)};x[181]=w[181]=function(){var b=m();192<=
+b?za(6):xa?Vc(5,l(b),b>>3&7):Uc(5,l(b),b>>2&14)};x[182]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];g[b>>2&14]=c};w[182]=function(){var b=m(),c=192>b?ka(l(b)):v[b<<2&12|b>>2&1];y[b>>3&7]=c};x[183]=w[183]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14];y[b>>3&7]=c};x[184]=w[184]=function(){z()};x[185]=w[185]=function(){z()};x[186]=w[186]=function(){var b=m();switch(b>>3&7){case 4:xa?192>b?pd(l(b),m()&31):ld(y[b&7],m()&31):192>b?pd(l(b),m()&31):ld(g[b<<1&14],m()&15);break;case 5:xa?192>
+b?sd(l(b),m()&31):y[b&7]=nd(e[b&7],m()&31):192>b?sd(l(b),m()&31):g[b<<1&14]=nd(g[b<<1&14],m()&15);break;case 6:xa?192>b?rd(l(b),m()&31):y[b&7]=od(e[b&7],m()&31):192>b?rd(l(b),m()&31):g[b<<1&14]=od(g[b<<1&14],m()&15);break;case 7:xa?192>b?qd(l(b),m()&31):y[b&7]=md(e[b&7],m()&31):192>b?qd(l(b),m()&31):g[b<<1&14]=md(g[b<<1&14],m()&15);break;default:z()}};x[187]=w[187]=function(){var b=m();xa?192>b?qd(l(b),e[b>>3&7]):y[b&7]=md(e[b&7],e[b>>3&7]&31):192>b?qd(l(b),Ga[b>>2&14]):g[b<<1&14]=md(g[b<<1&14],Ga[b>>
+2&14]&15)};x[188]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14],d=g;var e=g[b>>2&14];K=0;0===c?(u|=64,c=e):(u&=-65,c=oe[((-c&c)>>>0)%37]);d[b>>2&14]=c};w[188]=function(){var b=m(),c=192>b?S(l(b)):e[b&7],d=y;var g=y[b>>3&7];K=0;0===c?(u|=64,c=g):(u&=-65,c=oe[((-c&c)>>>0)%37]);d[b>>3&7]=c};x[189]=function(){var b=m(),c=192>b?Q(l(b)):g[b<<1&14],d=g;var e=g[b>>2&14];K=0;0===c?(u|=64,c=e):(u&=-65,c=(e=c>>>8)?8+Ae[e]:Ae[c]);d[b>>2&14]=c};w[189]=function(){var b=m(),c=192>b?S(l(b)):e[b&7],d=y;var g=y[b>>
+3&7];K=0;if(0===c)u|=64,c=g;else{u&=-65;var g=c>>>16,k;c=g?(k=g>>>8)?24+Ae[k]:16+Ae[g]:(k=c>>>8)?8+Ae[k]:Ae[c]}d[b>>3&7]=c};x[190]=function(){var b=m(),c=192>b?Rc(l(b)):fc[b<<2&12|b>>2&1];g[b>>2&14]=c};w[190]=function(){var b=m(),c=192>b?Rc(l(b)):fc[b<<2&12|b>>2&1];e[b>>3&7]=c};x[191]=w[191]=function(){var b=m(),c=192>b?Q(l(b))<<16>>16:Ga[b<<1&14];e[b>>3&7]=c};x[192]=w[192]=function(){var b=m(),c,d;192>b?(d=q(l(b)),c=k.d(d),k.e(d,me(c,b>>1&12|b>>5&1))):(c=v[b<<2&12|b>>2&1],v[b<<2&12|b>>2&1]=me(c,
+b>>1&12|b>>5&1))};x[193]=function(){var b=m(),c,d,e;192>b?(c=l(b),d=q(c),J&&4095===(c&4095)?(e=q(c+1),c=B(d,e),n(d,e,Od(c,b>>2&14))):(c=k.b(d),k.a(d,Od(c,b>>2&14)))):(c=g[b<<1&14],g[b<<1&14]=Od(c,b>>2&14))};w[193]=function(){var b=m(),c,d,g;192>b?(c=l(b),d=q(c),J&&4093<=(c&4095)?(g=q(c+3),c=G(d,g)>>>0,H(d,g,Pd(c,b>>3&7))):(c=k.c(d)>>>0,k.f(d,Pd(c,b>>3&7)))):(c=y[b&7],e[b&7]=Pd(c,b>>3&7))};x[194]=w[194]=function(){z()};x[195]=w[195]=function(){z()};x[196]=w[196]=function(){z()};x[197]=w[197]=function(){z()};
+x[198]=w[198]=function(){z()};x[199]=w[199]=function(){var b=m(),b=l(b),c=S(b)>>>0,d=S(b+4)>>>0;y[0]===c&&y[2]===d?(u|=64,N(b,y[3]),N(b+4,y[1])):(u&=-65,y[0]=c,y[2]=d);K&=-65};x[200]=w[200]=function(){bc(0)};x[201]=w[201]=function(){bc(1)};x[202]=w[202]=function(){bc(2)};x[203]=w[203]=function(){bc(3)};x[204]=w[204]=function(){bc(4)};x[205]=w[205]=function(){bc(5)};x[206]=w[206]=function(){bc(6)};x[207]=w[207]=function(){bc(7)};x[208]=w[208]=function(){z()};x[209]=w[209]=function(){z()};x[210]=w[210]=
+function(){z()};x[211]=w[211]=function(){z()};x[212]=w[212]=function(){z()};x[213]=w[213]=function(){z()};x[214]=w[214]=function(){z()};x[215]=w[215]=function(){z()};x[216]=w[216]=function(){z()};x[217]=w[217]=function(){z()};x[218]=w[218]=function(){z()};x[219]=w[219]=function(){z()};x[220]=w[220]=function(){z()};x[221]=w[221]=function(){z()};x[222]=w[222]=function(){z()};x[223]=w[223]=function(){z()};x[224]=w[224]=function(){z()};x[225]=w[225]=function(){z()};x[226]=w[226]=function(){z()};x[227]=
+w[227]=function(){z()};x[228]=w[228]=function(){z()};x[229]=w[229]=function(){z()};x[230]=w[230]=function(){z()};x[231]=w[231]=function(){z()};x[232]=w[232]=function(){z()};x[233]=w[233]=function(){z()};x[234]=w[234]=function(){z()};x[235]=w[235]=function(){z()};x[236]=w[236]=function(){z()};x[237]=w[237]=function(){z()};x[238]=w[238]=function(){z()};x[239]=w[239]=function(){z()};x[240]=w[240]=function(){z()};x[241]=w[241]=function(){z()};x[242]=w[242]=function(){z()};x[243]=w[243]=function(){z()};
+x[244]=w[244]=function(){z()};x[245]=w[245]=function(){z()};x[246]=w[246]=function(){z()};x[247]=w[247]=function(){z()};x[248]=w[248]=function(){z()};x[249]=w[249]=function(){z()};x[250]=w[250]=function(){z()};x[251]=w[251]=function(){z()};x[252]=w[252]=function(){z()};x[253]=w[253]=function(){z()};x[254]=w[254]=function(){z()};x[255]=w[255]=function(){z()}};Object.bb=function(f){for(var t={},D=0;D<f.length;D++)t[f[D][0]]=f[D][1];return t};Object.cb=function(f,t){for(var D=Object.keys(t),C=0;C<D.length;C++)f[D[C]]=t[D[C]]};String.m=function(f){for(f=f?f+"":"";20>f.length;)f=f+" ";return f};String.i=function(f,t){for(f=f?f+"":"";f.length<t;)f="0"+f;return f};Array.a=function(f){for(var t=[],D=0;D<f;D++)t[D]=D;return t};function Be(f,t){return f?t?String.i(f.toString(16).toUpperCase(),t):f.toString(16).toUpperCase():String.i("",t||1)}
+Number.a=function(f){for(var t=[],D=31;-1<D;D--)f&1<<D&&t.push(D);return t.join(", ")};String.a=function(f,t){for(var D="";t--;)D+=f;return D};Math.wa=function(f){for(var t=0,D=0,C;f;)C=f%10,D|=C<<4*t,t++,f=(f-C)/10;return D};function ze(f){f="Unimplemented"+(f?": "+f:"");nc(f);nc("Execution stopped");return f}
+function Zd(f){this.byteLength=f.byteLength;this.get=function(t,D,C){C(new Uint8Array(f,t,D))};this.set=function(t,D,C){(new Uint8Array(f,t,D.byteLength)).set(D);C()};this.Ba=function(t){t(f)}}Math.Ya=function(f){return f>>>24|f>>8&65280|f<<8&16711680|f<<24};Math.Xa=function(f){return f>>>8&255|f<<8&65280};for(var Ae,De=new Int8Array(256),Ee=0,Fe=-2;256>Ee;Ee++)Ee&Ee-1||Fe++,De[Ee]=Fe;Ae=De;Math.qa=function(f){return 0<f?Math.ceil(f):Math.floor(f)};function ve(f,t){this.j=f.j;this.memory=f.memory;this.s=f.s;this.M=f.M;this.da=4098;this.Y=17170688;this.U=14;this.T=4293984256;this.a=2048;this.buffer=t;this.m=!0;this.q=8;this.w()}ve.prototype=new Ge;
+function we(f,t,D){function C(){var f=80;n<H.length&&(f|=8);return f}function O(){X.k(B.U)}var L=0===D?496:368,X=f.s;this.j=f.j;this.memory=f.memory;this.s=f.s;this.M=f.M;this.da=4098;this.Y=17170688;this.U=0===D?14:15;this.T=4293918720;this.a=512;this.o=t.byteLength/this.a;this.buffer=t;this.m=!1;this.q=16;this.i=t.byteLength/16/64/this.a;var B=this;this.j.h(L|7,C);this.j.h(L|518,C);var G=255,n=0,H=[];this.j.g(L|6,function(f){f&16||(G=f)});this.j.g(L|2,function(){});this.j.g(L|3,function(){});this.j.g(L|
+4,function(){});this.j.g(L|5,function(){});this.j.g(L|7,function(f){236===f?(n=0,H=new Uint8Array([64,0,B.i,B.i>>8,0,0,16,0,0,0,0,0,63,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,3,0,0,2,4,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0,0,1,0,0,3,0,0,0,2,0,2,7,0,B.i,B.i>>8,16,0,63,0,this.o&255,this.o>>8&255,this.o>>16&255,this.o>>24&255,0,0,this.o&255,this.o>>8&255,this.o>>16&255,this.o>>24&255,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,126,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,this.o&255,this.o>>8&255,this.o>>16&255,this.o>>24&255]),O()):145===f?O():16===f?O():(O(),H=[0,0,0,0,t.byteLength&255,t.byteLength>>8&255,t.byteLength>>16&255,t.byteLength>>24&255,0,0,0,0])});this.j.h(L|0,function(){return n<H.length?(0===(n&511)&&O(),H[n++]&255):0});this.j.h(L|1,function(){return 255});this.j.h(L|2,function(){return 255});this.j.h(L|
+3,function(){return 255});this.j.h(L|6,function(){return G});this.w()}we.prototype=new Ge;
+function Ge(){function f(f,n,t){var F=O.c(f+4)&16777215;f=O.b(f+12)*C.a;t?this.buffer.get(F*C.a,f,function(f){O.J(f,n)}):this.buffer.set(F*C.a,new Uint8Array(O.buffer,n,f),function(){})}function t(f){switch(f){case 0:return X;case 4:return L;case 12:return B;case 296:return 3;case 272:return G}}function D(t,B){switch(t){case 256:n=B;break;case 264:H=B;break;case 312:var N=O.c(n+8),F=N+128;O.b(n);var G=O.c(F)+4294967296*O.c(F+4);O.c(N+128+12);var F=O.d(N+64),D=O.d(N+2),L=N+0;if((160===D||161===D)&&
+!C.m)break;O.e(H+64+2,64);O.c(L+4);O.b(n+2);O.c(n+4);O.c(N+128+12);200===D||202===D?f(L,G,200===D):236===D?O.f(G+120,C.buffer.byteLength/C.a):161===D?O.f(G,1280):160===D&&40===F&&(N=N+64,F=Math.Ya(O.c(N+2)),D=Math.Xa(O.b(N+7)),O.d(N+1),C.buffer.get(F*C.a,D*C.a,function(f){O.J(f,G)}))}}var C,O;this.w=function(){C=this;O=this.memory;this.M.pa(this,this.q);this.memory.ba(this.T,16384,!0,t,D)};var L=0,X=1,B=1,G=1,n,H};function pe(f){f=f.j;var t=0,D=-1,C=-1,O=this;this.a={};f.h(3324,function(){return D&255});f.h(3325,function(){return D>>8&255});f.h(3326,function(){return D>>16&255});f.h(3327,function(){return D>>24&255});f.h(3320,function(){return C&255});f.h(3321,function(){return C>>8&255});f.h(3322,function(){return C>>16&255});f.h(3323,function(){return C>>24&255});f.g(3320,function(f){t=t&-256|f});f.g(3321,function(f){t=t&-65281|f<<8});f.g(3322,function(f){t=t&-16711681|f<<16});f.g(3323,function(f){var X=
+t=t&16777215|f<<24,B=(X&2147483647)>>8;f=X&252;-2147483648===X?C=2147483648:O.a[B]?(X=O.a[B],C=2147483648,0===f?D=X.da:8===f?D=X.Y:36===f?D=X.T:60===f?D=X.U:D=0):C=D=0});this.pa=function(f,t){this.a[t]=f};this.pa({U:0,T:0,da:2256543790,Y:100700192},0)};function ue(f,t){function D(){return 0}function C(){var f=128;ja<h&&(f|=80);0===(Fa&8)&&(f|=32);return f}function O(){return 0}function L(){return ja<h?fa[ja++]:255}function X(f){if(0<ha)za[qa++]=f,ha--,0===ha&&z(za);else{switch(f){case 3:z=N;ha=2;break;case 4:z=n;ha=1;break;case 5:case 197:z=function(h){R(!0,h)};ha=8;break;case 230:z=function(h){R(!1,h)};ha=8;break;case 7:z=V;ha=1;break;case 8:ja=0;h=2;fa[0]=32;fa[1]=Da;break;case 74:z=F;ha=1;break;case 15:ha=2;z=H;break;case 14:fa[0]=128,ja=0,h=
+1,ha=0}qa=0}}function B(){return Fa}function G(h){4===(h&4)&&0===(Fa&4)&&sa.k(6);Fa=h}function n(){ja=0;h=1;fa[0]=32}function H(h){Da=h[1];Fa&8&&sa.k(6)}function V(){Fa&8&&sa.k(6)}function R(f,z){function n(){B++;B>Ca&&(B=1,F++,1<F&&(F=0,N++));Da=N;ja=0;h=7;fa[0]=F<<2|32;fa[1]=0;fa[2]=0;fa[3]=N;fa[4]=F;fa[5]=B;fa[6]=z[4];Fa&8&&sa.k(6)}var F=z[2],N=z[1],B=z[3],G=128*(1<<z[4]),D=z[5]-z[3]+1,C=((F+2*N)*Ca+B-1)*G;f?pa.za(t,C,n):pa.ya(t,C,D*G,n)}function N(){}function F(){ja=0;h=7;fa[0]=0;fa[1]=0;fa[2]=
+0;fa[3]=0;fa[4]=0;fa[5]=0;fa[6]=0;Fa&8&&sa.k(6)}var na=f.j,sa=f.s,pa=f.xa,ha=0,za=new Uint8Array(10),qa=0,z,fa=new Uint8Array(10),ja=0,h=0;if(this.buffer=t){var Ca;368640===t.byteLength&&(this.type=1,Ca=9);if(1228800===t.byteLength)this.type=2,Ca=15;else if(737280===t.byteLength)this.type=3,Ca=9;else if(1474560===t.byteLength)this.type=4,Ca=18;else if(2949120===t.byteLength)this.type=5,Ca=36;else if(1763328===t.byteLength)this.type=5,Ca=21;else throw ze("Unknown floppy size: "+Be(t.byteLength));var Da=
+0;na.h(1008,D);na.h(1012,C);na.h(1013,L);na.h(1015,O);na.g(1013,X);var Fa=0;na.h(1010,B);na.g(1010,G)}else this.type=0};function be(){var f=new ArrayBuffer(67108864);function t(f){var F=f>>>14,n=V[F];return 1===H[F]?n(f):C(f&-4)>>8*(f&3)&255}function D(f,F){var n=R[f>>>14];1===H[f>>>14]&&n(f,F)}function C(f){var F=V[f>>>14];return 0===(f&3)&&H[f>>>14]===n?F(f):t(f)|t(f+1)<<8|t(f+2)<<16|t(f+3)<<24}function O(f,F){var t=R[f>>>14];0===(f&3)&&H[f>>>14]===n?t(f,F):(D(f,F&255),D(f+1,F>>8&255),D(f+2,F>>16&255),D(f+3,F>>24&255))}var L=new Uint8Array(f),X=new Uint16Array(f),B=new Int8Array(f),G=new Int32Array(f);this.m=L;this.Q=
+B;this.i=G;this.buffer=f;for(var n=4,H=new Int8Array(262144),V=[],R=[],f=0;262144>f;f++)V[f]=R[f]=void 0;this.ba=function(f,F,t,B,G){var D=f>>>14;for(t=t?n:1;0<F;D++)H[D]=t,V[D]=function(n){return B(n-f|0)},R[D]=function(n,F){G(n-f|0,F)},F-=16384};this.R=function(f){return H[f>>>14]?t(f)<<24>>24:B[f]};this.d=function(f){return H[f>>>14]?t(f):L[f]};this.b=function(f){return H[f>>>14]?t(f)|t(f+1)<<8:L[f]|L[f+1]<<8};this.q=function(f){return H[f>>>14]?t(f)|t(f+1)<<8:X[f>>1]};this.c=function(f){return H[f>>>
+14]?C(f)|0:L[f]|L[f+1]<<8|L[f+2]<<16|L[f+3]<<24};this.v=function(f){return H[f>>>14]?C(f)|0:G[f>>2]};this.e=function(f,n){H[f>>>14]?D(f,n):L[f]=n};this.a=function(f,n){H[f>>>14]?(D(f,n&255),D(f+1,n>>8&255)):(L[f]=n,L[f+1]=n>>8)};this.S=function(f,n){H[f>>>14]?(D(f,n&255),D(f+1,n>>8&255)):X[f>>1]=n};this.f=function(f,n){H[f>>>14]?O(f,n):(L[f]=n,L[f+1]=n>>8,L[f+2]=n>>16,L[f+3]=n>>24)};this.D=function(f,n){H[f>>>14]?O(f,n):G[f>>2]=n};this.J=function(f,n){L.set(f,n)};this.aa=function(f){for(var n=0;n<
+f.length;n++)this.e(63488+n,f.charCodeAt(n))}};function qe(f){function t(f,t){if(8>f){var n=f>>1;f&1?L[n].count=D(L[n].count,t):L[n].p=D(L[n].p,t)}}function D(f,t){return(X^=1)?f&-256|t:f&-65281|t<<8}var C=f.j,O=f.memory,L=[{p:0,count:0},{p:0,count:0},{p:0,count:0},{p:0,count:0}],X=0;C.g(4,t.bind(0,4));C.g(5,t.bind(0,5));C.g(10,function(){});C.g(11,function(){});C.g(12,function(){X=0});C.g(129,function(f){L[2].p=L[2].p&65535|f<<16});this.ya=function(f,t,n,D){var C=L[2].p;L[2].p+=n;f.get(t,n,function(f){O.J(f,C);D()})};this.za=function(f,t,n){f.set(t,
+new Uint8Array(O.buffer,L[2].p,L[2].count+1),function(){n()})}};function xe(f){function t(f){var n=H[f];if(n)return H[f]--,2===n?V[f]&255:V[f]>>8;n=X[f];3===G[f]&&(X[f]^=1);return n?N[f]&255:N[f]>>8}function D(f,t){R[f]=X[f]?R[f]&-256|t:R[f]&255|t<<8;3===n[f]&&X[f]||(R[f]||(R[f]=65535),N[f]=R[f],B[f]=!0);3===n[f]&&(X[f]^=1)}var C=f.j,O=f.s,L=Date.now(),X=new Uint8Array(3),B=new Uint8Array(3),G=new Uint8Array(3),n=new Uint8Array(3),H=new Uint8Array(3),V=new Uint16Array(3),R=new Uint16Array(3),N=new Uint16Array(3),F=0,na=0;C.h(97,function(){na^=16;return na|F<<
+5});this.a=function(f){var n,t=1193.1816666*(f-L)>>>0;if(t){L+=t/1193.1816666;if(B[0]&&(f=N[0]-=t,0>=f))if(O.k(0),n=G[0],0===n)B[0]=0,N[0]=0;else if(3===n||2===n)N[0]=R[0]+f%R[0];B[2]&&(f=N[2]-=t,0>=f&&(n=G[2],0===n?(F=1,B[2]=0,N[2]=0):2===n?(F=1,N[2]=R[2]+f%R[2]):3===n&&(F^=1,N[2]=R[2]+f%R[2])))}};C.h(64,function(){return t(0)});C.h(65,function(){return t(1)});C.h(66,function(){return t(2)});C.g(64,function(f){D(0,f)});C.g(65,function(f){D(1,f)});C.g(66,function(f){D(2,f)});C.g(67,function(f){var t=
+f>>1&7,D=f>>6&3;f=f>>4&3;3!==D&&(0===f?(H[D]=2,V[D]=N[D]):(6<=t&&(t&=-5),X[D]=1===f?1:2===f?0:1,G[D]=t,n[D]=f,2===D&&(F=0===t?0:1)))})};function re(f,t){function D(){var f=(L-N)/n|0,h=(L-N)%n,f=Math.min(H-1,f);t.Za(f,h)}var C=f.j,O=f.memory,L=0,X=14,B=15,G=this,n,H,V,R,N=0,F=!0,na=!1,sa=!1,pa=new Int32Array(256),ha=new Uint8Array(262144),za=new Uint8Array(ha.buffer,0,65536),qa=new Uint8Array(ha.buffer,65536,65536),z=new Uint8Array(ha.buffer,131072,65536),fa=new Uint8Array(ha.buffer,196608,65536),ja=0,h=0,Ca=0,Da=0,Fa=new Uint8Array(8388608),Xb=!1,gc=0,hc=0,ic=0;this.i=function(){if(sa)if(sa=!1,na){if(!F)for(var f=0,h,D=0;D<R;D++)for(var B=
+0;B<V;B+=8){for(var C=0;8>C;C++)h=za[f]>>C&1|qa[f]>>C<<1&2|z[f]>>C<<2&4|fa[f]>>C<<3&8,t.Qa(B+7-C,D,pa[zb[h]]);f++}}else for(f=98304|N<<1,B=0;B<H;B++)for(C=0;C<n;C++)h=ha[f],D=ha[f|1],t.ka(B,C,h,pa[D>>4&15],pa[D&15]),f+=2;na||Xb?t.Va():t.Wa()};this.N=function(f,h){n=f;H=h;t.N(f,h)};this.I=function(f,h){t.I(f,h)};this.O=function(){t.O(X,B)};this.Z=function(){t.Z()};this.a=function(f){var h=!1;switch(f){case 3:this.N(80,25);break;case 16:this.I(640,350);V=640;R=350;h=!0;F=!1;break;case 18:this.I(640,
+480);V=640;R=480;h=!0;F=!1;break;case 19:this.I(320,200),V=320,R=200,F=h=!0}t.va(h);na=h};this.n=function(){};var cb=0,$a=0;C.g(967,function(){});C.g(968,function(f){$a=3*f});C.g(969,function(f){var h=$a/3|0,n=$a%3,z=pa[h];f=f<<2&255|3;pa[h]=0===n?z&-16711681|f<<16:1===n?z&-65281|f<<8:z&-256|f;$a++;sa=!0});C.g(980,function(f){cb=f});C.g(981,function(f){switch(cb){case 2:G.N(f,25);break;case 10:X=f;G.O();break;case 11:B=f;G.O();break;case 12:N=N&255|f<<8;sa=!0;break;case 13:N=N&65280|f;sa=!0;break;
+case 14:L=L&255|f<<8;D();break;case 15:L=L&65280|f,D()}});C.h(981,function(){return 10===cb?X:11===cb?B:14===cb?L>>8:15===cb?L&255:0});var jc=255;C.h(972,function(){return jc});C.g(962,function(f){jc=f;103===f?G.a(3):227===f?G.a(18):99===f?G.a(19):163===f?G.a(16):G.a(3)});C.h(986,function(){La=-1;return 255});var La=-1;C.h(961,function(){return La=-1});var zb=new Uint8Array(16);C.g(960,function(f){-1===La?La=f:(16>La&&(zb[La]=f),La=-1)});C.h(960,function(){var f=La;La=-1;return f});var Bb=-1;C.g(964,
+function(f){Bb=f});var ca=15;C.g(965,function(f){switch(Bb){case 2:ca=f}});C.h(965,function(){switch(Bb){case 6:return 18}});var tc=-1;C.g(974,function(f){tc=f});var Qc=0,Wb=0,gd=0,Ia=255;C.g(975,function(f){switch(tc){case 3:gd=f;break;case 4:Qc=f;break;case 5:Wb=f;break;case 8:Ia=f}});var hb=-1,db=-1,ib=!0;C.g(462,function(f){hb=f;ib=!0});C.g(463,function(f){ib?(hb|=f<<8,ib=!1):(ib=!0,db=f)});C.g(464,function(f){db|=f<<8;switch(hb){case 1:gc=db;break;case 2:hc=db;break;case 3:ic=db;break;case 4:Xb=
+1===(db&1)}Xb&&(G.I(gc,hc),t.va(!0))});C.h(463,function(){switch(hb){case 0:return 192;case 1:return 1280;case 2:return 1024;case 3:return 32;case 10:return 128}});C.h(464,function(){switch(hb){case 0:return 176;case 1:return 5;case 2:return 4;case 3:return 0;case 10:return 0}});G.N(80,25);G.O();O.ba(655360,131072,!1,function(f){if(!na||F)return ha[f];f&=65535;ja=za[f];h=qa[f];Ca=z[f];Da=fa[f];return ha[Qc<<16|f]},function(f,D){if(na)if(F){var C=f<<2,B=pa[D];t.r(C|2,B>>16&255);t.r(C|1,B>>8&255);t.r(C,
+B&255);ha[f]=D}else{if(!(65535<f)){var G,H;0===Wb?C=B=G=H=D:2===Wb&&(ca&1&&(C=ja&~Ia|(D&1?255:0)&Ia),ca&2&&(B=h&~Ia|(D&2?255:0)&Ia),ca&4&&(G=Ca&~Ia|(D&4?255:0)&Ia),ca&8&&(H=Da&~Ia|(D&8?255:0)&Ia));if(0===Wb||2===Wb){switch(gd&24){case 8:C&=ja;B&=h;G&=Ca;H&=Da;break;case 16:C|=ja;B|=h;G|=Ca;H|=Da;break;case 24:C^=ja,B^=h,G^=Ca,H^=Da}ca&1&&(C=ja&~Ia|C&Ia);ca&2&&(B=h&~Ia|B&Ia);ca&4&&(G=Ca&~Ia|G&Ia);ca&8&&(H=Da&~Ia|H&Ia)}else 1===Wb&&(C=ja,B=h,G=Ca,H=Da);ca&1?za[f]=C:C=za[f];ca&2?qa[f]=B:B=qa[f];ca&4?
+z[f]=G:G=z[f];ca&8?fa[f]=H:H=fa[f];if(!(f>=V*R<<3)){B<<=1;G<<=2;H<<=3;for(var L=(f<<3|7)<<2,O=0;8>O;O++){var Fa=pa[zb[C>>O&1|B>>O&2|G>>O&4|H>>O&8]];t.r(L|2,Fa>>16);t.r(L|1,Fa>>8&255);t.r(L,Fa&255);L-=4}}}}else 98304>f||(B=(f-98304>>1)-N,C=B/n|0,B%=n,f&1?(H=D,G=ha[f&-2]):(G=D,H=ha[f|1]),t.ka(C,B,G,pa[H>>4&15],pa[H&15]),ha[f]=D)});O.ba(3758096384,8388608,!1,function(f){return Fa[f]},function(f,h){Fa[f]=h;switch(ic){case 32:if(3===(f&3))break;t.r(f,h);break;case 24:t.r(4/3*f|0,h);break;case 16:if(f&
+1){var n=Fa[f^1]>>5&7|h<<3&56,z;z=255*(h>>3&31)/31|0;f<<=1;t.r(f-1,255*n/63|0);t.r(f-2,z)}else n=255*(h&31)/31|0,t.r((f<<1)+2,n)}})};function se(f,t,D){function C(){n.k(1)}function O(f){qa.push(f);C()}function L(f,h){if(R&&V&&(N+=f,F+=h,H)){var n=Date.now();n-fa<1E3/z||(fa=n,B())}}function X(f,h,n){R&&V&&(na=f|n<<1|h<<2,H&&B())}function B(){ja.push((0>F)<<5|(0>N)<<4|8|na,N&255,F&255);if(15<ja.length){var f=ja.length%3;ja=ja.slice(0,f).concat(ja.slice(f+3))}F=N=0;n.k(12)}var G=f.j,n=f.s,H=!1,V=!1,R=!1,N=0,F=0,na=0,sa=!1,pa=!1,ha=!1,za=!1,qa=[],z=100,fa=-1,ja=[];t&&(sa=!0,t.w(O));D&&(R=!0,D.w(X,L));this.G=O;this.n=function(){sa&&
+t.n();R&&D.n()};var h=0,Ca=!1,Da=!1;G.h(96,function(){if(!qa.length&&!ja.length)return 255;if(qa.length&&ja.length?0===(n.Ca()&2):!qa.length)return 1<ja.length&&n.k(12),ja.shift();1<qa.length&&C();return qa.shift()});G.h(100,function(){var f=16;if(ja.length||qa.length)f|=1;ja.length&&(f|=32);return f});G.g(96,function(f){Da?(h=f,Da=!1):Ca?(Ca=!1,ja=[f],n.k(12)):ha?(ha=!1,ja=[250],z=f,n.k(12)):za?za=!1:pa?R&&(ja=[250],255===f?(ja.push(170,0),V=!0,D.enabled=!0):242===f?ja.push(0,0):243===f?ha=!0:244===
+f?(V=H=!0,D.enabled=!0):245===f?H=!0:246===f&&(H=!1,z=100),n.k(12)):(qa.push(250),255===f?qa.push(170,0):242===f?qa.push(171,83):244!==f&&245!==f&&237===f&&(za=!0),C())});G.g(100,function(n){254===n?f.Ta():32===n?(qa.push(h),C()):96===n?Da=!0:211===n?Ca=!0:212===n?pa=!0:169===n?(qa=[0],C()):170===n?(qa=[85],C()):171===n&&(qa=[0],C())})};function de(f,t,D,C){var O=f.j,L=0,X=0,B=0,G=0,n=void 0===C,H;n?(H=new de(f,t,D,this),this.a=function(){var f=G&L;if(!f)return!1;f&=-f;if(B&&(B&-B)<=f)return!1;G&=~f;B|=f;if(4===f)return H.a();t(X+Ae[f],!1,!1);return!0}):this.a=function(){var f=G&L;if(!f)return!1;f&=-f;if(B&&(B&-B)<=f)return!1;G&=~f;B|=f;t(X+Ae[f],!1,!1);G&&C.k(2);return!0};this.i=function(){n&&H.i()};var V,R=0,N=1;f=n?32:160;O.g(f,function(f){if(f&16)V=f&1,R=1;else if(f&8)N=f&1;else{var n=f>>5;1===n?B=0:3===n&&(B&=~(1<<(f&7)))}});
+O.h(f,function(){return N?G:B});O.g(f|1,function(f){0===R?V?V=!1:L=~f:1===R?(X=f,R++):2===R&&(R=0)});O.h(f|1,function(){return~L});this.k=n?function(f){8<=f&&(H.k(f-8),f=2);G|=1<<f;D()}:function(f){G|=1<<f};this.Ca=function(){return B}};function ye(f,t){function D(f){return N&4?f:Math.wa(f)}var C=f.j,O=f.s,L=0,X=this,B=Date.now(),G=B,n,H=!1,V=0.9765625,R=38,N=2;this.i=0;this.a=function(f){if(H)for(;n<f;)n+=V,O.k(8);B+=f-G;G=f};C.g(112,function(f){L=f&127;X.i=f>>7});C.g(113,function(f){switch(L){case 10:R=f&127;V=1E3/(32768>>(R&15)-1);break;case 11:N=f,N&64&&(n=Date.now())}H=64===(N&64)&&0<(R&15)});C.h(113,function(){var f=L;L=13;switch(f){case 0:return D((new Date(B)).getUTCSeconds());case 2:return D((new Date(B)).getUTCMinutes());
+case 4:return D((new Date(B)).getUTCHours());case 7:return D((new Date(B)).getUTCDate());case 8:return D((new Date(B)).getUTCMonth()+1);case 9:return D((new Date(B)).getUTCFullYear()%100);case 10:return R;case 11:return N;case 14:return 0;case 12:return 0;case 15:return 0;case 16:return t;case 20:return 45;case 50:return D((new Date(B)).getUTCFullYear()/100|0);case 52:return 0;case 53:return 3;case 56:return 48;case 61:return 33;case 91:case 92:case 93:return 0}return 255})};function te(f){var t="";f.j.g(1016,function(f){10===f?(nc(t),t=""):t+=String.fromCharCode(f)})};window.requestAnimationFrame||(window.requestAnimationFrame=window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame);
+function Wd(){function f(){na=!0;for(var f=0;f<ha;f++)F[f]&&(N.i(f),F[f]=0)}function t(f,n,t){n=""+(1===n?"":" scaleX("+n+")")+(1===t?"":" scaleY("+t+")");f.style.webkitTransform=f.style.MozTransform=n}var D=document.getElementById("screen"),C=document.getElementById("vga"),O=C.getContext("2d"),L=document.createElement("div"),X,B,G,n,H=1,V=1,R,N=this,F,na=!0,sa,pa,ha,za=new Uint16Array([199,252,233,226,228,224,229,231,234,235,232,239,238,236,196,197,201,230,198,244,246,242,251,249,255,214,220,162,
+163,165,8359,402,225,237,243,250,241,209,170,186,191,8976,172,189,188,161,171,187,9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488,9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575,9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600,945,223,915,960,931,963,181,964,934,920,937,948,8734,966,949,8745,8801,177,8805,8804,8992,8993,247,8776,176,8729,183,8730,8319,178,9632,160]),qa=new Uint16Array([32,9786,9787,9829,
+9830,9827,9824,8226,9688,9675,9689,9794,9792,9834,9835,9788,9658,9668,8597,8252,182,167,9644,8616,8593,8595,8594,8592,8735,8596,9650,9660]);O.imageSmoothingEnabled=!1;O.mozImageSmoothingEnabled=!1;O.webkitImageSmoothingEnabled=!1;L.id="cursor";D.style.display="block";C.style.display="none";this.ka=function(f,n,t,h,B){F[f]=1;f=3*(f*pa+n);sa[f]=t;sa[f+1]=h;sa[f+2]=B};this.Wa=function(){na&&(na=!1,requestAnimationFrame(f))};this.Qa=function(f,n,t){f=n*R+f<<2;B[f]=t>>16&255;B[f+1]=t>>8&255;B[f+2]=t&255};
+this.r=function(f,n){B[(f^3)-1]=n};this.Va=function(){na&&(na=!1,requestAnimationFrame(function(){na=!0;O.putImageData(X,0,0)}))};this.n=function(){};this.va=function(f){f?(D.style.display="none",C.style.display="block"):(D.style.display="block",C.style.display="none")};this.Z=function(){O.fillStyle="#000";O.fillRect(0,0,C.width,C.height)};this.N=function(f,n){F=new Int8Array(n);sa=new Int32Array(f*n*3);pa=f;for(ha=n;D.firstChild;)D.removeChild(D.firstChild);for(var t=0;t<n;t++)D.appendChild(document.createElement("div"))};
+this.I=function(f,n){C.style.display="block";C.width=f;C.height=n;N.Z();X=O.getImageData(0,0,f,n);B=X.data;R=f};this.a=function(f,n){H=f;V=n;t(C,H,V);t(D,H,V)};this.a(H,V);this.O=function(f,n){f&32?L.style.display="none":(L.style.display="inline",L.style.height=n-f+"px",L.style.marginTop=f+"px")};this.Za=function(f,t){if(f!==G||t!==n)F[f]=1,F[G]=1,G=f,n=t};this.i=function(f){var t=3*f*pa,B,h,C,F,H;B=document.createElement("div");for(var R=0;R<pa;){h=document.createElement("span");C=sa[t+1];F=sa[t+
+2];h.style.backgroundColor="#"+Be(C,6);h.style.color="#"+Be(F,6);for(H="";R<pa&&sa[t+1]===C&&sa[t+2]===F;){var N=sa[t];H+=127<N?String.fromCharCode(za[N-128]):32>N?String.fromCharCode(qa[N]):String.fromCharCode(N);R++;t+=3;if(f===G)if(R===n)break;else if(R===n+1){B.appendChild(L);break}}h.textContent=H;B.appendChild(h)}D.replaceChild(B,D.childNodes[f])}};function Xd(){function f(f){return f.shiftKey&&f.ctrlKey&&74===f.keyCode||116===f.keyCode||!X.enabled?!1:f.target?"phone_keyboard"===f.target.className||"INPUT"!==f.target.nodeName&&"TEXTAREA"!==f.target.nodeName:!0}function t(n){if(f(n)){var t=n.keyCode;if(!L[t])return!1;L[t]=!1;O(t,!1)||n.preventDefault()}}function D(n){if(f(n)){var t=n.keyCode;L[t]&&O(t,!1);L[t]=!0;O(t,!0)||n.preventDefault()}}function C(){for(var f=Object.keys(L),t,B=0;B<f.length;B++)t=+f[B],L[t]&&O(t,!1);L={}}function O(f,t){if(f>=
+G.length||0===G[f])return!0;var C=G[f];t||(C|=128);255<C?(B(C>>8),B(C&255)):B(C);return!1}var L={},X=this,B;this.enabled=!0;var G=new Uint16Array([0,0,0,0,0,0,0,0,14,15,0,0,0,28,0,0,42,29,56,0,58,0,0,0,0,0,0,1,0,0,0,0,57,57417,57425,79,71,75,72,77,80,0,0,0,0,82,83,0,11,2,3,4,5,6,7,8,9,10,0,39,0,13,0,0,0,30,48,46,32,18,33,34,35,23,36,37,38,50,49,24,25,16,19,31,20,22,47,17,45,21,44,0,0,0,0,0,82,79,80,81,75,76,77,71,72,73,0,0,0,0,0,0,59,60,61,62,63,64,65,66,67,68,87,88,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,12,0,0,0,0,0,0,0,0,0,0,0,0,39,13,51,12,52,53,41,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,26,43,27,40,0]);this.w=function(f){this.n();B=f;window.addEventListener("keyup",t,!1);window.addEventListener("keydown",D,!1);window.addEventListener("blur",C,!1)};this.n=function(){window.removeEventListener("keyup",t,!1);window.removeEventListener("keydown",D,!1);window.removeEventListener("blur",C,!1)}};function Yd(){function f(f){return V.enabled&&(!f.target||"mousemove"===f.type||"INPUT"!==f.target.nodeName&&"TEXTAREA"!==f.target.nodeName)}function t(n){if(f(n)){var t;t=n.webkitMovementX||n.mozMovementX||0;n=n.webkitMovementY||n.mozMovementY||0;t=Math.qa(0.5*t);n=Math.qa(0.5*n);H(t,-n)}}function D(n){f(n)&&n.preventDefault()}function C(n){f(n)&&L(n,!0)}function O(n){f(n)&&L(n,!1)}function L(f,t){1===f.which?X=t:2===f.which?G=t:3===f.which&&(B=t);n(X,G,B);f.preventDefault()}var X=!1,B=!1,G=!1,n,
+H,V=this;this.enabled=!1;this.n=function(){window.removeEventListener("mousemove",t,!1);document.removeEventListener("contextmenu",D,!1);window.removeEventListener("mousedown",C,!1);window.removeEventListener("mouseup",O,!1)};this.w=function(f,B){this.n();n=f;H=B;window.addEventListener("mousemove",t,!1);document.addEventListener("contextmenu",D,!1);window.addEventListener("mousedown",C,!1);window.addEventListener("mouseup",O,!1)}};
+//# sourceMappingURL=src/v86_all.js.map

Some files were not shown because too many files changed in this diff