Repository: ozaki-r/arm-js Branch: master Commit: 5afce124bfbc Files: 32 Total size: 1.5 MB Directory structure: gitextract_vip1hlxs/ ├── .gitmodules ├── Image-3.10.29 ├── README.md ├── arm-js.html ├── css/ │ └── jquery-ui-1.8.18.custom.css ├── initramfs.cpio.lzo ├── js/ │ ├── 9p.js │ ├── JSONlocalStorage.js │ ├── armv7-cp15.js │ ├── armv7-cpu.js │ ├── armv7-mmu.js │ ├── bitops.js │ ├── display.js │ ├── filesystem.js │ ├── jquery-1.7.2.js │ ├── logger.js │ ├── number64.js │ ├── option.js │ ├── parameter.js │ ├── symbols.js │ ├── tracer.js │ ├── utils.js │ ├── vexpress.js │ └── virtio.js ├── misc/ │ ├── busybox-1.20.2.config │ ├── jquery-1.7.2-binary.patch │ ├── linux-3.6.1.config │ ├── simple-http-server.rb │ ├── symbols.rb │ ├── term.js.patch │ └── vexpress-armjs.dts └── vexpress-armjs.dtb ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitmodules ================================================ [submodule "js/simpleterm"] path = js/simpleterm url = https://github.com/michaelko/simpleterm.git ================================================ FILE: README.md ================================================ Arm-js ====== Arm-js is an ARM emulator written in Javascript. It emulates ARMv7-A and some peripherals of Versatile Express. It can boot Linux 3.10.29 and run busybox processes. Emulator Features --- * Suspend/resume (Chrome only) * You can restore emulator states at any time * Persistent storage (Chrome only) * Guest can access part of browser [filesystem](http://www.w3.org/TR/file-system-api/) via virtio-9p * Many debugging functions Tested Browsers --- * Chrome 33 beta (recommended) * Firefox 27 Get Started --- 1. Download the source code 1. git clone git://github.com/ozaki-r/arm-js.git 2. cd arm-js/ 2. git submodule init 3. git submodule update 2. Execute ruby misc/simple-http-server.rb on terminal 3. Access http://localhost:8080/arm-js.html 4. Push Boot button at the top-left corner to start the emulator Further Information --- See the [wiki](https://github.com/ozaki-r/arm-js/wiki). ================================================ FILE: arm-js.html ================================================
0.0 KIPS / 0 instructions executed / 0 sec elapsed


)

  
  • )
================================================ FILE: css/jquery-ui-1.8.18.custom.css ================================================ /* * jQuery UI CSS Framework 1.8.18 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Theming/API */ /* Layout helpers ----------------------------------*/ .ui-helper-hidden { display: none; } .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } .ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; } .ui-helper-clearfix:after { clear: both; } .ui-helper-clearfix { zoom: 1; } .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } /* Interaction Cues ----------------------------------*/ .ui-state-disabled { cursor: default !important; } /* Icons ----------------------------------*/ /* states and images */ .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } /* Misc visuals ----------------------------------*/ /* Overlays */ .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } /* * jQuery UI CSS Framework 1.8.18 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Theming/API * * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px */ /* Component containers ----------------------------------*/ .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } .ui-widget .ui-widget { font-size: 1em; } .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } .ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(../images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } .ui-widget-content a { color: #222222; } .ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(../images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; } .ui-widget-header a { color: #222222; } /* Interaction states ----------------------------------*/ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(../images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; } .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; } .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(../images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } .ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; } .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(../images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; } .ui-widget :active { outline: none; } /* Interaction Cues ----------------------------------*/ .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(../images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; } .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(../images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } /* Icons ----------------------------------*/ /* states and images */ .ui-icon { width: 16px; height: 16px; background-image: url(../images/ui-icons_222222_256x240.png); } .ui-widget-content .ui-icon {background-image: url(../images/ui-icons_222222_256x240.png); } .ui-widget-header .ui-icon {background-image: url(../images/ui-icons_222222_256x240.png); } .ui-state-default .ui-icon { background-image: url(../images/ui-icons_888888_256x240.png); } .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(../images/ui-icons_454545_256x240.png); } .ui-state-active .ui-icon {background-image: url(../images/ui-icons_454545_256x240.png); } .ui-state-highlight .ui-icon {background-image: url(../images/ui-icons_2e83ff_256x240.png); } .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(../images/ui-icons_cd0a0a_256x240.png); } /* positioning */ .ui-icon-carat-1-n { background-position: 0 0; } .ui-icon-carat-1-ne { background-position: -16px 0; } .ui-icon-carat-1-e { background-position: -32px 0; } .ui-icon-carat-1-se { background-position: -48px 0; } .ui-icon-carat-1-s { background-position: -64px 0; } .ui-icon-carat-1-sw { background-position: -80px 0; } .ui-icon-carat-1-w { background-position: -96px 0; } .ui-icon-carat-1-nw { background-position: -112px 0; } .ui-icon-carat-2-n-s { background-position: -128px 0; } .ui-icon-carat-2-e-w { background-position: -144px 0; } .ui-icon-triangle-1-n { background-position: 0 -16px; } .ui-icon-triangle-1-ne { background-position: -16px -16px; } .ui-icon-triangle-1-e { background-position: -32px -16px; } .ui-icon-triangle-1-se { background-position: -48px -16px; } .ui-icon-triangle-1-s { background-position: -64px -16px; } .ui-icon-triangle-1-sw { background-position: -80px -16px; } .ui-icon-triangle-1-w { background-position: -96px -16px; } .ui-icon-triangle-1-nw { background-position: -112px -16px; } .ui-icon-triangle-2-n-s { background-position: -128px -16px; } .ui-icon-triangle-2-e-w { background-position: -144px -16px; } .ui-icon-arrow-1-n { background-position: 0 -32px; } .ui-icon-arrow-1-ne { background-position: -16px -32px; } .ui-icon-arrow-1-e { background-position: -32px -32px; } .ui-icon-arrow-1-se { background-position: -48px -32px; } .ui-icon-arrow-1-s { background-position: -64px -32px; } .ui-icon-arrow-1-sw { background-position: -80px -32px; } .ui-icon-arrow-1-w { background-position: -96px -32px; } .ui-icon-arrow-1-nw { background-position: -112px -32px; } .ui-icon-arrow-2-n-s { background-position: -128px -32px; } .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } .ui-icon-arrow-2-e-w { background-position: -160px -32px; } .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } .ui-icon-arrowstop-1-n { background-position: -192px -32px; } .ui-icon-arrowstop-1-e { background-position: -208px -32px; } .ui-icon-arrowstop-1-s { background-position: -224px -32px; } .ui-icon-arrowstop-1-w { background-position: -240px -32px; } .ui-icon-arrowthick-1-n { background-position: 0 -48px; } .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } .ui-icon-arrowthick-1-e { background-position: -32px -48px; } .ui-icon-arrowthick-1-se { background-position: -48px -48px; } .ui-icon-arrowthick-1-s { background-position: -64px -48px; } .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } .ui-icon-arrowthick-1-w { background-position: -96px -48px; } .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } .ui-icon-arrow-4 { background-position: 0 -80px; } .ui-icon-arrow-4-diag { background-position: -16px -80px; } .ui-icon-extlink { background-position: -32px -80px; } .ui-icon-newwin { background-position: -48px -80px; } .ui-icon-refresh { background-position: -64px -80px; } .ui-icon-shuffle { background-position: -80px -80px; } .ui-icon-transfer-e-w { background-position: -96px -80px; } .ui-icon-transferthick-e-w { background-position: -112px -80px; } .ui-icon-folder-collapsed { background-position: 0 -96px; } .ui-icon-folder-open { background-position: -16px -96px; } .ui-icon-document { background-position: -32px -96px; } .ui-icon-document-b { background-position: -48px -96px; } .ui-icon-note { background-position: -64px -96px; } .ui-icon-mail-closed { background-position: -80px -96px; } .ui-icon-mail-open { background-position: -96px -96px; } .ui-icon-suitcase { background-position: -112px -96px; } .ui-icon-comment { background-position: -128px -96px; } .ui-icon-person { background-position: -144px -96px; } .ui-icon-print { background-position: -160px -96px; } .ui-icon-trash { background-position: -176px -96px; } .ui-icon-locked { background-position: -192px -96px; } .ui-icon-unlocked { background-position: -208px -96px; } .ui-icon-bookmark { background-position: -224px -96px; } .ui-icon-tag { background-position: -240px -96px; } .ui-icon-home { background-position: 0 -112px; } .ui-icon-flag { background-position: -16px -112px; } .ui-icon-calendar { background-position: -32px -112px; } .ui-icon-cart { background-position: -48px -112px; } .ui-icon-pencil { background-position: -64px -112px; } .ui-icon-clock { background-position: -80px -112px; } .ui-icon-disk { background-position: -96px -112px; } .ui-icon-calculator { background-position: -112px -112px; } .ui-icon-zoomin { background-position: -128px -112px; } .ui-icon-zoomout { background-position: -144px -112px; } .ui-icon-search { background-position: -160px -112px; } .ui-icon-wrench { background-position: -176px -112px; } .ui-icon-gear { background-position: -192px -112px; } .ui-icon-heart { background-position: -208px -112px; } .ui-icon-star { background-position: -224px -112px; } .ui-icon-link { background-position: -240px -112px; } .ui-icon-cancel { background-position: 0 -128px; } .ui-icon-plus { background-position: -16px -128px; } .ui-icon-plusthick { background-position: -32px -128px; } .ui-icon-minus { background-position: -48px -128px; } .ui-icon-minusthick { background-position: -64px -128px; } .ui-icon-close { background-position: -80px -128px; } .ui-icon-closethick { background-position: -96px -128px; } .ui-icon-key { background-position: -112px -128px; } .ui-icon-lightbulb { background-position: -128px -128px; } .ui-icon-scissors { background-position: -144px -128px; } .ui-icon-clipboard { background-position: -160px -128px; } .ui-icon-copy { background-position: -176px -128px; } .ui-icon-contact { background-position: -192px -128px; } .ui-icon-image { background-position: -208px -128px; } .ui-icon-video { background-position: -224px -128px; } .ui-icon-script { background-position: -240px -128px; } .ui-icon-alert { background-position: 0 -144px; } .ui-icon-info { background-position: -16px -144px; } .ui-icon-notice { background-position: -32px -144px; } .ui-icon-help { background-position: -48px -144px; } .ui-icon-check { background-position: -64px -144px; } .ui-icon-bullet { background-position: -80px -144px; } .ui-icon-radio-off { background-position: -96px -144px; } .ui-icon-radio-on { background-position: -112px -144px; } .ui-icon-pin-w { background-position: -128px -144px; } .ui-icon-pin-s { background-position: -144px -144px; } .ui-icon-play { background-position: 0 -160px; } .ui-icon-pause { background-position: -16px -160px; } .ui-icon-seek-next { background-position: -32px -160px; } .ui-icon-seek-prev { background-position: -48px -160px; } .ui-icon-seek-end { background-position: -64px -160px; } .ui-icon-seek-start { background-position: -80px -160px; } /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ .ui-icon-seek-first { background-position: -80px -160px; } .ui-icon-stop { background-position: -96px -160px; } .ui-icon-eject { background-position: -112px -160px; } .ui-icon-volume-off { background-position: -128px -160px; } .ui-icon-volume-on { background-position: -144px -160px; } .ui-icon-power { background-position: 0 -176px; } .ui-icon-signal-diag { background-position: -16px -176px; } .ui-icon-signal { background-position: -32px -176px; } .ui-icon-battery-0 { background-position: -48px -176px; } .ui-icon-battery-1 { background-position: -64px -176px; } .ui-icon-battery-2 { background-position: -80px -176px; } .ui-icon-battery-3 { background-position: -96px -176px; } .ui-icon-circle-plus { background-position: 0 -192px; } .ui-icon-circle-minus { background-position: -16px -192px; } .ui-icon-circle-close { background-position: -32px -192px; } .ui-icon-circle-triangle-e { background-position: -48px -192px; } .ui-icon-circle-triangle-s { background-position: -64px -192px; } .ui-icon-circle-triangle-w { background-position: -80px -192px; } .ui-icon-circle-triangle-n { background-position: -96px -192px; } .ui-icon-circle-arrow-e { background-position: -112px -192px; } .ui-icon-circle-arrow-s { background-position: -128px -192px; } .ui-icon-circle-arrow-w { background-position: -144px -192px; } .ui-icon-circle-arrow-n { background-position: -160px -192px; } .ui-icon-circle-zoomin { background-position: -176px -192px; } .ui-icon-circle-zoomout { background-position: -192px -192px; } .ui-icon-circle-check { background-position: -208px -192px; } .ui-icon-circlesmall-plus { background-position: 0 -208px; } .ui-icon-circlesmall-minus { background-position: -16px -208px; } .ui-icon-circlesmall-close { background-position: -32px -208px; } .ui-icon-squaresmall-plus { background-position: -48px -208px; } .ui-icon-squaresmall-minus { background-position: -64px -208px; } .ui-icon-squaresmall-close { background-position: -80px -208px; } .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } .ui-icon-grip-solid-vertical { background-position: -32px -224px; } .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } .ui-icon-grip-diagonal-se { background-position: -80px -224px; } /* Misc visuals ----------------------------------*/ /* Corner radius */ .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } /* Overlays */ .ui-widget-overlay { background: #aaaaaa url(../images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(../images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* * jQuery UI Tabs 1.8.18 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Tabs#theming */ .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } .ui-tabs .ui-tabs-hide { display: none !important; } ================================================ FILE: js/9p.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ /* * 9P2000.u Protocol */ function Protocol9P2000u(net9p) { this.net9p = net9p; this.VERSION = "9P2000.u"; this.EXTENSION = ".u"; this.IOUNIT = 4096; this.TERROR = 106; this.HEADER_SIZE = 4 + 1 + 2; // size + id + tag this.msize = 0; this.fid2qid = {}; this.path2qid = {}; } Protocol9P2000u.prototype.hashCode32 = function(string) { var hash = 0; if (string.length === 0) return hash; for (var i=0; i < string.length; i++) { hash = 31 * hash + string.charCodeAt(i); hash = hash & hash; if (hash < 0) hash += 0x100000000; } return hash; }; Protocol9P2000u.prototype.add_qid = function(fid, qid) { //display.log("Adding fid=" + fid + ", name=" + qid.name + ", fullPath=" + qid.entry.fullPath); this.fid2qid[fid] = qid; this.path2qid[qid.fullPath] = qid; }; Protocol9P2000u.prototype.del_qid = function(fid) { var qid = this.fid2qid[fid]; delete this.fid2qid[fid]; delete this.path2qid[qid.fullPath]; }; Protocol9P2000u.prototype.get_qid = function(fid_or_path) { if (typeof fid_or_path == "number") return this.fid2qid[fid_or_path]; else if (typeof fid_or_path == "string") return this.path2qid[fid_or_path]; else abort("get_qid: unknown key type: " + (typeof fid_or_path)); }; Protocol9P2000u.prototype.build_reply = function(id, tag, payload) { var size = this.HEADER_SIZE + payload.length; // Reply ID is always +1 to request one var header = this.net9p.marshal(["w", "b", "h"], [size, id + 1, tag]); return header.concat(payload); }; Protocol9P2000u.prototype.version = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w", "s"], next_data); display.log("[version] msize=" + req[0] + ", version=" + req[1]); this.msize = req[0]; var payload = this.net9p.marshal(["w", "s"], [this.msize, this.VERSION]); return this.build_reply(id, tag, payload); }; Protocol9P2000u.prototype[100] = Protocol9P2000u.prototype.version; Protocol9P2000u.prototype.attach = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w", "w", "s", "s", "w"], next_data); var fid = req[0]; var afid = req[1]; // For auth. Ignored. var uname = req[2]; // Username. Ignored. var aname = req[3]; // Mount point. Ignored. var n_uname = req[4]; display.log("[attach] fid=" + fid + ", afid=" + afid + ", uname=" + uname + ", aname=" + aname + ", n_uname=" + n_uname); // Return root directory's QID var net9p = this.net9p; var that = this; net9p.fs.getRoot(function(entry) { var qid = { type: 0x10 | 0x80, // mount point & directory version: entry.mtime.getTime(), path: that.hashCode32(entry.fullPath), name: "/", entry: entry, // It includes fullPath and mtime atime: getCurrentTime(), // Need to update parent: null // See below }; qid.parent = qid; // To avoid checking null display.log("[attach] fullPath=" + entry.fullPath); that.add_qid(fid, qid); var payload = net9p.marshal(["Q"], [qid]); var reply = that.build_reply(id, tag, payload); net9p.send_reply(reply); }); return null; }; Protocol9P2000u.prototype[104] = Protocol9P2000u.prototype.attach; Protocol9P2000u.prototype.walk = function(id, tag, next_data) { // path elements ["root", "dir1", "dir2",...] var req = this.net9p.unmarshal(["w", "w", "h"], next_data); var fid = req[0]; var nwfid = req[1]; var nwname = req[2]; display.log("[walk] fid=" + fid + ", nwfid=" + nwfid + ", nwname=" + nwname); var qid = this.get_qid(fid); if (!qid) abort("No such QID found for fid=" + fid); if (nwname === 0) { this.add_qid(nwfid, qid); var payload = this.net9p.marshal(["h"], [0]); return this.build_reply(id, tag, payload); } var wnames = []; for (var i=0; i < nwname; i++) { wnames.push(this.net9p.unmarshal(["s"], next_data)); } display.log("[walk] wnames=[" + wnames.toString() + "]"); display.log("[walk] path=" + qid.entry.fullPath); ret_qids = []; var net9p = this.net9p; var that = this; var walk = function(pqid, names) { var name = names[0]; if (name) { display.log("[walk] walking: " + name); net9p.fs.getEntry(pqid.entry, name, function(entry) { var _qid = { type: (entry.isDirectory ? 0x80 : 0), version: entry.mtime.getTime(), path: that.hashCode32(entry.fullPath), name: entry.name, entry: entry, atime: (entry.isDirectory ? getCurrentTime() : entry.mtime.getTime()), parent: pqid }; ret_qids.push(_qid); walk(_qid, names.slice(1)); }, function(e) { if (names == wnames) { // FIXME: have to check error code var payload = net9p.marshal(["s", "w"], ["No such file or directory", 2]); var reply = that.build_reply(that.TERROR, tag, payload); } else { // Return walked QIDs var types = ["h"]; var data = [ret_qids.length]; for (var i in ret_qids) { types.push("Q"); types.push(ret_qids[i]); } var payload = net9p.marshal(types, data); var reply = that.build_reply(id, tag, payload); } net9p.send_reply(reply); }); } else { // Walked sucessfully that.add_qid(nwfid, ret_qids[ret_qids.length - 1]); // Return walked QIDs var types = ["h"]; var data = [ret_qids.length]; for (var i in ret_qids) { types.push("Q"); data.push(ret_qids[i]); } var payload = net9p.marshal(types, data); var reply = that.build_reply(id, tag, payload); net9p.send_reply(reply); } }; walk(qid, wnames); return null; }; Protocol9P2000u.prototype[110] = Protocol9P2000u.prototype.walk; Protocol9P2000u.prototype.open = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w", "b"], next_data); var fid = req[0]; var mode = req[1]; display.log("[open] fid=" + fid + ", mode=" + mode.toString(16)); var qid = this.get_qid(fid); if (!qid) abort("[open] No such QID found for fid=" + fid); if (mode & 0x10) { var net9p = this.net9p; var that = this; net9p.fs.truncate(qid.entry, function(entry) { // Update qid.entry = entry; qid.version = entry.mtime.getTime(); qid.atime = getCurrentTime(); var payload = net9p.marshal(["Q", "w"], [qid, this.IOUNIT]); var reply = that.build_reply(id, tag, payload); net9p.send_reply(reply); }); return null; } else { var payload = this.net9p.marshal(["Q", "w"], [qid, this.IOUNIT]); return this.build_reply(id, tag, payload); } }; Protocol9P2000u.prototype[112] = Protocol9P2000u.prototype.open; Protocol9P2000u.prototype.create = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w", "s", "w", "b", "s"], next_data); var fid = req[0]; var name = req[1]; var perm = req[2]; var mode = req[3]; var extension = req[4]; display.log("[create] fid=" + fid + ", name=" + name + ", perm=" + perm.toString(16) + ", mode=" + mode.toString(2) + ", extension=" + extension); var qid = this.get_qid(fid); if (!qid) abort("[create] No such QID found for fid=" + fid); var net9p = this.net9p; var that = this; var isfile = perm & 0x80000000 ? false : true; net9p.fs.create(qid.entry, name, {file: isfile}, function(entry) { display.log("[create] fullPath=" + entry.fullPath); var cqid = { type: isfile ? 0 : 0x80, version: entry.mtime.getTime(), path: that.hashCode32(entry.fullPath), name: entry.name, entry: entry, atime: isfile ? entry.mtime.getTime() : getCurrentTime(), parent: qid }; that.add_qid(fid, cqid); qid.parent.atime = getCurrentTime(); // FIXME: different from entry.mtime var payload = net9p.marshal(["Q", "w"], [cqid, that.IOUNIT]); var reply = that.build_reply(id, tag, payload); net9p.send_reply(reply); }); return null; }; Protocol9P2000u.prototype[114] = Protocol9P2000u.prototype.create; Protocol9P2000u.prototype.read_directory = function(qid, offset, entries) { var atime = qid.atime; var mtime = qid.entry.mtime.getTime(); var data = this.build_stat(".", 0, qid, atime, mtime); atime = qid.parent.atime; mtime = qid.parent.entry.mtime.getTime(); data = data.concat(this.build_stat("..", 0, qid.parent, atime, mtime)); for (var i in entries) { var ent = entries[i]; display.log("[read] name=" + ent.name); var cqid = this.get_qid(ent.fullPath); mtime = ent.mtime.getTime(); if (!cqid) { cqid = { type: (ent.isDirectory ? 0x80 : 0), version: mtime, path: this.hashCode32(ent.fullPath) }; } var stat = this.build_stat(ent.name, ent.size, cqid, atime, mtime); data = data.concat(stat); } if (offset) data = data.slice(offset); var count = this.net9p.marshal(["w"], [data.length]); display.log("[read] count=" + data.length); return count.concat(data); }; Protocol9P2000u.prototype.read = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w", "w", "w", "w"], next_data); var fid = req[0]; var offset = req[1]; //var offset = req[2]; // FIXME var count = req[3]; display.log("[read] fid=" + fid + ", offset=" + offset + ", count=" + count); var qid = this.get_qid(fid); if (!qid) abort("[read] No such QID found for fid=" + fid); var net9p = this.net9p; var that = this; if (qid.type & 0x80) { // directory display.log("[read] reading directory: " + qid.entry.fullPath); net9p.fs.getDirectoryEntries(qid.entry, function(entries) { qid.atime = getCurrentTime(); var payload = that.read_directory(qid, offset, entries); var reply = that.build_reply(id, tag, payload); net9p.send_reply(reply); }); } else { // file net9p.fs.read(qid.entry, offset, count, function(data) { qid.atime = getCurrentTime(); var count = net9p.marshal(["w"], [data.length]); display.log("[read] count=" + data.length); var payload = count.concat(data); var reply = that.build_reply(id, tag, payload); net9p.send_reply(reply); }); } return null; }; Protocol9P2000u.prototype[116] = Protocol9P2000u.prototype.read; Protocol9P2000u.prototype.write = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w", "w", "w", "w"], next_data); var fid = req[0]; var offset = req[1]; //var offset = req[2]; var count = req[3]; var qid = this.get_qid(fid); if (!qid) abort("[write] No such QID found for fid=" + fid); display.log("[write] fid=" + fid + ", offset=" + offset + ", count=" + count + ", fullPath=" + qid.entry.path); var buffer = new ArrayBuffer(count); var data = new Uint8Array(buffer, 0, count); for (var i=0; i < count; i++) data[i] = next_data(); var net9p = this.net9p; var that = this; net9p.fs.write(qid.entry, buffer, offset, function(entry) { qid.entry = entry; qid.atime = getCurrentTime(); var payload = net9p.marshal(["w"], [data.byteLength]); display.log("[write] count=" + data.byteLength); var reply = that.build_reply(id, tag, payload); net9p.send_reply(reply); }); return null; }; Protocol9P2000u.prototype[118] = Protocol9P2000u.prototype.write; Protocol9P2000u.prototype.clunk = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w"], next_data); var fid = req[0]; display.log("[clunk] fid=" + fid); this.del_qid(fid); return this.build_reply(id, tag, []); }; Protocol9P2000u.prototype[120] = Protocol9P2000u.prototype.clunk; Protocol9P2000u.prototype.remove = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w"], next_data); var fid = req[0]; var qid = this.get_qid(fid); if (!qid) abort("[remove] No such QID found for fid=" + fid); display.log("[remove] fid=" + fid); var net9p = this.net9p; var that = this; net9p.fs.remove(qid.entry, function() { // clunk fid as well that.del_qid(fid); qid.parent.atime = getCurrentTime(); // FIXME: different from entry.mtime var reply = that.build_reply(id, tag, []); net9p.send_reply(reply); }); return null; }; Protocol9P2000u.prototype[122] = Protocol9P2000u.prototype.remove; Protocol9P2000u.prototype.build_stat = function(name, filesize, qid, atime, mtime) { //display.log("qid.type=" + qid.type.toString(16)); var types = [ "h", // size "h", // type "w", // dev "Q", // qid "w", // mode "w", // atime "w", // mtime "w", // length0 "w", // length1 "s", // name "s", // uid "s", // gid "s", // muid "s", // extension "w", // n_uid "w", // n_gid "w" // n_muid ]; var data = [ 0, // size 0, // type 0, // dev qid, // qid (qid.type & 0x80) ? (0x80000000 | 0755) : 0644, // mode atime, // atime mtime, // mtime filesize, // length0 FIXME 0, // length1 name, // name "root", // uid "root", // gid "root", // muid ".u", // extension 0, // n_uid 0, // n_guid 0 // n_muid ]; var stat = this.net9p.marshal(types, data); // Fill size stat[0] = (stat.length - 2) & 0xff; stat[1] = (stat.length - 2) >>> 8; return stat; }; Protocol9P2000u.prototype.build_stat_vals_from_data = function(data) { var types = [ "h", // size "h", // type "w", // dev "Q", // qid "w", // mode "w", // atime "w", // mtime "w", // length0 "w", // length1 "s", // name "s", // uid "s", // gid "s", // muid "s", // extension "w", // n_uid "w", // n_gid "w" // n_muid ]; var stat_vals = this.net9p.unmarshal(types, data); return stat_vals; }; Protocol9P2000u.prototype.stat = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w"], next_data); var fid = req[0]; var qid = this.get_qid(fid); if (!qid) abort("[stat] No such QID found for fid=" + fid); display.log("[stat] path=" + qid.entry.fullPath); var filesize = qid.entry.isDirectory ? 0 : qid.entry.size; var payload = this.build_stat(qid.name, filesize, qid, qid.atime, qid.entry.mtime.getTime()); payload = [0, 0].concat(payload); // Ignored by guest but required var reply = this.build_reply(id, tag, payload); return reply; }; Protocol9P2000u.prototype[124] = Protocol9P2000u.prototype.stat; Protocol9P2000u.prototype.wstat = function(id, tag, next_data) { var req = this.net9p.unmarshal(["w"], next_data); var fid = req[0]; next_data(); next_data(); // We need it to remove unknown halfword data var stat_vals = this.build_stat_vals_from_data(next_data); var qid = this.get_qid(fid); if (!qid) abort("[wstat] No such QID found for fid=" + fid); display.log("[wstat] path=" + qid.entry.fullPath); qid.atime = qid.parent.atime = getCurrentTime(); if (stat_vals[9] && stat_vals[9] != qid.name) { var newname = stat_vals[9]; // FIXME: support only rename display.log("[wstat] newname=" + newname + ", name=" + name); var net9p = this.net9p; var that = this; display.log("[wstat] parent=" + qid.parent.entry.fullPath); net9p.fs.rename(qid.parent.entry, qid.name, newname, function(entry) { var reply = that.build_reply(id, tag, []); net9p.send_reply(reply); }); return null; } else { return this.build_reply(id, tag, []); } }; Protocol9P2000u.prototype[126] = Protocol9P2000u.prototype.wstat; function Net9p(virtio) { this.virtio = virtio; this.proto = new Protocol9P2000u(this); this.fs = new HTML5FileSystem('/9proot', 50 * 1024 * 1024); } Net9p.prototype.marshal = function(type, data) { var out = []; var item; for (var i=0; i < type.length; i++) { item = data[i]; switch (type[i]) { case "w": out.push(item & 0xff); out.push((item >>> 8) & 0xff); out.push((item >>> 16) & 0xff); out.push(item >>> 24); break; case "h": out.push(item & 0xff); out.push(item >>> 8); break; case "b": out.push(item); break; case "s": // Prepend size out.push(item.length & 0xff); out.push(item.length >>> 8); for (var j in item) out.push(item.charCodeAt(j)); break; case "D": for (var j in item) out.push(item[j]); break; case "Q": out = out.concat(this.marshal(["b"], item.type)); out = out.concat(this.marshal(["w"], item.version)); out = out.concat(this.marshal(["w"], item.path)); out = out.concat(this.marshal(["w"], 0)); // FIXME break; default: abort("marshal: Unknown type=" + type[i]); } } return out; }; Net9p.prototype.unmarshal = function(type, data_or_generator) { var out = []; var get = (typeof data_or_generator == "function") ? data_or_generator : function() { return data_or_generator.shift(); }; for (var i=0; i < type.length; i++) { switch (type[i]) { case "w": var val = get(); val += get() << 8; val += get() << 16; var tmp = get() << 24; if (tmp < 0) tmp += 0x100000000; out.push(val + tmp); break; case "h": var val = get(); out.push(val + (get() << 8)); break; case "b": out.push(get()); break; case "s": var len = get(); len += get() << 8; var str = ''; for (var j=0; j < len; j++) str += String.fromCharCode(get()); out.push(str); break; case "Q": var stat = { type: 0, version: 0, path: 0 }; stat.type = this.unmarshal(["b"], get)[0]; stat.version = this.unmarshal(["w"], get)[0]; stat.path = this.unmarshal(["w", "w"], get)[0]; // FIXME out.push(stat); break; default: abort("unmarshal: Unknown type=" + type[i]); } } return out; }; Net9p.prototype.get_header_size = function() { return this.proto.HEADER_SIZE; }; Net9p.prototype.get_body_size = function(id) { return this.proto.HEADER_SIZE; }; Net9p.prototype.unmarshal_header = function(data) { var header = this.unmarshal(["w", "b", "h"], data); return { size: header[0], id: header[1], tag: header[2] }; }; Net9p.prototype.send_reply = function(reply) { this.virtio.send_reply(reply); }; ================================================ FILE: js/JSONlocalStorage.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ function JSONlocalStorage(name, target, list) { this.name = name; this.target = target; this.list = list; } JSONlocalStorage.prototype.restore = function() { var json = localStorage[this.name]; if (!json) return; var data = JSON.parse(json); for (var i in this.list) { var name = this.list[i]; var val = data[name]; if (val || val === false) { //console.debug("localStorage[" + opt + "] => " + val); this.target[name] = val; } } }; JSONlocalStorage.prototype.save = function() { var data = Object(); //localStorage.clear(); for (var i in this.list) { var name = this.list[i]; var val = this.target[name]; if (val || val === false) { //console.debug("localStorage[" + opt + "] <= " + val); data[name] = val; } } localStorage[this.name] = JSON.stringify(data); }; ================================================ FILE: js/armv7-cp15.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ /* * CP15 System Control Coprocessor */ function ARMv7_CP15(options, cpu) { this.options = options; this.cpu = cpu; this.interrupt_vector_address = 0; this.read = new Object(); this.write = new Object(); this.data = new Object(); this.TRANS_FAULT_SECTION = 5; // 0b00101 this.TRANS_FAULT_PAGE = 7; // 0b00111 this.PERMISSION_FAULT_SECTION = 0xd; // 0b01101 this.PERMISSION_FAULT_PAGE = 0xf; // 0b01111 // crn, opc1, crm, opc2 this.MIDR = [0, 0, 0, 0]; this.CTR = [0, 0, 0, 1]; this.ID_PFR0 = [0, 0, 1, 0]; this.ID_MMFR0 = [0, 0, 1, 4]; this.ID_MMFR1 = [0, 0, 1, 5]; this.ID_ISAR0 = [0, 0, 2, 0]; this.CCSIDR = [0, 1, 0, 0]; this.CLIDR = [0, 1, 0, 1]; this.CSSELR = [0, 2, 0, 0]; this.SCTLR = [1, 0, 0, 0]; this.CPACR = [1, 0, 0, 2]; this.TTBR0 = [2, 0, 0, 0]; this.TTBR1 = [2, 0, 0, 1]; this.TTBCR = [2, 0, 0, 2]; this.ICIALLU = [7, 0, 5, 0]; this.ICIMVAU = [7, 0, 5, 1]; this.BPIALL = [7, 0, 5, 6]; this.BPIMVA = [7, 0, 5, 7]; this.ISB = [7, 0, 5, 4]; this.DCCMVAC = [7, 0,10, 1]; this.DSB = [7, 0,10, 4]; this.DMB = [7, 0,10, 5]; this.DCCMVAU = [7, 0,11, 1]; this.DCCIMVAC = [7, 0,14, 1]; this.DACR = [3, 0, 0, 0]; this.DFSR = [5, 0, 0, 0]; this.IFSR = [5, 0, 0, 1]; // Instruction Fault Status Register (IFSR) this.DFAR = [6, 0, 0, 0]; // Data Fault Address Register (DFAR) this.IFAR = [6, 0, 0, 2]; // Instruction Fault Address Register (IFAR) this.ITLBIALL = [8, 0, 5, 0]; this.ITLBIMVA = [8, 0, 5, 1]; this.ITLBIASID = [8, 0, 5, 2]; this.DTLBIALL = [8, 0, 6, 0]; this.DTLBIMVA = [8, 0, 6, 1]; this.DTLBIASID = [8, 0, 6, 2]; this.UTLBIALL = [8, 0, 7, 0]; this.UTLBIMVA = [8, 0, 7, 1]; this.UTLBIASID = [8, 0, 7, 2]; this.PRRR = [10, 0, 2, 0]; // c10, Primary Region Remap Register (PRRR) this.NMRR = [10, 0, 2, 1]; // c10, Normal Memory Remap Register (NMRR) this.CONTEXTIDR = [13, 0, 0, 1]; this.TPIDRURW = [13, 0, 0, 2]; // User Read/Write Thread ID Register, TPIDRURW this.TPIDRURO = [13, 0, 0, 3]; // User Read-only Thread ID Register, TPIDRURO var mmu = this.cpu.mmu; var that = this; // [31:24]=Implementor [23:20]=Variant [19:16]=Architecture [15:4]=Primary part number [3:0]=Revision // MRC p15,0,,c0,c0,0 ; Read CP15 Main ID Register var midr = 0; midr = bitops.set_bits(midr, 31, 24, 0x41); // ARM Limited midr = bitops.set_bits(midr, 23, 20, 0x1); // Major Revison Number midr = bitops.set_bits(midr, 19, 16, 0xf); // Defined by CPUID scheme // [15:12] != 0x0 and != 0x7 midr = bitops.set_bits(midr, 15, 12, 0xf); midr = bitops.set_bits(midr, 3, 0, 0x1); // Minor Revison Number this.register_readonly("MIDR", midr); /* * CTR: Cache Type Register * Bits [31:29]: Set to 0b100 for the ARMv7 register format. * CWG, bits [27:24]: Cache Writeback Granule * ERG, bits [23:20]: Exclusives Reservation Granule // Manual is wrong :-) * DminLine, bits [19:16]: Log2 of the number of words in the smallest cache line of all the data caches and unified caches that are controlled by the core. * L1Ip, bits [15:14]: Level 1 instruction cache policy. * IminLine, bits [3:0]: Log2 of the number of words in the smallest cache line of all the instruction caches that are controlled by the core. */ var ctr = 0; ctr = bitops.set_bits(ctr, 31, 29, 4); ctr = bitops.set_bits(ctr, 27, 24, 0); ctr = bitops.set_bits(ctr, 23, 20, 0); ctr = bitops.set_bits(ctr, 19, 16, 0); // FIXME ctr = bitops.set_bits(ctr, 15, 14, 1); // ASID-tagged Virtual Index, Virtual Tag (AIVIVT) ctr = bitops.set_bits(ctr, 3, 0, 0); // FIXME this.register_readonly("CTR", ctr); // TODO var sctlr = 0; sctlr = bitops.set_bit(sctlr, 31, 0); // UNK/SBZP sctlr = bitops.set_bit(sctlr, 30, 0); // TE: Thumb Exception enable sctlr = bitops.set_bit(sctlr, 29, 0); // AFE: Access Flag Enable bit sctlr = bitops.set_bit(sctlr, 28, 0); // TRE: TEX Remap Enable bit sctlr = bitops.set_bit(sctlr, 27, 0); // NMFI: Non-maskable Fast Interrupts enable sctlr = bitops.set_bit(sctlr, 25, 0); // EE: Exception Endianness bit sctlr = bitops.set_bit(sctlr, 24, 0); // VE: Interrupt Vectors Enable bit sctlr = bitops.set_bit(sctlr, 22, 1); // U: In ARMv7 this bit is RAO/SBOP sctlr = bitops.set_bit(sctlr, 21, 0); // FI: Fast Interrupts configuration enable bit sctlr = bitops.set_bit(sctlr, 17, 0); // HA: Hardware Access Flag Enable bit sctlr = bitops.set_bit(sctlr, 14, 0); // RR: Round Robin bit sctlr = bitops.set_bit(sctlr, 13, 0); // V: Vectors bit sctlr = bitops.set_bit(sctlr, 12, 0); // I: Instruction cache enable bit sctlr = bitops.set_bit(sctlr, 11, 0); // Z: Branch prediction enable bit sctlr = bitops.set_bit(sctlr, 7, 0); // B: In ARMv7 this bit is RAZ/SBZP sctlr = bitops.set_bit(sctlr, 2, 0); // C: Cache enable bit sctlr = bitops.set_bit(sctlr, 1, 0); // A: Alignment bit sctlr = bitops.set_bit(sctlr, 0, 0); // M: MMU enable bit this.register_writable("SCTLR", sctlr, function (word) { that.data["SCTLR"] = word; if (word & 1) { mmu.enabled = true; } else { mmu.enabled = false; } if (!(word & 0x01000000)) { // SCTLR.VE[24] if (word & 0x00002000) { // SCTLR.V[13] that.interrupt_vector_address = 0xffff0000; } else { that.interrupt_vector_address = 0x00000000; } } if (word & 2) {// SCTLR.A[1] mmu.check_unaligned = true; throw "Check unaligned access!"; } else { mmu.check_unaligned = false; } // Always 1 that.data["SCTLR"] = bitops.set_bit(that.data["SCTLR"], 22, 1); }); var id_pfr0 = 0; id_pfr0 = bitops.set_bits(id_pfr0, 3, 0, 1); // ARM instruction set supported this.register_readonly("ID_PFR0", id_pfr0); var id_mmfr0 = 0; id_mmfr0 = bitops.set_bits(id_mmfr0, 31, 28, 0); // Reserved, Read-As-Zero id_mmfr0 = bitops.set_bits(id_mmfr0, 37, 24, 0); // FCSE support id_mmfr0 = bitops.set_bits(id_mmfr0, 23, 20, 0); // Auxiliary registers // ARMv7 requires this setting. id_mmfr0 = bitops.set_bits(id_mmfr0, 19, 16, 1); // TCM support id_mmfr0 = bitops.set_bits(id_mmfr0, 15, 12, 0); // Outer Shareable id_mmfr0 = bitops.set_bits(id_mmfr0, 11, 8, 0); // Cache coherence id_mmfr0 = bitops.set_bits(id_mmfr0, 7, 4, 0); // PMSA support // VMSAv7 supported, with support for remapping and the access flag. ARMv7-A profile. id_mmfr0 = bitops.set_bits(id_mmfr0, 3, 0, 3); // VMSA support this.register_readonly("ID_MMFR0", id_mmfr0); var id_isar0 = 0; this.register_readonly("ID_ISAR0", id_mmfr0); var id_mmfr1 = 0; // For execution correctness, Branch Predictor requires no flushing at any time. id_mmfr1 = bitops.set_bits(id_mmfr1, 31, 28, 4); // Branch Predictor // None supported. This is the required setting for ARMv7. id_mmfr1 = bitops.set_bits(id_mmfr1, 37, 24, 0); // L1 cache Test and Clean // None supported. This is the required setting for ARMv7, because ARMv7 requires a hierarchical cache implementation. id_mmfr1 = bitops.set_bits(id_mmfr1, 23, 20, 0); // L1 unified cache id_mmfr1 = bitops.set_bits(id_mmfr1, 19, 16, 0); // L1 Harvard cache id_mmfr1 = bitops.set_bits(id_mmfr1, 15, 12, 0); // L1 unified cache s/w id_mmfr1 = bitops.set_bits(id_mmfr1, 11, 8, 0); // L1 Harvard cache s/w id_mmfr1 = bitops.set_bits(id_mmfr1, 7, 4, 0); // L1 unified cache VA id_mmfr1 = bitops.set_bits(id_mmfr1, 3, 0, 0); // L1 Harvard cache VA this.register_readonly("ID_MMFR1", id_mmfr1); // c0, Cache Size ID Registers (CCSIDR) /* * WT, bit [31]: Indicates whether the cache level supports Write-Through * WB, bit [30]: Indicates whether the cache level supports Write-Back * RA, bit [29]: Indicates whether the cache level supports Read-Allocation * WA, bit [28]: Indicates whether the cache level supports Write-Allocation * NumSets, bits [27:13]: Number of sets in cache * Associativity, bits [12:3]: Associativity of cache * LineSize, bits [2:0]: Log2(Number of words in cache line) */ var ccsidr = 0; ccsidr = bitops.set_bit(ccsidr, 31, 1); ccsidr = bitops.set_bit(ccsidr, 30, 1); ccsidr = bitops.set_bit(ccsidr, 29, 1); ccsidr = bitops.set_bit(ccsidr, 28, 1); ccsidr = bitops.set_bits(ccsidr, 27, 13, 0); // One set ccsidr = bitops.set_bits(ccsidr, 12, 3, 1); // Two set associative ccsidr = bitops.set_bits(ccsidr, 2, 0, 0); // 4 words length this.register_readonly("CCSIDR", ccsidr); // Cache Level ID Register var clidr = 0; clidr = bitops.set_bits(clidr, 29, 27, 0); // LoU: Level of Unification for the cache hierarchy clidr = bitops.set_bits(clidr, 26, 24, 0); // LoC: Level of Coherency for the cache hierarchy clidr = bitops.set_bits(clidr, 23, 21, 0); // Ctype8 clidr = bitops.set_bits(clidr, 20, 18, 0); // Ctype7 clidr = bitops.set_bits(clidr, 17, 15, 0); // Ctype6 clidr = bitops.set_bits(clidr, 14, 12, 0); // Ctype5 clidr = bitops.set_bits(clidr, 11, 9, 0); // Ctype4 clidr = bitops.set_bits(clidr, 8, 6, 0); // Ctype3 clidr = bitops.set_bits(clidr, 5, 3, 0); // Ctype2 clidr = bitops.set_bits(clidr, 2, 0, 0); // Ctype1 this.register_readonly("CLIDR", clidr); // CSSELR, Cache Size Selection Register var csselr = 0; csselr = bitops.set_bits(csselr, 3, 1, 0); // Level: Cache level of required cache csselr = bitops.set_bit(csselr, 0, 0); // InD: Instruction not Data bit this.register_writable("CSSELR", csselr); /* * Translation Table Base Register 0 (TTBR0) * bits[31:14-N]: Translation table base 0 address * bit[5]: NOS: Not Outer Shareable bit * bits[4:3]: RGN: Region bits * bit[2]: IMP: Implementation defined bit * bit[1]: S: Shareable bit * bit[0]: C: Cacheable bit */ this.register_writable("TTBR0", 0, function (word) { display.log("TTBR0"); that.data["TTBR0"] = word; mmu.baseaddr0 = bitops.clear_bits(word, 13 - mmu.width, 0); if (that.options.enable_logger) { that.log_value(word, "ttbr0"); that.log_value(mmu.baseaddr0, "baseaddr0 in ttbr0"); } }); /* * Translation Table Base Register 1 (TTBR1) * bits[31:14]: Translation table base 1 address * bit[5]: NOS: Not Outer Shareable bit * bits[4:3]: RGN: Region bits * bit[2]: IMP: Implementation defined bit * bit[1]: S: Shareable bit * bit[0]: C: Cacheable bit */ this.register_writable("TTBR1", 0, function (word) { display.log("TTBR1"); that.data["TTBR1"] = word; mmu.baseaddr1 = bitops.clear_bits(word, 13, 0); if (that.options.enable_logger) { that.log_value(word, "ttbr1"); that.log_value(mmu.baseaddr1, "baseaddr1 in ttbr1"); } }); /* * Translation Table Base Control Register (TTBCR) * bits[2:0]: N: Indicate the width of the base address held in TTBR0 */ this.register_writable("TTBCR", 0, function(word) { display.log("TTBCR"); that.data["TTBCR"] = word; var width = bitops.get_bits(word, 2, 0); var ttbr0 = that.data["TTBR0"]; mmu.width = width; mmu.mask = (1 << (31 - width - 20 + 1)) - 1; mmu.baseaddr0 = bitops.clear_bits(ttbr0, 13 - width, 0); that.log_value(word, "word"); that.log_value(mmu.baseaddr0, "baseaddr0 in ttbcr"); if (width) { throw "width > 0"; var ttbr1 = that.data["TTBR1"]; mmu.baseaddr1 = bitops.clear_bits(ttbr1, 13, 0); that.log_value(word, "word"); that.log_value(mmu.baseaddr1, "baseaddr1 in ttbcr"); } display.log("TTBCR called."); }); /* * CPACR, Coprocessor Access Control Register */ this.register_writable("CPACR", 0); /* * Domain Access Control Register (DACR) * bits[31:30]: D15 * ... * bits[1:0]: D0 * 00: No access. Any access to the domain generates a Domain fault. * 01: Client. Accesses are checked against the permission bits in the translation tables. * 10: Reserved, effect is UNPREDICTABLE * 11: Manager. Accesses are not checked against the permission bits in the translation tables. */ var dacr = 0; this.domains = new Array(); for (var i=0; i < 16; i++) this.domains[i] = 0; this.register_writable("DACR", dacr, function (word) { that.data["DACR"] = word; for (var i=0; i < 16; i++) { that.domains[i] = bitops.get_bits(word, i*2+1, i*2); } }); /* * Data Fault Status Register (DFSR) * bit[12]: ExT, External abort type * bit[11]: WnR, Write not Read * bits[10,3:0]: FS, Fault status * //bits[7:4]: Domain, The domain of the fault address */ // Note that these registers are actually read-only POV of software, // however, the values are changed by hardware during memory abort. // To be save/restore-able, register them as writable. this.register_writable("DFAR", 0); this.register_writable("IFAR", 0); this.register_writable("DFSR", 0); this.register_writable("IFSR", 0); this.register_writeonly("ICIALLU"); this.register_writeonly("ICIMVAU"); this.register_writeonly("BPIALL"); this.register_writeonly("BPIMVA"); this.register_writeonly("ISB"); // Instruction Synchronization Barrier this.register_writeonly("DCCMVAC"); // Clean data cache linux by MVA to PoU this.register_writeonly("DSB"); // Data Synchronization Barrier this.register_writeonly("DMB"); // Data Memory Barrier this.register_writeonly("DCCMVAU"); // Clean data cache line by MVA to PoU this.register_writeonly("DCCIMVAC"); // Clean and invalidate data cache line by MVA to PoU this.register_writeonly("ITLBIALL"); // invalidate instruction TLB this.register_writeonly("ITLBIMVA"); // invalidate instruction TLB entry by MVA this.register_writeonly("ITLBIASID"); // invalidate instruction TLB by ASID match this.register_writeonly("DTLBIALL"); // invalidate data TLB this.register_writeonly("DTLBIMVA"); // invalidate data TLB entry by MVA this.register_writeonly("DTLBIASID"); // invalidate data TLB by ASID match this.register_writeonly("UTLBIALL"); // invalidate unified TLB this.register_writeonly("UTLBIMVA"); // invalidate unified TLB entry by MVA this.register_writeonly("UTLBIASID"); this.register_writable("PRRR", 0); // TODO this.register_writable("NMRR", 0); // TODO // Context ID Register (CONTEXTIDR) this.register_writable("CONTEXTIDR", 0, function (word) { var procid = (word >>> 8) & 0x00ffffff; var asid = word & 0xff; var old_asid = that.data["CONTEXTIDR"] & 0xff; display.log("PROCID=" + procid + ", ASID=" + asid + ", ASID(old)=" + old_asid); mmu.asid = asid; that.data["CONTEXTIDR"] = word; }); // Software Thread ID registers this.register_writable("TPIDRURW", 0); this.register_writable("TPIDRURO", 0); } ARMv7_CP15.prototype.register_readonly = function(name, initval) { this.read[this[name]] = initval; }; ARMv7_CP15.prototype.register_writeonly = function(name, cb_w) { if (cb_w) { this.write[this[name]] = cb_w; } else { var that = this; this.write[this[name]] = function(word) { that.data[name] = word; }; } }; ARMv7_CP15.prototype.register_writable = function(name, initval, cb_w) { this.data[name] = initval; var that = this; if (cb_w) { this.read[this[name]] = function() { return that.data[name]; }; this.write[this[name]] = cb_w; } else { this.read[this[name]] = function() { return that.data[name]; }; this.write[this[name]] = function(word) { that.data[name] = word; }; } }; ARMv7_CP15.prototype.send_word = function(inst, word) { var opc1 = (inst >>> 21) & 0x7; var crn = (inst >>> 16) & 0xf; // the major register specifier var opc2 = (inst >>> 5) & 0x7; var crm = inst & 0xf; var func = this.write[[crn, opc1, crm, opc2]]; if (func) func(word); else throw "write: " + [crn, opc1, crm, opc2]; }; ARMv7_CP15.prototype.get_word = function(inst) { var opc1 = (inst >>> 21) & 0x7; var crn = (inst >>> 16) & 0xf; // the major register specifier var opc2 = (inst >>> 5) & 0x7; var crm = inst & 0xf; //this.dump_inst(inst); var ret = this.read[[crn, opc1, crm, opc2]]; if (typeof ret == "number") return ret; else if (ret !== undefined) return ret(); else throw "read: " + [crn, opc1, crm, opc2]; }; ARMv7_CP15.prototype.save = function() { var params = Object(); for (var i in this.data) { params[i] = this.data[i]; } return params; }; ARMv7_CP15.prototype.restore = function(params) { for (var i in this.data) { this.data[i] = params[i]; } if (bitops.get_bit(this.data["SCTLR"], 0)) this.cpu.mmu.enabled = true; else this.cpu.mmu.enabled = false; if (!bitops.get_bit(this.data["SCTLR"], 24)) { // SCTLR.VE if (bitops.get_bit(this.data["SCTLR"], 13)) { // SCTLR.V this.interrupt_vector_address = 0xffff0000; } else { this.interrupt_vector_address = 0x00000000; } } if (bitops.get_bit(this.data["SCTLR"], 1)) // SCTLR.A this.cpu.mmu.check_unaligned = true; else this.cpu.mmu.check_unaligned = false; for (var i=0; i < 16; i++) this.domains[i] = bitops.get_bits(this.data["DACR"], i*2+1, i*2); }; ARMv7_CP15.prototype.dump_reg = function(name) { var val = this.data[name]; display.log(name + ":\t" + toStringHex32(val) + " (" + toStringBin32(val) + ")"); }; ARMv7_CP15.prototype.sctlr_get_nmfi = function() { var sctlr = this.data["SCTLR"]; return (sctlr >>> 27) & 1; }; ARMv7_CP15.prototype.dump_sctlr = function() { var sctlr = this.data["SCTLR"]; var val; var msgs = new Array(); val = bitops.get_bit(sctlr, 29); msgs.push("AFE=" + (val ? "simple" : "full")); val = bitops.get_bit(sctlr, 28); msgs.push("TRE=" + (val ? "enabled" : "disabled")); val = bitops.get_bit(sctlr, 27); msgs.push("NMFI=" + (val ? "non-maskable" : "maskable")); val = bitops.get_bit(sctlr, 24); msgs.push("VE=" + val); val = bitops.get_bit(sctlr, 21); msgs.push("FI=" + (val ? "all" : "some")); val = bitops.get_bit(sctlr, 17); msgs.push("HA=" + (val ? "enabled" : "disabled")); val = bitops.get_bit(sctlr, 13); msgs.push("V=" + val); val = bitops.get_bit(sctlr, 1); msgs.push("A=" + (val ? "check unaligned" : "NOT check unaligned")); val = bitops.get_bit(sctlr, 0); msgs.push("MMU=" + (val ? "enabled" : "disabled")); display.log("SCTLR: " + msgs.join(", ")); }; ARMv7_CP15.prototype.dump = function() { this.dump_reg("CSSELR"); this.dump_reg("TTBCR"); this.dump_reg("SCTLR"); this.dump_reg("TTBR0"); this.dump_reg("TTBR1"); this.dump_reg("DACR"); this.dump_sctlr(); display.log("domains=" + this.domains.toString()); display.log("interrupt vector address=" + this.interrupt_vector_address.toString(16)); }; ARMv7_CP15.prototype.dump_inst = function(inst) { var opc1 = bitops.get_bits(inst, 23, 21); var crn = bitops.get_bits(inst, 19, 16); var opc2 = bitops.get_bits(inst, 7, 5); var crm = bitops.get_bits(inst, 3, 0); var msg = ""; msg += "crn=" + crn.toString(16) + "(" + crn.toString(2) + ")"; msg += ", "; msg += "opc1=" + opc1.toString(16) + "(" + opc1.toString(2) + ")"; msg += ", "; msg += "crm=" + crm.toString(16) + "(" + crm.toString(2) + ")"; msg += ", "; msg += "opc2=" + opc2.toString(16) + "(" + opc2.toString(2) + ")"; logger.log(msg); }; ARMv7_CP15.prototype.dump_value = function(value, name) { this.output_value(display, value, name); }; ARMv7_CP15.prototype.log_value = function(value, name) { if (!this.options.enable_logger) return; this.output_value(logger, value, name); }; ARMv7_CP15.prototype.output_value = function(target, value, name) { if (name) target.log(name + "=" + value.toString(10) + "\t" + toStringHex32(value) + "(" + toStringBin32(value) + ")"); else target.log("value=" + value.toString(10) + "\t" + toStringHex32(value) + "(" + toStringBin32(value) + ")"); }; ARMv7_CP15.prototype.set_memory_abort = function(vaddr, status, is_write) { this.data["DFAR"] = vaddr; this.data["IFAR"] = vaddr; // XXX var dfsr = is_write ? (1 << 11) : 0; // This bit is for hardware error, so we can ignore it. //dfsr = bitops.set_bit(dfsr, 10, is_write); dfsr = dfsr + status; this.data["DFSR"] = dfsr; this.data["IFSR"] = dfsr; // XXX }; ================================================ FILE: js/armv7-cpu.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ function ARMv7_CPU(options, memctlr) { this.options = options; this.memctlr = memctlr; this.USR_MODE = 0x10; this.FIQ_MODE = 0x11; this.IRQ_MODE = 0x12; this.SVC_MODE = 0x13; this.MON_MODE = 0x16; this.ABT_MODE = 0x17; this.UND_MODE = 0x1b; this.SYS_MODE = 0x1f; this.mode2string = new Array(); this.mode2string[this.USR_MODE] = "USR"; this.mode2string[this.FIQ_MODE] = "FIQ"; this.mode2string[this.IRQ_MODE] = "IRQ"; this.mode2string[this.SVC_MODE] = "SVC"; this.mode2string[this.MON_MODE] = "MON"; this.mode2string[this.ABT_MODE] = "ABT"; this.mode2string[this.UND_MODE] = "UND"; this.mode2string[this.SYS_MODE] = "SYS"; this.is_good_mode = new Object(); this.is_good_mode[this.USR_MODE] = true; this.is_good_mode[this.FIQ_MODE] = true; this.is_good_mode[this.IRQ_MODE] = true; this.is_good_mode[this.SVC_MODE] = true; this.is_good_mode[this.ABT_MODE] = true; this.is_good_mode[this.UND_MODE] = true; this.is_good_mode[this.SYS_MODE] = true; this.regs = new Array(); for (i = 0; i < 16; i++) this.regs[i] = 0; /* * regs[10]: SL: * regs[11]: FP: * regs[12]: IP: A general register * regs[13]: SP: Stack pointer * regs[14]: LR: Link register * regs[15]: PC: Program counter */ this.regs_usr = new Array(); for (i = 0; i < 16; i++) this.regs_usr[i] = 0; this.regs_svc = new Array(); this.regs_svc[13] = 0; this.regs_svc[14] = 0; this.regs_mon = new Array(); this.regs_mon[13] = 0; this.regs_mon[14] = 0; this.regs_abt = new Array(); this.regs_abt[13] = 0; this.regs_abt[14] = 0; this.regs_und = new Array(); this.regs_und[13] = 0; this.regs_und[14] = 0; this.regs_irq = new Array(); this.regs_irq[13] = 0; this.regs_irq[14] = 0; this.regs_fiq = new Array(); this.regs_fiq[8] = 0; this.regs_fiq[9] = 0; this.regs_fiq[10] = 0; this.regs_fiq[11] = 0; this.regs_fiq[12] = 0; this.regs_fiq[13] = 0; this.regs_fiq[14] = 0; // CPSR: Current program status register /* * bit[31]: N: Negative condition code flag (APSR) * bit[30]: Z: Zero condition code flag (APSR) * bit[29]: C: Carry condition code flag (APSR) * bit[28]: V: Overflow condition code flag (APSR) * bit[27]: Q: Cumulative saturation flag (APSR) * bits[26:25]: IT: If-Then execution state bits * bit[24]: J: Jazelle bit * bits[23:20]: Reserved * bits[19:16]: Greater than or Equal flags (APSR) * bit[9]: E: Endianness execution state bit * bit[8]: A: Asynchronous abort disable bit * bit[7]: I: Interrupt disable bit * bit[6]: F: Fast interrupt disable bit * bit[5]: T: Thumb execution state bit * bits[4:0]: M: Mode field */ this.cpsr = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; // SPSR: banked Saved Program Status Register this.spsr_svc = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; this.spsr_mon = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; this.spsr_abt = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; this.spsr_und = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; this.spsr_irq = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; this.spsr_fiq = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; this.mmu = new ARMv7_MMU(this, this.memctlr); this.coprocs = new Array(); for (i = 0; i < 16; i++) this.coprocs[i] = null; this.coprocs[15] = new ARMv7_CP15(options, this); this.mmu.cp15 = this.coprocs[15]; this.shift_t = 0; this.shift_n = 0; this.carry_out = 0; this.overflow = 0; this.SRType_LSL = 0; this.SRType_LSR = 1; this.SRType_ASR = 2; this.SRType_RRX = 3; this.SRType_ROR = 4; this.no_cond_insts = new Object(); this.no_cond_insts["cps"] = true; this.no_cond_insts["clrex"] = true; this.no_cond_insts["dsb"] = true; this.no_cond_insts["dmb"] = true; this.no_cond_insts["isb"] = true; this.allow_unaligned = new Array(); this.allow_unaligned["ldrh"] = true; this.allow_unaligned["ldrht"] = true; this.allow_unaligned["ldrsh_imm"] = true; this.allow_unaligned["ldrsh_reg"] = true; this.allow_unaligned["ldrsht"] = true; this.allow_unaligned["strh_imm"] = true; this.allow_unaligned["strh_reg"] = true; this.allow_unaligned["strht"] = true; this.allow_unaligned["tbh"] = true; this.allow_unaligned["ldr_imm"] = true; this.allow_unaligned["ldr_reg"] = true; this.allow_unaligned["ldr_lit"] = true; this.allow_unaligned["ldrt"] = true; this.allow_unaligned["str_imm"] = true; this.allow_unaligned["str_reg"] = true; this.allow_unaligned["strt"] = true; this.is_halted = false; this.current = ""; } ARMv7_CPU.prototype.save = function() { var params = Object(); params.regs = this.regs; params.regs_usr = this.regs_usr; params.regs_svc = this.regs_svc; params.regs_mon = this.regs_mon; params.regs_abt = this.regs_abt; params.regs_und = this.regs_und; params.regs_irq = this.regs_irq; params.regs_fiq = this.regs_fiq; params.spsr_svc = this.spsr_svc; params.spsr_mon = this.spsr_mon; params.spsr_abt = this.spsr_abt; params.spsr_und = this.spsr_und; params.spsr_irq = this.spsr_irq; params.spsr_fiq = this.spsr_fiq; params.cpsr = this.cpsr; params.spsr = this.spsr; params.is_halted = this.is_halted; return params; }; ARMv7_CPU.prototype.restore = function(params) { this.regs = params.regs; this.regs_usr = params.regs_usr; this.regs_svc = params.regs_svc; this.regs_mon = params.regs_mon; this.regs_abt = params.regs_abt; this.regs_und = params.regs_und; this.regs_irq = params.regs_irq; this.regs_fiq = params.regs_fiq; this.spsr_svc = params.spsr_svc; this.spsr_mon = params.spsr_mon; this.spsr_abt = params.spsr_abt; this.spsr_und = params.spsr_und; this.spsr_irq = params.spsr_irq; this.spsr_fiq = params.spsr_fiq; this.cpsr = params.cpsr; this.spsr = params.spsr; this.is_halted = params.is_halted; }; ARMv7_CPU.prototype.dump = function(params) { display.log("mode=" + this.mode2string[this.cpsr.m]); display.log("halted=" + this.is_halted); this.dump_regs(null); this.dump_banked_regs(); this.dump_cpsr(); this.dump_spsr(); }; ARMv7_CPU.prototype.dump_stack = function() { var sp = this.regs[13]; display.wipe(); display.log("Stack values:"); for (var i = 0; i < 50; i++) { var addr = sp + i*4; var val = this.ld_word(addr); display.log("\t" + toStringHex32(addr) + ":\t" + toStringHex32(val) + "(" + val.toString(10) + ")"); } }; ARMv7_CPU.prototype.get_pc = function() { return this.regs[15] + 8; }; ARMv7_CPU.prototype.reg = function(i) { if (i == 15) return this.get_pc(); else return this.regs[i]; }; ARMv7_CPU.prototype.dump_banked_regs = function() { this.output_banked_regs(display); }; ARMv7_CPU.prototype.output_banked_regs = function(target) { var indent = " "; var msg = "USR: "; var i; for (i = 0; i <= 7; i++) msg += "[ " + i.toString() + "]=" + toStringHex32(this.regs_usr[i]) + " "; target.log(msg); msg = " "; for (i = 8; i <= 9; i++) msg += "[ " + i.toString() + "]=" + toStringHex32(this.regs_usr[i]) + " "; for (i = 10; i <= 15; i++) msg += "[" + i.toString() + "]=" + toStringHex32(this.regs_usr[i]) + " "; target.log(msg); msg = "SVC: " + indent; for (i = 13; i <= 14; i++) msg += "[" + i.toString() + "]=" + toStringHex32(this.regs_svc[i]) + " "; target.log(msg); msg = "MON: " + indent; for (i = 13; i <= 14; i++) msg += "[" + i.toString() + "]=" + toStringHex32(this.regs_mon[i]) + " "; target.log(msg); msg = "ABT: " + indent; for (i = 13; i <= 14; i++) msg += "[" + i.toString() + "]=" + toStringHex32(this.regs_abt[i]) + " "; target.log(msg); msg = "UND: " + indent; for (i = 13; i <= 14; i++) msg += "[" + i.toString() + "]=" + toStringHex32(this.regs_und[i]) + " "; target.log(msg); msg = "IRQ: " + indent; for (i = 13; i <= 14; i++) msg += "[" + i.toString() + "]=" + toStringHex32(this.regs_irq[i]) + " "; target.log(msg); msg = "FIQ: "; for (i = 8; i <= 9; i++) msg += "[ " + i.toString() + "]=" + toStringHex32(this.regs_fiq[i]) + " "; for (i = 10; i <= 14; i++) msg += "[" + i.toString() + "]=" + toStringHex32(this.regs_fiq[i]) + " "; target.log(msg); }; ARMv7_CPU.prototype.log_cpsr = function() { if (!this.options.enable_logger) return; this.output_cpsr(logger); }; ARMv7_CPU.prototype.dump_cpsr = function() { this.output_cpsr(display); }; ARMv7_CPU.prototype.dump_spsr = function() { this.output_spsr(display); }; ARMv7_CPU.prototype.log_apsr = function() { if (!this.options.enable_logger) return; this.output_apsr(logger); }; ARMv7_CPU.prototype.dump_apsr = function() { this.output_apsr(display); }; ARMv7_CPU.prototype.output_apsr = function(target) { var msg = "APSR: "; msg += "N[" + this.cpsr.n + "] "; msg += "Z[" + this.cpsr.z + "] "; msg += "C[" + this.cpsr.c + "] "; msg += "V[" + this.cpsr.v + "] "; msg += "Q[" + this.cpsr.q + "] "; target.log(msg); }; ARMv7_CPU.prototype.output_psr = function(name, psr, target) { var msg = name + ": "; msg += "N[" + psr.n + "] "; msg += "Z[" + psr.z + "] "; msg += "C[" + psr.c + "] "; msg += "V[" + psr.v + "] "; msg += "Q[" + psr.q + "] "; msg += "A[" + psr.a + "] "; msg += "I[" + psr.i + "] "; msg += "F[" + psr.f + "] "; msg += "M[" + psr.m.toString(2) + "] "; target.log(msg); }; ARMv7_CPU.prototype.output_cpsr = function(target) { this.output_psr("CPSR", this.cpsr, target); }; ARMv7_CPU.prototype.output_spsr = function(target) { this.output_psr("SPSR_svc", this.spsr_svc, target); this.output_psr("SPSR_mon", this.spsr_mon, target); this.output_psr("SPSR_abt", this.spsr_abt, target); this.output_psr("SPSR_und", this.spsr_und, target); this.output_psr("SPSR_irq", this.spsr_irq, target); this.output_psr("SPSR_fiq", this.spsr_fiq, target); }; ARMv7_CPU.prototype.log_regs = function(oldregs) { if (!this.options.enable_logger) return; this.output_regs(logger, oldregs); }; ARMv7_CPU.prototype.dump_regs = function(oldregs) { this.output_regs(display, oldregs); }; ARMv7_CPU.prototype.output_regs = function(target, oldregs) { var i; var indent = " "; var msg = indent; if (oldregs === null) { for (i = 0; i < 8; i++) msg += "[ " + i.toString() + "]=" + toStringHex32(this.regs[i]) + " "; target.log(msg); msg = indent; for (i = 8; i < 16; i++) msg += "[" + (i < 10 ? " " : "") + i.toString() + "]=" + toStringHex32(this.regs[i]) + " "; target.log(msg); } else { var changed = false; for (i = 0; i < 8; i++) { if (this.regs[i] == oldregs[i]) // " [10]=60000093" msg += " "; else { msg += "[ " + i.toString() + "]=" + toStringHex32(this.regs[i]) + " "; changed = true; } } if (changed) target.log(msg); changed = false; msg = indent; for (i = 8; i < 15; i++) { // PC will change every execution, so don't show it if (this.regs[i] == oldregs[i]) { // " [10]=60000093" msg += " "; } else { msg += "[" + (i < 10 ? " " : "") + i.toString() + "]=" + toStringHex32(this.regs[i]) + " "; changed = true; } } if (changed) target.log(msg); } }; ARMv7_CPU.prototype.dump_value = function(value, name) { this.output_value(display, value, name); }; ARMv7_CPU.prototype.log_value = function(value, name) { if (!this.options.enable_logger) return; this.output_value(logger, value, name); }; ARMv7_CPU.prototype.output_value = function(target, value, name) { if (name) target.log(name + "=" + value.toString(10) + "\t" + toStringHex32(value) + "(" + toStringBin32(value) + ")"); else target.log("value=" + value.toString(10) + "\t" + toStringHex32(value) + "(" + toStringBin32(value) + ")"); }; ARMv7_CPU.prototype.is_bad_mode = function(mode) { switch (mode) { case this.SVC_MODE: case this.IRQ_MODE: case this.USR_MODE: case this.ABT_MODE: case this.FIQ_MODE: case this.UND_MODE: case this.SYS_MODE: return false; case this.MON_MODE: // !HaveSecurityExt() default: return true; } }; ARMv7_CPU.prototype.is_priviledged = function() { var mode = this.cpsr.m; if (mode == this.USR_MODE) return false; else return true; }; ARMv7_CPU.prototype.is_user_or_system = function() { var mode = this.cpsr.m; if (mode == this.USR_MODE || mode == this.SYS_MODE) return true; else return false; }; ARMv7_CPU.prototype.is_secure = function() { return false; }; ARMv7_CPU.prototype.scr_get_aw = function() { return 1; // the CPSR.A bit can be modified in any security state. }; ARMv7_CPU.prototype.scr_get_fw = function() { return 1; // the CPSR.F bit can be modified in any security state. }; ARMv7_CPU.prototype.nsacr_get_rfr = function() { return 0; // FIQ mode and the FIQ Banked registers are accessible in Secure and Non-secure security states. }; ARMv7_CPU.prototype.sctlr_get_nmfi = function() { return this.coprocs[15].sctlr_get_nmfi(); }; ARMv7_CPU.prototype.parse_psr = function(value) { var psr = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; psr.n = value >>> 31; psr.z = (value >>> 30) & 1; psr.c = (value >>> 29) & 1; psr.v = (value >>> 28) & 1; psr.q = (value >>> 27) & 1; psr.e = (value >>> 9) & 1; psr.a = (value >>> 8) & 1; psr.i = (value >>> 7) & 1; psr.f = (value >>> 6) & 1; psr.t = (value >>> 5) & 1; psr.m = value & 0x1f; return psr; }; ARMv7_CPU.prototype.psr_to_value = function(psr) { var value = psr.m; value += psr.t << 5; value += psr.f << 6; value += psr.i << 7; value += psr.a << 8; value += psr.e << 9; value += psr.q << 27; value += psr.v << 28; value += psr.c << 29; value += psr.z << 30; value += psr.n << 31; return value; }; ARMv7_CPU.prototype.clone_psr = function(src) { var dst = {n:0, z:0, c:0, v:0, q:0, e:0, a:0, i:0, f:0, t:0, m:0}; dst.n = src.n; dst.z = src.z; dst.c = src.c; dst.v = src.v; dst.q = src.q; dst.e = src.e; dst.a = src.a; dst.i = src.i; dst.f = src.f; dst.t = src.t; dst.m = src.m; return dst; }; ARMv7_CPU.prototype.set_current_spsr = function(spsr) { switch (this.cpsr.m) { case this.USR_MODE: throw "set_current_spsr user"; break; case this.FIQ_MODE: this.spsr_fiq = spsr; break; case this.IRQ_MODE: this.spsr_irq = spsr; break; case this.SVC_MODE: this.spsr_svc = spsr; break; case this.MON_MODE: this.spsr_mon = spsr; break; case this.ABT_MODE: this.spsr_abt = spsr; break; case this.UND_MODE: this.spsr_und = spsr; break; case this.SYS_MODE: throw "set_current_spsr system user"; break; default: throw "set_current_spsr unknown"; break; } }; ARMv7_CPU.prototype.get_current_spsr = function() { switch (this.cpsr.m) { case this.USR_MODE: throw "get_current_spsr user"; break; case this.FIQ_MODE: return this.spsr_fiq; case this.IRQ_MODE: return this.spsr_irq; case this.SVC_MODE: return this.spsr_svc; case this.MON_MODE: return this.spsr_mon; case this.ABT_MODE: return this.spsr_abt; case this.UND_MODE: return this.spsr_und; case this.SYS_MODE: throw "get_current_spsr system user"; break; default: throw "get_current_spsr unknown"; break; } return null; }; ARMv7_CPU.prototype.spsr_write_by_instr0 = function(spsr, psr, bytemask) { if (this.is_user_or_system()) this.abort_unpredictable("spsr_write_by_instr0"); if (bytemask & 8) { spsr.n = psr.n; spsr.z = psr.z; spsr.c = psr.c; spsr.v = psr.v; spsr.q = psr.q; } if (bytemask & 4) { spsr.ge = psr.ge; } if (bytemask & 2) { spsr.e = psr.e; spsr.a = psr.a; } if (bytemask & 1) { spsr.i = psr.i; spsr.f = psr.f; spsr.t = psr.t; if (!this.is_good_mode[psr.m]) this.abort_unpredictable("spsr_write_by_instr0", psr.m); else spsr.m = psr.m; } return spsr; }; ARMv7_CPU.prototype.spsr_write_by_instr = function(psr, bytemask) { var spsr = this.get_current_spsr(); this.spsr_write_by_instr0(spsr, psr, bytemask); this.set_current_spsr(spsr); // XXX }; ARMv7_CPU.prototype.cpsr_write_by_instr = function(psr, bytemask, affect_execstate) { var is_priviledged = this.is_priviledged(); var nmfi = this.sctlr_get_nmfi() == 1; if (this.options.enable_logger) { var oldregs = new Array(); this.store_regs(oldregs); this.log_cpsr(); } if (bytemask & 8) { this.cpsr.n = psr.n; this.cpsr.z = psr.z; this.cpsr.c = psr.c; this.cpsr.v = psr.v; this.cpsr.q = psr.q; } if (bytemask & 2) { this.cpsr.e = psr.e; if (is_priviledged && (this.is_secure() || this.scr_get_aw() == 1)) this.cpsr.a = psr.a; } if (bytemask & 1) { if (is_priviledged) { this.cpsr.i = psr.i; } if (is_priviledged && (this.is_secure() || this.scr_get_fw() == 1) && (!nmfi || psr.f === 0)) this.cpsr.f = psr.f; if (affect_execstate) this.cpsr.t = psr.t; if (is_priviledged) { if (!this.is_good_mode[psr.m]) this.abort_unpredictable("cpsr_write_by_instr", psr.m); else { if (!this.is_secure() && psr.m == this.MON_MODE) this.abort_unpredictable("cpsr_write_by_instr", psr.m); if (!this.is_secure() && psr.m == this.FIQ_MODE && this.nsacr_get_rfr() == 1) this.abort_unpredictable("cpsr_write_by_instr", psr.m); if (this.cpsr.m != psr.m) this.change_mode(psr.m); } } } if (this.options.enable_logger) { this.log_cpsr(); this.log_regs(oldregs); } }; ARMv7_CPU.prototype.save_to_regs = function(mode) { switch (mode) { case this.USR_MODE: this.regs_usr[13] = this.regs[13]; this.regs_usr[14] = this.regs[14]; break; case this.FIQ_MODE: this.regs_fiq[8] = this.regs[8]; this.regs_fiq[9] = this.regs[9]; this.regs_fiq[10] = this.regs[10]; this.regs_fiq[11] = this.regs[11]; this.regs_fiq[12] = this.regs[12]; this.regs_fiq[13] = this.regs[13]; this.regs_fiq[14] = this.regs[14]; break; case this.IRQ_MODE: this.regs_irq[13] = this.regs[13]; this.regs_irq[14] = this.regs[14]; break; case this.SVC_MODE: this.regs_svc[13] = this.regs[13]; this.regs_svc[14] = this.regs[14]; break; case this.MON_MODE: this.regs_mon[13] = this.regs[13]; this.regs_mon[14] = this.regs[14]; break; case this.ABT_MODE: this.regs_abt[13] = this.regs[13]; this.regs_abt[14] = this.regs[14]; break; case this.UND_MODE: this.regs_und[13] = this.regs[13]; this.regs_und[14] = this.regs[14]; break; case this.SYS_MODE: throw "save_to_regs system"; break; default: throw "save_to_regs unknown: " + mode.toString(16); break; } }; ARMv7_CPU.prototype.restore_from_regs = function(mode) { switch (mode) { case this.USR_MODE: this.regs[13] = this.regs_usr[13]; this.regs[14] = this.regs_usr[14]; break; case this.FIQ_MODE: this.regs[8] = this.regs_fiq[8]; this.regs[9] = this.regs_fiq[9]; this.regs[10] = this.regs_fiq[10]; this.regs[11] = this.regs_fiq[11]; this.regs[12] = this.regs_fiq[12]; this.regs[13] = this.regs_fiq[13]; this.regs[14] = this.regs_fiq[14]; break; case this.IRQ_MODE: this.regs[13] = this.regs_irq[13]; this.regs[14] = this.regs_irq[14]; break; case this.SVC_MODE: this.regs[13] = this.regs_svc[13]; this.regs[14] = this.regs_svc[14]; break; case this.MON_MODE: this.regs[13] = this.regs_mon[13]; this.regs[14] = this.regs_mon[14]; break; case this.ABT_MODE: this.regs[13] = this.regs_abt[13]; this.regs[14] = this.regs_abt[14]; break; case this.UND_MODE: this.regs[13] = this.regs_und[13]; this.regs[14] = this.regs_und[14]; break; case this.SYS_MODE: throw "restore_from_regs system"; break; default: throw "restore_from_regs unknown: " + mode.toString(16); break; } }; ARMv7_CPU.prototype.change_mode = function(mode) { if (!mode) throw "Invalid mode: " + mode; if (this.options.enable_logger) logger.log("changing mode from " + this.mode2string[this.cpsr.m] + " to " + this.mode2string[mode]); this.save_to_regs(this.cpsr.m); this.cpsr.m = mode; this.restore_from_regs(this.cpsr.m); }; ARMv7_CPU.prototype.set_apsr = function(val, set_overflow) { this.cpsr.n = val >>> 31; this.cpsr.z = (val === 0) ? 1 : 0; this.cpsr.c = this.carry_out; if (set_overflow) this.cpsr.v = this.overflow; if (this.options.enable_logger) this.log_apsr(); }; ARMv7_CPU.prototype.store_regs = function(regs) { for (var i = 0; i < 16; i++) regs[i] = this.regs[i]; }; /* * Coprocessors */ ARMv7_CPU.prototype.coproc_accepted = function(cp) { return cp == 15; // FIXME }; ARMv7_CPU.prototype.coproc_get_word = function(cp, inst) { return this.coprocs[cp].get_word(inst); }; ARMv7_CPU.prototype.coproc_send_word = function(cp, inst, word) { return this.coprocs[cp].send_word(inst, word); }; ARMv7_CPU.prototype.coproc_internal_operation = function(cp, inst) { this.log_value(cp, "cp"); throw "coproc"; return this.coprocs[cp].internal_operation(inst); }; /* * Alignment */ ARMv7_CPU.prototype.align = function(value, align) { assert((value & 3) === 0, "align"); return value; // FIXME }; ARMv7_CPU.prototype.unaligned_support = function() { return true; }; /* * Instruction printers */ ARMv7_CPU.prototype.abort_unknown_inst = function(inst, addr) { display.log("\nUnknown instruction: " + toStringInst(inst)); throw "UNKNOWN"; }; ARMv7_CPU.prototype.abort_simdvfp_inst = function(inst, addr) { display.log("\nSIMD or VFP instruction: " + toStringInst(inst)); throw "SIMD or VFP"; }; ARMv7_CPU.prototype.abort_not_impl = function(name, inst, addr) { display.log("\n--" + name + " not implemented: " + toStringInst(inst)); throw "NOT IMPLEMENTED: " + name; }; ARMv7_CPU.prototype.abort_undefined_instruction = function(category, inst, addr) { display.log("\nUndefined instruction in " + category + ": " + toStringInst(inst)); throw "UNDEFINED: " + category; }; ARMv7_CPU.prototype.abort_unpredictable = function(category, value) { display.log("\nUnpredictable in " + category + ": " + value.toString(16) + "(" + value.toString(2) + ")"); throw "UNPREDICTABLE: " + category; }; ARMv7_CPU.prototype.abort_unpredictable_instruction = function(category, inst, addr) { display.log("\nUnpredictable instruction in " + category + ": " + inst.toString(16) + "(" + inst.toString(2) + ")"); throw "UNPREDICTABLE: " + category; }; ARMv7_CPU.prototype.abort_decode_error = function(inst, addr) { display.log("\nDecode error: " + toStringInst(inst)); throw "Decode error"; }; ARMv7_CPU.prototype.print_inst = function(name, inst, addr) { if (!this.options.enable_logger) return; var msg = "\n@" + toStringHex32(addr) + ": "; if (name) { msg += toStringInst(inst) + ": " + name; } else { msg += toStringInst(inst); } logger.log(msg); }; ARMv7_CPU.prototype.toRegName = function(i) { switch (i) { case 15: return "pc"; case 14: return "lr"; case 13: return "sp"; case 12: return "ip"; case 11: return "fp"; case 10: return "sl"; default: return "r" + i.toString(); } }; ARMv7_CPU.prototype.print_inst_unimpl = function(addr, inst, name) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + "\t"; tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_uxtab = function(addr, inst, name, d, n, m, rotation) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + "\t"; var items = []; items.push(this.toRegName(d)); if (n) items.push(this.toRegName(n)); items.push(this.toRegName(m)); if (rotation) items.push(rotation.toString()); msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_ubfx = function(addr, inst, name, d, n, msbit, lsbit) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + "\t"; var items = []; items.push(this.toRegName(d)); if (n) items.push(this.toRegName(n)); items.push("#" + msbit.toString()); items.push("#" + lsbit.toString()); msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_mcrmrc = function(addr, inst, name, t, cp) { if (!this.options.enable_tracer) return; var opc1 = bitops.get_bits(inst, 23, 21); var crn = bitops.get_bits(inst, 19, 16); var opc2 = bitops.get_bits(inst, 7, 5); var crm = bitops.get_bits(inst, 3, 0); var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + "\t"; var items = []; items.push(cp.toString()); items.push(opc1.toString()); items.push(this.toRegName(t)); items.push("cr" + crn.toString()); items.push("cr" + crm.toString()); //if (opc2) items.push("{" + opc2.toString() + "}"); msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_svc = function(addr, inst, val) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; msg += "svc\t"; msg += "0x" + toStringHex32(val); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_mrs = function(addr, inst, d) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; msg += "mrs\t"; msg += this.toRegName(d) + ", CPSR"; tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_msr = function(addr, inst, n, imm) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; msg += "msr\t"; if (n) { msg += "CPSR_c, " + this.toRegName(n); } else if (imm) { var imm_str = "#" + imm.toString(); msg += "CPSR_c, " + imm_str; } tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_ldstm = function(addr, inst, name, wback, t, reglist) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + "\t"; var items = []; if (t !== null) items.push(this.toRegName(t) + (wback ? "!" : "")); var _items = []; for (var i in reglist) { _items.push(this.toRegName(reglist[i])); } items.push("{" + _items.join(", ") + "}"); msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_rsr = function(addr, inst, name, s, d, n, m, stype, sn) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + (s == 1 ? "s" : "") + "\t"; var items = []; if (d) items.push(this.toRegName(d)); if (n) items.push(this.toRegName(n)); if (m) items.push(this.toRegName(m)); items.push(this.shift_type_name(stype) + " " + this.toRegName(sn)); msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_mul = function(addr, inst, name, s, dhi, dlo, n, m) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + (s == 1 ? "s" : "") + "\t"; var items = []; if (dlo !== null) items.push(this.toRegName(dlo)); if (dhi !== null) items.push(this.toRegName(dhi)); if (n !== null) items.push(this.toRegName(n)); if (m !== null) items.push(this.toRegName(m)); msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_reg = function(addr, inst, name, s, d, n, m, stype, sn, ldst, wback) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + (s == 1 ? "s" : "") + "\t"; var items = []; if (d !== null) items.push(this.toRegName(d)); if (ldst) { var _items = []; if (n !== null) _items.push(this.toRegName(n)); if (m !== null) _items.push(this.toRegName(m)); if (sn) _items.push(this.shift_type_name(stype) + " #" + sn.toString()); items.push("[" + _items.join(", ") + "]" + (wback ? "!" : "")); } else { if (n !== null) items.push(this.toRegName(n)); if (m !== null) items.push(this.toRegName(m)); if (sn) items.push(this.shift_type_name(stype) + " #" + sn.toString()); } msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_imm = function(addr, inst, name, s, d, n, imm, ldst, wback, add, index) { if (!this.options.enable_tracer) return; var is_add = add == undefined ? true : add; var is_index = index == undefined ? true : index; var imm_str = "#" + (is_add ? imm : -imm).toString(); var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + (s == 1 ? "s" : "") + "\t"; var items = []; if (d !== null) items.push(this.toRegName(d)); if (ldst) { if (is_index) { var _items = []; _items.push(this.toRegName(n)); if (imm !== 0) _items.push(imm_str); items.push("[" + _items.join(", ") + "]" + (wback ? "!" : "")); } else { items.push("[" + this.toRegName(n) + "]"); items.push(imm_str); } } else { if (n !== null) items.push(this.toRegName(n)); items.push(imm_str); } msg += items.join(', '); tracer.log(msg, inst); }; ARMv7_CPU.prototype.print_inst_branch = function(addr, inst, name, branch_to, reg) { if (!this.options.enable_tracer) return; var msg = toStringHex32(addr) + ":\t"; msg += toStringHex32(inst) + "\t"; var pf = this.cond_postfix(inst); msg += name + pf + "\t"; if (reg) { msg += this.toRegName(reg); msg += "\t; " + toStringHex32(branch_to); } else { if (Symbols[branch_to]) msg += toStringHex32(branch_to) + " <" + Symbols[branch_to] + ">"; else msg += toStringHex32(branch_to); } tracer.log(msg, inst); }; ARMv7_CPU.prototype.sp_used = function(name, inst) { if (!this.options.enable_logger) return; logger.log("SP: " + name + ": " + toStringInst(inst)); }; ARMv7_CPU.prototype.push_used = function(name, list) { if (!this.options.enable_logger) return; logger.log("PUSH: " + name + ": " + toStringBin16(list)); }; ARMv7_CPU.prototype.pop_used = function(name, list) { if (!this.options.enable_logger) return; logger.log("POP: " + name + ": " + toStringBin16(list)); }; ARMv7_CPU.prototype.print_pc = function(newpc, oldpc) { if (!this.options.enable_logger) return; if (oldpc) logger.log("PC: " + newpc.toString(16) + " from " + oldpc.toString(16) + "(" + (newpc-oldpc).toString(16) + ")"); else logger.log("PC: " + newpc.toString(16)); }; ARMv7_CPU.prototype.call_supervisor = function() { throw "SUPERVISOR"; }; ARMv7_CPU.prototype.toStringSymbol = function(addr) { if (Symbols[addr]) return Symbols[addr] + "(" + addr.toString(16) + ")"; else return addr.toString(16); }; /* * Load/Store operations */ ARMv7_CPU.prototype.allow_unaligned_access = function() { if (!this.mmu.check_unaligned) return true; else return false; }; ARMv7_CPU.prototype.ld_word = function(addr) { if (addr == this.options.show_act_on_viraddr) display.log("@" + this.regs[15].toString(16) + ": " + this.toStringSymbol(addr) + ": read"); var phyaddr; if (addr & 3) { if (!this.allow_unaligned_access()) { throw "Unaligned ld_word: " + this.current + "@" + toStringHex32(addr); } else { var val = 0; var mmu = this.mmu; var memctlr = this.memctlr; for (var i=0; i < 4; i++) { phyaddr = mmu.trans_to_phyaddr(addr + i); val = bitops.set_bits(val, 8*i+7, 8*i, memctlr.ld_byte(phyaddr)); } return val; } } else { phyaddr = this.mmu.trans_to_phyaddr(addr); return this.memctlr.ld_word(phyaddr); } }; ARMv7_CPU.prototype.st_word = function(addr, word) { if (addr == this.options.show_act_on_viraddr) display.log("@" + this.regs[15].toString(16) + ": " + this.toStringSymbol(addr) + ": write " + toStringNum(word)); var phyaddr; if (addr & 3) { if (!this.allow_unaligned_access()) { throw "Unaligned st_word: " + this.current + "@" + toStringHex32(addr); } else { var mmu = this.mmu; var memctlr = this.memctlr; for (var i=0; i < 4; i++) { phyaddr = mmu.trans_to_phyaddr(addr + i); memctlr.st_byte(phyaddr, bitops.get_bits(word, 8*i+7, 8*i)); } } } else { phyaddr = this.mmu.trans_to_phyaddr(addr, true); this.memctlr.st_word(phyaddr, word); } }; ARMv7_CPU.prototype.ld_halfword = function(addr) { var phyaddr; if (addr & 1) { if (!this.allow_unaligned_access()) { throw "Unaligned ld_halfword: " + this.current + "@" + toStringHex32(addr); } else { var val = 0; var mmu = this.mmu; var memctlr = this.memctlr; for (var i=0; i < 2; i++) { phyaddr = mmu.trans_to_phyaddr(addr + i); val = bitops.set_bits(val, 8*i+7, 8*i, memctlr.ld_byte(phyaddr)); } return val; } } else { phyaddr = this.mmu.trans_to_phyaddr(addr); return this.memctlr.ld_halfword(phyaddr); } }; ARMv7_CPU.prototype.st_halfword = function(addr, hw) { var phyaddr; if (addr & 1) { if (!this.allow_unaligned_access()) { throw "Unaligned st_halfword: " + this.current + "@" + toStringHex32(addr); } else { var mmu = this.mmu; var memctlr = this.memctlr; for (var i=0; i < 2; i++) { phyaddr = mmu.trans_to_phyaddr(addr + i); memctlr.st_byte(phyaddr, bitops.get_bits(hw, 8*i+7, 8*i)); } } } else { phyaddr = this.mmu.trans_to_phyaddr(addr, true); this.memctlr.st_halfword(phyaddr, hw); } }; ARMv7_CPU.prototype.ld_byte = function(addr) { var phyaddr = this.mmu.trans_to_phyaddr(addr); return this.memctlr.ld_byte(phyaddr); }; ARMv7_CPU.prototype.st_byte = function(addr, b) { var phyaddr = this.mmu.trans_to_phyaddr(addr, true); this.memctlr.st_byte(phyaddr, b); }; ARMv7_CPU.prototype.fetch_instruction = function(addr) { var phyaddr = this.mmu.trans_to_phyaddr(addr); return this.memctlr.ld_word_fast(phyaddr); }; /* * Shift Operations */ ARMv7_CPU.prototype.shift_type_name = function(type) { switch (type) { case this.SRType_LSL: return "lsl"; case this.SRType_LSR: return "lsr"; case this.SRType_ASR: return "asr"; case this.SRType_RRX: return "rrx"; case this.SRType_ROR: return "ror"; default: return "unknown"; } }; ARMv7_CPU.prototype.shift = function(value, type, amount, carry_in) { return this.shift_c(value, type, amount, carry_in); }; ARMv7_CPU.prototype.decode_imm_shift = function(type, imm5) { /* * 0: LSL * 1: LSR * 2: ASR * 3: RRX or ROR (ARM encoding) * 3: RRX (In this emulator) * 4: ROR (In this emulator) */ switch (type) { case 0: this.shift_t = type; this.shift_n = imm5; break; case 1: case 2: this.shift_t = type; if (imm5 === 0) this.shift_n = 32; else this.shift_n = imm5; break; case 3: if (imm5 === 0) { this.shift_t = type; this.shift_n = 1; } else { this.shift_t = this.SRType_ROR; this.shift_n = imm5; } break; default: throw "decode_imm_shift"; break; } }; ARMv7_CPU.prototype.shift_c = function(value, type, amount, carry_in) { var res; var result; if (amount === 0) { this.carry_out = carry_in; return value; } else { switch (type) { // FIXME case 0: // LSL //assert(amount > 0, "lsl: amount > 0"); var val64 = new Number64(0, value); var extended = val64.lsl(amount); this.carry_out = extended.high & 1; return extended.low; case 1: // LSR //assert(amount > 0, "lsr: amount > 0"); this.carry_out = (amount == 32) ? 0 : ((value >>> (amount - 1)) & 1); result = bitops.lsr(value, amount); //assert(result >= 0, "lsr: result = " + result.toString()); return result; case 2: // ASR //assert(amount > 0, "asr: amount > 0"); this.carry_out = (amount == 32) ? 0 : ((value >>> (amount - 1)) & 1); result = bitops.asr(value, amount); return result; case 3: // RRX this.carry_out = value & 1; result = bitops.set_bit(value >>> 1, 31, carry_in); //assert(result >= 0, "rrx"); return result; case 4: // ROR return this.ror_c(value, amount, true); default: throw "shift_c"; return 0; } } }; ARMv7_CPU.prototype.ror_c = function(value, amount, write) { //assert(amount !== 0); var result = bitops.ror(value, amount); //assert(result >= 0, "ror"); if (write) this.carry_out = result >>> 31; return result; }; ARMv7_CPU.prototype.ror = function(val, rotation) { if (rotation === 0) return val; return this.ror_c(val, rotation, false); }; ARMv7_CPU.prototype.is_zero_bit = function(val) { if (val === 0) return 1; else return 0; }; ARMv7_CPU.prototype.expand_imm_c = function(imm12, carry_in) { var unrotated_value = imm12 & 0xff; var amount = 2*(imm12 >>> 8); if (!amount) { this.carry_out = carry_in; return unrotated_value; } return this.ror_c(unrotated_value, amount, true); }; ARMv7_CPU.prototype.expand_imm = function(imm12) { return this.expand_imm_c(imm12, this.cpsr.c); }; ARMv7_CPU.prototype.add_with_carry = function(x, y, carry_in) { var unsigned_sum = x + y + carry_in; var signed_sum = (x|0) + (y|0) + carry_in; //var result = bitops.get_bits64(unsigned_sum, 31, 0); var result = unsigned_sum % 0x100000000; if (result < 0) result += 0x100000000; this.carry_out = (result == unsigned_sum) ? 0 : 1; this.overflow = ((result|0) == signed_sum) ? 0 : 1; return result; }; ARMv7_CPU.prototype.decode_reg_shift = function(type) { this.shift_t = type; return type; }; ARMv7_CPU.prototype.cond_postfix = function(inst) { var cond = bitops.get_bits(inst, 31, 28); switch (cond) { case 0: return "eq"; case 1: return "ne"; case 2: return "cs"; case 3: return "cc"; case 4: return "mi"; case 8: return "hi"; case 9: return "ls"; case 0xa: return "ge"; case 0xb: return "lt"; case 0xc: return "gt"; case 0xd: return "le"; default: return ""; } }; ARMv7_CPU.prototype.is_valid = function(inst) { return (inst != 0xe1a00000 && inst !== 0); // NOP or NULL? }; ARMv7_CPU.prototype.cond = function(inst) { var cond = inst >>> 28; var ret = false; switch (cond >> 1) { case 0: ret = this.cpsr.z == 1; // EQ or NE break; case 1: ret = this.cpsr.c == 1; // CS or CC break; case 2: ret = this.cpsr.n == 1; // MI or PL break; case 3: ret = this.cpsr.v == 1; // VS or VC break; case 4: ret = this.cpsr.c == 1 && this.cpsr.z === 0; // HI or LS break; case 5: ret = this.cpsr.n == this.cpsr.v; // GE or LT break; case 6: ret = this.cpsr.n == this.cpsr.v && this.cpsr.z === 0; // GT or LE break; case 7: ret = true; // AL break; default: break; } if ((cond & 1) && cond !== 0xf) ret = !ret; return ret; }; /* * * Instruction Execution * */ /* * Immediate */ ARMv7_CPU.prototype.adc_imm = function(inst, addr) { this.print_inst("ADC (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var ret = this.add_with_carry(this.reg(n), imm32, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_imm(addr, inst, "adc", s, d, n, imm32); }; ARMv7_CPU.prototype.add_imm = function(inst, addr) { this.print_inst("ADD (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var ret = this.add_with_carry(this.reg(n), imm32, 0); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_imm(addr, inst, "add", s, d, n, imm32); }; ARMv7_CPU.prototype.adr_a1 = function(inst, addr) { this.print_inst("ADR A1", inst, addr); var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var ret = this.align(this.get_pc(), 4) + imm32; if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; } this.print_inst_imm(addr, inst, "adr", null, d, null, imm32); }; ARMv7_CPU.prototype.adr_a2 = function(inst, addr) { this.print_inst("ADR A2", inst, addr); var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var ret = this.align(this.get_pc(), 4) - imm32; if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; } this.print_inst_imm(addr, inst, "adr", null, d, null, imm32); }; ARMv7_CPU.prototype.and_imm = function(inst, addr) { this.print_inst("AND (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var valn = this.reg(n); var ret = bitops.and(valn, imm32); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "and", s, d, n, imm32); }; ARMv7_CPU.prototype.asr_imm = function(inst, addr) { this.print_inst("ASR (immediate)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var m = inst & 0xf; this.decode_imm_shift(2, imm5); var ret = this.shift_c(this.reg(m), this.SRType_ASR, this.shift_n, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "asr", s, d, m, imm5); }; ARMv7_CPU.prototype.bic_imm = function(inst, addr) { this.print_inst("BIC (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var valn = this.reg(n); var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var ret = bitops.and(valn, bitops.not(imm32)); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "bic", s, d, n, bitops.sint32(imm32)); }; ARMv7_CPU.prototype.b = function(inst, addr) { this.print_inst("B", inst, addr); var imm24 = inst & 0x00ffffff; //imm32 = SignExtend(imm24:'00', 32); //var imm32 = bitops.sign_extend(imm24 << 2, 26, 32); var imm26 = imm24 << 2; var imm32 = imm26; if (imm26 & 0x02000000) imm32 = imm26 | 0xfc000000; this.branch_to = this.get_pc() + imm32; if (this.branch_to >= 0x100000000) this.branch_to -= 0x100000000; this.print_inst_branch(addr, inst, "b", this.branch_to); }; ARMv7_CPU.prototype.bl_imm = function(inst, addr) { this.print_inst("BL, BLX (immediate)", inst, addr); //var imm24 = bitops.get_bits(inst, 23, 0); //var imm32 = bitops.sign_extend(imm24 << 2, 26, 32); var imm24 = inst & 0x00ffffff; var imm26 = imm24 << 2; var imm32 = imm26; if (imm26 & 0x02000000) imm32 = imm26 | 0xfc000000; this.regs[14] = this.get_pc() - 4; // BranchWritePC(Align(PC,4) + imm32); this.branch_to = this.align(bitops.lsl((this.get_pc()) >>> 2, 2), 4) + imm32; if (this.branch_to >= 0x100000000) this.branch_to -= 0x100000000; this.print_inst_branch(addr, inst, "bl", this.branch_to); }; ARMv7_CPU.prototype.cmn_imm = function(inst, addr) { this.print_inst("CMN (immediate)", inst, addr); var n = (inst >>> 16) & 0xf; var imm12 = inst & 0xfff; var valn = this.reg(n); var imm32 = this.expand_imm(imm12); var ret = this.add_with_carry(valn, imm32, 0); this.set_apsr(ret, true); this.print_inst_imm(addr, inst, "cmn", null, null, n, imm32); }; ARMv7_CPU.prototype.cmp_imm = function(inst, addr) { this.print_inst("CMP (immediate)", inst, addr); var n = (inst >>> 16) & 0xf; var imm12 = inst & 0xfff; var valn = this.reg(n); var imm32 = this.expand_imm(imm12); var ret = this.add_with_carry(valn, bitops.not(imm32), 1); this.set_apsr(ret, true); this.print_inst_imm(addr, inst, "cmp", null, null, n, imm32); }; ARMv7_CPU.prototype.eor_imm = function(inst, addr) { this.print_inst("EOR (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var valn = this.reg(n); var ret = bitops.xor(valn, imm32); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "eor", s, d, n, imm32); }; ARMv7_CPU.prototype.ldr_imm = function(inst, addr) { this.print_inst("LDR (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm12 = (inst & 0xfff); if (n == 13 && p === 0 && u == 1 && w === 0 && imm12 === 4) { // POP A2 if (t == 15) this.branch_to = this.ld_word(this.regs[13]); else this.regs[t] = this.ld_word(this.regs[13]); this.regs[13] = this.regs[13] + 4; this.print_inst_unimpl(addr, inst, "pop"); return; } var imm32 = imm12; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn= this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; var data = this.ld_word(address); if (is_wback) this.regs[n] = offset_addr; if (t == 15) this.branch_to = data; else this.regs[t] = data; this.print_inst_imm(addr, inst, "ldr", null, t, n, imm32, true, is_wback, is_add, is_index); }; ARMv7_CPU.prototype.ldrb_imm = function(inst, addr) { this.print_inst("LDRB (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm32 = inst & 0xfff; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn= this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; var data = this.ld_byte(address); this.regs[t] = data; if (is_wback) this.regs[n] = offset_addr; this.print_inst_imm(addr, inst, "ldrb", null, t, n, imm32, true, is_wback, is_add, is_index); }; ARMv7_CPU.prototype.ldrd_imm = function(inst, addr) { this.print_inst("LDRD (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm4h = (inst >>> 8) & 0xf; var imm4l = inst & 0xf; var t2 = t + 1; var imm32 = (imm4h << 4) + imm4l; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn= this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; this.regs[t] = this.ld_word(address); this.regs[t2] = this.ld_word(address+4); if (is_wback) this.regs[n] = offset_addr; this.print_inst_imm(addr, inst, "ldrd", null, t, n, imm32, true, is_wback, is_add, is_index); }; ARMv7_CPU.prototype.ldrsh_imm = function(inst, addr) { this.print_inst("LDRSH (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm4h = (inst >>> 8) & 0xf; var imm4l = inst & 0xf; var imm32 = (imm4h << 4) + imm4l; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn= this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; var data = this.ld_halfword(address); if (is_wback) this.regs[n] = offset_addr; this.regs[t] = bitops.sign_extend(data, 16, 32); this.print_inst_imm(addr, inst, "ldrsh", null, t, n, imm32, true, is_wback, is_add, is_index); }; ARMv7_CPU.prototype.ldrsh_reg = function(inst, addr) { this.print_inst("LDRSH (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn= this.reg(n); var offset = this.shift(this.reg(m), this.SRType_LSL, 0, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; var data = this.ld_halfword(address); if (is_wback) this.regs[n] = offset_addr; this.regs[t] = bitops.sign_extend(data, 16, 32); this.print_inst_reg(addr, inst, "ldrsh", null, t, n, m, this.SRType_LSL, 0); }; ARMv7_CPU.prototype.lsl_imm = function(inst, addr) { this.print_inst("LSL (immediate)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var m = inst & 0xf; var valm = this.reg(m); this.decode_imm_shift(0, imm5); var ret = this.shift_c(valm, this.SRType_LSL, this.shift_n, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "lsl", s, d, m, imm5); }; ARMv7_CPU.prototype.lsr_imm = function(inst, addr) { this.print_inst("LSR (immediate)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var m = inst & 0xf; var valm = this.reg(m); this.decode_imm_shift(1, imm5); var ret = this.shift_c(valm, this.SRType_LSR, this.shift_n, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "lsr", s, d, m, imm5); }; ARMv7_CPU.prototype.mov_imm_a1 = function(inst, addr) { this.print_inst("MOV (immediate)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var ret = imm32; if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "mov", s, d, null, imm32); }; ARMv7_CPU.prototype.mov_imm_a2 = function(inst, addr) { this.print_inst("MOV (immediate) A2", inst, addr); var imm4 = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = (imm4 << 12) + imm12; var ret = imm32; if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; } this.print_inst_imm(addr, inst, "movw", false, d, null, imm32); }; ARMv7_CPU.prototype.movt = function(inst, addr) { this.print_inst("MOVT", inst, addr); var imm4 = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm16 = (imm4 << 12) + imm12; this.regs[d] = bitops.set_bits(this.reg(d), 16, 31, imm16); //this.print_inst_imm(addr, inst, "movw", false, d, null, imm32); }; ARMv7_CPU.prototype.msr_imm_sys = function(inst, addr) { this.print_inst("MSR (immediate) (system level)", inst, addr); var r = inst & (1 << 22); var mask = (inst >>> 16) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); if (r) { // SPSRWriteByInstr(R[n], mask); this.spsr_write_by_instr(this.parse_psr(imm32), mask); } else { // CPSRWriteByInstr(R[n], mask, FALSE); this.cpsr_write_by_instr(this.parse_psr(imm32), mask, false); } this.print_inst_msr(addr, inst, null, imm32); }; ARMv7_CPU.prototype.mvn_imm = function(inst, addr) { this.print_inst("MVN (immediate)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var ret = bitops.not(imm32); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "mvn", s, d, null, imm32); }; ARMv7_CPU.prototype.orr_imm = function(inst, addr) { this.print_inst("ORR (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var valn = this.reg(n); var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var ret = bitops.or(valn, imm32); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "orr", s, d, n, imm32); }; ARMv7_CPU.prototype.hint_preload_data = function(address) { // FIXME this.log_value(address, "preload address"); }; ARMv7_CPU.prototype.pld_imm = function(inst, addr) { this.print_inst("PLD (immediate, literal)", inst, addr); var u = (inst >>> 23) & 1; var n = (inst >>> 16) & 0xf; var imm12 = inst & 0xfff; var valn = this.reg(n); var imm32 = imm12; var is_add = u == 1; var base = (n == 15) ? this.align(this.get_pc(), 4) : valn; var address = base + (is_add ? imm32 : -imm32); this.hint_preload_data(address); this.print_inst_imm(addr, inst, "pld", null, null, n, imm32, true, null, is_add, true); }; ARMv7_CPU.prototype.rsb_imm = function(inst, addr) { this.print_inst("RSB (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var valn = this.reg(n); var ret = this.add_with_carry(bitops.not(valn), imm32, 1); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_imm(addr, inst, "rsb", s, d, n, imm32); }; ARMv7_CPU.prototype.rsc_imm = function(inst, addr) { this.print_inst("RSC (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var valn = this.reg(n); var ret = this.add_with_carry(bitops.not(valn), imm32, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_imm(addr, inst, "rsc", s, d, n, imm32); }; ARMv7_CPU.prototype.ror_imm = function(inst, addr) { this.print_inst("ROR (immediate)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var m = inst & 0xf; var valm = this.reg(m); this.decode_imm_shift(3, imm5); var ret = this.shift_c(valm, this.SRType_ROR, this.shift_n, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_imm(addr, inst, "ror", s, d, m, imm5); }; ARMv7_CPU.prototype.rrx = function(inst, addr) { this.print_inst("RRX", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var m = inst & 0xf; var valm = this.reg(m); var ret = this.shift_c(valm, this.SRType_RRX, 1, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } //this.print_inst_imm(addr, inst, "rrx", s, d, m, null); this.print_inst_unimpl(addr, inst, "rrx"); }; ARMv7_CPU.prototype.sbc_imm = function(inst, addr) { this.print_inst("SBC (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var valn = this.reg(n); var ret = this.add_with_carry(valn, bitops.not(imm32), this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_imm(addr, inst, "sbc", s, d, n, imm32); }; ARMv7_CPU.prototype.str_imm = function(inst, addr) { this.print_inst("STR (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var address; if (n == 13 && p == 1 && u === 0 && w == 1 && imm12 == 4) { // PUSH A2 var sp = this.reg(13); address = sp - 4; this.st_word(address, this.reg(t)); this.regs[13] = sp - 4; return; } var imm32 = imm12; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn= this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); address = is_index ? offset_addr : valn; var valt = this.reg(t); this.st_word(address, valt); if (is_wback) this.regs[n] = offset_addr; this.print_inst_imm(addr, inst, "str", null, t, n, imm32, true, is_wback, is_add, is_index); }; ARMv7_CPU.prototype.strb_imm = function(inst, addr) { this.print_inst("STRB (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm32 = inst & 0xfff; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn= this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; this.st_byte(address, this.reg(t) & 0xff); if (is_wback) this.regs[n] = offset_addr; this.print_inst_imm(addr, inst, "strb", null, t, n, imm32, true, is_wback, is_add, is_index); }; ARMv7_CPU.prototype.sub_imm = function(inst, addr) { this.print_inst("SUB (immediate)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm12 = inst & 0xfff; var imm32 = this.expand_imm(imm12); var ret = this.add_with_carry(this.reg(n), bitops.not(imm32), 1); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_imm(addr, inst, "sub", s, d, n, imm32); }; ARMv7_CPU.prototype.teq_imm = function(inst, addr) { this.print_inst("TEQ (immediate)", inst, addr); var n = (inst >>> 16) & 0xf; var imm12 = inst & 0xfff; var valn = this.reg(n); var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var ret = bitops.xor(valn, imm32); this.set_apsr(ret, false); this.print_inst_imm(addr, inst, "teq", null, null, n, imm32); }; ARMv7_CPU.prototype.tst_imm = function(inst, addr) { this.print_inst("TST (immediate)", inst, addr); var n = (inst >>> 16) & 0xf; var imm12 = inst & 0xfff; var valn = this.reg(n); var imm32 = this.expand_imm_c(imm12, this.cpsr.c); var ret = bitops.and(valn, imm32); this.set_apsr(ret, false); this.print_inst_imm(addr, inst, "tst", null, null, n, imm32); }; /* * Literal */ ARMv7_CPU.prototype.ldr_lit = function(inst, addr) { this.print_inst("LDR (literal)", inst, addr); var u = inst & (1 << 23); var t = (inst >>> 12) & 0xf; var imm32 = inst & 0xfff; var base = this.align(this.get_pc(), 4); var address = base + (u ? imm32 : -imm32); var data = this.ld_word(address); if (t == 15) this.branch_to = data; else this.regs[t] = data; this.print_inst_imm(addr, inst, "ldr", null, t, 15, imm32, true, null, u, true); }; /* * Register */ ARMv7_CPU.prototype.adc_reg = function(inst, addr) { this.print_inst("ADC (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = this.add_with_carry(valn, shifted, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_reg(addr, inst, "adc", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.add_reg = function(inst, addr) { this.print_inst("ADD (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = this.add_with_carry(valn, shifted, 0); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_reg(addr, inst, "add", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.and_reg = function(inst, addr) { this.print_inst("AND (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift_c(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = bitops.and(valn, shifted); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "and", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.asr_reg = function(inst, addr) { this.print_inst("ASR (register)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var shift_n = bitops.get_bits(this.reg(m), 7, 0); var ret = this.shift_c(this.reg(n), this.SRType_ASR, shift_n, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "asr", s, d, n, m); }; ARMv7_CPU.prototype.bic_reg = function(inst, addr) { this.print_inst("BIC (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift_c(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = bitops.and(valn, bitops.not(shifted)); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "bic", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.bfc = function(inst, addr) { this.print_inst("BFC", inst, addr); var msbit = (inst >>> 16) & 0x1f; var d = (inst >>> 12) & 0xf; var lsbit = (inst >>> 7) & 0x1f; if (msbit >= lsbit) this.regs[d] = bitops.clear_bits(this.regs[d], msbit, lsbit); else this.abort_unpredictable("BFC", inst, addr); this.print_inst_ubfx(addr, inst, "bfc", d, null, msbit, lsbit); }; ARMv7_CPU.prototype.bfi = function(inst, addr) { this.print_inst("BFI", inst, addr); var msbit = (inst >>> 16) & 0x1f; var d = (inst >>> 12) & 0xf; var lsbit = (inst >>> 7) & 0x1f; var n = inst & 0xf; if (msbit >= lsbit) this.regs[d] = bitops.set_bits(this.regs[d], msbit, lsbit, bitops.get_bits(this.reg(n), msbit-lsbit, 0)); else this.abort_unpredictable("BFI", inst, addr); this.print_inst_ubfx(addr, inst, "bfi", d, n, msbit, lsbit); }; ARMv7_CPU.prototype.blx_reg = function(inst, addr) { this.print_inst("BLX (register)", inst, addr); var m = inst & 0xf; var next_instr_addr = this.get_pc() - 4; this.regs[14] = next_instr_addr; this.branch_to = this.reg(m); //this.print_inst_reg(addr, inst, "blx", null, null, null, m); this.print_inst_branch(addr, inst, "blx", this.branch_to, m); }; ARMv7_CPU.prototype.bx = function(inst, addr) { this.print_inst("BX", inst, addr); var m = inst & 0xf; this.branch_to = this.reg(m); this.print_inst_branch(addr, inst, "bx", this.branch_to, m); }; ARMv7_CPU.prototype.cdp_a1 = function(inst, addr) { this.print_inst("CDP, CDP2 A1?", inst, addr); var t = (inst >>> 12) & 0xf; var cp = (inst >>> 8) & 0xf; if ((cp >> 1) == 5) { this.abort_simdvfp_inst(inst, addr); } if (!this.coproc_accepted(cp)) { throw "GenerateCoprocessorException(): " + cp; } else { this.coproc_internal_operation(cp, inst); } //this.print_inst_mcrmrc(inst, "cdp", t, cp); this.print_inst_unimpl(addr, inst, "cdp"); }; ARMv7_CPU.prototype.clz = function(inst, addr) { this.print_inst("CLZ", inst, addr); var d = (inst >>> 12) & 0xf; var m = inst & 0xf; this.regs[d] = bitops.count_leading_zero_bits(this.reg(m)); this.print_inst_reg(addr, inst, "clz", null, d, null, m); }; ARMv7_CPU.prototype.cmn_reg = function(inst, addr) { this.print_inst("CMN (register)", inst, addr); var n = (inst >>> 16) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = this.add_with_carry(valn, shifted, 0); this.set_apsr(ret, true); this.print_inst_reg(addr, inst, "cmn", null, null, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.cmp_reg = function(inst, addr) { this.print_inst("CMP (register)", inst, addr); var n = (inst >>> 16) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = this.add_with_carry(valn, bitops.not(shifted), 1); this.set_apsr(ret, true); this.print_inst_reg(addr, inst, "cmp", null, null, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.eor_reg = function(inst, addr) { this.print_inst("EOR (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift_c(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = bitops.xor(valn, shifted); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "eor", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.ldr_reg = function(inst, addr) { this.print_inst("LDR (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); this.decode_imm_shift(type, imm5); var offset = this.shift(this.reg(m), this.shift_t, this.shift_n, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; address = bitops.get_bits64(address, 31, 0); // XXX var data = this.ld_word(address); if (is_wback) this.regs[n] = offset_addr; if (t == 15) this.branch_to = data; else this.regs[t] = data; this.print_inst_reg(addr, inst, "ldr", null, t, n, m, this.shift_t, this.shift_n, true, is_wback); }; ARMv7_CPU.prototype.ldrb_reg = function(inst, addr) { this.print_inst("LDRB (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; this.decode_imm_shift(type, imm5); var valn = this.reg(n); var offset = this.shift(this.reg(m), this.shift_t, this.shift_n, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; var data = this.ld_byte(address); this.regs[t] = data; if (is_wback) this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "ldrb", null, t, n, m, this.shift_t, this.shift_n, true, is_wback, is_index); }; ARMv7_CPU.prototype.ldrd_reg = function(inst, addr) { this.print_inst("LDRD (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var m = inst & 0xf; var t2 = t + 1; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var valm = this.reg(m); var offset_addr = valn + (is_add ? valm : -valm); var address = is_index ? offset_addr : valn; this.regs[t] = this.ld_word(address); this.regs[t2] = this.ld_word(address + 4); if (is_wback) this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "ldrd", null, t, n, m, null, null, true, is_wback, is_index); }; ARMv7_CPU.prototype.ldrex = function(inst, addr) { this.print_inst("LDREX", inst, addr); var n = bitops.get_bits(inst, 19, 16); var t = bitops.get_bits(inst, 15, 12); var imm32 = 0; var address = this.reg(n) + imm32; // SetExclusiveMonitors(address,4); // R[t] = MemA[address,4]; this.regs[t] = this.ld_word(address); this.print_inst_reg(addr, inst, "ldrex", null, t, n, null, null, null, true, false); }; ARMv7_CPU.prototype.ldrexd = function(inst, addr) { this.print_inst("LDREXD", inst, addr); var n = bitops.get_bits(inst, 19, 16); var t = bitops.get_bits(inst, 15, 12); var t2 = t + 1; var address = this.reg(n); // SetExclusiveMonitors(address,8); // value = MemA[address,8]; // R[t] = value<31:0> // R[t2] = value<63:31> this.regs[t] = this.ld_word(address); this.regs[t2] = this.ld_word(address + 4); this.print_inst_reg(addr, inst, "ldrexd", null, t, n, null, null, null, true, false); }; ARMv7_CPU.prototype.ldrt_a1 = function(inst, addr) { this.print_inst("LDRT A1", inst, addr); var u = (inst >>> 23) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm32 = inst & 0xfff; var is_add = u == 1; var valn = this.reg(n); var offset = imm32; var offset_addr = valn + (is_add ? offset : -offset); var address = valn; address = bitops.get_bits64(address, 31, 0); // XXX var data = this.ld_word(address); if (t == 15) this.branch_to = data; else this.regs[t] = data; //this.print_inst_reg(addr, inst, "ldrt", null, t, n, m, this.shift_t, this.shift_n, true, is_wback); }; ARMv7_CPU.prototype.lsl_reg = function(inst, addr) { this.print_inst("LSL (register)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var shift_n = bitops.get_bits(this.reg(m), 7, 0); var ret = this.shift_c(this.reg(n), this.SRType_LSL, shift_n, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "lsl", s, d, n, m); }; ARMv7_CPU.prototype.lsr_reg = function(inst, addr) { this.print_inst("LSR (register)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var shift_n = bitops.get_bits(this.reg(m), 7, 0); var ret = this.shift_c(this.reg(n), this.SRType_LSR, shift_n, this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "lsr", s, d, n, m); }; ARMv7_CPU.prototype.mcr_a1 = function(inst, addr) { this.print_inst("MCR, MCR2 A1", inst, addr); var t = (inst >>> 12) & 0xf; var cp = (inst >>> 8) & 0xf; if ((cp >> 1) == 5) { this.abort_simdvfp_inst(inst, addr); } if (!this.coproc_accepted(cp)) { throw "GenerateCoprocessorException()"; } else { this.coproc_send_word(cp, inst, this.regs[t]); } this.print_inst_mcrmrc(addr, inst, "mcr", t, cp); }; ARMv7_CPU.prototype.mla = function(inst, addr) { this.print_inst("MLA", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 16) & 0xf; var a = (inst >>> 12) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var ope1 = this.reg(n); var ope2 = this.reg(m); var addend = this.reg(a); var n64_ope1 = new Number64(0, ope1); var n64_ope2 = new Number64(0, ope2); var n64_addend = new Number64(0, addend); var n64 = n64_ope1.mul(n64_ope2); var ret = n64.add(n64_addend); this.regs[d] = ret.low; if (s) { this.cpsr.n = (ret.low >>> 31) & 1; this.cpsr.z = (ret === 0) ? 1 : 0; this.log_apsr(); } this.print_inst_reg(addr, inst, "mla", s, d, n, m); // FIXME }; ARMv7_CPU.prototype.mls = function(inst, addr) { this.print_inst("MLS", inst, addr); var d = (inst >>> 16) & 0xf; var a = (inst >>> 12) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var ope1 = this.reg(n); var ope2 = this.reg(m); var addend = this.reg(a); var n64_ope1 = new Number64(0, ope1); var n64_ope2 = new Number64(0, ope2); var n64_addend = new Number64(0, addend); var n64 = n64_ope1.mul(n64_ope2); var ret = n64_addend.sub(n64); this.regs[d] = ret.low; this.print_inst_mul(addr, inst, "mls", null, n, d, m, a); }; ARMv7_CPU.prototype.subs_pc_lr_a2 = function(inst, addr) { var opcode = (inst >>> 21) & 0xf; var n = (inst >>> 16) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; this.decode_imm_shift(type, imm5); var operand2 = this.shift(this.reg(m), this.shift_t, this.shift_n, this.cpsr.c); var ret; switch (opcode) { case 0: ret = bitops.and(this.reg(n), operand2); break; case 1: ret = bitops.xor(this.reg(n), operand2); break; case 2: ret = this.add_with_carry(this.reg(n), bitops.not(operand2), 1); break; case 3: ret = this.add_with_carry(bitops.not(this.reg(n)), operand2, 1); break; case 4: ret = this.add_with_carry(this.reg(n), operand2, 0); break; case 5: ret = this.add_with_carry(this.reg(n), operand2, this.cpsr.c); break; case 6: ret = this.add_with_carry(this.reg(n), bitops.not(operand2), this.cpsr.c); break; case 7: ret = this.add_with_carry(bitops.not(this.reg(n)), operand2, this.cpsr.c); break; case 0xc: ret = bitops.or(this.reg(n), operand2); break; case 0xd: ret = operand2; break; case 0xe: ret = bitops.and(this.reg(n), bitops.not(operand2)); break; case 0xf: ret = bitops.not(operand2); break; default: throw "subs_pc_lr_a2: unknown opcode"; break; } this.cpsr_write_by_instr(this.get_current_spsr(), 15, true); this.branch_to = ret; this.print_inst_unimpl(addr, inst, "subs"); }; ARMv7_CPU.prototype.mov_reg = function(inst, addr) { var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var m = inst & 0xf; if (d == 15 && s) { this.print_inst("SUBS PC LR A2", inst, addr); this.subs_pc_lr_a2(inst, addr); return; } var ret = this.reg(m); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) { this.cpsr.n = ret >>> 31; this.cpsr.z = (ret === 0) ? 1 : 0; // FIXME: APSR.C = carry; // I guess carry == 0 //this.cpsr.c(bitops.get_bit(value, 29)); //this.abort_not_impl("MOV (register) flag", inst, addr); this.log_apsr(); } } this.print_inst_reg(addr, inst, "mov", s, d, null, m); }; ARMv7_CPU.prototype.mrc_a1 = function(inst, addr) { var t = (inst >>> 12) & 0xf; var cp = (inst >>> 8) & 0xf; if ((cp >> 1) == 5) { this.abort_simdvfp_inst(inst, addr); } if (!this.coproc_accepted(cp)) { throw "GenerateCoprocessorException()"; } else { var value = this.coproc_get_word(cp, inst); if (t != 15) { this.regs[t] = value; } else { this.cpsr.n = (value >>> 31) & 1; this.cpsr.z = (value >>> 30) & 1; this.cpsr.c = (value >>> 29) & 1; this.cpsr.v = (value >>> 28) & 1; this.log_apsr(); } } this.print_inst_mcrmrc(addr, inst, "mrc", t, cp); }; ARMv7_CPU.prototype.mrs = function(inst, addr) { this.print_inst("MRS", inst, addr); var read_spsr = inst & (1 << 22); var d = (inst >>> 12) & 0xf; if (read_spsr) { if (this.is_user_or_system()) this.abort_unpredictable("MRS", inst, addr); else this.regs[d] = this.psr_to_value(this.get_current_spsr()); } else { // CPSR AND '11111000 11111111 00000011 11011111' this.regs[d] = bitops.and(this.psr_to_value(this.cpsr), 0xf8ff03df); } this.print_inst_mrs(addr, inst, d); }; ARMv7_CPU.prototype.msr_reg_sys = function(inst, addr) { this.print_inst("MSR (register) (system level)", inst, addr); var r = inst & (1 << 22); var mask = (inst >>> 16) & 0xf; var n = inst & 0xf; if (r) { // SPSRWriteByInstr(R[n], mask); this.spsr_write_by_instr(this.parse_psr(this.reg(n)), mask); } else { // CPSRWriteByInstr(R[n], mask, FALSE); this.cpsr_write_by_instr(this.parse_psr(this.reg(n)), mask, false); } this.print_inst_msr(addr, inst, n); }; ARMv7_CPU.prototype.mul = function(inst, addr) { this.print_inst("MUL", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 16) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var ope1 = this.reg(n); var ope2 = this.reg(m); var n64_ope1 = new Number64(0, ope1); var n64_ope2 = new Number64(0, ope2); var ret = n64_ope1.mul(n64_ope2); this.regs[d] = ret.low; if (s) { //this.cpsr.n = bitops.get_bit(ret.low, 31); this.cpsr.n = ret.low >>> 31; this.cpsr.z = (ret === 0) ? 1 : 0; this.log_apsr(); } this.print_inst_reg(addr, inst, "mul", s, d, n, m); // FIXME }; ARMv7_CPU.prototype.mvn_reg = function(inst, addr) { this.print_inst("MVN (register)", inst, addr); var s = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift_c(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = bitops.not(shifted); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "mvn", s, d, null, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.orr_reg = function(inst, addr) { this.print_inst("ORR (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift_c(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = bitops.or(valn, shifted); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, false); } this.print_inst_reg(addr, inst, "orr", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.rev = function(inst, addr) { this.print_inst("REV", inst, addr); var d = bitops.get_bits(inst, 15, 12); var m = bitops.get_bits(inst, 3, 0); var valm = this.reg(m); var ret = 0; ret = bitops.set_bits(ret, 31, 24, bitops.get_bits(valm, 7, 0)); ret = bitops.set_bits(ret, 23, 16, bitops.get_bits(valm, 15, 8)); ret = bitops.set_bits(ret, 15, 8, bitops.get_bits(valm, 23, 16)); ret = bitops.set_bits(ret, 7, 0, bitops.get_bits(valm, 31, 24)); this.regs[d] = ret; this.print_inst_reg(addr, inst, "rev", null, d, null, m); }; ARMv7_CPU.prototype.rev16 = function(inst, addr) { this.print_inst("REV16", inst, addr); var d = bitops.get_bits(inst, 15, 12); var m = bitops.get_bits(inst, 3, 0); var valm = this.reg(m); var ret = 0; ret = bitops.set_bits(ret, 31, 24, bitops.get_bits(valm, 23, 16)); ret = bitops.set_bits(ret, 23, 16, bitops.get_bits(valm, 31, 24)); ret = bitops.set_bits(ret, 15, 8, bitops.get_bits(valm, 7, 0)); ret = bitops.set_bits(ret, 7, 0, bitops.get_bits(valm, 15, 8)); this.regs[d] = ret; this.print_inst_reg(addr, inst, "rev16", null, d, null, m); }; ARMv7_CPU.prototype.rsb_reg = function(inst, addr) { this.print_inst("RSB (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = this.add_with_carry(bitops.not(valn), shifted, 1); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_reg(addr, inst, "rsb", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.sbc_reg = function(inst, addr) { this.print_inst("SBC (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = this.add_with_carry(valn, bitops.not(shifted), this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_reg(addr, inst, "sbc", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.sbfx = function(inst, addr) { this.print_inst("SBFX", inst, addr); var widthminus1 = (inst >>> 16) & 0x1f; var d = (inst >>> 12) & 0xf; var lsbit = (inst >>> 7) & 0x1f; var n = inst & 0xf; var msbit = lsbit + widthminus1; if (msbit <= 31) this.regs[d] = bitops.sign_extend(bitops.get_bits(this.reg(n), msbit, lsbit), msbit-lsbit+1, 32); else this.abort_unpredictable("SBFX", inst, addr); this.print_inst_ubfx(addr, inst, "sbfx", d, n, lsbit, widthminus1 + 1); }; ARMv7_CPU.prototype.smlal = function(inst, addr) { this.print_inst("SMLAL", inst, addr); var s = inst & 0x00100000; var dhi = (inst >>> 16) & 0xf; var dlo = (inst >>> 12) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var n64_n = new Number64(0, this.reg(n)); var n64_m = new Number64(0, this.reg(m)); var n64 = new Number64(this.reg(dhi), this.reg(dlo)); var ret = n64_n.mul(n64_m).add(n64); this.regs[dhi] = ret.high; this.regs[dlo] = ret.low; if (s) { this.cpsr.n = bitops.get_bit(ret.high, 31); this.cpsr.z = ret.is_zero() ? 1 : 0; this.log_apsr(); } this.print_inst_mul(addr, inst, "smlal", s, dhi, dlo, n, m); }; ARMv7_CPU.prototype.smull = function(inst, addr) { this.print_inst("SMULL", inst, addr); var s = inst & 0x00100000; var dhi = (inst >>> 16) & 0xf; var dlo = (inst >>> 12) & 0xf; var m = (inst >>> 8) & 0xf; var n = inst & 0xf; var n64_n = new Number64(0, this.reg(n)); var n64_m = new Number64(0, this.reg(m)); var ret = n64_n.mul(n64_m); this.regs[dhi] = ret.high; this.regs[dlo] = ret.low; if (s) { this.cpsr.n = bitops.get_bit(ret.high, 31); this.cpsr.z = ret.is_zero() ? 1 : 0; this.log_apsr(); } this.print_inst_mul(addr, inst, "smull", s, dhi, dlo, n, m); }; ARMv7_CPU.prototype.swp = function(inst,addr){ this.print_inst("SWP(B?)", inst, addr); var B = (inst >> 22) & 0x1; var Rn = (inst >> 16) & 0xF; var Rd = (inst >> 12) & 0xF; var Rm = inst & 0xF; var valn = this.reg(Rn); var valm = this.reg(Rm); address = valn; if(B){ var data = this.ld_byte(address); this.st_byte(address, bitops.get_bits(valm, 7, 0)); this.regs[Rd] = data; } else { var data = this.ld_word(address); this.st_word(address, valm); this.regs[Rd] = data; } this.print_inst_reg(addr, inst, "swp"+B?"B":"", null, Rn, Rd, Rm, null, null, false, false); }; ARMv7_CPU.prototype.strex = function(inst, addr) { this.print_inst("STREX", inst, addr); var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var t = inst & 0xf; var imm32 = 0; var address = this.reg(n) + imm32; // ExclusiveMonitorsPass(address,4) this.st_word(address, this.reg(t)); this.regs[d] = 0; // FIXME this.print_inst_reg(addr, inst, "strex", null, t, n, d, null, null, true, false); }; ARMv7_CPU.prototype.strexd = function(inst, addr) { this.print_inst("STREXD", inst, addr); var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var t = inst & 0xf; var t2 = t + 1; var address = this.reg(n); // ExclusiveMonitorsPass(address,8) this.st_word(address, this.reg(t)); this.st_word(address + 4, this.reg(t2)); this.regs[d] = 0; // FIXME this.print_inst_reg(addr, inst, "strexd", null, t, n, d, null, null, true, false); }; ARMv7_CPU.prototype.sub_reg = function(inst, addr) { this.print_inst("SUB (register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = this.add_with_carry(valn, bitops.not(shifted), 1); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (s) this.set_apsr(ret, true); } this.print_inst_reg(addr, inst, "sub", s, d, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.sxtb = function(inst, addr) { this.print_inst("SXTB", inst, addr); var d = (inst >>> 12) & 0xf; var m = inst & 0xf; var rotation = ((inst >>> 10) & 3) << 3; var rotated = this.ror(this.reg(m), rotation); this.regs[d] = bitops.sign_extend(bitops.get_bits64(rotated, 7, 0), 8, 32); this.print_inst_reg(addr, inst, "sxtb", null, d, null, m); }; ARMv7_CPU.prototype.sxth = function(inst, addr) { this.print_inst("SXTH", inst, addr); var d = (inst >>> 12) & 0xf; var m = inst & 0xf; var rotation = ((inst >>> 10) & 3) << 3; var rotated = this.ror(this.reg(m), rotation); this.regs[d] = bitops.sign_extend(bitops.get_bits64(rotated, 15, 0), 16, 32); this.print_inst_reg(addr, inst, "sxth", null, d, null, m); }; ARMv7_CPU.prototype.sxtah = function(inst, addr) { this.print_inst("SXTAH", inst, addr); var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var m = inst & 0xf; var rotation = ((inst >>> 10) & 3) << 3; var rotated = this.ror(this.reg(m), rotation); var n64 = new Number64(0, this.reg(n)); this.regs[d] = n64.add(bitops.sign_extend(bitops.get_bits64(rotated, 15, 0), 16, 32)).low; this.print_inst_reg(addr, inst, "sxtah", null, d, null, m); }; ARMv7_CPU.prototype.teq_reg = function(inst, addr) { this.print_inst("TEQ (register)", inst, addr); var n = (inst >>> 16) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var valn = this.reg(n); var valm = this.reg(m); this.decode_imm_shift(type, imm5); var shifted = this.shift(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = bitops.xor(valn, shifted); this.set_apsr(ret, false); this.print_inst_reg(addr, inst, "teq", null, null, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.tst_reg = function(inst, addr) { this.print_inst("TST (register)", inst, addr); var n = (inst >>> 16) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; this.decode_imm_shift(type, imm5); var valn = this.reg(n); var valm = this.reg(m); var shifted = this.shift_c(valm, this.shift_t, this.shift_n, this.cpsr.c); var ret = bitops.and(valn, shifted); this.set_apsr(ret, false); this.print_inst_reg(addr, inst, "tst", null, null, n, m, this.shift_t, this.shift_n); }; ARMv7_CPU.prototype.ubfx = function(inst, addr) { this.print_inst("UBFX", inst, addr); var widthminus1 = bitops.get_bits(inst, 20, 16); var d = bitops.get_bits(inst, 15, 12); var lsbit = bitops.get_bits(inst, 11, 7); var n = bitops.get_bits(inst, 3, 0); var msbit = lsbit + widthminus1; if (msbit <= 31) this.regs[d] = bitops.get_bits(this.reg(n), msbit, lsbit); else this.abort_unpredictable("UBFX", inst, addr); this.print_inst_ubfx(addr, inst, "ubfx", d, n, lsbit, widthminus1 + 1); }; ARMv7_CPU.prototype.umlal = function(inst, addr) { this.print_inst("UMLAL", inst, addr); var s = inst & 0x00100000; var dhi = bitops.get_bits(inst, 19, 16); var dlo = bitops.get_bits(inst, 15, 12); var m = bitops.get_bits(inst, 11, 8); var n = bitops.get_bits(inst, 3, 0); var n64_n = new Number64(0, this.reg(n)); var n64_m = new Number64(0, this.reg(m)); var n64_d = new Number64(this.reg(dhi), this.reg(dlo)); var ret = n64_n.mul(n64_m).add(n64_d); this.regs[dhi] = ret.high; this.regs[dlo] = ret.low; if (s) { this.cpsr.n = bitops.get_bit(ret.high, 31); this.cpsr.z = ret.is_zero() ? 1 : 0; this.log_apsr(); } this.print_inst_mul(addr, inst, "umlal", s, dhi, dlo, n, m); }; ARMv7_CPU.prototype.umull = function(inst, addr) { this.print_inst("UMULL", inst, addr); var s = inst & 0x00100000; var dhi = bitops.get_bits(inst, 19, 16); var dlo = bitops.get_bits(inst, 15, 12); var m = bitops.get_bits(inst, 11, 8); var n = bitops.get_bits(inst, 3, 0); var n64_n = new Number64(0, this.reg(n)); var n64_m = new Number64(0, this.reg(m)); var ret = n64_n.mul(n64_m); this.regs[dhi] = ret.high; this.regs[dlo] = ret.low; if (s) { this.cpsr.n = bitops.get_bit(ret.high, 31); this.cpsr.z = ret.is_zero() ? 1 : 0; this.log_apsr(); } this.print_inst_mul(addr, inst, "umull", s, dhi, dlo, n, m); }; ARMv7_CPU.prototype.unsigned_satq = function(i, n) { var ret; if (i > (Math.pow(2, n) - 1)) { ret = Math.pow(2, n) - 1; this.saturated = true; } else if (i < 0) { ret = 0; this.saturated = true; } else { ret = i; this.saturated = false; } return bitops.get_bits64(ret, 31, 0); }; ARMv7_CPU.prototype.usat = function(inst, addr) { this.print_inst("USAT", inst, addr); var saturate_to = bitops.get_bits(inst, 20, 16); var d = bitops.get_bits(inst, 15, 12); var imm5 = bitops.get_bits(inst, 11, 7); var sh = bitops.get_bit(inst, 6); var n = bitops.get_bits(inst, 3, 0); this.decode_imm_shift(sh << 1, imm5); var operand = this.shift(this.reg(n), this.shift_t, this.shift_n, this.cpsr.c); var ret = this.unsigned_satq(this.sint32(operand), saturate_to); this.regs[n] = ret; if (this.saturated) this.cpsr.q = 1; this.print_inst_unimpl(addr, inst, "usat"); }; ARMv7_CPU.prototype.uxtab = function(inst, addr) { this.print_inst("UXTAB", inst, addr); var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var rotation = ((inst >>> 10) & 3) << 3; var m = inst & 0xf; var rotated = this.ror(this.reg(m), rotation); this.regs[d] = this.reg(n) + bitops.get_bits64(rotated, 7, 0); this.print_inst_uxtab(addr, inst, "uxtab", d, n, m, rotation); }; ARMv7_CPU.prototype.uxtah = function(inst, addr) { this.print_inst("UXTAH", inst, addr); var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var m = inst & 0xf; var rotation = ((inst >>> 10) & 3) << 3; var rotated = this.ror(this.reg(m), rotation); this.regs[d] = this.reg(n) + bitops.get_bits64(rotated, 15, 0); this.print_inst_uxtab(addr, inst, "uxtah", d, null, m, rotation); }; ARMv7_CPU.prototype.uxtb = function(inst, addr) { this.print_inst("UXTB", inst, addr); var d = (inst >>> 12) & 0xf; var m = inst & 0xf; var rotation = ((inst >>> 10) & 3) << 3; var rotated = this.ror(this.reg(m), rotation); this.regs[d] = bitops.get_bits64(rotated, 7, 0); this.print_inst_uxtab(addr, inst, "uxtb", d, null, m, rotation); }; ARMv7_CPU.prototype.uxth = function(inst, addr) { this.print_inst("UXTH", inst, addr); var d = (inst >>> 12) & 0xf; var m = inst & 0xf; var rotation = ((inst >>> 10) & 3) << 3; var rotated = this.ror(this.reg(m), rotation); this.regs[d] = bitops.get_bits64(rotated, 15, 0); this.print_inst_uxtab(addr, inst, "uxth", d, null, m, rotation); }; /* * Register-shifted Register */ ARMv7_CPU.prototype.add_rsr = function(inst, addr) { this.print_inst("ADD (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = this.add_with_carry(this.reg(n), shifted, 0); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (sf) this.set_apsr(ret, true); } this.print_inst_rsr(addr, inst, "add", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.and_rsr = function(inst, addr) { this.print_inst("AND (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift_c(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = bitops.and(this.reg(n), shifted); this.regs[d] = ret; if (sf) this.set_apsr(ret, false); this.print_inst_rsr(addr, inst, "and", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.bic_rsr = function(inst, addr) { this.print_inst("BIC (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift_c(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = bitops.and(this.reg(n), bitops.not(shifted)); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (sf) this.set_apsr(ret, false); } this.print_inst_rsr(addr, inst, "bic", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.cmp_rsr = function(inst, addr) { this.print_inst("CMP (register-shifted register)", inst, addr); var n = (inst >>> 16) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = this.add_with_carry(this.reg(n), bitops.not(shifted), 1); this.set_apsr(ret, true); this.print_inst_rsr(addr, inst, "cmp", null, null, n, m, shift_t, s); }; ARMv7_CPU.prototype.eor_rsr = function(inst, addr) { this.print_inst("EOR (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift_c(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = bitops.xor(this.reg(n), shifted); this.regs[d] = ret; if (sf) this.set_apsr(ret, false); this.print_inst_rsr(addr, inst, "eor", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.mvn_rsr = function(inst, addr) { this.print_inst("MVN (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift_c(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = bitops.not(shifted); this.regs[d] = ret; if (sf) this.set_apsr(ret, false); this.print_inst_rsr(addr, inst, "mvn", sf, d, null, m, shift_t, s); }; ARMv7_CPU.prototype.orr_rsr = function(inst, addr) { this.print_inst("ORR (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift_c(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = bitops.or(this.reg(n), shifted); this.regs[d] = ret; if (sf) this.set_apsr(ret, false); this.print_inst_rsr(addr, inst, "orr", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.rsb_rsr = function(inst, addr) { this.print_inst("RSB (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = this.add_with_carry(bitops.not(this.reg(n)), shifted, 1); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (sf) this.set_apsr(ret, true); } this.print_inst_rsr(addr, inst, "rsb", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.sbc_rsr = function(inst, addr) { this.print_inst("SBC (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = this.add_with_carry(this.reg(n), bitops.not(shifted), this.cpsr.c); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (sf) this.set_apsr(ret, true); } this.print_inst_rsr(addr, inst, "sbc", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.sub_rsr = function(inst, addr) { this.print_inst("SUB (register-shifted register)", inst, addr); var sf = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var d = (inst >>> 12) & 0xf; var s = (inst >>> 8) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = this.add_with_carry(this.reg(n), bitops.not(shifted), 1); if (d == 15) { this.branch_to = ret; } else { this.regs[d] = ret; if (sf) this.set_apsr(ret, true); } this.print_inst_rsr(addr, inst, "sub", sf, d, n, m, shift_t, s); }; ARMv7_CPU.prototype.tst_rsr = function(inst, addr) { this.print_inst("TST (register-shifted register)", inst, addr); var s = inst & 0x00100000; var n = (inst >>> 16) & 0xf; var type = (inst >>> 5) & 3; var m = inst & 0xf; var shift_t = this.decode_reg_shift(type); var shift_n = bitops.get_bits(this.reg(s), 7, 0); var shifted = this.shift_c(this.reg(m), shift_t, shift_n, this.cpsr.c); var ret = bitops.and(this.reg(n), shifted); this.set_apsr(ret, false); this.print_inst_rsr(addr, inst, "tst", null, null, n, m, shift_t, s); }; /* * Load Store */ ARMv7_CPU.prototype.ldrh_imm = function(inst, addr) { this.print_inst("LDRH (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm4h = (inst >>> 8) & 0xf; var imm4l = inst & 0xf; var imm32 = (imm4h << 4) + imm4l; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; // data = MemU[address,2]; var data = this.ld_halfword(address); if (is_wback) this.regs[n] = offset_addr; this.regs[t] = data; this.log_regs(null); this.print_inst_imm(addr, inst, "ldrh", null, t, n, imm32, true, is_wback, is_add); }; ARMv7_CPU.prototype.ldrh_reg = function(inst, addr) { this.print_inst("LDRH (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var offset = this.shift(this.reg(m), this.SRType_LSL, 0, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; // data = MemU[address,2]; var data = this.ld_halfword(address); if (is_wback) this.regs[n] = offset_addr; this.regs[t] = data; this.print_inst_reg(addr, inst, "ldrh", null, t, n, m, this.SRType_LSL, 0, true, is_wback, is_add); }; ARMv7_CPU.prototype.ldrsb_imm = function(inst, addr) { this.print_inst("LDRSB (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm4h = (inst >>> 8) & 0xf; var imm4l = inst & 0xf; var imm32 = (imm4h << 4) + imm4l; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; this.regs[t] = bitops.sign_extend(this.ld_byte(address), 8, 32); if (is_wback) this.regs[n] = offset_addr; //this.print_inst_reg(addr, inst, "ldrsb", null, t, n, m, null, null, true, is_wback, is_add); this.print_inst_unimpl(addr, inst, "ldrsb"); }; ARMv7_CPU.prototype.ldrsb_reg = function(inst, addr) { this.print_inst("LDRSB (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var offset = this.shift(this.reg(m), this.SRType_LSL, 0, this.cpsr.c); var valn = this.reg(n); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; this.regs[t] = bitops.sign_extend(this.ld_byte(address), 8, 32); if (is_wback) this.regs[n] = offset_addr; //this.print_inst_reg(addr, inst, "ldrsb", null, t, n, m, null, null, true, is_wback, is_add); this.print_inst_unimpl(addr, inst, "ldrsb"); }; ARMv7_CPU.prototype.str_reg = function(inst, addr) { this.print_inst("STR (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; this.decode_imm_shift(type, imm5); var valn = this.reg(n); var offset = this.shift(this.reg(m), this.shift_t, this.shift_n, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; address = bitops.get_bits64(address, 31, 0); // XXX var data = this.reg(t); this.st_word(address, data); if (is_wback) this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "str", null, t, n, m, this.shift_t, this.shift_n, true, is_wback); }; ARMv7_CPU.prototype.strbt_a1 = function(inst, addr) { this.print_inst("STRBT A1", inst, addr); var u = inst & (1 << 23); var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm32 = inst & 0xfff; var is_add = u == 1; var valn = this.reg(n); var offset = imm32; var offset_addr = valn + (is_add ? offset : -offset); this.st_byte(valn, bitops.get_bits(this.reg(t), 7, 0)); this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "strbt", null, t, n, m, this.shift_t, this.shift_n, true, true); }; ARMv7_CPU.prototype.strbt_a2 = function(inst, addr) { this.print_inst("STRBT A2", inst, addr); var u = (inst >>> 23) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var is_add = u == 1; this.decode_imm_shift(type, imm5); var valn = this.reg(n); var offset = this.shift(this.reg(m), this.shift_t, this.shift_n, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); this.st_byte(valn, bitops.get_bits(this.reg(t), 7, 0)); this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "strbt", null, t, n, m, this.shift_t, this.shift_n, true, true); }; ARMv7_CPU.prototype.strb_reg = function(inst, addr) { this.print_inst("STRB (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm5 = (inst >>> 7) & 0x1f; var type = (inst >>> 5) & 3; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; this.decode_imm_shift(type, imm5); var valn = this.reg(n); var offset = this.shift(this.reg(m), this.shift_t, this.shift_n, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; this.st_byte(address, bitops.get_bits(this.reg(t), 7, 0)); if (is_wback) this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "strb", null, t, n, m, this.shift_t, this.shift_n, true, is_wback); }; ARMv7_CPU.prototype.strd_reg = function(inst, addr) { this.print_inst("STRD (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var m = inst & 0xf; var t2 = t + 1; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var valm = this.reg(m); var offset_addr = valn + (is_add ? valm : -valm); var address = is_index ? offset_addr : valn; this.st_word(address, this.reg(t)); this.st_word(address + 4, this.reg(t2)); if (is_wback) this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "strd", null, t, n, m, null, null, true, is_wback, is_index); }; ARMv7_CPU.prototype.strd_imm = function(inst, addr) { this.print_inst("STRD (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm4h = (inst >>> 8) & 0xf; var imm4l = inst & 0xf; var t2 = t + 1; var imm32 = (imm4h << 4) + imm4l; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; this.st_word(address, this.reg(t)); this.st_word(address + 4, this.reg(t2)); if (is_wback) this.regs[n] = offset_addr; this.print_inst_imm(addr, inst, "strd", null, t, n, imm32, true, is_wback, is_add); }; ARMv7_CPU.prototype.strh_imm = function(inst, addr) { this.print_inst("STRH (immediate)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var imm4h = (inst >>> 8) & 0xf; var imm4l = inst & 0xf; var imm32 = (imm4h << 4) + imm4l; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var offset_addr = valn + (is_add ? imm32 : -imm32); var address = is_index ? offset_addr : valn; this.st_halfword(address, bitops.get_bits(this.reg(t), 15, 0)); if (is_wback) this.regs[n] = offset_addr; this.print_inst_imm(addr, inst, "strh", null, t, n, imm32, true, is_wback, is_add); }; ARMv7_CPU.prototype.strh_reg = function(inst, addr) { this.print_inst("STRH (register)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var t = (inst >>> 12) & 0xf; var m = inst & 0xf; var is_index = p == 1; var is_add = u == 1; var is_wback = p === 0 || w == 1; var valn = this.reg(n); var offset = this.shift(this.reg(m), this.SRType_LSL, 0, this.cpsr.c); var offset_addr = valn + (is_add ? offset : -offset); var address = is_index ? offset_addr : valn; this.st_halfword(address, bitops.get_bits(this.reg(t), 15, 0)); if (is_wback) this.regs[n] = offset_addr; this.print_inst_reg(addr, inst, "strh", null, t, n, m, this.SRType_LSL, 0, true, is_wback, is_add); }; ARMv7_CPU.prototype.ldm = function(inst, addr) { this.print_inst("LDM / LDMIA / LDMFD", inst, addr); var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); var is_pop = false; if (w && n == 13 && n_registers >= 2) { is_pop = true; } var is_wback = w == 1; var valn = this.reg(n); var address = valn; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.regs[i] = this.ld_word(address); address += 4; } } //if ((register_list >>> 15) & 1) { if (register_list & 0x8000) { reglist.push(15); this.branch_to = this.ld_word(address); } if (is_wback) this.regs[n] = this.reg(n) + 4 * n_registers; this.log_regs(null); if (is_pop) this.print_inst_ldstm(addr, inst, "pop", is_wback, null, reglist); else this.print_inst_ldstm(addr, inst, "ldm", is_wback, n, reglist); }; ARMv7_CPU.prototype.ldm_er = function(inst, addr) { this.print_inst("LDM (exception return)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0x7fff; var n_registers = bitops.bit_count(register_list, 15); var is_wback = w == 1; var is_increment = u == 1; var is_wordhigher = p == u; var valn = this.reg(n); if (this.is_user_or_system()) this.abort_unpredictable("LDM (exception return)", inst, addr); var length = 4*n_registers + 4; var address = valn + (is_increment ? 0 : -length); if (is_wordhigher) address += 4; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.regs[i] = this.ld_word(address); address += 4; } } var new_pc = this.ld_word(address); if (is_wback) this.regs[n] = valn + (is_increment ? length : -length); this.log_regs(null); this.cpsr_write_by_instr(this.get_current_spsr(), 15, true); this.branch_to = new_pc; //this.print_inst_ldstm(addr, inst, "ldm", is_wback, n, reglist); this.print_inst_unimpl(addr, inst, "ldm"); }; ARMv7_CPU.prototype.ldm_ur = function(inst, addr) { this.print_inst("LDM (user registers)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0x7fff; var n_registers = bitops.bit_count(register_list, 15); var is_increment = u == 1; var is_wordhigher = p == u; var valn = this.reg(n); if (this.is_user_or_system()) this.abort_unpredictable("LDM (user registers)", inst, addr); var length = 4*n_registers; var address = valn + (is_increment ? 0 : -length); if (is_wordhigher) address += 4; var reglist = []; this.log_regs(null); for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); // FIXME this.regs_usr[i] = this.ld_word(address); if (this.cpsr.m == this.FIQ_MODE) { if (!(i >= 8 && i <= 14)) this.regs[i] = this.regs_usr[i]; } else { if (!(i >= 13 && i <= 14)) this.regs[i] = this.regs_usr[i]; } address += 4; } } logger.log(reglist.toString()); this.print_inst_unimpl(addr, inst, "ldm"); }; ARMv7_CPU.prototype.ldmda = function(inst, addr) { this.print_inst("LDMDA / LDMFA", inst, addr); var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); var address = this.reg(n) - 4 * n_registers + 4; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.regs[i] = this.ld_word(address); address += 4; } } if (register_list & 0x8000) { reglist.push(15); this.branch_to = this.ld_word(address); } if (w) this.regs[n] = this.reg(n) - 4 * n_registers; this.log_regs(null); this.print_inst_ldstm(addr, inst, "ldmda", w, n, reglist); }; ARMv7_CPU.prototype.ldmdb = function(inst, addr) { this.print_inst("LDMDB / LDMEA", inst, addr); var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); var address = this.reg(n) - 4 * n_registers; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.regs[i] = this.ld_word(address); address += 4; } } if (register_list & 0x8000) { reglist.push(15); this.branch_to = this.ld_word(address); } if (w) this.regs[n] = this.reg(n) - 4 * n_registers; this.log_regs(null); this.print_inst_ldstm(addr, inst, "ldmdb", w, n, reglist); }; ARMv7_CPU.prototype.ldmib = function(inst, addr) { this.print_inst("LDMIB / LDMED", inst, addr); var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); var address = this.reg(n) + 4; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.regs[i] = this.ld_word(address); address += 4; } } if (register_list & 0x8000) { reglist.push(15); this.branch_to = this.ld_word(address); } if (w) this.regs[n] = this.reg(n) + 4 * n_registers; this.log_regs(null); this.print_inst_ldstm(addr, inst, "ldmib", w, n, reglist); }; ARMv7_CPU.prototype.stm = function(inst, addr) { this.print_inst("STM / STMIA / STMEA", inst, addr); var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); this.log_regs(null); var address = this.reg(n); var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.st_word(address, this.regs[i]); address += 4; } } if (register_list & 0x8000) { reglist.push(15); this.st_word(address, this.get_pc()); } if (w) this.regs[n] = this.reg(n) + 4 * n_registers; this.print_inst_ldstm(addr, inst, "stm", w, n, reglist); }; ARMv7_CPU.prototype.stmdb = function(inst, addr) { this.print_inst("STMDB / STMFD", inst, addr); var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); var is_push = false; var valn = this.reg(n); if (w && n == 13 && n_registers >= 2) { is_push = true; } this.log_regs(null); var address = valn - 4 * n_registers; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.st_word(address, this.regs[i]); address += 4; } } if (register_list & 0x8000) { reglist.push(15); this.st_word(address, this.get_pc()); } if (w || is_push) this.regs[n] = this.reg(n) - 4 * n_registers; if (is_push) this.print_inst_ldstm(addr, inst, "push", w, null, reglist); else this.print_inst_ldstm(addr, inst, "stmdb", w, n, reglist); }; ARMv7_CPU.prototype.stmib = function(inst, addr) { this.print_inst("STMIB / STMFA", inst, addr); var w = (inst >>> 21) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); var valn = this.reg(n); this.log_regs(null); var address = valn + 4; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); this.st_word(address, this.regs[i]); address += 4; } } if (register_list & 0x8000) { reglist.push(15); this.st_word(address, this.get_pc()); } if (w) this.regs[n] = this.reg(n) + 4 * n_registers; this.print_inst_ldstm(addr, inst, "stmib", w, n, reglist); }; ARMv7_CPU.prototype.stm_ur = function(inst, addr) { this.print_inst("STM (user registers)", inst, addr); var p = (inst >>> 24) & 1; var u = (inst >>> 23) & 1; var n = (inst >>> 16) & 0xf; var register_list = inst & 0xffff; var n_registers = bitops.bit_count(register_list, 16); var is_increment = u == 1; var is_wordhigher = p == u; if (n == 15 || n_registers < 1) this.abort_unpredictable("STM (user registers)", inst, addr); if (this.is_user_or_system()) this.abort_unpredictable("STM (user registers)"); var length = 4*n_registers; this.log_regs(null); var address = this.reg(n) + (is_increment ? 0 : -length); if (is_wordhigher) address += 4; var reglist = []; for (var i=0; i < 15; i++) { if ((register_list >>> i) & 1) { reglist.push(i); // XXX if (this.cpsr.m == this.FIQ_MODE) { if (i >= 8 && i <= 14) this.st_word(address, this.regs_usr[i]); else this.st_word(address, this.regs[i]); } else { if (i >= 13 && i <= 14) this.st_word(address, this.regs_usr[i]); else this.st_word(address, this.regs[i]); } address += 4; } } if (register_list & 0x8000) { reglist.push(15); //this.st_word(address, this.regs_usr[15] + 8); this.st_word(address, this.get_pc()); } this.print_inst_ldstm(addr, inst, "stm_usr", null, n, reglist); // FIXME }; ARMv7_CPU.prototype.cps = function(inst, addr) { this.print_inst("CPS", inst, addr); var imod = (inst >>> 18) & 3; var m = inst & (1 << 17); var a = inst & (1 << 8); var i = inst & (1 << 7); var f = inst & (1 << 6); var mode = inst & 0xf; var enable = imod == 2; var disable = imod == 3; if (this.is_priviledged()) { var new_cpsr = this.clone_psr(this.cpsr); if (enable) { if (a) new_cpsr.a = 0; if (i) new_cpsr.i = 0; if (f) new_cpsr.f = 0; } if (disable) { if (a) new_cpsr.a = 1; if (i) new_cpsr.i = 1; if (f) new_cpsr.f = 1; } if (m) new_cpsr.m = mode; this.cpsr_write_by_instr(new_cpsr, 15, true); } this.print_inst_unimpl(addr, inst, "cps"); }; ARMv7_CPU.prototype.svc = function(inst, addr) { this.print_inst("SVC (previously SWI)", inst, addr); var imm32 = inst & 0x00ffffff; this.print_inst_svc(inst, imm32); this.call_supervisor(); }; ARMv7_CPU.prototype.clrex = function(inst, addr) { this.print_inst("CLREX", inst, addr); // Clear Exclusive clears the local record of the executing processor that an address has had a request for an exclusive access. // FIXME: Need to do nothing? this.print_inst_unimpl(addr, inst, "clrex"); }; ARMv7_CPU.prototype.dsb = function(inst, addr) { this.print_inst("DSB", inst, addr); //var option = bitops.get_bits(inst, 3, 0); // Data Synchronization Barrier // FIXME: Need to do nothing? this.print_inst_unimpl(addr, inst, "dsb"); }; ARMv7_CPU.prototype.dmb = function(inst, addr) { this.print_inst("DMB", inst, addr); //var option = bitops.get_bits(inst, 3, 0); // Data Memory Barrier // FIXME: Need to do nothing? this.print_inst_unimpl(addr, inst, "dmb"); }; ARMv7_CPU.prototype.isb = function(inst, addr) { this.print_inst("ISB", inst, addr); //var option = bitops.get_bits(inst, 3, 0); // Instruction Synchronization Barrier // FIXME: Need to do nothing? this.print_inst_unimpl(addr, inst, "isb"); }; ARMv7_CPU.prototype.wfi = function(inst, addr) { this.print_inst("WFI", inst, addr); this.is_halted = true; this.cpsr.i = 0; this.print_inst_unimpl(addr, inst, "wfi"); }; ARMv7_CPU.prototype.vmrs = function(inst_name, inst, addr) { this.print_inst("VMRS", inst, addr); // XXX: VFP support v0.3: no double precision support this.regs[6] = 1<<20; this.print_inst_unimpl(addr, inst, "vmrs"); }; ARMv7_CPU.prototype.nop = function(inst_name, inst, addr) { this.print_inst("NOP", inst, addr); this.print_inst_unimpl(addr, inst, "nop"); }; ARMv7_CPU.prototype.exec = function(inst_name, inst, addr) { this.current = inst_name; return this[inst_name](inst, addr); }; /* * * Decoder * */ ARMv7_CPU.prototype.decode_uncond = function(inst, addr) { // Unconditional instructions var op = 0; var op1 = 0; var op2 = 0; var tmp = 0; op1 = (inst >>> 20) & 0xff; if ((op1 >> 7) === 0) { // [31:27]=11110 // Miscellaneous instructions, memory hints, and Advanced SIMD instructions op1 = (inst >>> 20) & 0x7f; op = (inst >>> 16) & 1; op2 = (inst >>> 4) & 0xf; tmp = (op1 >>> 5) & 3; switch (tmp) { case 0: if (op1 == 0x10 && (op2 & 2) === 0) { if (op) { // SETEND this.abort_not_impl("SETEND", inst, addr); } else { // CPS return "cps"; } break; } this.abort_unknown_inst(inst, addr); break; case 1: // Advanced SIMD data-processing instructions this.abort_simdvfp_inst(inst, addr); break; case 2: if ((op1 & 1) === 0) { // Advanced SIMD element or structure load/store instructions this.abort_simdvfp_inst(inst, addr); } switch (op1 >> 1 & 3) { case 2: if (op1 & 0x10) { // PLD (immediate, literal) return "pld_imm"; } else { // PLI (immediate, literal) this.abort_not_impl("PLI (immediate, literal)", inst, addr); } break; case 3: if ((op1 & 0x18) == 0x10) { switch (op2) { case 1: // CLREX return "clrex"; // Clear Exclusive clears the local record of the executing processor that an address has had a request for an exclusive access. // FIXME: Need to do nothing? break; case 4: // DSB return "dsb"; //var option = bitops.get_bits(inst, 3, 0); // Data Synchronization Barrier // FIXME: Need to do nothing? break; case 5: // DMB return "dmb"; //var option = bitops.get_bits(inst, 3, 0); // Data Memory Barrier // FIXME: Need to do nothing? break; case 6: // ISB return "isb"; //var option = bitops.get_bits(inst, 3, 0); // Instruction Synchronization Barrier // FIXME: Need to do nothing? break; default: // UNPREDICTABLE this.abort_unpredictable_instruction("Miscellaneous instructions, memory hints, and Advanced SIMD instructions", inst, addr); break; } } else { // UNPREDICTABLE this.abort_unpredictable_instruction("Miscellaneous instructions, memory hints, and Advanced SIMD instructions", inst, addr); } break; default: this.abort_unknown_inst(inst, addr); break; } break; case 3: if ((op2 & 1) === 0) { switch (op1 & 7) { case 5: if (op1 & 0x10) { // PLD (register) this.abort_not_impl("PLD (register)", inst, addr); } else { // PLI (register) this.abort_not_impl("PLI (register)", inst, addr); } break; case 7: // UNPREDICTABLE this.abort_unpredictable_instruction("Miscellaneous instructions, memory hints, and Advanced SIMD instructions", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } } break; default: this.abort_decode_error(inst, addr); break; } } else { switch (op1) { case 0xc4: // MCRR, MCRR2 this.abort_not_impl("MCRR, MCRR2", inst, addr); break; case 0xc5: // MRRC, MRRC2 this.abort_not_impl("MRRC, MRRC2", inst, addr); break; default: tmp = (op1 >>> 5) & 7; switch (tmp) { case 4: if (op1 & 4) { if (!(op1 & 1)) { // SRS this.abort_not_impl("SRS", inst, addr); break; } } else { if (op1 & 1) { // RFE this.abort_not_impl("RFE", inst, addr); break; } } this.abort_unknown_inst(inst, addr); break; case 5: // BL, BLX (immediate) this.abort_not_impl("BL, BLX (immediate)", inst, addr); break; case 6: if (op1 & 1) { // LDC, LDC2 (immediate) & LDC, LDC2 (literal) throw "UND"; } else { // STC, STC2 throw "UND"; } case 7: if (!(op1 & 1<<4)) { if (op & 1) { if (op1 & 1) { // MRC, MRC2 // TODO this.abort_not_impl("MRC, MRC2", inst, addr); } else { // MCR, MCR2 // TODO this.abort_not_impl("MCR, MCR2", inst, addr); } } else { // CDP, CDP2 throw "UND"; } break; } // Fall through default: this.abort_unknown_inst(inst, addr); break; } } } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_sync_prim = function(inst, addr) { // Synchronization primitives // [27:24]=0001 [7:4]=1001 var op = (inst >>> 20) & 0xf; if ((op & 8) === 0) { if ((op & 3) === 0) { // SWP, SWPB return "swp"; } else { this.abort_unknown_inst(inst, addr); } } else { switch (op & 7) { case 0: // STREX return "strex"; break; case 1: // LDREX return "ldrex"; break; case 2: // STREXD return "strexd"; break; case 3: // LDREXD return "ldrexd"; break; case 4: // STREXB this.abort_not_impl("STREXB", inst, addr); break; case 5: // LDREXB this.abort_not_impl("LDREXB", inst, addr); break; case 6: // STREXH this.abort_not_impl("STREXH", inst, addr); break; case 7: // LDREXH this.abort_not_impl("LDREXH", inst, addr); break; default: break; } } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_dataproc_imm = function(inst, addr) { // [27:25]=001 // Data-processing (immediate) var op = (inst >>> 20) & 0x1f; var rn; switch (op >> 1) { case 0: // AND (immediate) return "and_imm"; break; case 1: // EOR (immediate) return "eor_imm"; break; case 2: rn = (inst >>> 16) & 0xf; if (rn == 0xf) { // [24:21]=0010 // ADR A2 return "adr_a2"; } else { // SUB (immediate) return "sub_imm"; } break; case 3: // RSB (immediate) return "rsb_imm"; break; case 4: rn = (inst >>> 16) & 0xf; if (rn == 0xf) { // [24:21]=0100 // ADR A1 return "adr_a1"; } else { // ADD (immediate) return "add_imm"; } break; case 5: // ADC (immediate) return "adc_imm"; break; case 6: // SBC (immediate) return "sbc_imm"; break; case 7: // RSC (immediate) return "rsc_imm"; break; case 8: if ((op & 1) === 0) { this.abort_unknown_inst(inst, addr); } // TST (immediate) return "tst_imm"; break; case 9: if ((op & 1) === 0) { this.abort_unknown_inst(inst, addr); } // TEQ (immediate) return "teq_imm"; break; case 0xa: if ((op & 1) === 0) { this.abort_unknown_inst(inst, addr); } // CMP (immediate) return "cmp_imm"; break; case 0xb: if ((op & 1) === 0) { this.abort_unknown_inst(inst, addr); } // CMN (immediate) return "cmn_imm"; break; case 0xc: // ORR (immediate) return "orr_imm"; break; case 0xd: // MOV (immediate) A1 return "mov_imm_a1"; break; case 0xe: // BIC (immediate) return "bic_imm"; break; case 0xf: // MVN (immediate) return "mvn_imm"; break; default: break; } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_msr_imm_and_hints = function(inst, addr) { // [27:23]=00110 [21:20]=10 // MSR (immediate), and hints var op = inst & (1 << 22); var op1 = (inst >>> 16) & 0xf; var op2 = inst & 0xff; if (op) { // MSR (immediate) (system level) return "msr_imm_sys"; } else { if ((op1 & 2)) { // MSR (immediate) (system level) return "msr_imm_sys"; } else { if ((op1 & 1)) { // MSR (immediate) (system level) return "msr_imm_sys"; } else { if (op1 & 8) { // MSR (immediate) (application level) this.abort_not_impl("MSR (immediate) (application level)", inst, addr); } else { if (op1 & 4) { // MSR (immediate) (application level) this.abort_not_impl("MSR (immediate) (application level)", inst, addr); } else { if ((op2 & 0xf0) == 0xf0) { // DBG this.abort_not_impl("DBG", inst, addr); } else { switch (op2) { case 0: // NOP return "nop"; case 1: // YIELD this.abort_not_impl("YIELD", inst, addr); break; case 2: // WFE this.abort_not_impl("WFE", inst, addr); break; case 3: // WFI return "wfi"; break; case 4: // SEV this.abort_not_impl("SEV", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } } } } } } } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_half_mul = function(inst, addr) { throw "decode_half_mul"; }; ARMv7_CPU.prototype.decode_misc = function(inst, addr) { // [27:23]=00010 [20]=0 [7]=0 // Miscellaneous instructions var op = (inst >>> 21) & 0x3; var op1 = (inst >>> 16) & 0xf; var op2 = (inst >>> 4) & 0x7; switch (op2) { case 0: if (op & 1) { if (!((op & 2) == 2) && (op1 & 3) === 0) { // MSR (register) (application level) this.abort_not_impl("MSR (register) (application level)", inst, addr); } else { // MSR (register) (system level) return "msr_reg_sys"; } } else { // MRS return "mrs"; } break; case 1: switch (op) { case 1: // BX return "bx"; break; case 3: // CLZ return "clz"; break; default: this.abort_unknown_inst(inst, addr); break; } break; case 2: if (op != 1) { this.abort_unknown_inst(inst, addr); } // BXJ this.abort_not_impl("BXJ", inst, addr); break; case 3: if (op != 1) { this.abort_unknown_inst(inst, addr); } // BLX (register) return "blx_reg"; break; case 5: // Saturating addition and subtraction this.abort_not_impl("Saturating addition and subtraction", inst, addr); break; case 7: switch (op) { case 1: // BKPT this.abort_not_impl("BKPT", inst, addr); break; case 3: // SMC (previously SMI) this.abort_not_impl("SMC (previously SMI)", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; default: this.abort_unknown_inst(inst, addr); break; } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_dataproc_reg = function(inst, addr) { // [27:25]=000 [4]=0 // Data-processing (register) var op1 = (inst >>> 20) & 0x1f; var op2 = (inst >>> 7) & 0x1f; var op3 = (inst >>> 5) & 0x3; // op1 != 0b10xx0 switch (op1 >> 1) { case 0: // AND (register) return "and_reg"; break; case 1: // EOR (register) return "eor_reg"; break; case 2: // SUB (register) return "sub_reg"; break; case 3: // RSB (register) return "rsb_reg"; break; case 4: // ADD (register) return "add_reg"; break; case 5: // ADC (register) return "adc_reg"; break; case 6: // SBC (register) return "sbc_reg"; break; case 7: // RSC (register) this.abort_not_impl("RSC (register)", inst, addr); break; case 8: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // TST (register) return "tst_reg"; break; case 9: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // TEQ (register) return "teq_reg"; break; case 0xa: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // CMP (register) return "cmp_reg"; break; case 0xb: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // CMN (register) return "cmn_reg"; break; case 0xc: // ORR (register) return "orr_reg"; break; case 0xd: switch (op3) { case 0: if (op2 === 0) { // MOV (register) return "mov_reg"; } else { // LSL (immediate) return "lsl_imm"; } break; case 1: // LSR (immediate) return "lsr_imm"; break; case 2: // ASR (immediate) return "asr_imm"; break; case 3: if (op2 === 0) { // RRX return "rrx"; } else { // ROR (immediate) return "ror_imm"; } break; default: break; } break; case 0xe: // BIC (register) return "bic_reg"; break; case 0xf: // MVN (register) return "mvn_reg"; break; default: break; } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_dataproc_rsr = function(inst, addr) { // [27:25]=000 [7]=0 [4]=1 // Data-processing (register-shifted register) var op1 = (inst >>> 20) & 0x1f; var op2 = (inst >>> 5) & 0x3; // op1 != 0b10xx0 switch (op1 >> 1) { case 0: // AND (register-shifted register) return "and_rsr"; break; case 1: // EOR (register-shifted register) return "eor_rsr"; break; case 2: // SUB (register-shifted register) return "sub_rsr"; break; case 3: // RSB (register-shifted register) return "rsb_rsr"; break; case 4: // ADD (register-shifted register) return "add_rsr"; break; case 5: // ADC (register-shifted register) this.abort_not_impl("ADC (register-shifted register)", inst, addr); break; case 6: // SBC (register-shifted register) return "sbc_rsr"; break; case 7: // RSC (register-shifted register) this.abort_not_impl("RSC (register-shifted register)", inst, addr); break; case 8: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // TST (register-shifted register) return "tst_rsr"; break; case 9: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // TEQ (register-shifted register) this.abort_not_impl("TEQ (register-shifted register)", inst, addr); break; case 0xa: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // CMP (register-shifted register) return "cmp_rsr"; break; case 0xb: if ((op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); } // CMN (register-shifted register) this.abort_not_impl("CMN (register-shifted register)", inst, addr); break; case 0xc: // ORR (register-shifted register) return "orr_rsr"; break; case 0xd: switch (op2) { case 0: // LSL (register) return "lsl_reg"; break; case 1: // LSR (register) return "lsr_reg"; break; case 2: // ASR (register) return "asr_reg"; break; case 3: // ROR (register) this.abort_not_impl("ROR (register)", inst, addr); break; default: break; } break; case 0xe: // BIC (register-shifted register) return "bic_rsr"; break; case 0xf: // MVN (register-shifted register) return "mvn_rsr"; break; default: break; } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_extra_ldst_unpriv1 = function(inst, addr) { // [27:24]=0000 [21]=1 [7]=1 [4]=1 // [7:4]=1011 // Extra load/store instructions (unprivileged) #1 op = bitops.get_bit(inst, 20); //op2=01 //if ((op2 & 3) === 0) { // this.abort_unknown_inst(inst, addr); //} if (op) { // LDRHT this.abort_not_impl("LDRHT", inst, addr); } else { // STRHT this.abort_not_impl("STRHT", inst, addr); } }; ARMv7_CPU.prototype.decode_extra_ldst_unpriv2 = function(inst, addr) { // [27:24]=0000 [21]=1 [7]=1 [4]=1 // [7:4]=11x1 // Extra load/store instructions (unprivileged) #2 // op2=1x op2 = bitops.get_bits(inst, 6, 5); //if ((op2 & 3) === 0) { // this.abort_unknown_inst(inst, addr); //} if (op) { switch (op2) { case 2: // LDRSBT this.abort_not_impl("LDRSBT", inst, addr); break; case 3: // LDRSHT this.abort_not_impl("LDRSHT", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } } else { var rt = bitops.get_bits(inst, 15, 12); if (rt & 1) { // UNDEFINED this.abort_undefined_instruction("Extra load/store instructions (unprivileged) #2", inst, addr); } else { // UNPREDICTABLE this.abort_unpredictable_instruction("Extra load/store instructions (unprivileged) #2", inst, addr); } } }; ARMv7_CPU.prototype.decode_extra_ldst1 = function(inst, addr) { // [27:25]=000 [7]=1 [4]=1 // [7:4]=1011 // Extra load/store instructions #1 op1 = (inst >>> 20) & 0x1f; //op2 = bitops.get_bits(inst, 6, 5); //op2=01 if (op1 & 1) { if (op1 & 4) { rn = (inst >>> 16) & 0xf; if (rn == 0xf) { // LDRH (literal) this.abort_not_impl("LDRH (literal)", inst, addr); } else { // LDRH (immediate) return "ldrh_imm"; } } else { // LDRH (register) return "ldrh_reg"; } } else { if (op1 & 4) { // STRH (immediate) return "strh_imm"; } else { // STRH (register) return "strh_reg"; } } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_extra_ldst2 = function(inst, addr) { // [27:25]=000 [7]=1 [4]=1 // [7:4]=11x1 // Extra load/store instructions #2 var op1 = (inst >>> 20) & 0x1f; var op2 = (inst >>> 5) & 0x3; //op2=1x var rn = (inst >>> 16) & 0xf; if (op2 & 1) { if (op1 & 1) { if (op1 & 4) { if (rn == 0xf) { // LDRSH (literal) this.abort_not_impl("LDRSH (literal)", inst, addr); } else { // LDRSH (immediate) return "ldrsh_imm"; } } else { // LDRSH (register) return "ldrsh_reg"; } } else { if (op1 & 4) { // STRD (immediate) return "strd_imm"; } else { // STRD (register) return "strd_reg"; } } } else { if (op1 & 1) { if (op1 & 4) { if (rn == 0xf) { // LDRSB (literal) this.abort_not_impl("LDRSB (literal)", inst, addr); } else { // LDRSB (immediate) return "ldrsb_imm"; } } else { // LDRSB (register) return "ldrsb_reg"; } } else { if (op1 & 4) { if (rn == 0xf) { // LDRD (literal) this.abort_not_impl("LDRD (literal)", inst, addr); } else { // LDRD (immediate) return "ldrd_imm"; } } else { // LDRD (register) return "ldrd_reg"; } } } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_multi = function(inst, addr) { // [27:24]=0000 [7:4]=1001 // Multiply and multiply-accumulate var op = (inst >>> 20) & 0xf; switch (op >> 1) { case 0: // MUL return "mul"; break; case 1: // MLA return "mla"; break; case 2: if (op & 1) { // UNDEFINED this.abort_undefined_instruction("Multiply and multiply-accumulate", inst, addr); } else { // UMAAL this.abort_not_impl("UMAAL", inst, addr); } break; case 3: if (op & 1) { // UNDEFINED this.abort_undefined_instruction("Multiply and multiply-accumulate", inst, addr); } else { // MLS return "mls"; } break; case 4: // UMULL return "umull"; break; case 5: // UMLAL return "umlal"; break; case 6: // SMULL return "smull"; break; case 7: // SMLAL return "smlal"; break; default: break; } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_datamisc = function(inst, addr) { // Data-processing and miscellaneous instructions var op = (inst >>> 25) & 1; var op1 = (inst >>> 20) & 0x1f; var op2 = (inst >>> 4) & 0xf; var rn = null; if (op) { //if ((op1 >> 3) == 2 && (op1 & 3) == 2) { // 10x10 if (op1 == 0x12 || op1 == 0x16) { // 10x10 return this.decode_msr_imm_and_hints(inst, addr); } else { switch (op1) { case 0x10: // MOV (immediate) A2? return "mov_imm_a2"; break; case 0x14: // MOVT return "movt"; break; default: if ((op1 >> 3) == 2 && (op1 & 1) === 0) { this.abort_unknown_inst(inst, addr); return null; } else { //if (!(op1 >> 3 == 2 && (op1 & 1) === 0)) { // [27:25]=001 // Data-processing (immediate) return this.decode_dataproc_imm(inst, addr); } break; } } } else { if (op2 & 1) { if (op2 >> 3) { if ((op2 & 4) == 4) { if ((op1 >> 4) === 0 && (op1 & 2) == 2) { // 0xx1x // Extra load/store instructions (unprivileged) #2 return this.decode_extra_ldst_unpriv2(inst, addr); } else { // Extra load/store instructions #2 return this.decode_extra_ldst2(inst, addr); } } else { if (op2 & 2) { if ((op1 >> 4) === 0 && (op1 & 2) == 2) { // 0xx1x // Extra load/store instructions (unprivileged) #1 return this.decode_extra_ldst_unpriv1(inst, addr); } else { // Extra load/store instructions #1 return this.decode_extra_ldst1(inst, addr); } } else { if (op1 >> 4) { // Synchronization primitives return this.decode_sync_prim(inst, addr); } else { // Multiply and multiply-accumulate return this.decode_multi(inst, addr); } } } } else { if ((op1 >> 3) == 2 && (op1 & 1) === 0) { // 10xx0 // Miscellaneous instructions return this.decode_misc(inst, addr); } else { // Data-processing (register-shifted register) return this.decode_dataproc_rsr(inst, addr); } } } else { if ((op1 >> 3) == 2 && (op1 & 1) === 0) { // 10xx0 if (op2 >> 3) { // Halfword multiply and multiply-accumulate this.abort_not_impl("Halfword multiply and multiply-accumulate", inst, addr); } else { // Miscellaneous instructions return this.decode_misc(inst, addr); } } else { // Data-processing (register) return this.decode_dataproc_reg(inst, addr); } } } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode_media = function(inst, addr) { // [27:25]=011 [4]=1 // Media instructions var op1 = (inst >>> 20) & 0x1f; var op2 = (inst >>> 5) & 0x7; var tmp = op1 >> 3; var rn = null; var a = null; switch (tmp) { case 0: if (op1 & 4) { // [27:22]=011001 [4]=1 // Parallel addition and subtraction, unsigned op1 = bitops.get_bits(inst, 21, 20); op2 = bitops.get_bits(inst, 7, 5); switch (op1) { case 1: switch (op2) { case 0: // UADD16 this.abort_not_impl("UADD16", inst, addr); break; case 1: // UASX this.abort_not_impl("UASX", inst, addr); break; case 2: // USAX this.abort_not_impl("USAX", inst, addr); break; case 3: // USUB16 this.abort_not_impl("USUB16", inst, addr); break; case 4: // UADD8 this.abort_not_impl("UADD8", inst, addr); break; case 7: // USUB8 this.abort_not_impl("USUB8", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; case 2: switch (op2) { case 0: // UQADD16 this.abort_not_impl("UQADD16", inst, addr); break; case 1: // UQASX this.abort_not_impl("UQASX", inst, addr); break; case 2: // UQSAX this.abort_not_impl("UQSAX", inst, addr); break; case 3: // UQSUB16 this.abort_not_impl("UQSUB16", inst, addr); break; case 4: // UQADD8 this.abort_not_impl("UQADD8", inst, addr); break; case 7: // UQSUB8 this.abort_not_impl("UQSUB8", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; case 3: switch (op2) { case 0: // UHADD16 this.abort_not_impl("UHADD16", inst, addr); break; case 1: // UHASX this.abort_not_impl("UHASX", inst, addr); break; case 2: // UHSAX this.abort_not_impl("UHSAX", inst, addr); break; case 3: // UHSUB16 this.abort_not_impl("UHSUB16", inst, addr); break; case 4: // UHADD8 this.abort_not_impl("UHADD8", inst, addr); break; case 7: // UHSUB8 this.abort_not_impl("UHSUB8", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } default: this.abort_unknown_inst(inst, addr); break; } } else { // [27:22]=011000 [4]=1 // Parallel addition and subtraction, signed op1 = bitops.get_bits(inst, 21, 20); op2 = bitops.get_bits(inst, 7, 5); switch (op1) { case 1: switch (op2) { case 0: // SADD16 this.abort_not_impl("SADD16", inst, addr); break; case 1: // SASX this.abort_not_impl("SASX", inst, addr); break; case 2: // SSAX this.abort_not_impl("SSAX", inst, addr); break; case 3: // SSUB16 this.abort_not_impl("SSUB16", inst, addr); break; case 4: // SADD8 this.abort_not_impl("SADD8", inst, addr); break; case 7: // SSUB8 this.abort_not_impl("SSUB8", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; case 2: switch (op2) { case 0: // QADD16 this.abort_not_impl("QADD16", inst, addr); break; case 1: // QASX this.abort_not_impl("QASX", inst, addr); break; case 2: // QSAX this.abort_not_impl("QSAX", inst, addr); break; case 3: // QSUB16 this.abort_not_impl("QSUB16", inst, addr); break; case 4: // QADD8 this.abort_not_impl("QADD8", inst, addr); break; case 7: // QSUB8 this.abort_not_impl("QSUB8", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; case 3: switch (op2) { case 0: // SHADD16 this.abort_not_impl("SHADD16", inst, addr); break; case 1: // SHASX this.abort_not_impl("SHASX", inst, addr); break; case 2: // SHSAX this.abort_not_impl("SHSAX", inst, addr); break; case 3: // SHSUB16 this.abort_not_impl("SHSUB16", inst, addr); break; case 4: // SHADD8 this.abort_not_impl("SHADD8", inst, addr); break; case 7: // SHSUB8 this.abort_not_impl("SHSUB8", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; default: this.abort_unknown_inst(inst, addr); break; } } break; case 1: // [27:23]=01101 [4]=1 // Packing, unpacking, saturation, and reversal op1 = (inst >>> 20) & 0x7; op2 = (inst >>> 5) & 0x7; tmp = op1 >> 1; switch (tmp) { case 0: if (op1) { this.abort_unknown_inst(inst, addr); } if (op2 & 1) { switch (op2 >> 1) { case 1: a = bitops.get_bits(inst, 19, 16); if (a == 0xf) { // SXTB16 this.abort_not_impl("SXTB16", inst, addr); } else { // SXTAB16 this.abort_not_impl("SXTAB16", inst, addr); } break; case 2: // SEL this.abort_not_impl("SEL", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } } else { throw "PKH"; } break; case 1: if (op2 & 1) { switch (op1) { case 2: switch (op2) { case 1: // SSAT16 this.abort_not_impl("SSAT16", inst, addr); break; case 3: a = bitops.get_bits(inst, 19, 16); if (a == 0xf) { // SXTB return "sxtb"; } else { // SXTAB this.abort_not_impl("SXTAB", inst, addr); } break; default: this.abort_unknown_inst(inst, addr); break; } break; case 3: switch (op2) { case 1: // REV return "rev"; break; case 3: a = (inst >>> 16) & 0xf; if (a == 0xf) { // SXTH return "sxth"; } else { // SXTAH return "sxtah"; } break; case 5: // REV16 return "rev16"; default: this.abort_unknown_inst(inst, addr); break; } break; default: this.abort_unknown_inst(inst, addr); break; } } else { // SSAT this.abort_not_impl("SSAT", inst, addr); } break; case 2: if (op2 != 3) { this.abort_unknown_inst(inst, addr); } a = bitops.get_bits(inst, 19, 16); if (a == 0xf) { // UXTB16 this.abort_not_impl("UXTB16", inst, addr); } else { // UXTAB16 this.abort_not_impl("UXTAB16", inst, addr); } break; case 3: if (op2 & 1) { switch (op1) { case 6: switch (op2) { case 1: // USAT16 this.abort_not_impl("USAT16", inst, addr); break; case 3: a = (inst >>> 16) & 0xf; if (a == 0xf) { // UXTB return "uxtb"; } else { // UXTAB return "uxtab"; } break; default: this.abort_unknown_inst(inst, addr); break; } break; case 7: switch (op2) { case 1: // RBIT this.abort_not_impl("RBIT", inst, addr); break; case 3: a = (inst >>> 16) & 0xf; if (a == 0xf) { // UXTH return "uxth"; } else { // UXTAH return "uxtah"; } break; case 5: // REVSH this.abort_not_impl("REVSH", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; default: this.abort_unknown_inst(inst, addr); break; } } else { // USAT return "usat"; } break; default: break; } break; case 2: // [27:23]=01110 [4]=1 // Signed multiplies op1 = (inst >>> 20) & 0x7; op2 = (inst >>> 5) & 0x7; a = (inst >>> 12) & 0xf; switch (op1) { case 0: switch (op2 >> 1) { case 0: if (a == 0xf) { // SMUAD this.abort_not_impl("SMUAD", inst, addr); } else { // SMLAD this.abort_not_impl("SMLAD", inst, addr); } break; case 1: if (a == 0xf) { // SMUSD this.abort_not_impl("SMUSD", inst, addr); } else { // SMLSD this.abort_not_impl("SMLSD", inst, addr); } break; default: this.abort_unknown_inst(inst, addr); break; } break; case 4: switch (op2 >> 1) { case 0: // SMLALD this.abort_not_impl("SMLALD", inst, addr); break; case 1: // SMLSLD this.abort_not_impl("SMLSLD", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; case 5: switch (op2 >> 1) { case 0: if (a == 0xf) { // SMMUL this.abort_not_impl("SMMUL", inst, addr); } else { // SMMLA this.abort_not_impl("SMMLA", inst, addr); } break; case 3: // SMMLS this.abort_not_impl("SMMLS", inst, addr); break; default: this.abort_unknown_inst(inst, addr); break; } break; default: this.abort_unknown_inst(inst, addr); break; } break; case 3: if (op1 == 0x1f && op2 == 7) { // UNDEFINED this.abort_undefined_instruction("Signed multiplies", inst, addr); } switch (op1 >> 1 & 3) { case 0: if ((op1 & 1) === 0 && op2 === 0) { var rd = bitops.get_bits(inst, 15, 12); if (rd == 0xf) { // USAD8 this.abort_not_impl("USAD8", inst, addr); } else { // USADA8 this.abort_not_impl("USADA8", inst, addr); } break; } this.abort_unknown_inst(inst, addr); break; case 1: if ((op2 & 3) == 2) { // SBFX return "sbfx"; } this.abort_unknown_inst(inst, addr); break; case 2: if ((op2 & 3) === 0) { rn = inst & 0xf; if (rn == 0xf) { // BFC return "bfc"; } else { // BFI return "bfi"; } break; } this.abort_unknown_inst(inst, addr); break; case 3: if ((op2 & 3) == 2) { // UBFX return "ubfx"; } this.abort_unknown_inst(inst, addr); break; default: break; } break; default: break; } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.decode = function(inst, addr) { /* * bits[31:28]: cond * bits[27:25]: op1 * bit[4]: op */ var cond = inst >>> 28; var op = (inst >>> 4) & 1; var op1 = (inst >>> 25) & 7; var op2 = null; var tmp = null; var rn = null; var coproc = null; this.shift_t = 0; this.shift_n = 0; this.carry_out = 0; this.overflow = 0; if (inst == 0xeef06a10) return "vmrs"; if (cond == 0xf) { // Unconditional instructions return this.decode_uncond(inst, addr); } else { // cond != 0xf switch (op1 >> 1) { case 0: // Data-processing and miscellaneous instructions return this.decode_datamisc(inst, addr); break; case 1: if (op1 & 1) { if (op) { // [27:25]=011 [4]=1 // Media instructions return this.decode_media(inst, addr); } else { // [27:25]=011 [4]=0 // Load/store word and unsigned byte #2 op1 = (inst >>> 20) & 0x1f; // A=1 B=0 if (op1 & 1) { if (op1 & 4) { // xx1x1 if (op1 == 7 || op1 == 15) { // 0x111 // LDRBT this.abort_not_impl("LDRBT", inst, addr); } else { // LDRB (register) return "ldrb_reg"; } } else { // xx0x1 if (op1 == 3 || op1 == 11) { // 0x011 // LDRT this.abort_not_impl("LDRT A2", inst, addr); } else { // LDR (register) return "ldr_reg"; } } } else { if (op1 & 4) { // xx1x0 if (op1 == 6 || op1 == 14) { // 0x110 // STRBT A2 return "strbt_a2"; } else { // STRB (register) return "strb_reg"; } } else { // xx0x0 if (op1 == 2 || op1 == 10) { // 0x010 // STRT this.abort_not_impl("STRT", inst, addr); } else { // STR (register) return "str_reg"; } } } } } else { // [27:25]=010 [4]=x // Load/store word and unsigned byte #1 op1 = (inst >>> 20) & 0x1f; // A=0 B=x if (op1 & 1) { if (op1 & 4) { // xx1x1 if (op1 == 7 || op1 == 15) { // 0x111 // LDRBT this.abort_not_impl("LDRBT", inst, addr); } else { rn = (inst >>> 16) & 0xf; if (rn == 0xf) { // LDRB (literal) this.abort_not_impl("LDRB (literal)", inst, addr); } else { // LDRB (immediate) return "ldrb_imm"; } } //break; } else { // xx0x1 if (op1 == 3 || op1 == 0xb) { // 0x011 // LDRT return "ldrt_a1"; } else { rn = (inst >>> 16) & 0xf; if (rn == 0xf) { // LDR (literal) return "ldr_lit"; } else { // LDR (immediate) return "ldr_imm"; } } } } else { if (op1 & 4) { // xx1x0 if (op1 == 6 || op1 == 14) { // 0x110 // STRBT A1 return "strbt_a1"; } else { // STRB (immediate) return "strb_imm"; } } else { // xx0x0 if (op1 == 2 || op1 == 10) { // 0x010 // STRT this.abort_not_impl("STRT", inst, addr); } else { // STR (immediate) return "str_imm"; } } } } break; case 2: // [27:26]=10 // Branch, branch with link, and block data transfer op = (inst >>> 20) & 0x3f; if (op & 0x20) { if (op & 0x10) { // BL, BLX (immediate) return "bl_imm"; } else { // [27:24]=1010 // B (branch) return "b"; } } else { if (op & 4) { if (op & 1) { var r = (inst >>> 15) & 1; if (r) { // LDM (exception return) return "ldm_er"; } else { // LDM (user registers) return "ldm_ur"; } } else { // STM (user registers) return "stm_ur"; } } else { if (op & 1) { switch (op >> 2 & 7) { // 0b11100 case 0: // LDMDA / LDMFA return "ldmda"; break; case 2: // LDM / LDMIA / LDMFD return "ldm"; break; case 4: // LDMDB / LDMEA return "ldmdb"; break; case 6: // LDMIB / LDMED return "ldmib"; break; default: this.abort_unknown_inst(inst, addr); break; } } else { switch (op >> 2 & 7) { // 0b11100 case 0: // STMDA / STMED this.abort_not_impl("STMDA / STMED", inst, addr); break; case 2: // STM / STMIA / STMEA return "stm"; break; case 4: // STMDB / STMFD return "stmdb"; break; case 6: // STMIB / STMFA return "stmib"; break; default: this.abort_unknown_inst(inst, addr); break; } } } } break; case 3: // [27:26]=11 // System call, and coprocessor instructions op1 = (inst >>> 20) & 0x3f; op = (inst >>> 4) & 1; if (op1 & 0x20) { if (op1 & 0x10) { // SVC (previously SWI) return "svc"; } else { coproc = (inst >>> 8) & 0xf; if (op) { if ((coproc >> 1) == 5) { // 0b101x // Advanced SIMD, VFP // 8, 16, and 32-bit transfer between ARM core and extension registers this.abort_simdvfp_inst(inst, addr); } else { if (op1 & 1) { // cond != 1111 // MRC, MRC2 A1 return "mrc_a1"; } else { // cond != 1111 // MCR, MCR2 A1 return "mcr_a1"; } } } else { if ((coproc >> 1) == 5) { // 0b101x // VFP data-processing instructions this.abort_simdvfp_inst(inst, addr); } else { // CDP, CDP2 throw "UND"; } } } } else { if ((op1 >> 3) === 0 && (op1 & 2) === 0) { // 000x0x switch (op1 >> 1) { case 0: // UNDEFINED this.abort_undefined_instruction("System call, and coprocessor instructions", inst, addr); break; case 2: coproc = bitops.get_bits(inst, 11, 8); if ((coproc >> 1) == 5) { // 0b101x // 64-bit transfers between ARM core and extension registers this.abort_simdvfp_inst(inst, addr); } else { if (op1 & 1) { // MRRC, MRRC2 this.abort_not_impl("MRRC, MRRC2", inst, addr); } else { // MCRR, MCRR2 this.abort_not_impl("MCRR, MCRR2", inst, addr); } } break; default: this.abort_unknown_inst(inst, addr); break; } } else { coproc = bitops.get_bits(inst, 11, 8); if ((coproc >> 1) == 5) { // 0b101x // Advanced SIMD, VFP // Extension register load/store instructions this.abort_simdvfp_inst(inst, addr); } else { if (op1 & 1) { rn = bitops.get_bits(inst, 19, 16); if (rn == 0xf) { // LDC, LDC2 (literal) throw "UND"; } else { // LDC, LDC2 (immediate) throw "UND"; } } else { // STC, STC2 throw "UND"; } } } } break; default: break; } } this.abort_unknown_inst(inst, addr); return null; }; ARMv7_CPU.prototype.interrupt = function(irq) { logger.log("got interrupt"); this.spsr_irq = this.clone_psr(this.cpsr); this.regs_irq[14] = this.get_pc() - 4; this.change_mode(this.IRQ_MODE); this.cpsr.i = 1; this.cpsr.a = 1; var cp15 = this.coprocs[15]; this.regs[15] = cp15.interrupt_vector_address + 0x18; }; ARMv7_CPU.prototype.data_abort = function() { logger.log("got data abort"); this.spsr_abt = this.clone_psr(this.cpsr); this.regs_abt[14] = this.get_pc(); this.change_mode(this.ABT_MODE); this.cpsr.i = 1; var cp15 = this.coprocs[15]; this.regs[15] = cp15.interrupt_vector_address + 0x10; }; ARMv7_CPU.prototype.prefetch_abort = function() { logger.log("got prefetch abort"); this.spsr_abt = this.clone_psr(this.cpsr); this.regs_abt[14] = this.get_pc() - 4; this.change_mode(this.ABT_MODE); this.cpsr.i = 1; var cp15 = this.coprocs[15]; this.regs[15] = cp15.interrupt_vector_address + 0x0c; }; ARMv7_CPU.prototype.supervisor = function() { logger.log("got svc"); this.spsr_svc = this.clone_psr(this.cpsr); this.regs_svc[14] = this.get_pc() - 4; this.change_mode(this.SVC_MODE); this.cpsr.i = 1; var cp15 = this.coprocs[15]; this.regs[15] = cp15.interrupt_vector_address + 0x08; }; ARMv7_CPU.prototype.undefined_instruction = function() { logger.log("undef instr"); this.spsr_und = this.clone_psr(this.cpsr); this.regs_und[14] = this.get_pc() - 4; this.change_mode(this.UND_MODE); this.cpsr.i = 1; var cp15 = this.coprocs[15]; this.regs[15] = cp15.interrupt_vector_address + 0x04; }; ================================================ FILE: js/armv7-mmu.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ /* * ARMv7 MMU (VMPA) */ function ARMv7_MMU(cpu, memctlr) { this.cpu = cpu; this.enabled = false; this.baseaddr0 = 0; this.baseaddr1 = 0; this.memctlr = memctlr; this.asid = 0; this.width = 0; this.mask = (1 << (31 - this.width - 20 + 1)) - 1; this.cp15 = null; this.check_unaligned = false; } ARMv7_MMU.prototype.trans_to_phyaddr = function(vaddr, is_write) { if (this.enabled) { return this.walk_table(vaddr, is_write); } else { return vaddr; } }; /* * Page Table Walk */ ARMv7_MMU.prototype.ld_word = function(addr) { return this.memctlr.ld_word(addr); }; ARMv7_MMU.prototype.get_1st_ptaddr = function(vaddr) { var index = (vaddr >>> 20) & this.mask; var ptaddr; if (this.width) { var is_zero = bitops.get_bits(vaddr, 31, 32 - this.width) === 0; if (is_zero) ptaddr = bitops.set_bits(this.baseaddr0, 13 - this.width, 2, index); else ptaddr = bitops.set_bits(this.baseaddr1, 13, 2, index); return bitops.clear_bits(ptaddr, 1, 0); } else { return this.baseaddr0 + (index << 2); } }; ARMv7_MMU.prototype.get_2nd_ptaddr = function(vaddr, table) { var index = (vaddr >>> 12) & 0xff; var tmp = table & 0xfffffc00; if (tmp < 0) tmp += 0x100000000; return tmp + (index << 2); }; ARMv7_MMU.prototype.check_permission = function(vaddr, ap2, ap10, is_write, is_section) { if (ap2) { switch (ap10) { case 0: throw "Reserved"; break; case 1: if (is_write || !this.cpu.is_priviledged()) throw "Permission Fault: ap2 == 1, ap10 == 1"; break; case 2: // Deprecated if (is_write) throw "Permission Fault: ap2 == 1, ap10 == 2"; break; case 3: if (is_write) { if (is_section) this.cp15.set_memory_abort(vaddr, this.cp15.PERMISSION_FAULT_SECTION, is_write); else this.cp15.set_memory_abort(vaddr, this.cp15.PERMISSION_FAULT_PAGE, is_write); throw "PF"; } break; default: throw "Unknown ap10"; break; } } else { switch (ap10) { case 0: if (is_section) this.cp15.set_memory_abort(vaddr, this.cp15.PERMISSION_FAULT_SECTION, is_write); else this.cp15.set_memory_abort(vaddr, this.cp15.PERMISSION_FAULT_PAGE, is_write); break; case 1: if (!this.cpu.is_priviledged()) throw "Permission Fault: ap2 == 0, ap10 == 1"; break; case 2: if (is_write && !this.cpu.is_priviledged()) throw "Permission Fault: ap2 == 0, ap10 == 2"; break; case 3: // Full access break; default: throw "Unknown ap10"; break; } } }; ARMv7_MMU.prototype.check_permission_table1 = function(vaddr, table, is_write) { var ap2 = (table >>> 15) & 1; var ap10 = (table >>> 10) & 3; this.check_permission(vaddr, ap2, ap10, is_write, true); }; ARMv7_MMU.prototype.check_permission_table2 = function(vaddr, table, is_write) { var ap2 = (table >>> 9) & 1; var ap10 = (table >>> 4) & 3; this.check_permission(vaddr, ap2, ap10, is_write, false); }; ARMv7_MMU.prototype.need_perm_check = function(table, is_supersection) { var domain; if (is_supersection) domain = this.cp15.domains[0]; else domain = this.cp15.domains[(table >>> 5) & 0xf]; switch (domain) { case 0: throw "Domain Fault"; break; case 1: return true; break; case 2: throw "Domain Reserved"; break; case 3: return false; break; default: throw "Unknown Domain"; break; } throw "Unknown Domain"; }; ARMv7_MMU.prototype.walk_table = function(vaddr, is_write) { var paddr; var ptaddr1 = this.get_1st_ptaddr(vaddr); /* * First-level descriptors */ var table1 = this.ld_word(ptaddr1); var format = table1 & 3; switch (format) { case 0: //throw "Translation fault (1st 0): " + vaddr.toString(16); this.cp15.set_memory_abort(vaddr, this.cp15.TRANS_FAULT_SECTION); throw "PF"; break; case 1: // Small Pages or Large Pages. See the below. break; case 2: var is_supersection = (table1 >>> 18) & 1; if (is_supersection) { // Supersection if (this.need_perm_check(table1, true)) this.check_permission(vaddr, table1, is_write); throw "Supersection"; } else { // Section if (this.need_perm_check(table1)) this.check_permission_table1(vaddr, table1, is_write); var tmp = table1 & 0xfff00000; if (tmp < 0) tmp += 0x100000000; paddr = tmp + (vaddr & 0x000fffff); } return paddr; case 3: throw "Translation fault (1st 3): " + vaddr.toString(16); break; default: throw "Unknown format: " + format.toString(); break; } /* * Second-level descriptors */ var ptaddr2 = this.get_2nd_ptaddr(vaddr, table1); var table2 = this.ld_word(ptaddr2); if (this.need_perm_check(table1)) // table1 is correct this.check_permission_table2(vaddr, table2, is_write); var format2 = table2 & 3; switch (format2) { case 0: this.cp15.set_memory_abort(vaddr, this.cp15.TRANS_FAULT_PAGE); throw "PF"; break; case 1: throw "Large page: " + vaddr.toString(16); break; case 2: case 3: // See the below; break; default: throw "Unknown format: " + format2.toString(); break; } var tmp2 = table2 & 0xfffff000; if (tmp2 < 0) tmp2 += 0x100000000; paddr = tmp2 + (vaddr & 0x00000fff); return paddr; }; ARMv7_MMU.prototype.dump_table = function(table, ptaddr) { display.log(ptaddr.toString(16) + " PT=" + toStringHex32(table) + "(" + toStringBinInst(table) + ")"); }; function toStringPageTable(table, addr) { if (table) return toStringHex32(table) + "@" + toStringHex32(addr); else return "null@" + toStringHex32(addr); } ARMv7_MMU.prototype.show_table = function(vaddr) { var paddr; var str = ""; str += vaddr.toString(16); var ptaddr = this.get_1st_ptaddr(vaddr); /* * First-level descriptor */ var table = this.ld_word(ptaddr); str += " => " + toStringPageTable(table, ptaddr); var format = bitops.get_bits(table, 1, 0); switch (format) { case 0: str += "(Invalid)"; return str; case 1: // See the below; break; case 2: is_supersection = bitops.get_bit(table, 18); if (is_supersection) { str += "(Supersection)"; return str; } else { str += "(Section)"; paddr = bitops.copy_bits(table, 19, 0, vaddr); } str += " => " + paddr.toString(16); return str; case 3: str += "(Reserved)"; return str; default: return null; } /* * Second-level descriptor */ ptaddr = this.get_2nd_ptaddr(vaddr, table); table = this.ld_word(ptaddr); str += " => " + toStringPageTable(table, ptaddr); if (!table) return str; var format2 = bitops.get_bits(table, 1, 0); switch (format2) { case 0: str += "(Invalid)"; return str; case 1: str += "(LargePage)"; return str; case 2: case 3: // See the below; break; default: return null; } paddr = bitops.copy_bits(table, 11, 0, vaddr); str += " => " + paddr.toString(16); return str; }; ARMv7_MMU.prototype.show_current_tables = function() { var size; switch (this.width) { case 0: size = 16*1024; break; case 1: size = 8*1024; break; case 2: size = 4*1024; break; case 3: size = 2*1024; break; case 4: size = 1024; break; case 5: size = 512; break; case 6: size = 256; break; case 7: size = 128; break; default: throw "Uknown width: " + this.width.toString(); break; } var str = ""; var addr; for (addr=0xc0000000; addr < 0xc1000000; addr += 0x10000) str += this.show_table(addr) + "\n"; for (addr=0xf8000000; addr < 0xf8100000; addr += 0x10000) str += this.show_table(addr) + "\n"; for (addr=0xf8e00000; addr < 0xf8f00000; addr += 0x10000) str += this.show_table(addr) + "\n"; display.log(str); }; ARMv7_MMU.prototype.save = function() { var params = Object(); params.baseaddr0 = this.baseaddr0; params.baseaddr1 = this.baseaddr1; params.width = this.width; return params; }; ARMv7_MMU.prototype.restore = function(params) { this.baseaddr0 = params.baseaddr0; this.baseaddr1 = params.baseaddr1; this.width = params.width; this.mask = (1 << (31 - this.width - 20 + 1)) - 1; }; ARMv7_MMU.prototype.dump = function() { var msg = ""; msg += "baseaddr0: " + toStringHex32(this.baseaddr0) + "\n"; msg += "baseaddr1: " + toStringHex32(this.baseaddr1) + "\n"; msg += "width: " + this.width + "\n"; display.log(msg); }; ARMv7_MMU.prototype.dump_phymem = function(addr) { for (var i=0; i < 100; i++) { var cur = addr + i*4; var val = this.ld_word(cur); if (val !== undefined && val !== null) display.log(toStringHex32(cur) + ": " + toStringHex32(val) + "\t" + toStringAscii(val)); else display.log(toStringHex32(cur) + ": (null)"); } }; ARMv7_MMU.prototype.dump_virmem = function(addr) { for (var i=0; i < 100; i++) { var cur = addr + i*4; var phyaddr = this.trans_to_phyaddr(cur); var val = this.ld_word(phyaddr); if (val !== undefined && val !== null) display.log(toStringHex32(phyaddr) + ": " + toStringHex32(val) + "\t" + toStringAscii(val)); else display.log(toStringHex32(phyaddr) + ": (null)"); } }; ================================================ FILE: js/bitops.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ function assert(cond, val) { if (!cond) { if (typeof val == "string") throw "Assertion failed: " + val; if (val) throw "Assertion failed: " + val.toString(16) + "(" + val.toString(2) + ")"; else throw "Assertion failed." } } function assert2(x, y, str) { if (x != y) { var msg = ""; if (str == undefined) throw "Assertion failed: " + toStringNum(x) + " != " + toStringNum(y); else throw "Assertion failed(" + str + "): " + toStringNum(x) + " != " + toStringNum(y); } } function BitOps() { } BitOps.prototype.xor = function(x, y) { var ret = x ^ y; if (ret >= 0) return ret; else return ret + 0x100000000; }; BitOps.prototype.xor64 = function(x, y) { var xh = Math.floor(x / 0x100000000); var yh = Math.floor(y / 0x100000000); var xl = x % 0x100000000; var yl = y % 0x100000000; return this.xor(xh, yh) * 0x100000000 + this.xor(xl, yl); }; BitOps.prototype.and = function(x, y) { var ret = x & y; if (ret >= 0) return ret; else return ret + 0x100000000; }; BitOps.prototype.and64 = function(x, y) { var xh = Math.floor(x / 0x100000000); var yh = Math.floor(y / 0x100000000); var xl = x % 0x100000000; var yl = y % 0x100000000; return this.and(xh, yh) * 0x100000000 + this.and(xl, yl); }; BitOps.prototype.or = function(x, y) { var ret = x | y; if (ret >= 0) return ret; else return ret + 0x100000000; }; BitOps.prototype.or64 = function(x, y) { var xh = Math.floor(x / 0x100000000); var yh = Math.floor(y / 0x100000000); var xl = x % 0x100000000; var yl = y % 0x100000000; return this.or(xh, yh) * 0x100000000 + this.or(xl, yl); }; BitOps.prototype.not = function(x) { var ret = ~x; if (ret >= 0) return ret; else return ret + 0x100000000; }; BitOps.prototype.lowest_set_bit = function(val, len) { var pos = 0; for (var i=0; i < len; i++) { if (val & 1 << i) return i; } return len; }; BitOps.prototype.bit_count = function(val, len) { var count = 0; for (var i=0; i < len; i++) { if (val & 1 << i) count++; } return count; }; BitOps.prototype.clear_bit = function(uint, pos) { if (uint < 0x80000000 && pos < 31) return uint & ~(1 << pos); if (pos < 31) { var ret = uint & ~(1 << pos); if (ret < 0) ret += 0x100000000; return ret; } else { if (uint >= 0x80000000) return uint - 0x80000000; else return uint; } /* var uints = toStringBin32(uint); var ret = ""; for (var i=0; i < 32; i++) { if ((32-i-1) == pos) ret += "0"; else ret += uints[i]; } return parseInt(ret, 2); */ }; BitOps.prototype.clear_bits = function(uint, start, end) { if (uint < 0x80000000 && start < 31) return uint & ~(((1 << (start+1)) - 1) & ~((1 << end) - 1)); if (start < 31) { var ret = uint & ~(((1 << (start+1)) - 1) & ~((1 << end) - 1)); if (ret < 0) ret += 0x100000000; return ret; } var uints = toStringBin32(uint); var ret = ""; for (var i=0; i < 32; i++) { if ((32-i-1) <= start && (32-i-1) >= end) ret += "0"; else ret += uints[i]; } return parseInt(ret, 2); }; BitOps.prototype.set_bits = function(uint, start, end, val) { return this.or(this.clear_bits(uint, start, end), this.lsl(val, end)); }; BitOps.prototype.set_bit = function(uint, pos, val) { if (val) if (pos == 31) return this.or(uint, 0x80000000); else return this.or(uint, val << pos); else if (pos == 31) return this.clear_bit(uint, 31); else return this.and(uint, this.not(1 << pos)); }; BitOps.prototype.get_bit = function(uint, pos, dummy) { //assert(dummy === undefined, "get_bit: extra 3rd argument"); return (uint & (1 << pos)) >>> pos; }; BitOps.prototype.get_bit64 = function(ulong, pos, dummy) { //assert(dummy === undefined, "get_bit64: extra 3rd argument"); if (pos > 31) { var ulong_h = Math.floor(ulong / 0x100000000); return this.get_bit(ulong_h, pos - 31); } else { var ulong_l = ulong % 0x100000000; return this.get_bit(ulong_l, pos); } }; BitOps.prototype.zero_extend = function(val, n) { return val; }; BitOps.prototype.zero_extend64 = function(val, n) { return val; }; BitOps.prototype.get_bits = function(uint, start, end) { //assert(end != undefined, "get_bits: missing 3rd argument"); if (start == 31) { if (end !== 0) return uint >>> end; if (uint > 0xffffffff) this.and(uint, 0xffffffff); else return uint; } //return this.and(uint >>> end, ((1 << (start - end + 1)) - 1)); var ret = (uint >>> end) & ((1 << (start - end + 1)) - 1); if (ret >= 0x100000000) return ret - 0x100000000; else return ret; }; BitOps.prototype.get_bits64 = function(ulong, start, end) { assert(end != undefined, "get_bits64: missing 3rd argument"); assert(start != end, "get_bits64: start == end"); //assert(start < 32 && end < 32, "get_bits64: too high range"); if (ulong < 0x80000000 && start < 31 && end < 31) this.get_bits(ulong, start, end); var ulong_h = Math.floor(ulong / 0x100000000); var ulong_l = ulong % 0x100000000; var ret = 0; if (start > 31) { if (start == 32) { ret += this.get_bit(ulong_h, 0) << (31 - end + 1); } else { if (end > 31) ret += this.get_bits(ulong_h, start-32, end-32); else ret += this.get_bits(ulong_h, start-31, 0) << (31 - end + 1); } } if (end <= 31) { if (end == 31) ret += this.get_bit(ulong_l, 31); else ret += this.get_bits(ulong_l, start < 31 ? start : 31, end); } return ret; }; BitOps.prototype.sign_extend = function(x, x_len, n) { assert(n !== undefined); var sign = this.get_bit(x, x_len - 1); if (sign) { /* var extend = ""; for (var i=0; i < (n-x_len); i++) extend += "1"; var str = extend + toStringBin(x, x_len); return parseInt32(str, 2); */ if (n == 32) var tmp = 0xffffffff; else var tmp = (1<= 0 && ret >= x) { return ret; } else { return x * Math.pow(2, n); } }; BitOps.prototype.lsr = function(x, n) { return (n == 32) ? 0 : x >>> n; }; BitOps.prototype.asr = function(x, n) { if (n == 32) return 0; var ret = x >> n; if (ret < 0) ret += 0x100000000; return ret; }; BitOps.prototype.sint32 = function(x) { return x & 0xffffffff; }; BitOps.prototype.uint32 = function(x) { return this.and64(x, 0xffffffff); }; BitOps.prototype.toUint32 = function(x) { if (x < 0) { if (x < (1 << 31)) { //throw "toUint32: too small"; x = x + 0x10000000000000000; } else { x = x + 0x100000000; } } return this.and64(x, 0xffffffff); }; BitOps.prototype.copy_bits = function(dest, start, end, src) { return this.set_bits(dest, start, end, this.get_bits(src, start, end)); }; BitOps.prototype.copy_bit = function(dest, pos, src) { return this.set_bit(dest, pos, this.get_bit(src, pos)); }; BitOps.prototype.ror = function(value, amount) { var m = amount % 32; //var lo = this.get_bits(value, m-1, 0); //var result = this.or(value >>> m, this.lsl(lo, (32-m))); var lo = value & ((1 << m) - 1); var result = (value >>> m) + this.lsl(lo, (32-m)); //assert(result >= 0 && result <= 0xffffffff, "ror"); return result; }; BitOps.prototype.count_leading_zero_bits = function(val) { var n = 0; for (var i=31; i >= 0; i--) { if (bitops.get_bit(val, i)) break; n++; } return n; }; BitOps.prototype.test = function() { assert2(this.clear_bit(0xffffffff, 0), 0xfffffffe); assert2(this.clear_bit(0x13, 31), 0x13); assert2(this.clear_bit(0x13, 0), 0x12); assert2(this.clear_bits(0xffffffff, 31, 0), 0); assert2(this.clear_bits(0xffffffff, 31, 16), 0x0000ffff); assert2(this.clear_bits(0xffffffff, 15, 0), 0xffff0000); assert2(this.clear_bits(0xffffffff, 15, 12), 0xffff0fff); assert2(this.clear_bits(0x0fffffff, 15, 12), 0x0fff0fff); var tmp = 0; assert(this.xor(0xffffffff, 0xffffffff) == 0); assert(this.xor(0x11111111, 0x22222222) == 0x33333333); assert(this.xor(0xf0000000, 0xf0000000) == 0); assert(this.xor64(0xffffffff, 0xffffffff) == 0); assert(this.xor64(0x11111111, 0x22222222) == 0x33333333); assert(this.xor64(0xf0000000, 0xf0000000) == 0); assert(this.xor64(0x1f0000000, 0xf0000000) == 0x100000000); assert(this.not(0xffffffff) == 0x00000000); assert(this.not(0x00000000) == 0xffffffff); assert(this.not(0x00000001) == 0xfffffffe); assert(this.not(0x80000000) == 0x7fffffff); assert(this.or(0x11111111, 0x22222222) == 0x33333333); assert(this.or(0xffffffff, 0x00000000) == 0xffffffff); assert(this.or(0xffffffff, 0xffffffff) == 0xffffffff); assert(this.or64(0x11111111, 0x22222222) == 0x33333333); assert(this.or64(0xffffffff, 0x00000000) == 0xffffffff); assert(this.or64(0xffffffff, 0xffffffff) == 0xffffffff); assert(this.or64(0xf00000000, 0x00000000) == 0xf00000000); assert(this.or64(0xf00000000, 0x0000000f) == 0xf0000000f); assert(this.and(0x11111111, 0x22222222) == 0); assert(this.and(0xffffffff, 0) == 0); assert(this.and64(0x11111111, 0x22222222) == 0); assert2(this.and64(0xffffffff, 0), 0); assert2(this.and64(0xffffffffffff, 0), 0); assert2(this.and64(0xffffffffffff, 0xffffffff), 0xffffffff); assert2(this.get_bit(0xffffffff, 31), 1); assert2(this.get_bit(0xffffffff, 0), 1); assert(this.get_bit(0x80000000, 31) == 1); assert(this.get_bit(0, 31) == 0); assert(this.get_bit(0, 0) == 0); assert(this.get_bit(0x7fffffff, 31) == 0); assert2(this.get_bit(0x80000000, 31), 1); assert(this.get_bit64(0xffffffff, 31) == 1); assert2(this.get_bit64(0xffffffff, 0), 1); assert(this.get_bit64(0x80000000, 31) == 1); assert(this.get_bit64(0, 31) == 0); assert(this.get_bit64(0, 0) == 0); assert(this.get_bit64(0x7fffffff, 31) == 0); assert(this.get_bit64(0xffffffffffff, 31) == 1); assert2(this.get_bit64(0xffffffffffff, 50), 0); assert(this.get_bits(0xffffffff, 31, 0) == 0xffffffff); assert(this.get_bits(0xffffffff, 31, 16) == 0xffff); assert(this.get_bits(0, 31, 0) == 0); assert(this.get_bits(0x13, 4, 0) == 0x13, this.get_bits(0x13, 4, 0)); assert2(this.get_bits(0xf0000000, 31, 27), 0x1e); assert2(this.get_bits(0xc0000000, 31, 27), 0x18); assert2(this.get_bits64(0xffffffff, 31, 0), 0xffffffff); assert2(this.get_bits64(0xffffffff, 31, 16), 0xffff); assert2(this.get_bits64(0, 31, 0), 0); assert2(this.get_bits64(0x13, 4, 0), 0x13); assert2(this.get_bits64(0x100000000, 31, 0), 0); assert2(this.get_bits64(0x100000000, 31, 0), 0); assert2(this.get_bits64(0x100000000, 32, 31), 2); assert2(this.get_bits64(0x300000000, 32, 31), 2); assert2(this.get_bits64(0x180000000, 32, 31), 3); assert2(this.get_bits64(0xf00000000, 33, 32), 3); assert2(this.get_bits64(0xf00000000, 34, 33), 3); assert2(this.get_bits64(0x180000000, 34, 31), 3); assert2(this.get_bits64(0x180000000, 34, 30), 6); assert2(this.get_bits64(0x100000000, 51, 32), 1); assert(this.set_bit(0xffffffff, 0, 0) == 0xfffffffe, this.set_bit(0xffffffff, 0, 0)); assert(this.set_bit(0xffffffff, 31, 0) == 0x7fffffff, this.set_bit(0xffffffff, 31, 0)); assert(this.set_bit(0xffffffff, 31, 1) == 0xffffffff, this.set_bit(0xffffffff, 31, 1)); assert(this.set_bit(0x13, 31, 0) == 0x13, this.set_bit(0x13, 31, 0)); assert(this.set_bit(0, 31, 1) == 0x80000000); assert(this.set_bit(0, 0, 1) == 1); assert(this.set_bit(0, 2, 1) == 4, this.set_bit(0, 2, 1)); assert(this.set_bits(0xffffffff, 31, 0, 0) == 0); assert(this.set_bits(0xffffffff, 15, 0, 0) == 0xffff0000, this.set_bits(0xffffffff, 15, 0, 0)); assert(this.set_bits(0, 4, 0, 0x13) == 0x13); assert2(this.set_bits(0xf0000000, 31, 27, 0x1e), 0xf0000000); assert2(this.set_bits(0x00000000, 31, 27, 0x1e), 0xf0000000); assert2(this.set_bits(0xf0000000, 31, 27, 0x18), 0xc0000000); assert2(this.lsl(1, 1), 2); assert2(this.lsl(0xf0000000, 1), 0x1e0000000); assert2(this.lsl(0xffffffff, 1), 0x1fffffffe); assert2(this.lsl(0xf0f0f0f0, 4), 0xf0f0f0f00); assert2(this.lsl(0x100000000, 1), 0x200000000); assert2(this.lsr(1, 1), 0); assert2(this.lsr(0xf0000000, 1), 0x78000000); assert2(this.lsr(0xffffffff, 1), 0x7fffffff); assert2(this.lsr(0xf0f0f0f0, 4), 0x0f0f0f0f); assert2(this.lsr(0x80000000, 32), 0); assert2(this.lsr(0x80000000, 1), 0x40000000); assert2(this.lsr(1, 1), 0); assert2(this.lsr(0xf0000000, 1), 0x78000000); assert2(this.lsr(0xffffffff, 1), 0x7fffffff); assert2(this.lsr(0xf0f0f0f0, 4), 0x0f0f0f0f); assert2(this.lsr(0x80000000, 32), 0); assert2(this.lsr(0x80000000, 1), 0x40000000); assert2(this.sint32(0x00000000), 0x00000000); assert2(this.sint32(0x80000000), 0x80000000 & 0xffffffff); assert2(this.sint32(0x100000000), 0x00000000); assert2(this.uint32(0x00000000), 0x00000000); assert2(this.uint32(0x80000000), 0x80000000); assert2(this.uint32(0x100000000), 0x00000000); assert2(this.uint32(0xffffffff), 0xffffffff); assert2(this.uint32(0xfffffffff), 0xffffffff); assert2(this.sign_extend(0, 26, 32), 0); //assert2(this.sign_extend(0, 1, 32), this.sint32(0)); //assert2(this.sign_extend(1, 1, 32), this.sint32(0xffffffff)); //assert2(this.sign_extend(0x0000ffff, 16, 32), this.sint32(0xffffffff)); //assert2(this.sign_extend(0x00007fff, 16, 32), this.sint32(0x00007fff)); assert2(this.sign_extend(0, 1, 32), 0); assert2(this.sign_extend(1, 1, 32), 0xffffffff); assert2(this.sign_extend(0x0000ffff, 16, 32), 0xffffffff); assert2(this.sign_extend(0x00007fff, 16, 32), 0x00007fff); assert2(this.sign_extend(0xffffe3 << 2, 26, 32), 0xffffff8c); assert2(this.copy_bits(0xf0000000, 31, 27, 0), 0); assert2(this.copy_bits(0xf0000000, 31, 27, 0xc0000000), 0xc0000000); assert2(this.copy_bit(0, 0, 1), 1); assert2(this.copy_bit(1, 0, 0), 0); assert2(this.copy_bit(0xffffffff, 0, 0), 0xfffffffe); assert2(this.copy_bit(0xffffffff, 31, 0), 0x7fffffff); assert2(this.ror(0x10000000, 1), 0x08000000); assert2(this.ror(0x10000001, 1), 0x88000000); assert2(this.ror(0xffffffff, 1), 0xffffffff); assert2(this.ror(0x0000ffff, 16), 0xffff0000); assert2(this.ror(0x000ffff0, 16), 0xfff0000f); assert2(this.count_leading_zero_bits(0), 32); assert2(this.count_leading_zero_bits(0x80000000), 0); assert2(this.count_leading_zero_bits(0x00008000), 16); display.log("All BitOps tests passed successfully"); }; ================================================ FILE: js/display.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ function Display(display_id, options) { this.display_id = display_id; } Display.prototype.log = function(content) { var display = document.getElementById(this.display_id); display.innerHTML += content + "\n"; }; Display.prototype.wipe = function() { var display = document.getElementById(this.display_id); display.innerHTML = ""; }; ================================================ FILE: js/filesystem.js ================================================ /*! * Javascript ARMv7 Emulator * * Copyright 2012, Ryota Ozaki * Dual licensed under the MIT or GPL Version 2 licenses. */ window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; function fsTestAvailability(requestByte, sCallback, eCallback) { if (!window.webkitStorageInfo) { if (eCallback) eCallback(); return; } window.webkitStorageInfo.requestQuota(window.PERSISTENT, requestByte, function(grantedBytes) { window.requestFileSystem(window.PERSISTENT, requestByte, function(fs) { if (sCallback) sCallback(fs); }, eCallback); }, eCallback); } function HTML5FileSystem(root, requestByte) { this.rootDirectory = root; this.requestByte = requestByte; this.enabled = false; this.fs = null; var that = this; fsTestAvailability(requestByte, function(fs) { that.fs = fs; that.enabled = true; fs.root.getDirectory(that.rootDirectory, {create: true}, null, errorHandler); }, errorHandler); } function errorHandler(e) { var msg = ''; if (window.FileError === undefined) { console.log('Error: Unknown'); return; } switch (e.code) { case FileError.ABORT_ERR: msg = 'ABORT_ERR'; break; case FileError.ENCODING_ERR: msg = 'ENCODING_ERR'; break; case FileError.QUOTA_EXCEEDED_ERR: msg = 'QUOTA_EXCEEDED_ERR'; break; case FileError.NOT_FOUND_ERR: msg = 'NOT_FOUND_ERR'; break; case FileError.NOT_READABLE_ERR: msg = 'NOT_READABLE_ERR'; break; case FileError.NO_MODIFICATION_ALLOWED_ERR: msg = 'NO_MODIFICATION_ALLOWED_ERR'; break; case FileError.SECURITY_ERR: msg = 'SECURITY_ERR'; break; case FileError.INVALID_MODIFICATION_ERR: msg = 'INVALID_MODIFICATION_ERR'; break; case FileError.INVALID_STATE_ERR: msg = 'INVALID_STATE_ERR'; break; case FileError.SYNTAX_ERR: msg = 'SYNTAX_ERR'; break; case FileError.TYPE_MISMATCH_ERR: msg = 'TYPE_MISMATCH_ERR'; break; case FileError.PATH_EXISTS_ERR: msg = 'PATH_EXISTS_ERR'; break; default: msg = 'Unknown Error'; break; }; console.log('Error: ' + msg); } HTML5FileSystem.prototype.fileWrite = function(name, data, as, callback) { var that = this; this.getRoot(function(dir) { dir.getFile(name, {create: true}, function(entry) { entry.createWriter(function(fileWriter) { fileWriter.onwriteend = function(e) { fileWriter.onwriteend = function(e) { console.log("Write done"); if (callback) callback(_entry); }; fileWriter.onerror = function(e) { console.log('Write failed: ' + e.toString()); }; if (as.text) fileWriter.write(new Blob([data], {type: "text/plain"})); else fileWriter.write(new Blob([data], {type: "example/binary"})); }; fileWriter.onerror = function(e) { console.log('Truncate failed: ' + e.toString()); }; fileWriter.truncate(0); }); }); }); }; HTML5FileSystem.prototype.fileRead = function(name, as, callback) { this.getRoot(function(dir) { dir.getFile(name, {create: true}, function(entry) { entry.file(function(file) { var reader = new FileReader(); reader.onloadend = function() { if (callback) callback(this.result); }; reader.onerror = function(e) { console.log('Read failed: ' + e.toString()); }; if (as.text) reader.readAsText(file); else reader.readAsArrayBuffer(file); }); }, errorHandler); }); }; HTML5FileSystem.prototype.getRoot = function(callback) { this.fs.root.getDirectory(this.rootDirectory, {}, function(entry) { entry.getMetadata(function(metadata) { entry.size = metadata.size; entry.mtime = metadata.modificationTime; callback(entry); }); }); }; HTML5FileSystem.prototype.getEntry = function(parent, name, callback, errcb) { var _callback = function(entry) { entry.getMetadata(function(metadata) { entry.size = metadata.size; entry.mtime = metadata.modificationTime; callback(entry); }); }; parent.getFile(name, {}, _callback, function(e) { if (e.code == FileError.TYPE_MISMATCH_ERR) { parent.getDirectory(name, {}, _callback); } else { errorHandler(e); if (errcb) errcb(e); } }); }; HTML5FileSystem.prototype.getDirectoryEntries = function(dirEntry, callback) { var toArray = function(list) { return Array.prototype.slice.call(list || [], 0); } var readDirectoryEntries = function(entry, handler) { var dirReader = entry.createReader(); var entries = []; var readEntries = function() { dirReader.readEntries (function(results) { if (results.length === 0) { handler(entries.sort()); } else { entries = entries.concat(toArray(results)); readEntries(); } }, errorHandler); }; readEntries(); } readDirectoryEntries(dirEntry, function(entries) { var ret_entries = []; function getMetadata(entry) { if (!entry) { callback(ret_entries); return; } entry.getMetadata(function(metadata) { entry.size = metadata.size; entry.mtime = metadata.modificationTime; ret_entries.push(entry); if (entries.length) getMetadata(entries.shift()); else callback(ret_entries); }); }; getMetadata(entries.shift()); }); }; HTML5FileSystem.prototype.create = function(parent, name, is, callback) { var getEntry = is.file ? parent.getFile : parent.getDirectory; getEntry.call(parent, name, {create: true}, function(entry) { entry.getMetadata(function(metadata) { entry.size = metadata.size; entry.mtime = metadata.modificationTime; callback(entry); }); }); }; HTML5FileSystem.prototype.truncate = function(entry, callback) { entry.createWriter(function(fileWriter) { fileWriter.truncate(0); callback(entry); }); }; HTML5FileSystem.prototype.write = function(entry, buffer, offset, callback) { entry.createWriter(function(fileWriter) { fileWriter.onwriteend = function(e) { callback(entry); }; fileWriter.onerror = function(e) { console.log('Write failed: ' + e.toString()); }; fileWriter.seek(offset); var view = new Uint8Array(buffer); fileWriter.write(new Blob([view])); }); }; HTML5FileSystem.prototype.read = function(entry, offset, count, callback) { entry.file(function(file) { var fileReader = new FileReader(); fileReader.onloadend = function(e) { var ret = []; var buffer = this.result; if (offset > buffer.byteLength) { callback([]); return; } var size = buffer.byteLength < count ? buffer.byteLength : count; if ((size + offset) > buffer.byteLength) size = buffer.byteLength - offset; var data = new Uint8Array(buffer, offset, size); for (var i=0; i < size; i++) ret.push(data[i]); callback(ret); }; fileReader.readAsArrayBuffer(file); }); }; HTML5FileSystem.prototype.remove = function(entry, callback) { entry.remove(function() { callback(); }, function(e) { // FIXME: when directory is not empty errorHandler(e); callback(); }); }; HTML5FileSystem.prototype.rename = function(dir, oldname, newname, callback) { this.getEntry(dir, oldname, function(entry) { entry.moveTo(dir, newname, function() { callback(); }, function(e) { // FIXME errorHandler(e); callback(); }); }); }; ================================================ FILE: js/jquery-1.7.2.js ================================================ /*! * jQuery JavaScript Library v1.7.2 * http://jquery.com/ * * Copyright 2011, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * Includes Sizzle.js * http://sizzlejs.com/ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * * Date: Wed Mar 21 12:46:34 2012 -0700 */ (function( window, undefined ) { // Use the correct document accordingly with window argument (sandbox) var document = window.document, navigator = window.navigator, location = window.location; var jQuery = (function() { // Define a local copy of jQuery var jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); }, // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$, // A central reference to the root jQuery(document) rootjQuery, // A simple way to check for HTML strings or ID strings // Prioritize #id over to avoid XSS via location.hash (#9521) quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, // Check if a string has a non-whitespace character in it rnotwhite = /\S/, // Used for trimming whitespace trimLeft = /^\s+/, trimRight = /\s+$/, // Match a standalone tag rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, // JSON RegExp rvalidchars = /^[\],:{}\s]*$/, rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, // Useragent RegExp rwebkit = /(webkit)[ \/]([\w.]+)/, ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, rmsie = /(msie) ([\w.]+)/, rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, // Matches dashed string for camelizing rdashAlpha = /-([a-z]|[0-9])/ig, rmsPrefix = /^-ms-/, // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return ( letter + "" ).toUpperCase(); }, // Keep a UserAgent string for use with jQuery.browser userAgent = navigator.userAgent, // For matching the engine and version of the browser browserMatch, // The deferred used on DOM ready readyList, // The ready event handler DOMContentLoaded, // Save a reference to some core methods toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, push = Array.prototype.push, slice = Array.prototype.slice, trim = String.prototype.trim, indexOf = Array.prototype.indexOf, // [[Class]] -> type pairs class2type = {}; jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { var match, elem, ret, doc; // Handle $(""), $(null), or $(undefined) if ( !selector ) { return this; } // Handle $(DOMElement) if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; } // The body element only exists once, optimize finding it if ( selector === "body" && !context && document.body ) { this.context = document; this[0] = document.body; this.selector = selector; this.length = 1; return this; } // Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = quickExpr.exec( selector ); } // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; doc = ( context ? context.ownerDocument || context : document ); // If a single string is passed in and it's a single tag // just do a createElement and skip the rest ret = rsingleTag.exec( selector ); if ( ret ) { if ( jQuery.isPlainObject( context ) ) { selector = [ document.createElement( ret[1] ) ]; jQuery.fn.attr.call( selector, context, true ); } else { selector = [ doc.createElement( ret[1] ) ]; } } else { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; } return jQuery.merge( this, selector ); // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); }, // Start with an empty selector selector: "", // The current version of jQuery being used jquery: "1.7.2", // The default length of a jQuery object is 0 length: 0, // The number of elements contained in the matched element set size: function() { return this.length; }, toArray: function() { return slice.call( this, 0 ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( num < 0 ? this[ this.length + num ] : this[ num ] ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set var ret = this.constructor(); if ( jQuery.isArray( elems ) ) { push.apply( ret, elems ); } else { jQuery.merge( ret, elems ); } // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; if ( name === "find" ) { ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; } else if ( name ) { ret.selector = this.selector + "." + name + "(" + selector + ")"; } // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, ready: function( fn ) { // Attach the listeners jQuery.bindReady(); // Add the callback readyList.add( fn ); return this; }, eq: function( i ) { i = +i; return i === -1 ? this.slice( i ) : this.slice( i, i + 1 ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, slice: function() { return this.pushStack( slice.apply( this, arguments ), "slice", slice.call(arguments).join(",") ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, end: function() { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: [].sort, splice: [].splice }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend({ noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } return jQuery; }, // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function( hold ) { if ( hold ) { jQuery.readyWait++; } else { jQuery.ready( true ); } }, // Handle when the DOM is ready ready: function( wait ) { // Either a released hold or an DOMready/load event and not yet ready if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( !document.body ) { return setTimeout( jQuery.ready, 1 ); } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.fireWith( document, [ jQuery ] ); // Trigger any bound ready events if ( jQuery.fn.trigger ) { jQuery( document ).trigger( "ready" ).off( "ready" ); } } }, bindReady: function() { if ( readyList ) { return; } readyList = jQuery.Callbacks( "once memory" ); // Catch cases where $(document).ready() is called after the // browser event has already occurred. if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready return setTimeout( jQuery.ready, 1 ); } // Mozilla, Opera and webkit nightlies currently support this event if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); // If IE event model is used } else if ( document.attachEvent ) { // ensure firing before onload, // maybe late but safe also for iframes document.attachEvent( "onreadystatechange", DOMContentLoaded ); // A fallback to window.onload, that will always work window.attachEvent( "onload", jQuery.ready ); // If IE and not a frame // continually check to see if the document is ready var toplevel = false; try { toplevel = window.frameElement == null; } catch(e) {} if ( document.documentElement.doScroll && toplevel ) { doScrollCheck(); } } }, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { return jQuery.type(obj) === "function"; }, isArray: Array.isArray || function( obj ) { return jQuery.type(obj) === "array"; }, isWindow: function( obj ) { return obj != null && obj == obj.window; }, isNumeric: function( obj ) { return !isNaN( parseFloat(obj) ) && isFinite( obj ); }, type: function( obj ) { return obj == null ? String( obj ) : class2type[ toString.call(obj) ] || "object"; }, isPlainObject: function( obj ) { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { // Not own constructor property must be Object if ( obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } } catch ( e ) { // IE8,9 Will throw exceptions on certain host objects #9897 return false; } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for ( key in obj ) {} return key === undefined || hasOwn.call( obj, key ); }, isEmptyObject: function( obj ) { for ( var name in obj ) { return false; } return true; }, error: function( msg ) { throw new Error( msg ); }, parseJSON: function( data ) { if ( typeof data !== "string" || !data ) { return null; } // Make sure leading/trailing whitespace is removed (IE can't handle it) data = jQuery.trim( data ); // Attempt to parse using the native JSON parser first if ( window.JSON && window.JSON.parse ) { return window.JSON.parse( data ); } // Make sure the incoming data is actual JSON // Logic borrowed from http://json.org/json2.js if ( rvalidchars.test( data.replace( rvalidescape, "@" ) .replace( rvalidtokens, "]" ) .replace( rvalidbraces, "")) ) { return ( new Function( "return " + data ) )(); } jQuery.error( "Invalid JSON: " + data ); }, // Cross-browser xml parsing parseXML: function( data ) { if ( typeof data !== "string" || !data ) { return null; } var xml, tmp; try { if ( window.DOMParser ) { // Standard tmp = new DOMParser(); xml = tmp.parseFromString( data , "text/xml" ); } else { // IE xml = new ActiveXObject( "Microsoft.XMLDOM" ); xml.async = "false"; xml.loadXML( data ); } } catch( e ) { xml = undefined; } if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { jQuery.error( "Invalid XML: " + data ); } return xml; }, noop: function() {}, // Evaluates a script in a global context // Workarounds based on findings by Jim Driscoll // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { if ( data && rnotwhite.test( data ) ) { // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox ( window.execScript || function( data ) { window[ "eval" ].call( window, data ); } )( data ); } }, // Convert dashed to camelCase; used by the css and data modules // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); }, // args is for internal usage only each: function( object, callback, args ) { var name, i = 0, length = object.length, isObj = length === undefined || jQuery.isFunction( object ); if ( args ) { if ( isObj ) { for ( name in object ) { if ( callback.apply( object[ name ], args ) === false ) { break; } } } else { for ( ; i < length; ) { if ( callback.apply( object[ i++ ], args ) === false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isObj ) { for ( name in object ) { if ( callback.call( object[ name ], name, object[ name ] ) === false ) { break; } } } else { for ( ; i < length; ) { if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { break; } } } } return object; }, // Use native String.trim function wherever possible trim: trim ? function( text ) { return text == null ? "" : trim.call( text ); } : // Otherwise use our own trimming functionality function( text ) { return text == null ? "" : text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); }, // results is for internal usage only makeArray: function( array, results ) { var ret = results || []; if ( array != null ) { // The window, strings (and functions) also have 'length' // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 var type = jQuery.type( array ); if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { push.call( ret, array ); } else { jQuery.merge( ret, array ); } } return ret; }, inArray: function( elem, array, i ) { var len; if ( array ) { if ( indexOf ) { return indexOf.call( array, elem, i ); } len = array.length; i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) { // Skip accessing in sparse arrays if ( i in array && array[ i ] === elem ) { return i; } } } return -1; }, merge: function( first, second ) { var i = first.length, j = 0; if ( typeof second.length === "number" ) { for ( var l = second.length; j < l; j++ ) { first[ i++ ] = second[ j ]; } } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; } } first.length = i; return first; }, grep: function( elems, callback, inv ) { var ret = [], retVal; inv = !!inv; // Go through the array, only saving the items // that pass the validator function for ( var i = 0, length = elems.length; i < length; i++ ) { retVal = !!callback( elems[ i ], i ); if ( inv !== retVal ) { ret.push( elems[ i ] ); } } return ret; }, // arg is for internal usage only map: function( elems, callback, arg ) { var value, key, ret = [], i = 0, length = elems.length, // jquery objects are treated as arrays isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; // Go through the array, translating each of the items to their if ( isArray ) { for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } // Go through every key on the object, } else { for ( key in elems ) { value = callback( elems[ key ], key, arg ); if ( value != null ) { ret[ ret.length ] = value; } } } // Flatten any nested arrays return ret.concat.apply( [], ret ); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { if ( typeof context === "string" ) { var tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind var args = slice.call( arguments, 2 ), proxy = function() { return fn.apply( context, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; return proxy; }, // Mutifunctional method to get and set values to a collection // The value/s can optionally be executed if it's a function access: function( elems, fn, key, value, chainable, emptyGet, pass ) { var exec, bulk = key == null, i = 0, length = elems.length; // Sets many values if ( key && typeof key === "object" ) { for ( i in key ) { jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); } chainable = 1; // Sets one value } else if ( value !== undefined ) { // Optionally, function values get executed if exec is true exec = pass === undefined && jQuery.isFunction( value ); if ( bulk ) { // Bulk operations only iterate when executing function values if ( exec ) { exec = fn; fn = function( elem, key, value ) { return exec.call( jQuery( elem ), value ); }; // Otherwise they run against the entire set } else { fn.call( elems, value ); fn = null; } } if ( fn ) { for (; i < length; i++ ) { fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); } } chainable = 1; } return chainable ? elems : // Gets bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet; }, now: function() { return ( new Date() ).getTime(); }, // Use of jQuery.browser is frowned upon. // More details: http://docs.jquery.com/Utilities/jQuery.browser uaMatch: function( ua ) { ua = ua.toLowerCase(); var match = rwebkit.exec( ua ) || ropera.exec( ua ) || rmsie.exec( ua ) || ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || []; return { browser: match[1] || "", version: match[2] || "0" }; }, sub: function() { function jQuerySub( selector, context ) { return new jQuerySub.fn.init( selector, context ); } jQuery.extend( true, jQuerySub, this ); jQuerySub.superclass = this; jQuerySub.fn = jQuerySub.prototype = this(); jQuerySub.fn.constructor = jQuerySub; jQuerySub.sub = this.sub; jQuerySub.fn.init = function init( selector, context ) { if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { context = jQuerySub( context ); } return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); }; jQuerySub.fn.init.prototype = jQuerySub.fn; var rootjQuerySub = jQuerySub(document); return jQuerySub; }, browser: {} }); // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); browserMatch = jQuery.uaMatch( userAgent ); if ( browserMatch.browser ) { jQuery.browser[ browserMatch.browser ] = true; jQuery.browser.version = browserMatch.version; } // Deprecated, use jQuery.browser.webkit instead if ( jQuery.browser.webkit ) { jQuery.browser.safari = true; } // IE doesn't match non-breaking spaces with \s if ( rnotwhite.test( "\xA0" ) ) { trimLeft = /^[\s\xA0]+/; trimRight = /[\s\xA0]+$/; } // All jQuery objects should point back to these rootjQuery = jQuery(document); // Cleanup functions for the document ready method if ( document.addEventListener ) { DOMContentLoaded = function() { document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); jQuery.ready(); }; } else if ( document.attachEvent ) { DOMContentLoaded = function() { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( document.readyState === "complete" ) { document.detachEvent( "onreadystatechange", DOMContentLoaded ); jQuery.ready(); } }; } // The DOM ready check for Internet Explorer function doScrollCheck() { if ( jQuery.isReady ) { return; } try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ document.documentElement.doScroll("left"); } catch(e) { setTimeout( doScrollCheck, 1 ); return; } // and execute any waiting functions jQuery.ready(); } return jQuery; })(); // String to Object flags format cache var flagsCache = {}; // Convert String-formatted flags into Object-formatted ones and store in cache function createFlags( flags ) { var object = flagsCache[ flags ] = {}, i, length; flags = flags.split( /\s+/ ); for ( i = 0, length = flags.length; i < length; i++ ) { object[ flags[i] ] = true; } return object; } /* * Create a callback list using the following parameters: * * flags: an optional list of space-separated flags that will change how * the callback list behaves * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible flags: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( flags ) { // Convert flags from String-formatted to Object-formatted // (we check in cache first) flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; var // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = [], // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Add one or several callbacks to the list add = function( args ) { var i, length, elem, type, actual; for ( i = 0, length = args.length; i < length; i++ ) { elem = args[ i ]; type = jQuery.type( elem ); if ( type === "array" ) { // Inspect recursively add( elem ); } else if ( type === "function" ) { // Add if not in unique mode and callback is not in if ( !flags.unique || !self.has( elem ) ) { list.push( elem ); } } } }, // Fire callbacks fire = function( context, args ) { args = args || []; memory = !flags.memory || [ context, args ]; fired = true; firing = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { memory = true; // Mark as halted break; } } firing = false; if ( list ) { if ( !flags.once ) { if ( stack && stack.length ) { memory = stack.shift(); self.fireWith( memory[ 0 ], memory[ 1 ] ); } } else if ( memory === true ) { self.disable(); } else { list = []; } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { var length = list.length; add( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away, unless previous // firing was halted (stopOnFalse) } else if ( memory && memory !== true ) { firingStart = length; fire( memory[ 0 ], memory[ 1 ] ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { var args = arguments, argIndex = 0, argLength = args.length; for ( ; argIndex < argLength ; argIndex++ ) { for ( var i = 0; i < list.length; i++ ) { if ( args[ argIndex ] === list[ i ] ) { // Handle firingIndex and firingLength if ( firing ) { if ( i <= firingLength ) { firingLength--; if ( i <= firingIndex ) { firingIndex--; } } } // Remove the element list.splice( i--, 1 ); // If we have some unicity property then // we only need to do this once if ( flags.unique ) { break; } } } } } return this; }, // Control if a given callback is in the list has: function( fn ) { if ( list ) { var i = 0, length = list.length; for ( ; i < length; i++ ) { if ( fn === list[ i ] ) { return true; } } } return false; }, // Remove all callbacks from the list empty: function() { list = []; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory || memory === true ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( stack ) { if ( firing ) { if ( !flags.once ) { stack.push( [ context, args ] ); } } else if ( !( flags.once && memory ) ) { fire( context, args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; var // Static reference to slice sliceDeferred = [].slice; jQuery.extend({ Deferred: function( func ) { var doneList = jQuery.Callbacks( "once memory" ), failList = jQuery.Callbacks( "once memory" ), progressList = jQuery.Callbacks( "memory" ), state = "pending", lists = { resolve: doneList, reject: failList, notify: progressList }, promise = { done: doneList.add, fail: failList.add, progress: progressList.add, state: function() { return state; }, // Deprecated isResolved: doneList.fired, isRejected: failList.fired, then: function( doneCallbacks, failCallbacks, progressCallbacks ) { deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); return this; }, always: function() { deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); return this; }, pipe: function( fnDone, fnFail, fnProgress ) { return jQuery.Deferred(function( newDefer ) { jQuery.each( { done: [ fnDone, "resolve" ], fail: [ fnFail, "reject" ], progress: [ fnProgress, "notify" ] }, function( handler, data ) { var fn = data[ 0 ], action = data[ 1 ], returned; if ( jQuery.isFunction( fn ) ) { deferred[ handler ](function() { returned = fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); } else { newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); } }); } else { deferred[ handler ]( newDefer[ action ] ); } }); }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { if ( obj == null ) { obj = promise; } else { for ( var key in promise ) { obj[ key ] = promise[ key ]; } } return obj; } }, deferred = promise.promise({}), key; for ( key in lists ) { deferred[ key ] = lists[ key ].fire; deferred[ key + "With" ] = lists[ key ].fireWith; } // Handle state deferred.done( function() { state = "resolved"; }, failList.disable, progressList.lock ).fail( function() { state = "rejected"; }, doneList.disable, progressList.lock ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( firstParam ) { var args = sliceDeferred.call( arguments, 0 ), i = 0, length = args.length, pValues = new Array( length ), count = length, pCount = length, deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? firstParam : jQuery.Deferred(), promise = deferred.promise(); function resolveFunc( i ) { return function( value ) { args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; if ( !( --count ) ) { deferred.resolveWith( deferred, args ); } }; } function progressFunc( i ) { return function( value ) { pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; deferred.notifyWith( promise, pValues ); }; } if ( length > 1 ) { for ( ; i < length; i++ ) { if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); } else { --count; } } if ( !count ) { deferred.resolveWith( deferred, args ); } } else if ( deferred !== firstParam ) { deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); } return promise; } }); jQuery.support = (function() { var support, all, a, select, opt, input, fragment, tds, events, eventName, i, isSupported, div = document.createElement( "div" ), documentElement = document.documentElement; // Preliminary tests div.setAttribute("className", "t"); div.innerHTML = "
a"; all = div.getElementsByTagName( "*" ); a = div.getElementsByTagName( "a" )[ 0 ]; // Can't get basic test support if ( !all || !all.length || !a ) { return {}; } // First batch of supports tests select = document.createElement( "select" ); opt = select.appendChild( document.createElement("option") ); input = div.getElementsByTagName( "input" )[ 0 ]; support = { // IE strips leading whitespace when .innerHTML is used leadingWhitespace: ( div.firstChild.nodeType === 3 ), // Make sure that tbody elements aren't automatically inserted // IE will insert them into empty tables tbody: !div.getElementsByTagName("tbody").length, // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE htmlSerialize: !!div.getElementsByTagName("link").length, // Get the style information from getAttribute // (IE uses .cssText instead) style: /top/.test( a.getAttribute("style") ), // Make sure that URLs aren't manipulated // (IE normalizes it by default) hrefNormalized: ( a.getAttribute("href") === "/a" ), // Make sure that element opacity exists // (IE uses filter instead) // Use a regex to work around a WebKit issue. See #5145 opacity: /^0.55/.test( a.style.opacity ), // Verify style float existence // (IE uses styleFloat instead of cssFloat) cssFloat: !!a.style.cssFloat, // Make sure that if no value is specified for a checkbox // that it defaults to "on". // (WebKit defaults to "" instead) checkOn: ( input.value === "on" ), // Make sure that a selected-by-default option has a working selected property. // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) optSelected: opt.selected, // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) getSetAttribute: div.className !== "t", // Tests for enctype support on a form(#6743) enctype: !!document.createElement("form").enctype, // Makes sure cloning an html5 element does not cause problems // Where outerHTML is undefined, this still works html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", // Will be defined later submitBubbles: true, changeBubbles: true, focusinBubbles: false, deleteExpando: true, noCloneEvent: true, inlineBlockNeedsLayout: false, shrinkWrapBlocks: false, reliableMarginRight: true, pixelMargin: true }; // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); // Make sure checked status is properly cloned input.checked = true; support.noCloneChecked = input.cloneNode( true ).checked; // Make sure that the options inside disabled selects aren't marked as disabled // (WebKit marks them as disabled) select.disabled = true; support.optDisabled = !opt.disabled; // Test to see if it's possible to delete an expando from an element // Fails in Internet Explorer try { delete div.test; } catch( e ) { support.deleteExpando = false; } if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { div.attachEvent( "onclick", function() { // Cloning a node shouldn't copy over any // bound event handlers (IE does this) support.noCloneEvent = false; }); div.cloneNode( true ).fireEvent( "onclick" ); } // Check if a radio maintains its value // after being appended to the DOM input = document.createElement("input"); input.value = "t"; input.setAttribute("type", "radio"); support.radioValue = input.value === "t"; input.setAttribute("checked", "checked"); // #11217 - WebKit loses check when the name is after the checked attribute input.setAttribute( "name", "t" ); div.appendChild( input ); fragment = document.createDocumentFragment(); fragment.appendChild( div.lastChild ); // WebKit doesn't clone checked state correctly in fragments support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; // Check if a disconnected checkbox will retain its checked // value of true after appended to the DOM (IE6/7) support.appendChecked = input.checked; fragment.removeChild( input ); fragment.appendChild( div ); // Technique from Juriy Zaytsev // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ // We only care about the case where non-standard event systems // are used, namely in IE. Short-circuiting here helps us to // avoid an eval call (in setAttribute) which can cause CSP // to go haywire. See: https://developer.mozilla.org/en/Security/CSP if ( div.attachEvent ) { for ( i in { submit: 1, change: 1, focusin: 1 }) { eventName = "on" + i; isSupported = ( eventName in div ); if ( !isSupported ) { div.setAttribute( eventName, "return;" ); isSupported = ( typeof div[ eventName ] === "function" ); } support[ i + "Bubbles" ] = isSupported; } } fragment.removeChild( div ); // Null elements to avoid leaks in IE fragment = select = opt = div = input = null; // Run tests that need a body at doc ready jQuery(function() { var container, outer, inner, table, td, offsetSupport, marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, paddingMarginBorderVisibility, paddingMarginBorder, body = document.getElementsByTagName("body")[0]; if ( !body ) { // Return for frameset docs that don't have a body return; } conMarginTop = 1; paddingMarginBorder = "padding:0;margin:0;border:"; positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; html = "
" + "" + "
"; container = document.createElement("div"); container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; body.insertBefore( container, body.firstChild ); // Construct the test element div = document.createElement("div"); container.appendChild( div ); // Check if table cells still have offsetWidth/Height when they are set // to display:none and there are still other visible table cells in a // table row; if so, offsetWidth/Height are not reliable for use when // determining if an element has been hidden directly using // display:none (it is still safe to use offsets if a parent element is // hidden; don safety goggles and see bug #4512 for more information). // (only IE 8 fails this test) div.innerHTML = "
t
"; tds = div.getElementsByTagName( "td" ); isSupported = ( tds[ 0 ].offsetHeight === 0 ); tds[ 0 ].style.display = ""; tds[ 1 ].style.display = "none"; // Check if empty table cells still have offsetWidth/Height // (IE <= 8 fail this test) support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); // Check if div with explicit width and no margin-right incorrectly // gets computed margin-right based on width of container. For more // info see bug #3333 // Fails in WebKit before Feb 2011 nightlies // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right if ( window.getComputedStyle ) { div.innerHTML = ""; marginDiv = document.createElement( "div" ); marginDiv.style.width = "0"; marginDiv.style.marginRight = "0"; div.style.width = "2px"; div.appendChild( marginDiv ); support.reliableMarginRight = ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; } if ( typeof div.style.zoom !== "undefined" ) { // Check if natively block-level elements act like inline-block // elements when setting their display to 'inline' and giving // them layout // (IE < 8 does this) div.innerHTML = ""; div.style.width = div.style.padding = "1px"; div.style.border = 0; div.style.overflow = "hidden"; div.style.display = "inline"; div.style.zoom = 1; support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); // Check if elements with layout shrink-wrap their children // (IE 6 does this) div.style.display = "block"; div.style.overflow = "visible"; div.innerHTML = "
"; support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); } div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; div.innerHTML = html; outer = div.firstChild; inner = outer.firstChild; td = outer.nextSibling.firstChild.firstChild; offsetSupport = { doesNotAddBorder: ( inner.offsetTop !== 5 ), doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) }; inner.style.position = "fixed"; inner.style.top = "20px"; // safari subtracts parent border width here which is 5px offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); inner.style.position = inner.style.top = ""; outer.style.overflow = "hidden"; outer.style.position = "relative"; offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); if ( window.getComputedStyle ) { div.style.marginTop = "1%"; support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; } if ( typeof container.style.zoom !== "undefined" ) { container.style.zoom = 1; } body.removeChild( container ); marginDiv = div = container = null; jQuery.extend( support, offsetSupport ); }); return support; })(); var rbrace = /^(?:\{.*\}|\[.*\])$/, rmultiDash = /([A-Z])/g; jQuery.extend({ cache: {}, // Please use with caution uuid: 0, // Unique for each copy of jQuery on the page // Non-digits removed to match rinlinejQuery expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), // The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. noData: { "embed": true, // Ban all objects except for Flash (which handle expandos) "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", "applet": true }, hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); }, data: function( elem, name, data, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; } var privateCache, thisCache, ret, internalKey = jQuery.expando, getByName = typeof name === "string", // We have to handle DOM nodes and JS objects differently because IE6-7 // can't GC object references properly across the DOM-JS boundary isNode = elem.nodeType, // Only DOM nodes need the global jQuery cache; JS object data is // attached directly to the object so GC can occur automatically cache = isNode ? jQuery.cache : elem, // Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, isEvents = name === "events"; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { return; } if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { elem[ internalKey ] = id = ++jQuery.uuid; } else { id = internalKey; } } if ( !cache[ id ] ) { cache[ id ] = {}; // Avoids exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; } } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } } privateCache = thisCache = cache[ id ]; // jQuery data() is stored in a separate object inside the object's internal data // cache in order to avoid key collisions between internal data and user-defined // data. if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } thisCache = thisCache.data; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } // Users should not attempt to inspect the internal events object using jQuery.data, // it is undocumented and subject to change. But does anyone listen? No. if ( isEvents && !thisCache[ name ] ) { return privateCache.events; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified if ( getByName ) { // First Try to find as-is property data ret = thisCache[ name ]; // Test for null|undefined property data if ( ret == null ) { // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { ret = thisCache; } return ret; }, removeData: function( elem, name, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, i, l, // Reference to internal data cache key internalKey = jQuery.expando, isNode = elem.nodeType, // See jQuery.data for more information cache = isNode ? jQuery.cache : elem, // See jQuery.data for more information id = isNode ? elem[ internalKey ] : internalKey; // If there is already no cache entry for this object, there is no // purpose in continuing if ( !cache[ id ] ) { return; } if ( name ) { thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { // Support array or space separated string names for data keys if ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulation if ( name in thisCache ) { name = [ name ]; } else { // split the camel cased version by spaces unless a key with the spaces exists name = jQuery.camelCase( name ); if ( name in thisCache ) { name = [ name ]; } else { name = name.split( " " ); } } } for ( i = 0, l = name.length; i < l; i++ ) { delete thisCache[ name[i] ]; } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { return; } } } // See jQuery.data for more information if ( !pvt ) { delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object // had been the only thing left in it if ( !isEmptyDataObject(cache[ id ]) ) { return; } } // Browsers that fail expando deletion also refuse to delete expandos on // the window, but it will allow it on all other JS objects; other browsers // don't care // Ensure that `cache` is not a window object #10080 if ( jQuery.support.deleteExpando || !cache.setInterval ) { delete cache[ id ]; } else { cache[ id ] = null; } // We destroyed the cache and need to eliminate the expando on the node to avoid // false lookups in the cache for entries that no longer exist if ( isNode ) { // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases if ( jQuery.support.deleteExpando ) { delete elem[ internalKey ]; } else if ( elem.removeAttribute ) { elem.removeAttribute( internalKey ); } else { elem[ internalKey ] = null; } } }, // For internal use only. _data: function( elem, name, data ) { return jQuery.data( elem, name, data, true ); }, // A method for determining if a DOM node can handle the data expando acceptData: function( elem ) { if ( elem.nodeName ) { var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; if ( match ) { return !(match === true || elem.getAttribute("classid") !== match); } } return true; } }); jQuery.fn.extend({ data: function( key, value ) { var parts, part, attr, name, l, elem = this[0], i = 0, data = null; // Gets all values if ( key === undefined ) { if ( this.length ) { data = jQuery.data( elem ); if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { attr = elem.attributes; for ( l = attr.length; i < l; i++ ) { name = attr[i].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.substring(5) ); dataAttr( elem, name, data[ name ] ); } } jQuery._data( elem, "parsedAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } parts = key.split( ".", 2 ); parts[1] = parts[1] ? "." + parts[1] : ""; part = parts[1] + "!"; return jQuery.access( this, function( value ) { if ( value === undefined ) { data = this.triggerHandler( "getData" + part, [ parts[0] ] ); // Try to fetch any internally stored data first if ( data === undefined && elem ) { data = jQuery.data( elem, key ); data = dataAttr( elem, key, data ); } return data === undefined && parts[1] ? this.data( parts[0] ) : data; } parts[1] = value; this.each(function() { var self = jQuery( this ); self.triggerHandler( "setData" + part, parts ); jQuery.data( this, key, value ); self.triggerHandler( "changeData" + part, parts ); }); }, null, value, arguments.length > 1, null, false ); }, removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : jQuery.isNumeric( data ) ? +data : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // Make sure we set the data so it isn't changed later jQuery.data( elem, key, data ); } else { data = undefined; } } return data; } // checks a cache object for emptiness function isEmptyDataObject( obj ) { for ( var name in obj ) { // if the public data object is empty, the private is still empty if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } if ( name !== "toJSON" ) { return false; } } return true; } function handleQueueMarkDefer( elem, type, src ) { var deferDataKey = type + "defer", queueDataKey = type + "queue", markDataKey = type + "mark", defer = jQuery._data( elem, deferDataKey ); if ( defer && ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { // Give room for hard-coded callbacks to fire first // and eventually mark/queue something else on the element setTimeout( function() { if ( !jQuery._data( elem, queueDataKey ) && !jQuery._data( elem, markDataKey ) ) { jQuery.removeData( elem, deferDataKey, true ); defer.fire(); } }, 0 ); } } jQuery.extend({ _mark: function( elem, type ) { if ( elem ) { type = ( type || "fx" ) + "mark"; jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); } }, _unmark: function( force, elem, type ) { if ( force !== true ) { type = elem; elem = force; force = false; } if ( elem ) { type = type || "fx"; var key = type + "mark", count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); if ( count ) { jQuery._data( elem, key, count ); } else { jQuery.removeData( elem, key, true ); handleQueueMarkDefer( elem, type, "mark" ); } } }, queue: function( elem, type, data ) { var q; if ( elem ) { type = ( type || "fx" ) + "queue"; q = jQuery._data( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !q || jQuery.isArray(data) ) { q = jQuery._data( elem, type, jQuery.makeArray(data) ); } else { q.push( data ); } } return q || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), fn = queue.shift(), hooks = {}; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } jQuery._data( elem, type + ".run", hooks ); fn.call( elem, function() { jQuery.dequeue( elem, type ); }, hooks ); } if ( !queue.length ) { jQuery.removeData( elem, type + "queue " + type + ".run", true ); handleQueueMarkDefer( elem, type, "queue" ); } } }); jQuery.fn.extend({ queue: function( type, data ) { var setter = 2; if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; } if ( arguments.length < setter ) { return jQuery.queue( this[0], type ); } return data === undefined ? this : this.each(function() { var queue = jQuery.queue( this, type, data ); if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); } }); }, dequeue: function( type ) { return this.each(function() { jQuery.dequeue( this, type ); }); }, // Based off of the plugin by Clint Helfers, with permission. // http://blindsignals.com/index.php/2009/07/jquery-delay/ delay: function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; return this.queue( type, function( next, hooks ) { var timeout = setTimeout( next, time ); hooks.stop = function() { clearTimeout( timeout ); }; }); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, object ) { if ( typeof type !== "string" ) { object = type; type = undefined; } type = type || "fx"; var defer = jQuery.Deferred(), elements = this, i = elements.length, count = 1, deferDataKey = type + "defer", queueDataKey = type + "queue", markDataKey = type + "mark", tmp; function resolve() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } } while( i-- ) { if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { count++; tmp.add( resolve ); } } resolve(); return defer.promise( object ); } }); var rclass = /[\n\t\r]/g, rspace = /\s+/, rreturn = /\r/g, rtype = /^(?:button|input)$/i, rfocusable = /^(?:button|input|object|select|textarea)$/i, rclickable = /^a(?:rea)?$/i, rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, getSetAttribute = jQuery.support.getSetAttribute, nodeHook, boolHook, fixSpecified; jQuery.fn.extend({ attr: function( name, value ) { return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each(function() { jQuery.removeAttr( this, name ); }); }, prop: function( name, value ) { return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { name = jQuery.propFix[ name ] || name; return this.each(function() { // try/catch handles cases where IE balks (such as removing a property on window) try { this[ name ] = undefined; delete this[ name ]; } catch( e ) {} }); }, addClass: function( value ) { var classNames, i, l, elem, setClass, c, cl; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).addClass( value.call(this, j, this.className) ); }); } if ( value && typeof value === "string" ) { classNames = value.split( rspace ); for ( i = 0, l = this.length; i < l; i++ ) { elem = this[ i ]; if ( elem.nodeType === 1 ) { if ( !elem.className && classNames.length === 1 ) { elem.className = value; } else { setClass = " " + elem.className + " "; for ( c = 0, cl = classNames.length; c < cl; c++ ) { if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { setClass += classNames[ c ] + " "; } } elem.className = jQuery.trim( setClass ); } } } } return this; }, removeClass: function( value ) { var classNames, i, l, elem, className, c, cl; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).removeClass( value.call(this, j, this.className) ); }); } if ( (value && typeof value === "string") || value === undefined ) { classNames = ( value || "" ).split( rspace ); for ( i = 0, l = this.length; i < l; i++ ) { elem = this[ i ]; if ( elem.nodeType === 1 && elem.className ) { if ( value ) { className = (" " + elem.className + " ").replace( rclass, " " ); for ( c = 0, cl = classNames.length; c < cl; c++ ) { className = className.replace(" " + classNames[ c ] + " ", " "); } elem.className = jQuery.trim( className ); } else { elem.className = ""; } } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value, isBool = typeof stateVal === "boolean"; if ( jQuery.isFunction( value ) ) { return this.each(function( i ) { jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); }); } return this.each(function() { if ( type === "string" ) { // toggle individual class names var className, i = 0, self = jQuery( this ), state = stateVal, classNames = value.split( rspace ); while ( (className = classNames[ i++ ]) ) { // check each className given, space seperated list state = isBool ? state : !self.hasClass( className ); self[ state ? "addClass" : "removeClass" ]( className ); } } else if ( type === "undefined" || type === "boolean" ) { if ( this.className ) { // store className if set jQuery._data( this, "__className__", this.className ); } // toggle whole className this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; } }); }, hasClass: function( selector ) { var className = " " + selector + " ", i = 0, l = this.length; for ( ; i < l; i++ ) { if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { return true; } } return false; }, val: function( value ) { var hooks, ret, isFunction, elem = this[0]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { return ret; } ret = elem.value; return typeof ret === "string" ? // handle most common string cases ret.replace(rreturn, "") : // handle cases where value is null/undef or number ret == null ? "" : ret; } return; } isFunction = jQuery.isFunction( value ); return this.each(function( i ) { var self = jQuery(this), val; if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { val = value.call( this, i, self.val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) { val = ""; } else if ( typeof val === "number" ) { val += ""; } else if ( jQuery.isArray( val ) ) { val = jQuery.map(val, function ( value ) { return value == null ? "" : value + ""; }); } hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } }); } }); jQuery.extend({ valHooks: { option: { get: function( elem ) { // attributes.value is undefined in Blackberry 4.7 but // uses .value. See #6932 var val = elem.attributes.value; return !val || val.specified ? elem.value : elem.text; } }, select: { get: function( elem ) { var value, i, max, option, index = elem.selectedIndex, values = [], options = elem.options, one = elem.type === "select-one"; // Nothing was selected if ( index < 0 ) { return null; } // Loop through all the selected options i = one ? index : 0; max = one ? index + 1 : options.length; for ( ; i < max; i++ ) { option = options[ i ]; // Don't return options that are disabled or in a disabled optgroup if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { // Get the specific value for the option value = jQuery( option ).val(); // We don't need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } // Fixes Bug #2551 -- select.val() broken in IE after form.reset() if ( one && !values.length && options.length ) { return jQuery( options[ index ] ).val(); } return values; }, set: function( elem, value ) { var values = jQuery.makeArray( value ); jQuery(elem).find("option").each(function() { this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; }); if ( !values.length ) { elem.selectedIndex = -1; } return values; } } }, attrFn: { val: true, css: true, html: true, text: true, data: true, width: true, height: true, offset: true }, attr: function( elem, name, value, pass ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } if ( pass && name in jQuery.attrFn ) { return jQuery( elem )[ name ]( value ); } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === "undefined" ) { return jQuery.prop( elem, name, value ); } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); // All attributes are lowercase // Grab necessary hook if one is defined if ( notxml ) { name = name.toLowerCase(); hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); } if ( value !== undefined ) { if ( value === null ) { jQuery.removeAttr( elem, name ); return; } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; } else { elem.setAttribute( name, "" + value ); return value; } } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { ret = elem.getAttribute( name ); // Non-existent attributes return null, we normalize to undefined return ret === null ? undefined : ret; } }, removeAttr: function( elem, value ) { var propName, attrNames, name, l, isBool, i = 0; if ( value && elem.nodeType === 1 ) { attrNames = value.toLowerCase().split( rspace ); l = attrNames.length; for ( ; i < l; i++ ) { name = attrNames[ i ]; if ( name ) { propName = jQuery.propFix[ name ] || name; isBool = rboolean.test( name ); // See #9699 for explanation of this approach (setting first, then removal) // Do not do this for boolean attributes (see #10870) if ( !isBool ) { jQuery.attr( elem, name, "" ); } elem.removeAttribute( getSetAttribute ? name : propName ); // Set corresponding property to false for boolean attributes if ( isBool && propName in elem ) { elem[ propName ] = false; } } } } }, attrHooks: { type: { set: function( elem, value ) { // We can't allow the type property to be changed (since it causes problems in IE) if ( rtype.test( elem.nodeName ) && elem.parentNode ) { jQuery.error( "type property can't be changed" ); } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to it's default in case type is set after value // This is for element creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } }, // Use the value property for back compat // Use the nodeHook for button elements in IE6/7 (#1954) value: { get: function( elem, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.get( elem, name ); } return name in elem ? elem.value : null; }, set: function( elem, value, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.set( elem, value, name ); } // Does not return so that setAttribute is also used elem.value = value; } } }, propFix: { tabindex: "tabIndex", readonly: "readOnly", "for": "htmlFor", "class": "className", maxlength: "maxLength", cellspacing: "cellSpacing", cellpadding: "cellPadding", rowspan: "rowSpan", colspan: "colSpan", usemap: "useMap", frameborder: "frameBorder", contenteditable: "contentEditable" }, prop: function( elem, name, value ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); if ( notxml ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; } else { return ( elem[ name ] = value ); } } else { if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { return elem[ name ]; } } }, propHooks: { tabIndex: { get: function( elem ) { // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ var attributeNode = elem.getAttributeNode("tabindex"); return attributeNode && attributeNode.specified ? parseInt( attributeNode.value, 10 ) : rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? 0 : undefined; } } } }); // Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; // Hook for boolean attributes boolHook = { get: function( elem, name ) { // Align boolean attributes with corresponding properties // Fall back to attribute presence where some booleans are not supported var attrNode, property = jQuery.prop( elem, name ); return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? name.toLowerCase() : undefined; }, set: function( elem, value, name ) { var propName; if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else { // value is true since we know at this point it's type boolean and not false // Set boolean attributes to the same name and set the DOM property propName = jQuery.propFix[ name ] || name; if ( propName in elem ) { // Only set the IDL specifically if it already exists on the element elem[ propName ] = true; } elem.setAttribute( name, name.toLowerCase() ); } return name; } }; // IE6/7 do not support getting/setting some attributes with get/setAttribute if ( !getSetAttribute ) { fixSpecified = { name: true, id: true, coords: true }; // Use this for any attribute in IE6/7 // This fixes almost every IE6/7 issue nodeHook = jQuery.valHooks.button = { get: function( elem, name ) { var ret; ret = elem.getAttributeNode( name ); return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? ret.nodeValue : undefined; }, set: function( elem, value, name ) { // Set the existing or create a new attribute node var ret = elem.getAttributeNode( name ); if ( !ret ) { ret = document.createAttribute( name ); elem.setAttributeNode( ret ); } return ( ret.nodeValue = value + "" ); } }; // Apply the nodeHook to tabindex jQuery.attrHooks.tabindex.set = nodeHook.set; // Set width and height to auto instead of 0 on empty string( Bug #8150 ) // This is for removals jQuery.each([ "width", "height" ], function( i, name ) { jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { set: function( elem, value ) { if ( value === "" ) { elem.setAttribute( name, "auto" ); return value; } } }); }); // Set contenteditable to false on removals(#10429) // Setting to empty string throws an error as an invalid value jQuery.attrHooks.contenteditable = { get: nodeHook.get, set: function( elem, value, name ) { if ( value === "" ) { value = "false"; } nodeHook.set( elem, value, name ); } }; } // Some attributes require a special call on IE if ( !jQuery.support.hrefNormalized ) { jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { get: function( elem ) { var ret = elem.getAttribute( name, 2 ); return ret === null ? undefined : ret; } }); }); } if ( !jQuery.support.style ) { jQuery.attrHooks.style = { get: function( elem ) { // Return undefined in the case of empty string // Normalize to lowercase since IE uppercases css property names return elem.style.cssText.toLowerCase() || undefined; }, set: function( elem, value ) { return ( elem.style.cssText = "" + value ); } }; } // Safari mis-reports the default selected property of an option // Accessing the parent's selectedIndex property fixes it if ( !jQuery.support.optSelected ) { jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { get: function( elem ) { var parent = elem.parentNode; if ( parent ) { parent.selectedIndex; // Make sure that it also works with optgroups, see #5701 if ( parent.parentNode ) { parent.parentNode.selectedIndex; } } return null; } }); } // IE6/7 call enctype encoding if ( !jQuery.support.enctype ) { jQuery.propFix.enctype = "encoding"; } // Radios and checkboxes getter/setter if ( !jQuery.support.checkOn ) { jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { get: function( elem ) { // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified return elem.getAttribute("value") === null ? "on" : elem.value; } }; }); } jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { set: function( elem, value ) { if ( jQuery.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); } } }); }); var rformElems = /^(?:textarea|input|select)$/i, rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, quickParse = function( selector ) { var quick = rquickIs.exec( selector ); if ( quick ) { // 0 1 2 3 // [ _, tag, id, class ] quick[1] = ( quick[1] || "" ).toLowerCase(); quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); } return quick; }, quickIs = function( elem, m ) { var attrs = elem.attributes || {}; return ( (!m[1] || elem.nodeName.toLowerCase() === m[1]) && (!m[2] || (attrs.id || {}).value === m[2]) && (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) ); }, hoverHack = function( events ) { return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); }; /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { add: function( elem, types, handler, data, selector ) { var elemData, eventHandle, events, t, tns, type, namespaces, handleObj, handleObjIn, quick, handlers, special; // Don't attach events to noData or text/comment nodes (allow plain objects tho) if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first events = elemData.events; if ( !events ) { elemData.events = events = {}; } eventHandle = elemData.handle; if ( !eventHandle ) { elemData.handle = eventHandle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events eventHandle.elem = elem; } // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); types = jQuery.trim( hoverHack(types) ).split( " " ); for ( t = 0; t < types.length; t++ ) { tns = rtypenamespace.exec( types[t] ) || []; type = tns[1]; namespaces = ( tns[2] || "" ).split( "." ).sort(); // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend({ type: type, origType: tns[1], data: data, handler: handler, guid: handler.guid, selector: selector, quick: selector && quickParse( selector ), namespace: namespaces.join(".") }, handleObjIn ); // Init the event handler queue if we're the first handlers = events[ type ]; if ( !handlers ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener/attachEvent if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { // Bind the global event handler to the element if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } // Nullify elem to prevent memory leaks in IE elem = null; }, global: {}, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), t, tns, type, origType, namespaces, origCount, j, events, special, handle, eventType, handleObj; if ( !elemData || !(events = elemData.events) ) { return; } // Once for each type.namespace in types; type may be omitted types = jQuery.trim( hoverHack( types || "" ) ).split(" "); for ( t = 0; t < types.length; t++ ) { tns = rtypenamespace.exec( types[t] ) || []; type = origType = tns[1]; namespaces = tns[2]; // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector? special.delegateType : special.bindType ) || type; eventType = events[ type ] || []; origCount = eventType.length; namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; // Remove matching events for ( j = 0; j < eventType.length; j++ ) { handleObj = eventType[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !namespaces || namespaces.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { eventType.splice( j--, 1 ); if ( handleObj.selector ) { eventType.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( eventType.length === 0 && origCount !== eventType.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { handle = elemData.handle; if ( handle ) { handle.elem = null; } // removeData also checks for emptiness and clears the expando if empty // so use it instead of delete jQuery.removeData( elem, [ "events", "handle" ], true ); } }, // Events that are safe to short-circuit if no handlers are attached. // Native DOM events should not be added, they may have inline handlers. customEvent: { "getData": true, "setData": true, "changeData": true }, trigger: function( event, data, elem, onlyHandlers ) { // Don't do events on text and comment nodes if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { return; } // Event object or event type var type = event.type || event, namespaces = [], cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf( "!" ) >= 0 ) { // Exclusive events trigger only for the exact event (no namespaces) type = type.slice(0, -1); exclusive = true; } if ( type.indexOf( "." ) >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { // No jQuery handlers for this event type, and it can't have inline handlers return; } // Caller can pass in an Event, Object, or just an event type string event = typeof event === "object" ? // jQuery.Event object event[ jQuery.expando ] ? event : // Object literal new jQuery.Event( type, event ) : // Just the event type (string) new jQuery.Event( type ); event.type = type; event.isTrigger = true; event.exclusive = exclusive; event.namespace = namespaces.join( "." ); event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; // Handle a global trigger if ( !elem ) { // TODO: Stop taunting the data cache; remove global events and always attach to document cache = jQuery.cache; for ( i in cache ) { if ( cache[ i ].events && cache[ i ].events[ type ] ) { jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); } } return; } // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data != null ? jQuery.makeArray( data ) : []; data.unshift( event ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) eventPath = [[ elem, special.bindType || type ]]; if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; old = null; for ( ; cur; cur = cur.parentNode ) { eventPath.push([ cur, bubbleType ]); old = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( old && old === elem.ownerDocument ) { eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); } } // Fire handlers on the event path for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { cur = eventPath[i][0]; event.type = eventPath[i][1]; handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Note that this is a bare JS function and not a jQuery handler handle = ontype && cur[ ontype ]; if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { event.preventDefault(); } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. // Can't use an .isFunction() check here because IE6/7 fails that test. // Don't do default actions on window, that's where global variables be (#6170) // IE<9 dies on focus/blur to hidden element (#1486) if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method old = elem[ ontype ]; if ( old ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; elem[ type ](); jQuery.event.triggered = undefined; if ( old ) { elem[ ontype ] = old; } } } } return event.result; }, dispatch: function( event ) { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event || window.event ); var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), delegateCount = handlers.delegateCount, args = [].slice.call( arguments, 0 ), run_all = !event.exclusive && !event.namespace, special = jQuery.event.special[ event.type ] || {}, handlerQueue = [], i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers that should run if there are delegated events // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && !(event.button && event.type === "click") ) { // Pregenerate a single jQuery object for reuse with .is() jqcur = jQuery(this); jqcur.context = this.ownerDocument || this; for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { // Don't process events on disabled elements (#6911, #8165) if ( cur.disabled !== true ) { selMatch = {}; matches = []; jqcur[0] = cur; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; sel = handleObj.selector; if ( selMatch[ sel ] === undefined ) { selMatch[ sel ] = ( handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) ); } if ( selMatch[ sel ] ) { matches.push( handleObj ); } } if ( matches.length ) { handlerQueue.push({ elem: cur, matches: matches }); } } } } // Add the remaining (directly-bound) handlers if ( handlers.length > delegateCount ) { handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); } // Run delegates first; they may want to stop propagation beneath us for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { matched = handlerQueue[ i ]; event.currentTarget = matched.elem; for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { handleObj = matched.matches[ j ]; // Triggered event must either 1) be non-exclusive and have no namespace, or // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { event.data = handleObj.data; event.handleObj = handleObj; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args ); if ( ret !== undefined ) { event.result = ret; if ( ret === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } return event.result; }, // Includes some event props shared by KeyEvent and MouseEvent // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function( event, original ) { // Add which for key events if ( event.which == null ) { event.which = original.charCode != null ? original.charCode : original.keyCode; } return event; } }, mouseHooks: { props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function( event, original ) { var eventDoc, doc, body, button = original.button, fromElement = original.fromElement; // Calculate pageX/Y if missing and clientX/Y available if ( event.pageX == null && original.clientX != null ) { eventDoc = event.target.ownerDocument || document; doc = eventDoc.documentElement; body = eventDoc.body; event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); } // Add relatedTarget, if necessary if ( !event.relatedTarget && fromElement ) { event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; } // Add which for click: 1 === left; 2 === middle; 3 === right // Note: button is not normalized, so don't use it if ( !event.which && button !== undefined ) { event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event; } }, fix: function( event ) { if ( event[ jQuery.expando ] ) { return event; } // Create a writable copy of the event object and normalize some properties var i, prop, originalEvent = event, fixHook = jQuery.event.fixHooks[ event.type ] || {}, copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; event = jQuery.Event( originalEvent ); for ( i = copy.length; i; ) { prop = copy[ --i ]; event[ prop ] = originalEvent[ prop ]; } // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) if ( !event.target ) { event.target = originalEvent.srcElement || document; } // Target should not be a text node (#504, Safari) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) if ( event.metaKey === undefined ) { event.metaKey = event.ctrlKey; } return fixHook.filter? fixHook.filter( event, originalEvent ) : event; }, special: { ready: { // Make sure the ready event is setup setup: jQuery.bindReady }, load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { delegateType: "focusin" }, blur: { delegateType: "focusout" }, beforeunload: { setup: function( data, namespaces, eventHandle ) { // We only want to do this special case on windows if ( jQuery.isWindow( this ) ) { this.onbeforeunload = eventHandle; } }, teardown: function( namespaces, eventHandle ) { if ( this.onbeforeunload === eventHandle ) { this.onbeforeunload = null; } } } }, simulate: function( type, elem, event, bubble ) { // Piggyback on a donor event to simulate a different one. // Fake originalEvent to avoid donor's stopPropagation, but if the // simulated event prevents default then we do the same on the donor. var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true, originalEvent: {} } ); if ( bubble ) { jQuery.event.trigger( e, null, elem ); } else { jQuery.event.dispatch.call( elem, e ); } if ( e.isDefaultPrevented() ) { event.preventDefault(); } } }; // Some plugins are using, but it's undocumented/deprecated and will be removed. // The 1.7 special event interface should provide all the hooks needed now. jQuery.event.handle = jQuery.event.dispatch; jQuery.removeEvent = document.removeEventListener ? function( elem, type, handle ) { if ( elem.removeEventListener ) { elem.removeEventListener( type, handle, false ); } } : function( elem, type, handle ) { if ( elem.detachEvent ) { elem.detachEvent( "on" + type, handle ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !(this instanceof jQuery.Event) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; function returnFalse() { return false; } function returnTrue() { return true; } // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { preventDefault: function() { this.isDefaultPrevented = returnTrue; var e = this.originalEvent; if ( !e ) { return; } // if preventDefault exists run it on the original event if ( e.preventDefault ) { e.preventDefault(); // otherwise set the returnValue property of the original event to false (IE) } else { e.returnValue = false; } }, stopPropagation: function() { this.isPropagationStopped = returnTrue; var e = this.originalEvent; if ( !e ) { return; } // if stopPropagation exists run it on the original event if ( e.stopPropagation ) { e.stopPropagation(); } // otherwise set the cancelBubble property of the original event to true (IE) e.cancelBubble = true; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); }, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse }; // Create mouseenter/leave events using mouseover/out and event-time checks jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var target = this, related = event.relatedTarget, handleObj = event.handleObj, selector = handleObj.selector, ret; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || (related !== target && !jQuery.contains( target, related )) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; }); // IE submit delegation if ( !jQuery.support.submitBubbles ) { jQuery.event.special.submit = { setup: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Lazy-add a submit handler when a descendant form may potentially be submitted jQuery.event.add( this, "click._submit keypress._submit", function( e ) { // Node name check avoids a VML-related crash in IE (#9807) var elem = e.target, form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; if ( form && !form._submit_attached ) { jQuery.event.add( form, "submit._submit", function( event ) { event._submit_bubble = true; }); form._submit_attached = true; } }); // return undefined since we don't need an event listener }, postDispatch: function( event ) { // If form was submitted by the user, bubble the event up the tree if ( event._submit_bubble ) { delete event._submit_bubble; if ( this.parentNode && !event.isTrigger ) { jQuery.event.simulate( "submit", this.parentNode, event, true ); } } }, teardown: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Remove delegated handlers; cleanData eventually reaps submit handlers attached above jQuery.event.remove( this, "._submit" ); } }; } // IE change delegation and checkbox/radio fix if ( !jQuery.support.changeBubbles ) { jQuery.event.special.change = { setup: function() { if ( rformElems.test( this.nodeName ) ) { // IE doesn't fire change on a check/radio until blur; trigger it on click // after a propertychange. Eat the blur-change in special.change.handle. // This still fires onchange a second time for check/radio after blur. if ( this.type === "checkbox" || this.type === "radio" ) { jQuery.event.add( this, "propertychange._change", function( event ) { if ( event.originalEvent.propertyName === "checked" ) { this._just_changed = true; } }); jQuery.event.add( this, "click._change", function( event ) { if ( this._just_changed && !event.isTrigger ) { this._just_changed = false; jQuery.event.simulate( "change", this, event, true ); } }); } return false; } // Delegated event; lazy-add a change handler on descendant inputs jQuery.event.add( this, "beforeactivate._change", function( e ) { var elem = e.target; if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { jQuery.event.add( elem, "change._change", function( event ) { if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { jQuery.event.simulate( "change", this.parentNode, event, true ); } }); elem._change_attached = true; } }); }, handle: function( event ) { var elem = event.target; // Swallow native change events from checkbox/radio, we already triggered them above if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { return event.handleObj.handler.apply( this, arguments ); } }, teardown: function() { jQuery.event.remove( this, "._change" ); return rformElems.test( this.nodeName ); } }; } // Create "bubbling" focus and blur events if ( !jQuery.support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler while someone wants focusin/focusout var attaches = 0, handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; jQuery.event.special[ fix ] = { setup: function() { if ( attaches++ === 0 ) { document.addEventListener( orig, handler, true ); } }, teardown: function() { if ( --attaches === 0 ) { document.removeEventListener( orig, handler, true ); } } }; }); } jQuery.fn.extend({ on: function( types, selector, data, fn, /*INTERNAL*/ one ) { var origFn, type; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // && selector != null // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { this.on( type, selector, data, types[ type ], one ); } return this; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return this; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); }, one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event var handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( var type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each(function() { jQuery.event.remove( this, types, fn, selector ); }); }, bind: function( types, data, fn ) { return this.on( types, null, data, fn ); }, unbind: function( types, fn ) { return this.off( types, null, fn ); }, live: function( types, data, fn ) { jQuery( this.context ).on( types, this.selector, data, fn ); return this; }, die: function( types, fn ) { jQuery( this.context ).off( types, this.selector || "**", fn ); return this; }, delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); }, undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); }, trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); }, triggerHandler: function( type, data ) { if ( this[0] ) { return jQuery.event.trigger( type, data, this[0], true ); } }, toggle: function( fn ) { // Save reference to arguments for access in closure var args = arguments, guid = fn.guid || jQuery.guid++, i = 0, toggler = function( event ) { // Figure out which function to execute var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); // Make sure that clicks stop event.preventDefault(); // and execute the function return args[ lastToggle ].apply( this, arguments ) || false; }; // link all the functions, so any of them can unbind this click handler toggler.guid = guid; while ( i < args.length ) { args[ i++ ].guid = guid; } return this.click( toggler ); }, hover: function( fnOver, fnOut ) { return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); } }); jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { // Handle event binding jQuery.fn[ name ] = function( data, fn ) { if ( fn == null ) { fn = data; data = null; } return arguments.length > 0 ? this.on( name, null, data, fn ) : this.trigger( name ); }; if ( jQuery.attrFn ) { jQuery.attrFn[ name ] = true; } if ( rkeyEvent.test( name ) ) { jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; } if ( rmouseEvent.test( name ) ) { jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; } }); /*! * Sizzle CSS Selector Engine * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ (function(){ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, expando = "sizcache" + (Math.random() + '').replace('.', ''), done = 0, toString = Object.prototype.toString, hasDuplicate = false, baseHasDuplicate = true, rBackslash = /\\/g, rReturn = /\r\n/g, rNonWord = /\W/; // Here we check if the JavaScript engine is using some sort of // optimization where it does not always call our comparision // function. If that is the case, discard the hasDuplicate value. // Thus far that includes Google Chrome. [0, 0].sort(function() { baseHasDuplicate = false; return 0; }); var Sizzle = function( selector, context, results, seed ) { results = results || []; context = context || document; var origContext = context; if ( context.nodeType !== 1 && context.nodeType !== 9 ) { return []; } if ( !selector || typeof selector !== "string" ) { return results; } var m, set, checkSet, extra, ret, cur, pop, i, prune = true, contextXML = Sizzle.isXML( context ), parts = [], soFar = selector; // Reset the position of the chunker regexp (start from head) do { chunker.exec( "" ); m = chunker.exec( soFar ); if ( m ) { soFar = m[3]; parts.push( m[1] ); if ( m[2] ) { extra = m[3]; break; } } } while ( m ); if ( parts.length > 1 && origPOS.exec( selector ) ) { if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { set = posProcess( parts[0] + parts[1], context, seed ); } else { set = Expr.relative[ parts[0] ] ? [ context ] : Sizzle( parts.shift(), context ); while ( parts.length ) { selector = parts.shift(); if ( Expr.relative[ selector ] ) { selector += parts.shift(); } set = posProcess( selector, set, seed ); } } } else { // Take a shortcut and set the context if the root selector is an ID // (but not if it'll be faster if the inner selector is an ID) if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { ret = Sizzle.find( parts.shift(), context, contextXML ); context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; } if ( context ) { ret = seed ? { expr: parts.pop(), set: makeArray(seed) } : Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; if ( parts.length > 0 ) { checkSet = makeArray( set ); } else { prune = false; } while ( parts.length ) { cur = parts.pop(); pop = cur; if ( !Expr.relative[ cur ] ) { cur = ""; } else { pop = parts.pop(); } if ( pop == null ) { pop = context; } Expr.relative[ cur ]( checkSet, pop, contextXML ); } } else { checkSet = parts = []; } } if ( !checkSet ) { checkSet = set; } if ( !checkSet ) { Sizzle.error( cur || selector ); } if ( toString.call(checkSet) === "[object Array]" ) { if ( !prune ) { results.push.apply( results, checkSet ); } else if ( context && context.nodeType === 1 ) { for ( i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { results.push( set[i] ); } } } else { for ( i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && checkSet[i].nodeType === 1 ) { results.push( set[i] ); } } } } else { makeArray( checkSet, results ); } if ( extra ) { Sizzle( extra, origContext, results, seed ); Sizzle.uniqueSort( results ); } return results; }; Sizzle.uniqueSort = function( results ) { if ( sortOrder ) { hasDuplicate = baseHasDuplicate; results.sort( sortOrder ); if ( hasDuplicate ) { for ( var i = 1; i < results.length; i++ ) { if ( results[i] === results[ i - 1 ] ) { results.splice( i--, 1 ); } } } } return results; }; Sizzle.matches = function( expr, set ) { return Sizzle( expr, null, null, set ); }; Sizzle.matchesSelector = function( node, expr ) { return Sizzle( expr, null, null, [node] ).length > 0; }; Sizzle.find = function( expr, context, isXML ) { var set, i, len, match, type, left; if ( !expr ) { return []; } for ( i = 0, len = Expr.order.length; i < len; i++ ) { type = Expr.order[i]; if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { left = match[1]; match.splice( 1, 1 ); if ( left.substr( left.length - 1 ) !== "\\" ) { match[1] = (match[1] || "").replace( rBackslash, "" ); set = Expr.find[ type ]( match, context, isXML ); if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break; } } } } if ( !set ) { set = typeof context.getElementsByTagName !== "undefined" ? context.getElementsByTagName( "*" ) : []; } return { set: set, expr: expr }; }; Sizzle.filter = function( expr, set, inplace, not ) { var match, anyFound, type, found, item, filter, left, i, pass, old = expr, result = [], curLoop = set, isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); while ( expr && set.length ) { for ( type in Expr.filter ) { if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { filter = Expr.filter[ type ]; left = match[1]; anyFound = false; match.splice(1,1); if ( left.substr( left.length - 1 ) === "\\" ) { continue; } if ( curLoop === result ) { result = []; } if ( Expr.preFilter[ type ] ) { match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); if ( !match ) { anyFound = found = true; } else if ( match === true ) { continue; } } if ( match ) { for ( i = 0; (item = curLoop[i]) != null; i++ ) { if ( item ) { found = filter( item, match, i, curLoop ); pass = not ^ found; if ( inplace && found != null ) { if ( pass ) { anyFound = true; } else { curLoop[i] = false; } } else if ( pass ) { result.push( item ); anyFound = true; } } } } if ( found !== undefined ) { if ( !inplace ) { curLoop = result; } expr = expr.replace( Expr.match[ type ], "" ); if ( !anyFound ) { return []; } break; } } } // Improper expression if ( expr === old ) { if ( anyFound == null ) { Sizzle.error( expr ); } else { break; } } old = expr; } return curLoop; }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Utility function for retreiving the text value of an array of DOM nodes * @param {Array|Element} elem */ var getText = Sizzle.getText = function( elem ) { var i, node, nodeType = elem.nodeType, ret = ""; if ( nodeType ) { if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent || innerText for elements if ( typeof elem.textContent === 'string' ) { return elem.textContent; } else if ( typeof elem.innerText === 'string' ) { // Replace IE's carriage returns return elem.innerText.replace( rReturn, '' ); } else { // Traverse it's children for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } } else { // If no nodeType, this is expected to be an array for ( i = 0; (node = elem[i]); i++ ) { // Do not traverse comment nodes if ( node.nodeType !== 8 ) { ret += getText( node ); } } } return ret; }; var Expr = Sizzle.selectors = { order: [ "ID", "NAME", "TAG" ], match: { ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ }, leftMatch: {}, attrMap: { "class": "className", "for": "htmlFor" }, attrHandle: { href: function( elem ) { return elem.getAttribute( "href" ); }, type: function( elem ) { return elem.getAttribute( "type" ); } }, relative: { "+": function(checkSet, part){ var isPartStr = typeof part === "string", isTag = isPartStr && !rNonWord.test( part ), isPartStrNotTag = isPartStr && !isTag; if ( isTag ) { part = part.toLowerCase(); } for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { if ( (elem = checkSet[i]) ) { while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? elem || false : elem === part; } } if ( isPartStrNotTag ) { Sizzle.filter( part, checkSet, true ); } }, ">": function( checkSet, part ) { var elem, isPartStr = typeof part === "string", i = 0, l = checkSet.length; if ( isPartStr && !rNonWord.test( part ) ) { part = part.toLowerCase(); for ( ; i < l; i++ ) { elem = checkSet[i]; if ( elem ) { var parent = elem.parentNode; checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; } } } else { for ( ; i < l; i++ ) { elem = checkSet[i]; if ( elem ) { checkSet[i] = isPartStr ? elem.parentNode : elem.parentNode === part; } } if ( isPartStr ) { Sizzle.filter( part, checkSet, true ); } } }, "": function(checkSet, part, isXML){ var nodeCheck, doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !rNonWord.test( part ) ) { part = part.toLowerCase(); nodeCheck = part; checkFn = dirNodeCheck; } checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); }, "~": function( checkSet, part, isXML ) { var nodeCheck, doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !rNonWord.test( part ) ) { part = part.toLowerCase(); nodeCheck = part; checkFn = dirNodeCheck; } checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); } }, find: { ID: function( match, context, isXML ) { if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 return m && m.parentNode ? [m] : []; } }, NAME: function( match, context ) { if ( typeof context.getElementsByName !== "undefined" ) { var ret = [], results = context.getElementsByName( match[1] ); for ( var i = 0, l = results.length; i < l; i++ ) { if ( results[i].getAttribute("name") === match[1] ) { ret.push( results[i] ); } } return ret.length === 0 ? null : ret; } }, TAG: function( match, context ) { if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( match[1] ); } } }, preFilter: { CLASS: function( match, curLoop, inplace, result, not, isXML ) { match = " " + match[1].replace( rBackslash, "" ) + " "; if ( isXML ) { return match; } for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { if ( elem ) { if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { if ( !inplace ) { result.push( elem ); } } else if ( inplace ) { curLoop[i] = false; } } } return false; }, ID: function( match ) { return match[1].replace( rBackslash, "" ); }, TAG: function( match, curLoop ) { return match[1].replace( rBackslash, "" ).toLowerCase(); }, CHILD: function( match ) { if ( match[1] === "nth" ) { if ( !match[2] ) { Sizzle.error( match[0] ); } match[2] = match[2].replace(/^\+|\s*/g, ''); // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); // calculate the numbers (first)n+(last) including if they are negative match[2] = (test[1] + (test[2] || 1)) - 0; match[3] = test[3] - 0; } else if ( match[2] ) { Sizzle.error( match[0] ); } // TODO: Move to normal caching system match[0] = done++; return match; }, ATTR: function( match, curLoop, inplace, result, not, isXML ) { var name = match[1] = match[1].replace( rBackslash, "" ); if ( !isXML && Expr.attrMap[name] ) { match[1] = Expr.attrMap[name]; } // Handle if an un-quoted value was used match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); if ( match[2] === "~=" ) { match[4] = " " + match[4] + " "; } return match; }, PSEUDO: function( match, curLoop, inplace, result, not ) { if ( match[1] === "not" ) { // If we're dealing with a complex expression, or a simple one if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { match[3] = Sizzle(match[3], null, null, curLoop); } else { var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); if ( !inplace ) { result.push.apply( result, ret ); } return false; } } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { return true; } return match; }, POS: function( match ) { match.unshift( true ); return match; } }, filters: { enabled: function( elem ) { return elem.disabled === false && elem.type !== "hidden"; }, disabled: function( elem ) { return elem.disabled === true; }, checked: function( elem ) { return elem.checked === true; }, selected: function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, parent: function( elem ) { return !!elem.firstChild; }, empty: function( elem ) { return !elem.firstChild; }, has: function( elem, i, match ) { return !!Sizzle( match[3], elem ).length; }, header: function( elem ) { return (/h\d/i).test( elem.nodeName ); }, text: function( elem ) { var attr = elem.getAttribute( "type" ), type = elem.type; // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) // use getAttribute instead to test this case return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); }, radio: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; }, checkbox: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; }, file: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; }, password: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; }, submit: function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && "submit" === elem.type; }, image: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; }, reset: function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && "reset" === elem.type; }, button: function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && "button" === elem.type || name === "button"; }, input: function( elem ) { return (/input|select|textarea|button/i).test( elem.nodeName ); }, focus: function( elem ) { return elem === elem.ownerDocument.activeElement; } }, setFilters: { first: function( elem, i ) { return i === 0; }, last: function( elem, i, match, array ) { return i === array.length - 1; }, even: function( elem, i ) { return i % 2 === 0; }, odd: function( elem, i ) { return i % 2 === 1; }, lt: function( elem, i, match ) { return i < match[3] - 0; }, gt: function( elem, i, match ) { return i > match[3] - 0; }, nth: function( elem, i, match ) { return match[3] - 0 === i; }, eq: function( elem, i, match ) { return match[3] - 0 === i; } }, filter: { PSEUDO: function( elem, match, i, array ) { var name = match[1], filter = Expr.filters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } else if ( name === "contains" ) { return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; } else if ( name === "not" ) { var not = match[3]; for ( var j = 0, l = not.length; j < l; j++ ) { if ( not[j] === elem ) { return false; } } return true; } else { Sizzle.error( name ); } }, CHILD: function( elem, match ) { var first, last, doneName, parent, cache, count, diff, type = match[1], node = elem; switch ( type ) { case "only": case "first": while ( (node = node.previousSibling) ) { if ( node.nodeType === 1 ) { return false; } } if ( type === "first" ) { return true; } node = elem; /* falls through */ case "last": while ( (node = node.nextSibling) ) { if ( node.nodeType === 1 ) { return false; } } return true; case "nth": first = match[2]; last = match[3]; if ( first === 1 && last === 0 ) { return true; } doneName = match[0]; parent = elem.parentNode; if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { count = 0; for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { node.nodeIndex = ++count; } } parent[ expando ] = doneName; } diff = elem.nodeIndex - last; if ( first === 0 ) { return diff === 0; } else { return ( diff % first === 0 && diff / first >= 0 ); } } }, ID: function( elem, match ) { return elem.nodeType === 1 && elem.getAttribute("id") === match; }, TAG: function( elem, match ) { return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; }, CLASS: function( elem, match ) { return (" " + (elem.className || elem.getAttribute("class")) + " ") .indexOf( match ) > -1; }, ATTR: function( elem, match ) { var name = match[1], result = Sizzle.attr ? Sizzle.attr( elem, name ) : Expr.attrHandle[ name ] ? Expr.attrHandle[ name ]( elem ) : elem[ name ] != null ? elem[ name ] : elem.getAttribute( name ), value = result + "", type = match[2], check = match[4]; return result == null ? type === "!=" : !type && Sizzle.attr ? result != null : type === "=" ? value === check : type === "*=" ? value.indexOf(check) >= 0 : type === "~=" ? (" " + value + " ").indexOf(check) >= 0 : !check ? value && result !== false : type === "!=" ? value !== check : type === "^=" ? value.indexOf(check) === 0 : type === "$=" ? value.substr(value.length - check.length) === check : type === "|=" ? value === check || value.substr(0, check.length + 1) === check + "-" : false; }, POS: function( elem, match, i, array ) { var name = match[2], filter = Expr.setFilters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } } } }; var origPOS = Expr.match.POS, fescape = function(all, num){ return "\\" + (num - 0 + 1); }; for ( var type in Expr.match ) { Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); } // Expose origPOS // "global" as in regardless of relation to brackets/parens Expr.match.globalPOS = origPOS; var makeArray = function( array, results ) { array = Array.prototype.slice.call( array, 0 ); if ( results ) { results.push.apply( results, array ); return results; } return array; }; // Perform a simple check to determine if the browser is capable of // converting a NodeList to an array using builtin methods. // Also verifies that the returned array holds DOM nodes // (which is not the case in the Blackberry browser) try { Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; // Provide a fallback method if it does not work } catch( e ) { makeArray = function( array, results ) { var i = 0, ret = results || []; if ( toString.call(array) === "[object Array]" ) { Array.prototype.push.apply( ret, array ); } else { if ( typeof array.length === "number" ) { for ( var l = array.length; i < l; i++ ) { ret.push( array[i] ); } } else { for ( ; array[i]; i++ ) { ret.push( array[i] ); } } } return ret; }; } var sortOrder, siblingCheck; if ( document.documentElement.compareDocumentPosition ) { sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; return 0; } if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { return a.compareDocumentPosition ? -1 : 1; } return a.compareDocumentPosition(b) & 4 ? -1 : 1; }; } else { sortOrder = function( a, b ) { // The nodes are identical, we can exit early if ( a === b ) { hasDuplicate = true; return 0; // Fallback to using sourceIndex (in IE) if it's available on both nodes } else if ( a.sourceIndex && b.sourceIndex ) { return a.sourceIndex - b.sourceIndex; } var al, bl, ap = [], bp = [], aup = a.parentNode, bup = b.parentNode, cur = aup; // If the nodes are siblings (or identical) we can do a quick check if ( aup === bup ) { return siblingCheck( a, b ); // If no parents were found then the nodes are disconnected } else if ( !aup ) { return -1; } else if ( !bup ) { return 1; } // Otherwise they're somewhere else in the tree so we need // to build up a full list of the parentNodes for comparison while ( cur ) { ap.unshift( cur ); cur = cur.parentNode; } cur = bup; while ( cur ) { bp.unshift( cur ); cur = cur.parentNode; } al = ap.length; bl = bp.length; // Start walking down the tree looking for a discrepancy for ( var i = 0; i < al && i < bl; i++ ) { if ( ap[i] !== bp[i] ) { return siblingCheck( ap[i], bp[i] ); } } // We ended someplace up the tree so do a sibling check return i === al ? siblingCheck( a, bp[i], -1 ) : siblingCheck( ap[i], b, 1 ); }; siblingCheck = function( a, b, ret ) { if ( a === b ) { return ret; } var cur = a.nextSibling; while ( cur ) { if ( cur === b ) { return -1; } cur = cur.nextSibling; } return 1; }; } // Check to see if the browser returns elements by name when // querying by getElementById (and provide a workaround) (function(){ // We're going to inject a fake input element with a specified name var form = document.createElement("div"), id = "script" + (new Date()).getTime(), root = document.documentElement; form.innerHTML = ""; // Inject it into the root element, check its status, and remove it quickly root.insertBefore( form, root.firstChild ); // The workaround has to do additional checks after a getElementById // Which slows things down for other browsers (hence the branching) if ( document.getElementById( id ) ) { Expr.find.ID = function( match, context, isXML ) { if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; } }; Expr.filter.ID = function( elem, match ) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return elem.nodeType === 1 && node && node.nodeValue === match; }; } root.removeChild( form ); // release memory in IE root = form = null; })(); (function(){ // Check to see if the browser returns only elements // when doing getElementsByTagName("*") // Create a fake element var div = document.createElement("div"); div.appendChild( document.createComment("") ); // Make sure no comments are found if ( div.getElementsByTagName("*").length > 0 ) { Expr.find.TAG = function( match, context ) { var results = context.getElementsByTagName( match[1] ); // Filter out possible comments if ( match[1] === "*" ) { var tmp = []; for ( var i = 0; results[i]; i++ ) { if ( results[i].nodeType === 1 ) { tmp.push( results[i] ); } } results = tmp; } return results; }; } // Check to see if an attribute returns normalized href attributes div.innerHTML = ""; if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && div.firstChild.getAttribute("href") !== "#" ) { Expr.attrHandle.href = function( elem ) { return elem.getAttribute( "href", 2 ); }; } // release memory in IE div = null; })(); if ( document.querySelectorAll ) { (function(){ var oldSizzle = Sizzle, div = document.createElement("div"), id = "__sizzle__"; div.innerHTML = "

"; // Safari can't handle uppercase or unicode characters when // in quirks mode. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { return; } Sizzle = function( query, context, extra, seed ) { context = context || document; // Only use querySelectorAll on non-XML documents // (ID selectors don't work in non-HTML documents) if ( !seed && !Sizzle.isXML(context) ) { // See if we find a selector to speed up var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { // Speed-up: Sizzle("TAG") if ( match[1] ) { return makeArray( context.getElementsByTagName( query ), extra ); // Speed-up: Sizzle(".CLASS") } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { return makeArray( context.getElementsByClassName( match[2] ), extra ); } } if ( context.nodeType === 9 ) { // Speed-up: Sizzle("body") // The body element only exists once, optimize finding it if ( query === "body" && context.body ) { return makeArray( [ context.body ], extra ); // Speed-up: Sizzle("#ID") } else if ( match && match[3] ) { var elem = context.getElementById( match[3] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id === match[3] ) { return makeArray( [ elem ], extra ); } } else { return makeArray( [], extra ); } } try { return makeArray( context.querySelectorAll(query), extra ); } catch(qsaError) {} // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { var oldContext = context, old = context.getAttribute( "id" ), nid = old || id, hasParent = context.parentNode, relativeHierarchySelector = /^\s*[+~]/.test( query ); if ( !old ) { context.setAttribute( "id", nid ); } else { nid = nid.replace( /'/g, "\\$&" ); } if ( relativeHierarchySelector && hasParent ) { context = context.parentNode; } try { if ( !relativeHierarchySelector || hasParent ) { return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); } } catch(pseudoError) { } finally { if ( !old ) { oldContext.removeAttribute( "id" ); } } } } return oldSizzle(query, context, extra, seed); }; for ( var prop in oldSizzle ) { Sizzle[ prop ] = oldSizzle[ prop ]; } // release memory in IE div = null; })(); } (function(){ var html = document.documentElement, matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; if ( matches ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9 fails this) var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), pseudoWorks = false; try { // This should fail with an exception // Gecko does not error, returns false instead matches.call( document.documentElement, "[test!='']:sizzle" ); } catch( pseudoError ) { pseudoWorks = true; } Sizzle.matchesSelector = function( node, expr ) { // Make sure that attribute selectors are quoted expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); if ( !Sizzle.isXML( node ) ) { try { if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { var ret = matches.call( node, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || !disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9, so check for that node.document && node.document.nodeType !== 11 ) { return ret; } } } catch(e) {} } return Sizzle(expr, null, null, [node]).length > 0; }; } })(); (function(){ var div = document.createElement("div"); div.innerHTML = "
"; // Opera can't find a second classname (in 9.6) // Also, make sure that getElementsByClassName actually exists if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { return; } // Safari caches class attributes, doesn't catch changes (in 3.2) div.lastChild.className = "e"; if ( div.getElementsByClassName("e").length === 1 ) { return; } Expr.order.splice(1, 0, "CLASS"); Expr.find.CLASS = function( match, context, isXML ) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { return context.getElementsByClassName(match[1]); } }; // release memory in IE div = null; })(); function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { var match = false; elem = elem[dir]; while ( elem ) { if ( elem[ expando ] === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 && !isXML ){ elem[ expando ] = doneName; elem.sizset = i; } if ( elem.nodeName.toLowerCase() === cur ) { match = elem; break; } elem = elem[dir]; } checkSet[i] = match; } } } function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { var match = false; elem = elem[dir]; while ( elem ) { if ( elem[ expando ] === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { if ( !isXML ) { elem[ expando ] = doneName; elem.sizset = i; } if ( typeof cur !== "string" ) { if ( elem === cur ) { match = true; break; } } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { match = elem; break; } } elem = elem[dir]; } checkSet[i] = match; } } } if ( document.documentElement.contains ) { Sizzle.contains = function( a, b ) { return a !== b && (a.contains ? a.contains(b) : true); }; } else if ( document.documentElement.compareDocumentPosition ) { Sizzle.contains = function( a, b ) { return !!(a.compareDocumentPosition(b) & 16); }; } else { Sizzle.contains = function() { return false; }; } Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; var posProcess = function( selector, context, seed ) { var match, tmpSet = [], later = "", root = context.nodeType ? [context] : context; // Position selectors must be done after the filter // And so must :not(positional) so we move all PSEUDOs to the end while ( (match = Expr.match.PSEUDO.exec( selector )) ) { later += match[0]; selector = selector.replace( Expr.match.PSEUDO, "" ); } selector = Expr.relative[selector] ? selector + "*" : selector; for ( var i = 0, l = root.length; i < l; i++ ) { Sizzle( selector, root[i], tmpSet, seed ); } return Sizzle.filter( later, tmpSet ); }; // EXPOSE // Override sizzle attribute retrieval Sizzle.attr = jQuery.attr; Sizzle.selectors.attrMap = {}; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.filters; jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; })(); var runtil = /Until$/, rparentsprev = /^(?:parents|prevUntil|prevAll)/, // Note: This RegExp should be improved, or likely pulled from Sizzle rmultiselector = /,/, isSimple = /^.[^:#\[\.,]*$/, slice = Array.prototype.slice, POS = jQuery.expr.match.globalPOS, // methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend({ find: function( selector ) { var self = this, i, l; if ( typeof selector !== "string" ) { return jQuery( selector ).filter(function() { for ( i = 0, l = self.length; i < l; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } }); } var ret = this.pushStack( "", "find", selector ), length, n, r; for ( i = 0, l = this.length; i < l; i++ ) { length = ret.length; jQuery.find( selector, this[i], ret ); if ( i > 0 ) { // Make sure that the results are unique for ( n = length; n < ret.length; n++ ) { for ( r = 0; r < length; r++ ) { if ( ret[r] === ret[n] ) { ret.splice(n--, 1); break; } } } } } return ret; }, has: function( target ) { var targets = jQuery( target ); return this.filter(function() { for ( var i = 0, l = targets.length; i < l; i++ ) { if ( jQuery.contains( this, targets[i] ) ) { return true; } } }); }, not: function( selector ) { return this.pushStack( winnow(this, selector, false), "not", selector); }, filter: function( selector ) { return this.pushStack( winnow(this, selector, true), "filter", selector ); }, is: function( selector ) { return !!selector && ( typeof selector === "string" ? // If this is a positional selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". POS.test( selector ) ? jQuery( selector, this.context ).index( this[0] ) >= 0 : jQuery.filter( selector, this ).length > 0 : this.filter( selector ).length > 0 ); }, closest: function( selectors, context ) { var ret = [], i, l, cur = this[0]; // Array (deprecated as of jQuery 1.7) if ( jQuery.isArray( selectors ) ) { var level = 1; while ( cur && cur.ownerDocument && cur !== context ) { for ( i = 0; i < selectors.length; i++ ) { if ( jQuery( cur ).is( selectors[ i ] ) ) { ret.push({ selector: selectors[ i ], elem: cur, level: level }); } } cur = cur.parentNode; level++; } return ret; } // String var pos = POS.test( selectors ) || typeof selectors !== "string" ? jQuery( selectors, context || this.context ) : 0; for ( i = 0, l = this.length; i < l; i++ ) { cur = this[i]; while ( cur ) { if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { ret.push( cur ); break; } else { cur = cur.parentNode; if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { break; } } } } ret = ret.length > 1 ? jQuery.unique( ret ) : ret; return this.pushStack( ret, "closest", selectors ); }, // Determine the position of an element within // the matched set of elements index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; } // index in selector if ( typeof elem === "string" ) { return jQuery.inArray( this[0], jQuery( elem ) ); } // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem.jquery ? elem[0] : elem, this ); }, add: function( selector, context ) { var set = typeof selector === "string" ? jQuery( selector, context ) : jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), all = jQuery.merge( this.get(), set ); return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? all : jQuery.unique( all ) ); }, andSelf: function() { return this.add( this.prevObject ); } }); // A painfully simple check to see if an element is disconnected // from a document (should be improved, where feasible). function isDisconnected( node ) { return !node || !node.parentNode || node.parentNode.nodeType === 11; } jQuery.each({ parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return jQuery.dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return jQuery.dir( elem, "parentNode", until ); }, next: function( elem ) { return jQuery.nth( elem, 2, "nextSibling" ); }, prev: function( elem ) { return jQuery.nth( elem, 2, "previousSibling" ); }, nextAll: function( elem ) { return jQuery.dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return jQuery.dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return jQuery.dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return jQuery.dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return jQuery.sibling( elem.firstChild ); }, contents: function( elem ) { return jQuery.nodeName( elem, "iframe" ) ? elem.contentDocument || elem.contentWindow.document : jQuery.makeArray( elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var ret = jQuery.map( this, fn, until ); if ( !runtil.test( name ) ) { selector = until; } if ( selector && typeof selector === "string" ) { ret = jQuery.filter( selector, ret ); } ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { ret = ret.reverse(); } return this.pushStack( ret, name, slice.call( arguments ).join(",") ); }; }); jQuery.extend({ filter: function( expr, elems, not ) { if ( not ) { expr = ":not(" + expr + ")"; } return elems.length === 1 ? jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : jQuery.find.matches(expr, elems); }, dir: function( elem, dir, until ) { var matched = [], cur = elem[ dir ]; while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { if ( cur.nodeType === 1 ) { matched.push( cur ); } cur = cur[dir]; } return matched; }, nth: function( cur, result, dir, elem ) { result = result || 1; var num = 0; for ( ; cur; cur = cur[dir] ) { if ( cur.nodeType === 1 && ++num === result ) { break; } } return cur; }, sibling: function( n, elem ) { var r = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { r.push( n ); } } return r; } }); // Implement the identical functionality for filter and not function winnow( elements, qualifier, keep ) { // Can't pass null or undefined to indexOf in Firefox 4 // Set to 0 to skip string check qualifier = qualifier || 0; if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep(elements, function( elem, i ) { var retVal = !!qualifier.call( elem, i, elem ); return retVal === keep; }); } else if ( qualifier.nodeType ) { return jQuery.grep(elements, function( elem, i ) { return ( elem === qualifier ) === keep; }); } else if ( typeof qualifier === "string" ) { var filtered = jQuery.grep(elements, function( elem ) { return elem.nodeType === 1; }); if ( isSimple.test( qualifier ) ) { return jQuery.filter(qualifier, filtered, !keep); } else { qualifier = jQuery.filter( qualifier, filtered ); } } return jQuery.grep(elements, function( elem, i ) { return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; }); } function createSafeFragment( document ) { var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if ( safeFrag.createElement ) { while ( list.length ) { safeFrag.createElement( list.pop() ); } } return safeFrag; } var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, rleadingWhitespace = /^\s+/, rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, rtagName = /<([\w:]+)/, rtbody = /]", "i"), // checked="checked" or checked rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rscriptType = /\/(java|ecma)script/i, rcleanScript = /^\s*", "" ], legend: [ 1, "
", "
" ], thead: [ 1, "", "
" ], tr: [ 2, "", "
" ], td: [ 3, "", "
" ], col: [ 2, "", "
" ], area: [ 1, "", "" ], _default: [ 0, "", "" ] }, safeFragment = createSafeFragment( document ); wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; // IE can't serialize and