Merge branch 'wasm' into master
This commit is contained in:
commit
497f618cab
2
.cargo/config
Normal file
2
.cargo/config
Normal file
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
target-dir = "build"
|
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -1,17 +1,27 @@
|
|||
*.swp
|
||||
*.swo
|
||||
deploy.sh
|
||||
screenshots/
|
||||
tests/qemu/test-i386
|
||||
tests/jit-paging/test-jit
|
||||
*.map
|
||||
build/
|
||||
closure-compiler/
|
||||
images/
|
||||
*.bak
|
||||
*.orig
|
||||
*.wasm
|
||||
*.o
|
||||
*.bin
|
||||
*.img
|
||||
*.fixture
|
||||
*.fuse_hidden*
|
||||
*.DS_Store
|
||||
node_modules/
|
||||
Cargo.lock
|
||||
build-head
|
||||
src/rust/gen/interpreter.rs
|
||||
src/rust/gen/interpreter0f.rs
|
||||
src/rust/gen/analyzer.rs
|
||||
src/rust/gen/analyzer0f.rs
|
||||
src/rust/gen/jit.rs
|
||||
src/rust/gen/jit0f.rs
|
||||
bios/seabios
|
||||
|
|
25
.jshint.json
Normal file
25
.jshint.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"esversion": 8,
|
||||
"globalstrict": true,
|
||||
"sub": true,
|
||||
"expr": true,
|
||||
"-W058": true,
|
||||
"-W080": true,
|
||||
"-W082": true,
|
||||
"-W079": true,
|
||||
"-W117": true,
|
||||
"-W054": true,
|
||||
"-W027": true,
|
||||
"-W040": true,
|
||||
"-W087": true,
|
||||
"-W008": true,
|
||||
"loopfunc": true,
|
||||
"shadow": true,
|
||||
"funcscope": true,
|
||||
"globals": {
|
||||
"Blob": false,
|
||||
"alert": false,
|
||||
"console": false
|
||||
},
|
||||
"browser": true
|
||||
}
|
9
.rustfmt.toml
Normal file
9
.rustfmt.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
use_field_init_shorthand = true
|
||||
match_block_trailing_comma = true
|
||||
fn_single_line = true
|
||||
imports_indent = "Block"
|
||||
control_brace_style = "ClosingNextLine"
|
||||
single_line_if_else_max_width = 92
|
||||
ignore = [
|
||||
"src/rust/gen"
|
||||
]
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
mkdir -p images
|
||||
(cd images && curl --compressed -OOOOOOOOOO https://copy.sh/v86/images/{linux.iso,linux3.iso,kolibri.img,windows101.img,os8.dsk,freedos722.img,openbsd.img,oberon.dsk,oberon-boot.dsk})
|
||||
make build/libv86.js useacpi=true
|
||||
tests/full/run.js
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
mkdir -p images
|
||||
(cd images && curl --compressed -OOOOOOOOOO https://copy.sh/v86/images/{linux.iso,linux3.iso,kolibri.img,windows101.img,os8.dsk,freedos722.img,openbsd.img,oberon.dsk,oberon-boot.dsk})
|
||||
make build/libv86.js
|
||||
tests/full/run.js
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
make nasmtests
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
mkdir -p images
|
||||
(cd images && curl --compressed -O https://copy.sh/v86/images/linux3.iso)
|
||||
make qemutests
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
make build/libv86.js
|
||||
(cd tests/kvm-unit-tests && ./configure && make)
|
||||
tests/kvm-unit-tests/run.js tests/kvm-unit-tests/x86/realmode.flat
|
20
.travis.yml
20
.travis.yml
|
@ -1,20 +0,0 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "6.10.2"
|
||||
dist: trusty
|
||||
before_install:
|
||||
- sudo dpkg --add-architecture i386
|
||||
- sudo apt-get update -qq
|
||||
install:
|
||||
- sudo apt-get install -y gcc-multilib nasm gdb
|
||||
script:
|
||||
- "./.travis-run-$TEST_SUITE.sh"
|
||||
env:
|
||||
- TEST_SUITE=unit
|
||||
- TEST_SUITE=integration
|
||||
- TEST_SUITE=integration-acpi
|
||||
- TEST_SUITE=unit-qemu
|
||||
- TEST_SUITE=nasm
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: TEST_SUITE=unit-qemu
|
27
Cargo.toml
Normal file
27
Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "v86"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.6.2"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
profiler = []
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
path = "src/rust/lib.rs"
|
||||
|
||||
[profile.dev]
|
||||
lto = false
|
||||
opt-level = 2
|
||||
panic = "abort"
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
incremental = false
|
||||
panic = "abort"
|
6
LICENSE
6
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2012-2018, Fabian Hemmer
|
||||
Copyright (c) 2012, The v86 contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -20,7 +20,3 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
|
|
291
Makefile
291
Makefile
|
@ -1,44 +1,40 @@
|
|||
CLOSURE_DIR=closure-compiler
|
||||
CLOSURE=$(CLOSURE_DIR)/compiler.jar
|
||||
BROWSER=chromium
|
||||
NASM_TEST_DIR=./tests/nasm
|
||||
|
||||
all: build/v86_all.js
|
||||
browser: build/v86_all.js
|
||||
INSTRUCTION_TABLES=src/rust/gen/jit.rs src/rust/gen/jit0f.rs \
|
||||
src/rust/gen/interpreter.rs src/rust/gen/interpreter0f.rs \
|
||||
src/rust/gen/analyzer.rs src/rust/gen/analyzer0f.rs \
|
||||
|
||||
ACPI=false
|
||||
ifeq ($(useacpi),true)
|
||||
ACPI=true
|
||||
# Only the dependencies common to both generate_{jit,interpreter}.js
|
||||
GEN_DEPENDENCIES=$(filter-out gen/generate_interpreter.js gen/generate_jit.js gen/generate_analyzer.js, $(wildcard gen/*.js))
|
||||
JIT_DEPENDENCIES=$(GEN_DEPENDENCIES) gen/generate_jit.js
|
||||
INTERPRETER_DEPENDENCIES=$(GEN_DEPENDENCIES) gen/generate_interpreter.js
|
||||
ANALYZER_DEPENDENCIES=$(GEN_DEPENDENCIES) gen/generate_analyzer.js
|
||||
|
||||
STRIP_DEBUG_FLAG=
|
||||
ifeq ($(STRIP_DEBUG),true)
|
||||
STRIP_DEBUG_FLAG=--v86-strip-debug
|
||||
endif
|
||||
|
||||
default: build/v86-debug.wasm
|
||||
all: build/v86_all.js build/libv86.js build/v86.wasm
|
||||
all-debug: build/libv86-debug.js build/v86-debug.wasm
|
||||
browser: build/v86_all.js
|
||||
|
||||
# Used for nodejs builds and in order to profile code.
|
||||
# `debug` gives identifiers a readable name, make sure it doesn't have any side effects.
|
||||
CLOSURE_READABLE=--formatting PRETTY_PRINT --debug
|
||||
|
||||
CLOSURE_SOURCE_MAP=\
|
||||
--source_map_format V3\
|
||||
--source_map_include_content\
|
||||
--create_source_map '%outname%.map'
|
||||
|
||||
#--jscomp_error reportUnknownTypes\
|
||||
#--jscomp_error unusedLocalVariables\
|
||||
#--jscomp_error unusedPrivateMembers\
|
||||
#--new_type_inf\
|
||||
|
||||
# Easily breaks code:
|
||||
#--assume_function_wrapper\
|
||||
|
||||
# implies new type inferrence
|
||||
#--jscomp_error newCheckTypes\
|
||||
|
||||
CLOSURE_FLAGS=\
|
||||
--js lib/closure-base.js\
|
||||
--generate_exports\
|
||||
--externs src/externs.js\
|
||||
--warning_level VERBOSE\
|
||||
--jscomp_error accessControls\
|
||||
--jscomp_error ambiguousFunctionDecl\
|
||||
--jscomp_error checkEventfulObjectDisposal\
|
||||
--jscomp_error checkRegExp\
|
||||
--jscomp_error checkTypes\
|
||||
--jscomp_error checkVars\
|
||||
|
@ -48,15 +44,11 @@ CLOSURE_FLAGS=\
|
|||
--jscomp_error deprecated\
|
||||
--jscomp_error deprecatedAnnotations\
|
||||
--jscomp_error duplicateMessage\
|
||||
--jscomp_error es3\
|
||||
--jscomp_error es5Strict\
|
||||
--jscomp_error externsValidation\
|
||||
--jscomp_error fileoverviewTags\
|
||||
--jscomp_error globalThis\
|
||||
--jscomp_error internetExplorerChecks\
|
||||
--jscomp_error invalidCasts\
|
||||
--jscomp_error misplacedTypeAnnotation\
|
||||
--jscomp_error missingGetCssName\
|
||||
--jscomp_error missingProperties\
|
||||
--jscomp_error missingReturn\
|
||||
--jscomp_error msgDescriptions\
|
||||
|
@ -70,22 +62,35 @@ CLOSURE_FLAGS=\
|
|||
--jscomp_error visibility\
|
||||
--use_types_for_optimization\
|
||||
--summary_detail_level 3\
|
||||
--language_in ECMASCRIPT5_STRICT
|
||||
--language_in ECMASCRIPT_2017\
|
||||
--language_out ECMASCRIPT_2017
|
||||
|
||||
TRANSPILE_ES6_FLAGS=\
|
||||
--language_in ECMASCRIPT6_STRICT\
|
||||
--language_out ECMASCRIPT5_STRICT\
|
||||
CARGO_FLAGS_SAFE=\
|
||||
--target wasm32-unknown-unknown \
|
||||
-- \
|
||||
-C linker=tools/rust-lld-wrapper \
|
||||
-C link-args="--import-table --global-base=262144 $(STRIP_DEBUG_FLAG)" \
|
||||
-C link-args="build/softfloat.o" \
|
||||
-C link-args="build/zstddeclib.o" \
|
||||
--verbose
|
||||
|
||||
CARGO_FLAGS=$(CARGO_FLAGS_SAFE) -C target-feature=+bulk-memory
|
||||
|
||||
CORE_FILES=const.js config.js io.js main.js lib.js fpu.js ide.js pci.js floppy.js memory.js \
|
||||
dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js hpet.js acpi.js apic.js ioapic.js \
|
||||
CORE_FILES=const.js config.js io.js main.js lib.js ide.js pci.js floppy.js \
|
||||
memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js hpet.js \
|
||||
acpi.js apic.js ioapic.js \
|
||||
state.js ne2k.js sb16.js virtio.js bus.js log.js \
|
||||
cpu.js translate.js modrm.js string.js arith.js misc_instr.js instructions.js debug.js \
|
||||
elf.js
|
||||
cpu.js debug.js \
|
||||
elf.js kernel.js
|
||||
LIB_FILES=9p.js filesystem.js jor1k.js marshall.js utf8.js
|
||||
BROWSER_FILES=screen.js \
|
||||
keyboard.js mouse.js speaker.js serial.js \
|
||||
network.js lib.js starter.js worker_bus.js dummy_screen.js
|
||||
BROWSER_FILES=screen.js keyboard.js mouse.js speaker.js serial.js \
|
||||
network.js lib.js starter.js worker_bus.js dummy_screen.js \
|
||||
print_stats.js filestorage.js
|
||||
|
||||
RUST_FILES=$(shell find src/rust/ -name '*.rs') \
|
||||
src/rust/gen/interpreter.rs src/rust/gen/interpreter0f.rs \
|
||||
src/rust/gen/jit.rs src/rust/gen/jit0f.rs \
|
||||
src/rust/gen/analyzer.rs src/rust/gen/analyzer0f.rs
|
||||
|
||||
CORE_FILES:=$(addprefix src/,$(CORE_FILES))
|
||||
LIB_FILES:=$(addprefix lib/,$(LIB_FILES))
|
||||
|
@ -97,20 +102,27 @@ build/v86_all.js: $(CLOSURE) src/*.js src/browser/*.js lib/*.js
|
|||
java -jar $(CLOSURE) \
|
||||
--js_output_file build/v86_all.js\
|
||||
--define=DEBUG=false\
|
||||
--define=ENABLE_ACPI=$(ACPI)\
|
||||
$(CLOSURE_SOURCE_MAP)\
|
||||
$(CLOSURE_FLAGS)\
|
||||
--compilation_level ADVANCED\
|
||||
$(TRANSPILE_ES6_FLAGS)\
|
||||
--js $(CORE_FILES)\
|
||||
--js $(LIB_FILES)\
|
||||
--js $(BROWSER_FILES)\
|
||||
--js src/browser/main.js
|
||||
|
||||
echo '//# sourceMappingURL=v86_all.js.map' >> build/v86_all.js
|
||||
|
||||
ls -lh build/v86_all.js
|
||||
|
||||
build/v86_all_debug.js: $(CLOSURE) src/*.js src/browser/*.js lib/*.js
|
||||
mkdir -p build
|
||||
java -jar $(CLOSURE) \
|
||||
--js_output_file build/v86_all_debug.js\
|
||||
--define=DEBUG=true\
|
||||
$(CLOSURE_SOURCE_MAP)\
|
||||
$(CLOSURE_FLAGS)\
|
||||
--compilation_level ADVANCED\
|
||||
--js $(CORE_FILES)\
|
||||
--js $(LIB_FILES)\
|
||||
--js $(BROWSER_FILES)\
|
||||
--js src/browser/main.js
|
||||
|
||||
build/libv86.js: $(CLOSURE) src/*.js lib/*.js src/browser/*.js
|
||||
mkdir -p build
|
||||
|
@ -118,62 +130,209 @@ build/libv86.js: $(CLOSURE) src/*.js lib/*.js src/browser/*.js
|
|||
java -jar $(CLOSURE) \
|
||||
--js_output_file build/libv86.js\
|
||||
--define=DEBUG=false\
|
||||
--define=ENABLE_ACPI=$(ACPI)\
|
||||
$(CLOSURE_SOURCE_MAP)\
|
||||
$(CLOSURE_FLAGS)\
|
||||
--compilation_level SIMPLE\
|
||||
$(TRANSPILE_ES6_FLAGS)\
|
||||
--output_wrapper ';(function(){%output%}).call(this);'\
|
||||
--js $(CORE_FILES)\
|
||||
--js $(BROWSER_FILES)\
|
||||
--js $(LIB_FILES)
|
||||
ls -lh build/libv86.js
|
||||
|
||||
build/libv86-debug.js: $(CLOSURE) src/*.js lib/*.js src/browser/*.js
|
||||
mkdir -p build
|
||||
java -jar $(CLOSURE) \
|
||||
--js_output_file build/libv86-debug.js\
|
||||
--define=DEBUG=true\
|
||||
$(CLOSURE_FLAGS)\
|
||||
$(CLOSURE_READABLE)\
|
||||
--compilation_level SIMPLE\
|
||||
--output_wrapper ';(function(){%output%}).call(this);'\
|
||||
--js $(CORE_FILES)\
|
||||
--js $(BROWSER_FILES)\
|
||||
--js $(LIB_FILES)
|
||||
|
||||
echo '//# sourceMappingURL=libv86.js.map' >> build/libv86.js
|
||||
src/rust/gen/jit.rs: $(JIT_DEPENDENCIES)
|
||||
./gen/generate_jit.js --output-dir build/ --table jit
|
||||
src/rust/gen/jit0f.rs: $(JIT_DEPENDENCIES)
|
||||
./gen/generate_jit.js --output-dir build/ --table jit0f
|
||||
|
||||
ls -lh build/libv86.js
|
||||
src/rust/gen/interpreter.rs: $(INTERPRETER_DEPENDENCIES)
|
||||
./gen/generate_interpreter.js --output-dir build/ --table interpreter
|
||||
src/rust/gen/interpreter0f.rs: $(INTERPRETER_DEPENDENCIES)
|
||||
./gen/generate_interpreter.js --output-dir build/ --table interpreter0f
|
||||
|
||||
src/rust/gen/analyzer.rs: $(ANALYZER_DEPENDENCIES)
|
||||
./gen/generate_analyzer.js --output-dir build/ --table analyzer
|
||||
src/rust/gen/analyzer0f.rs: $(ANALYZER_DEPENDENCIES)
|
||||
./gen/generate_analyzer.js --output-dir build/ --table analyzer0f
|
||||
|
||||
build/v86.wasm: $(RUST_FILES) build/softfloat.o build/zstddeclib.o Cargo.toml
|
||||
mkdir -p build/
|
||||
-ls -lh build/v86.wasm
|
||||
cargo +nightly rustc --release $(CARGO_FLAGS)
|
||||
mv build/wasm32-unknown-unknown/release/v86.wasm build/v86.wasm
|
||||
ls -lh build/v86.wasm
|
||||
|
||||
build/v86-debug.wasm: $(RUST_FILES) build/softfloat.o build/zstddeclib.o Cargo.toml
|
||||
mkdir -p build/
|
||||
-ls -lh build/v86-debug.wasm
|
||||
cargo +nightly rustc $(CARGO_FLAGS)
|
||||
mv build/wasm32-unknown-unknown/debug/v86.wasm build/v86-debug.wasm
|
||||
ls -lh build/v86-debug.wasm
|
||||
|
||||
build/v86-fallback.wasm: $(RUST_FILES) build/softfloat.o build/zstddeclib.o Cargo.toml
|
||||
mkdir -p build/
|
||||
cargo +nightly rustc --release $(CARGO_FLAGS_SAFE)
|
||||
mv build/wasm32-unknown-unknown/release/v86.wasm build/v86-fallback.wasm || true
|
||||
|
||||
debug-with-profiler: $(RUST_FILES) build/softfloat.o build/zstddeclib.o Cargo.toml
|
||||
mkdir -p build/
|
||||
cargo +nightly rustc --features profiler $(CARGO_FLAGS)
|
||||
mv build/wasm32-unknown-unknown/debug/v86.wasm build/v86-debug.wasm || true
|
||||
|
||||
with-profiler: $(RUST_FILES) build/softfloat.o build/zstddeclib.o Cargo.toml
|
||||
mkdir -p build/
|
||||
cargo +nightly rustc --release --features profiler $(CARGO_FLAGS)
|
||||
mv build/wasm32-unknown-unknown/release/v86.wasm build/v86.wasm || true
|
||||
|
||||
build/softfloat.o: lib/softfloat/softfloat.c
|
||||
mkdir -p build
|
||||
clang -c -Wall \
|
||||
--target=wasm32 -O3 -flto -nostdlib -fvisibility=hidden -ffunction-sections -fdata-sections \
|
||||
-DSOFTFLOAT_FAST_INT64 -DINLINE_LEVEL=5 -DSOFTFLOAT_FAST_DIV32TO16 -DSOFTFLOAT_FAST_DIV64TO32 \
|
||||
-o build/softfloat.o \
|
||||
lib/softfloat/softfloat.c
|
||||
|
||||
build/zstddeclib.o: lib/zstd/zstddeclib.c
|
||||
mkdir -p build
|
||||
clang -c -Wall \
|
||||
--target=wasm32 -O3 -flto -nostdlib -fvisibility=hidden -ffunction-sections -fdata-sections \
|
||||
-I /usr/include \
|
||||
-o build/zstddeclib.o \
|
||||
lib/zstd/zstddeclib.c
|
||||
|
||||
clean:
|
||||
-rm build/libv86.js
|
||||
-rm build/libv86-debug.js
|
||||
-rm build/v86_all.js
|
||||
-rm build/libv86.js.map
|
||||
-rm build/v86_all.js.map
|
||||
-rm build/v86.wasm
|
||||
-rm build/v86-debug.wasm
|
||||
-rm $(INSTRUCTION_TABLES)
|
||||
-rm build/*.map
|
||||
-rm build/*.wast
|
||||
-rm build/*.o
|
||||
$(MAKE) -C $(NASM_TEST_DIR) clean
|
||||
|
||||
run:
|
||||
python3 -m http.server 2> /dev/null
|
||||
#sleep 1
|
||||
#$(BROWSER) http://localhost:8000/index.html &
|
||||
|
||||
update_version:
|
||||
set -e ;\
|
||||
COMMIT=`git log --format="%h" -n 1` ;\
|
||||
DATE=`git log --date="format:%b %e, %Y %H:%m" --format="%cd" -n 1` ;\
|
||||
SEARCH='<code>Version: <a href="https://github.com/copy/v86/commits/[a-f0-9]\+">[a-f0-9]\+</a> ([^(]\+)</a></code>' ;\
|
||||
REPLACE='<code>Version: <a href="https://github.com/copy/v86/commits/'$$COMMIT'">'$$COMMIT'</a> ('$$DATE')</a></code>' ;\
|
||||
SEARCH='<code>Version: <a href="https://github.com/copy/v86/commits/[a-f0-9]\+">[a-f0-9]\+</a> ([^(]\+)</code>' ;\
|
||||
REPLACE='<code>Version: <a href="https://github.com/copy/v86/commits/'$$COMMIT'">'$$COMMIT'</a> ('$$DATE')</code>' ;\
|
||||
sed -i "s@$$SEARCH@$$REPLACE@g" index.html ;\
|
||||
grep $$COMMIT index.html
|
||||
|
||||
|
||||
$(CLOSURE):
|
||||
wget -P $(CLOSURE_DIR) https://dl.google.com/closure-compiler/compiler-20190709.zip
|
||||
unzip -d closure-compiler $(CLOSURE_DIR)/compiler-20190709.zip \*.jar
|
||||
mv $(CLOSURE_DIR)/*.jar $(CLOSURE)
|
||||
rm $(CLOSURE_DIR)/compiler-20190709.zip
|
||||
mkdir -p $(CLOSURE_DIR)
|
||||
wget -nv -O $(CLOSURE) https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v20201207/closure-compiler-v20201207.jar
|
||||
|
||||
tests: build/libv86.js
|
||||
build/integration-test-fs/fs.json:
|
||||
mkdir -p build/integration-test-fs/flat
|
||||
cp images/buildroot-bzimage.bin build/integration-test-fs/bzImage
|
||||
touch build/integration-test-fs/initrd
|
||||
cd build/integration-test-fs && tar cfv fs.tar bzImage initrd
|
||||
./tools/fs2json.py build/integration-test-fs/fs.tar --out build/integration-test-fs/fs.json
|
||||
./tools/copy-to-sha256.py build/integration-test-fs/fs.tar build/integration-test-fs/flat
|
||||
rm build/integration-test-fs/fs.tar build/integration-test-fs/bzImage build/integration-test-fs/initrd
|
||||
|
||||
tests: all-debug build/integration-test-fs/fs.json
|
||||
./tests/full/run.js
|
||||
|
||||
nasmtests: build/libv86.js
|
||||
tests-release: all build/integration-test-fs/fs.json
|
||||
TEST_RELEASE_BUILD=1 ./tests/full/run.js
|
||||
|
||||
nasmtests: all-debug
|
||||
$(MAKE) -C $(NASM_TEST_DIR) all
|
||||
$(NASM_TEST_DIR)/gen_fixtures.js
|
||||
$(NASM_TEST_DIR)/run.js
|
||||
|
||||
qemutests: build/libv86.js
|
||||
make -C tests/qemu test-i386
|
||||
./tests/qemu/run.js > result
|
||||
./tests/qemu/test-i386 > reference
|
||||
diff result reference
|
||||
nasmtests-force-jit: all-debug
|
||||
$(MAKE) -C $(NASM_TEST_DIR) all
|
||||
$(NASM_TEST_DIR)/gen_fixtures.js
|
||||
$(NASM_TEST_DIR)/run.js --force-jit
|
||||
|
||||
kvm-unit-test: build/libv86.js
|
||||
(cd tests/kvm-unit-tests && ./configure)
|
||||
make -C tests/kvm-unit-tests
|
||||
jitpagingtests: all-debug
|
||||
$(MAKE) -C tests/jit-paging test-jit
|
||||
./tests/jit-paging/run.js
|
||||
|
||||
qemutests: all-debug
|
||||
$(MAKE) -C tests/qemu test-i386
|
||||
./tests/qemu/run.js > build/qemu-test-result
|
||||
./tests/qemu/run-qemu.js > build/qemu-test-reference
|
||||
diff build/qemu-test-result build/qemu-test-reference
|
||||
|
||||
qemutests-release: all
|
||||
$(MAKE) -C tests/qemu test-i386
|
||||
time TEST_RELEASE_BUILD=1 ./tests/qemu/run.js > build/qemu-test-result
|
||||
./tests/qemu/run-qemu.js > build/qemu-test-reference
|
||||
diff build/qemu-test-result build/qemu-test-reference
|
||||
|
||||
kvm-unit-test: all-debug
|
||||
(cd tests/kvm-unit-tests && ./configure && make)
|
||||
tests/kvm-unit-tests/run.js tests/kvm-unit-tests/x86/realmode.flat
|
||||
|
||||
kvm-unit-test-release: all
|
||||
(cd tests/kvm-unit-tests && ./configure && make)
|
||||
TEST_RELEASE_BUILD=1 tests/kvm-unit-tests/run.js tests/kvm-unit-tests/x86/realmode.flat
|
||||
|
||||
expect-tests: all-debug build/libwabt.js
|
||||
make -C tests/expect/tests
|
||||
./tests/expect/run.js
|
||||
|
||||
devices-test: all-debug
|
||||
./tests/devices/virtio_9p.js
|
||||
|
||||
rust-test: $(RUST_FILES)
|
||||
# RUSTFLAGS="-D warnings"
|
||||
env RUST_BACKTRACE=full RUST_TEST_THREADS=1 cargo +nightly test -- --nocapture
|
||||
./tests/rust/verify-wasmgen-dummy-output.js
|
||||
|
||||
rust-no-warnings:
|
||||
RUSTFLAGS="-D warnings" make all all-debug
|
||||
|
||||
rust-test-intensive:
|
||||
QUICKCHECK_TESTS=100000000 make rust-test
|
||||
|
||||
api-tests: all-debug
|
||||
./tests/api/clean-shutdown.js
|
||||
./tests/api/state.js
|
||||
./tests/api/reset.js
|
||||
|
||||
all-tests: jshint kvm-unit-test qemutests qemutests-release jitpagingtests api-tests nasmtests nasmtests-force-jit tests expect-tests
|
||||
# Skipping:
|
||||
# - devices-test (hangs)
|
||||
|
||||
jshint:
|
||||
jshint --config=./.jshint.json src tests gen lib --exclude lib/closure-base.js
|
||||
|
||||
rustfmt: $(RUST_FILES)
|
||||
cargo +nightly fmt --all -- --check
|
||||
|
||||
build/capstone-x86.min.js:
|
||||
mkdir -p build
|
||||
wget -P build https://github.com/AlexAltea/capstone.js/releases/download/v3.0.5-rc1/capstone-x86.min.js
|
||||
|
||||
build/libwabt.js:
|
||||
mkdir -p build
|
||||
wget -P build https://github.com/WebAssembly/wabt/archive/1.0.6.zip
|
||||
unzip -j -d build/ build/1.0.6.zip wabt-1.0.6/demo/libwabt.js
|
||||
rm build/1.0.6.zip
|
||||
|
||||
build/xterm.js:
|
||||
curl https://cdn.jsdelivr.net/npm/xterm@4.2.0-vscode1/lib/xterm.js > build/xterm.js
|
||||
curl https://cdn.jsdelivr.net/npm/xterm@4.2.0-vscode1/lib/xterm.js.map > build/xterm.js.map
|
||||
curl https://cdn.jsdelivr.net/npm/xterm@4.2.0-vscode1/css/xterm.css > build/xterm.css
|
||||
|
|
255
Readme.md
255
Readme.md
|
@ -1,16 +1,126 @@
|
|||
[![Join the chat at https://gitter.im/copy/v86](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/copy/v86)
|
||||
|
||||
v86 emulates an x86-compatible CPU and hardware. Machine code is translated to
|
||||
WebAssembly modules at runtime in order to achieve decent performance. Here's a
|
||||
list of emulated hardware:
|
||||
|
||||
- An x86-compatible CPU. The instruction set is around Pentium III level,
|
||||
including full SSE2 support. Some features are missing, in particular:
|
||||
- Task gates, far calls in protected mode
|
||||
- Some 16 bit protected mode features
|
||||
- Single stepping (trap flag, debug registers)
|
||||
- Some exceptions, especially floating point and SSE
|
||||
- Multicore
|
||||
- PAE
|
||||
- 64-bit extensions
|
||||
- A floating point unit (FPU). Calculations are done using the Berkeley
|
||||
SoftFloat library and therefore should be precise (but slow). Trigonometric
|
||||
and log functions are emulated using 64-bit floats and may be less precise.
|
||||
Not all FPU excpetions are supported.
|
||||
- A floppy disk controller (8272A).
|
||||
- An 8042 Keyboard Controller, PS2. With mouse support.
|
||||
- An 8254 Programmable Interval Timer (PIT).
|
||||
- An 8259 Programmable Interrupt Controller (PIC).
|
||||
- Partial APIC support.
|
||||
- A CMOS Real Time Clock (RTC).
|
||||
- A generic VGA card with SVGA support and Bochs VBE Extensions.
|
||||
- A PCI bus. This one is partly incomplete and not used by every device.
|
||||
- An IDE disk controller.
|
||||
- An NE2000 (8390) PCI network card.
|
||||
- A virtio filesystem.
|
||||
- A SoundBlaster 16 sound card.
|
||||
|
||||
Demos
|
||||
-
|
||||
|
||||
- [Windows 98](https://copy.sh/v86/?profile=windows98)
|
||||
- [Linux](https://copy.sh/v86/?profile=linux26)
|
||||
- [Linux 3](https://copy.sh/v86/?profile=linux3)
|
||||
- [KolibriOS](https://copy.sh/v86/?profile=kolibrios)
|
||||
- [FreeDOS](https://copy.sh/v86/?profile=freedos)
|
||||
- [Windows 1.01](https://copy.sh/v86/?profile=windows1)
|
||||
- [Archlinux](https://copy.sh/v86/?profile=archlinux)
|
||||
[Arch Linux](https://copy.sh/v86/?profile=archlinux) —
|
||||
[Damn Small Linux](https://copy.sh/v86/?profile=dsl) —
|
||||
[Buildroot Linux](https://copy.sh/v86/?profile=buildroot) —
|
||||
[ReactOS](https://copy.sh/v86/?profile=reactos) —
|
||||
[Windows 98](https://copy.sh/v86/?profile=windows98) —
|
||||
[Windows 95](https://copy.sh/v86/?profile=windows95) —
|
||||
[Windows 1.01](https://copy.sh/v86/?profile=windows1) —
|
||||
[MS-DOS](https://copy.sh/v86/?profile=msdos) —
|
||||
[FreeDOS](https://copy.sh/v86/?profile=freedos) —
|
||||
[FreeBSD](https://copy.sh/v86/?profile=freebsd) —
|
||||
[OpenBSD](https://copy.sh/v86/?profile=openbsd) —
|
||||
[9front](https://copy.sh/v86/?profile=9front) —
|
||||
[Haiku](https://copy.sh/v86/?profile=haiku) —
|
||||
[Oberon](https://copy.sh/v86/?profile=oberon) —
|
||||
[KolibriOS](https://copy.sh/v86/?profile=kolibrios) —
|
||||
[QNX](https://copy.sh/v86/?profile=qnx)
|
||||
|
||||
|
||||
Compatibility
|
||||
-
|
||||
|
||||
Here's an overview of the operating systems supported in v86:
|
||||
|
||||
- Linux works pretty well. Neither 64-bit nor PAE kernels are supported.
|
||||
- Damn Small Linux (2.4 Kernel) works.
|
||||
- All tested versions of TinyCore work.
|
||||
- [BuildRoot](https://buildroot.uclibc.org) can be used to build a minimal
|
||||
image. [humphd/browser-vm](https://github.com/humphd/browser-vm) has some
|
||||
useful scripts for building one.
|
||||
- Archlinux works. See [archlinux.md](docs/archlinux.md) for building an image.
|
||||
- Debian works. An image can be built from a Dockerfile, see [tools/docker/debian/](tools/docker/debian/).
|
||||
- Alpine Linux works.
|
||||
- ReactOS works.
|
||||
- FreeDOS, Windows 1.01 and MS-DOS run very well.
|
||||
- KolibriOS works.
|
||||
- Haiku works.
|
||||
- Android x86 1.6-r2 works if one selects VESA mode at the boot prompt. Newer
|
||||
versions haven't been tested.
|
||||
- Windows 1, 3.0, 95, 98 and ME work. Other versions currently don't (see #86, #208).
|
||||
- Many hobby operating systems work.
|
||||
- 9front works.
|
||||
- Plan 9 doesn't work.
|
||||
- QNX works.
|
||||
- OS/2 doesn't work.
|
||||
- FreeBSD works.
|
||||
- OpenBSD works with a specific boot configuration. At the `boot>` prompt type
|
||||
`boot -c`, then at the `UKC>` prompt `disable mpbios` and `exit`.
|
||||
- NetBSD works only with a custom kernel, see #350.
|
||||
- SerenityOS doesn't work due to missing PAE support.
|
||||
|
||||
You can get some infos on the disk images here: https://github.com/copy/images.
|
||||
|
||||
How to build, run and embed?
|
||||
-
|
||||
|
||||
You need:
|
||||
|
||||
- java (for Closure Compiler, not necessary when using `debug.html`)
|
||||
- make
|
||||
- gcc and libc-i386 for building some of the test binaries
|
||||
- nasm, gdb and qemu-system (for running tests)
|
||||
- rust-nightly with the wasm32-unknown-unknown target
|
||||
- A version of clang compatible with rust-nightly
|
||||
- nodejs (a recent version is required, 10.11.0 is known to be working)
|
||||
|
||||
See `tools/docker/test-image/Dockerfile` for a full setup on Debian.
|
||||
|
||||
|
||||
- Run `make` to build the debug build (at `debug.html`).
|
||||
- Run `make all` to build the optimized build (at `index.html`).
|
||||
- ROM and disk images are loaded via XHR, so if you want to try out `index.html`
|
||||
locally, make sure to serve it from a local webserver. You can use `make run`
|
||||
to serve the files using Python's http module.
|
||||
- If you only want to embed v86 in a webpage you can use libv86.js. For
|
||||
usage, check out the [examples](examples/).
|
||||
|
||||
|
||||
Testing
|
||||
-
|
||||
|
||||
The disk images for testing are not included in this repository. You can
|
||||
download them directly from the website using:
|
||||
|
||||
`wget -P images/ https://k.copy.sh/{linux.iso,linux4.iso,buildroot-bzimage.bin,openbsd-floppy.img,kolibri.img,windows101.img,os8.img,freedos722.img}`
|
||||
|
||||
Run all tests: `make jshint rustfmt kvm-unit-test nasmtests nasmtests-force-jit expect-tests jitpagingtests qemutests rust-test tests`
|
||||
|
||||
See [tests/Readme.md](tests/Readme.md) for more infos.
|
||||
|
||||
|
||||
API examples
|
||||
|
@ -40,131 +150,29 @@ var emulator = new V86Starter({
|
|||
});
|
||||
```
|
||||
|
||||
See [API](docs/api.md).
|
||||
See [starter.js](src/browser/starter.js).
|
||||
|
||||
|
||||
How does it work?
|
||||
-
|
||||
|
||||
v86 emulates an x86-compatible CPU and hardware. Here's a list of emulated hardware:
|
||||
|
||||
- An x86 compatible CPU. The instruction set is around Pentium 1 level. Some
|
||||
features are missing, more specifically:
|
||||
- Task gates, far calls in protected mode
|
||||
- 16 bit protected mode features
|
||||
- Single stepping
|
||||
- MMX, SSE
|
||||
- A bunch of FPU instructions
|
||||
- Some exceptions
|
||||
- A floating point unit (FPU). Calculations are done with JavaScript's double
|
||||
precision numbers (64 bit), so they are not as precise as calculations on a
|
||||
real FPU (80 bit).
|
||||
- A floppy disk controller (8272A).
|
||||
- An 8042 Keyboard Controller, PS2. With mouse support.
|
||||
- An 8254 Programmable Interval Timer (PIT).
|
||||
- An 8259 Programmable Interrupt Controller (PIC).
|
||||
- A CMOS Real Time Clock (RTC).
|
||||
- A generic VGA card with SVGA support and Bochs VBE Extensions.
|
||||
- A PCI bus. This one is partly incomplete and not used by every device.
|
||||
- An IDE disk controller.
|
||||
- An NE2000 (8390) PCI network card.
|
||||
- A virtio filesystem.
|
||||
- A SoundBlaster 16 sound card.
|
||||
|
||||
|
||||
Testing
|
||||
-
|
||||
|
||||
The disk images are not included in this repository. You can download them
|
||||
directly from the website using:
|
||||
|
||||
`wget -P images/ https://copy.sh/v86/images/{linux.iso,linux3.iso,kolibri.img,windows101.img,os8.dsk,freedos722.img,openbsd.img}`.
|
||||
|
||||
A testsuite is available in `tests/full/`. Run it using `node tests/full/run.js`.
|
||||
|
||||
|
||||
How to build, run and embed?
|
||||
-
|
||||
|
||||
- Building is only necessary for releases, open debug.html and everything should load out of the box
|
||||
- If you want a compressed and fast (i.e. with debug code removed) version, you
|
||||
need Closure Compiler. Download it as shown below and run `make build/v86_all.js`.
|
||||
- ROM and disk images are loaded via XHR, so if you want to try out `index.html`
|
||||
locally, make sure to serve it from a local webserver. You can use `make run`
|
||||
to serve the files using Python's SimpleHTTPServer.
|
||||
- If you only want to embed v86 in a webpage you can use libv86.js. For
|
||||
usage, check out the [API](docs/api.md) and [examples](examples/).
|
||||
- A couple of disk images are provided for testing. You can check them out
|
||||
using `wget -P images/ https://copy.sh/v86/images/{linux.iso,linux3.iso,kolibri.img,windows101.img,os8.dsk,freedos722.img,openbsd.img}`.
|
||||
|
||||
|
||||
**Short summary:**
|
||||
|
||||
```bash
|
||||
# grab the main repo
|
||||
git clone https://github.com/copy/v86.git && cd v86
|
||||
|
||||
# grab the disk images
|
||||
wget -P images/ https://copy.sh/v86/images/{linux.iso,linux3.iso,kolibri.img,windows101.img,os8.dsk,freedos722.img,openbsd.img}
|
||||
|
||||
# grab closure compiler
|
||||
wget -P closure-compiler https://dl.google.com/closure-compiler/compiler-latest.zip
|
||||
unzip -d closure-compiler closure-compiler/compiler-latest.zip *.jar
|
||||
|
||||
# build the library
|
||||
make build/libv86.js
|
||||
|
||||
# run the tests
|
||||
./tests/full/run.js
|
||||
```
|
||||
|
||||
Compatibility
|
||||
-
|
||||
|
||||
Here's an overview of the operating systems supported in v86:
|
||||
|
||||
- Linux works pretty well.
|
||||
- Tinycore (3.16, 4.8 kernel): Works.
|
||||
- Nanolinux works.
|
||||
- Archlinux works with some caveats. See [archlinux.md](docs/archlinux.md).
|
||||
- Damn Small Linux (2.4 Kernel): Doesn't work.
|
||||
- ReactOS works.
|
||||
- FreeDOS, Windows 1.01 and MS-DOS run very well.
|
||||
- KolibriOS works. A few applications need SSE.
|
||||
- Haiku boots, but takes very long (around 30 minutes).
|
||||
- No Android version seems to work, you still get a shell.
|
||||
- Windows 1, 3, 95 and 98 work. Windows XP is unstable, but can work with some
|
||||
tweaks ([see this issue](https://github.com/copy/v86/issues/86)). Other
|
||||
versions might work but haven't been tested.
|
||||
- Many hobby operating systems work.
|
||||
- FreeBSD works.
|
||||
- OS/2 doesn't work.
|
||||
|
||||
You can get some infos on the disk images here: https://github.com/copy/images.
|
||||
The Windows images are from [WinWorld](https://winworldpc.com/).
|
||||
|
||||
|
||||
How can I contribute?
|
||||
-
|
||||
|
||||
- Add new features (hardware devices, fill holes in the CPU), fix bugs. Check
|
||||
out the issues section and contact me if you need help.
|
||||
- Report bugs.
|
||||
- If you want to donate, let me know.
|
||||
|
||||
License
|
||||
-
|
||||
|
||||
- Source code and most tests: Simplified BSD License, see [LICENSE](LICENSE).
|
||||
- QEMU test suite: See [tests/qemu/LICENSE](LICENSE).
|
||||
v86 is distributed under the terms of the Simplified BSD License, see
|
||||
[LICENSE](LICENSE). The following third-party dependencies are included in the
|
||||
repository under their own licenses:
|
||||
|
||||
- [`lib/softfloat/softfloat.c`](lib/softfloat/softfloat.c)
|
||||
- [`lib/zstd/zstddeclib.c`](lib/zstd/zstddeclib.c)
|
||||
- [`tests/kvm-unit-tests/`](tests/kvm-unit-tests)
|
||||
- [`tests/qemutests/`](tests/qemutests)
|
||||
|
||||
|
||||
Credits
|
||||
-
|
||||
|
||||
- CPU test cases via QEMU, http://wiki.qemu.org/Main_Page
|
||||
- CPU test cases via [QEMU](https://wiki.qemu.org/Main_Page)
|
||||
- More tests via [kvm-unit-tests](https://www.linux-kvm.org/page/KVM-unit-tests)
|
||||
- [Disk Images](https://github.com/copy/images)
|
||||
- [zstd](https://github.com/facebook/zstd) support is included for better compression of state images
|
||||
- [Berkeley SoftFloat](http://www.jhauser.us/arithmetic/SoftFloat.html) is included to precisely emulate 80-bit floating point numbers
|
||||
- [The jor1k project](https://github.com/s-macke/jor1k) for 9p, filesystem and uart drivers
|
||||
- [WinWorld](https://winworldpc.com/) sources of some old operating systems
|
||||
|
||||
|
@ -172,11 +180,10 @@ Credits
|
|||
More questions?
|
||||
-
|
||||
|
||||
Shoot me an email to `copy@copy.sh`. Please don't tell about bugs via mail,
|
||||
create a bug report on GitHub instead.
|
||||
Shoot me an email to `copy@copy.sh`. Please report bugs on GitHub.
|
||||
|
||||
|
||||
Author
|
||||
-
|
||||
|
||||
Fabian Hemmer (http://copy.sh/, `copy@copy.sh`)
|
||||
Fabian Hemmer (https://copy.sh/, `copy@copy.sh`)
|
||||
|
|
13
bios/fetch-and-build-seabios.sh
Executable file
13
bios/fetch-and-build-seabios.sh
Executable file
|
@ -0,0 +1,13 @@
|
|||
set -e
|
||||
git clone https://git.seabios.org/seabios.git || true
|
||||
(cd seabios && git checkout rel-1.12.1)
|
||||
|
||||
cp seabios.config seabios/.config
|
||||
make -C seabios
|
||||
cp seabios/out/bios.bin seabios.bin
|
||||
cp seabios/out/vgabios.bin vgabios.bin
|
||||
|
||||
cp seabios-debug.config seabios/.config
|
||||
make -C seabios
|
||||
cp seabios/out/bios.bin seabios-debug.bin
|
||||
cp seabios/out/vgabios.bin vgabios-debug.bin
|
Binary file not shown.
117
bios/seabios-debug.config
Normal file
117
bios/seabios-debug.config
Normal file
|
@ -0,0 +1,117 @@
|
|||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# SeaBIOS Configuration
|
||||
#
|
||||
|
||||
#
|
||||
# General Features
|
||||
#
|
||||
# CONFIG_COREBOOT is not set
|
||||
CONFIG_QEMU=y
|
||||
# CONFIG_CSM is not set
|
||||
CONFIG_QEMU_HARDWARE=y
|
||||
CONFIG_XEN=y
|
||||
CONFIG_THREADS=y
|
||||
# CONFIG_RELOCATE_INIT is not set
|
||||
# CONFIG_BOOTMENU is not set
|
||||
CONFIG_BOOTORDER=y
|
||||
CONFIG_MULTIBOOT=y
|
||||
CONFIG_ENTRY_EXTRASTACK=y
|
||||
CONFIG_MALLOC_UPPERMEMORY=y
|
||||
CONFIG_ROM_SIZE=0
|
||||
|
||||
#
|
||||
# Hardware support
|
||||
#
|
||||
CONFIG_ATA=y
|
||||
CONFIG_ATA_DMA=y
|
||||
CONFIG_ATA_PIO32=y
|
||||
CONFIG_AHCI=y
|
||||
CONFIG_SDCARD=y
|
||||
CONFIG_VIRTIO_BLK=y
|
||||
CONFIG_VIRTIO_SCSI=y
|
||||
CONFIG_PVSCSI=y
|
||||
CONFIG_ESP_SCSI=y
|
||||
CONFIG_LSI_SCSI=y
|
||||
CONFIG_MEGASAS=y
|
||||
CONFIG_MPT_SCSI=y
|
||||
CONFIG_FLOPPY=y
|
||||
CONFIG_FLASH_FLOPPY=y
|
||||
# CONFIG_NVME is not set
|
||||
CONFIG_PS2PORT=y
|
||||
# CONFIG_USB is not set
|
||||
CONFIG_SERIAL=y
|
||||
# CONFIG_SERCON is not set
|
||||
CONFIG_LPT=y
|
||||
CONFIG_RTC_TIMER=y
|
||||
CONFIG_HARDWARE_IRQ=y
|
||||
CONFIG_USE_SMM=y
|
||||
CONFIG_CALL32_SMM=y
|
||||
CONFIG_MTRR_INIT=y
|
||||
CONFIG_PMTIMER=y
|
||||
CONFIG_TSC_TIMER=y
|
||||
|
||||
#
|
||||
# BIOS interfaces
|
||||
#
|
||||
CONFIG_DRIVES=y
|
||||
CONFIG_CDROM_BOOT=y
|
||||
CONFIG_CDROM_EMU=y
|
||||
CONFIG_PCIBIOS=y
|
||||
CONFIG_APMBIOS=y
|
||||
CONFIG_PNPBIOS=y
|
||||
CONFIG_OPTIONROMS=y
|
||||
CONFIG_PMM=y
|
||||
CONFIG_BOOT=y
|
||||
CONFIG_KEYBOARD=y
|
||||
CONFIG_KBD_CALL_INT15_4F=y
|
||||
CONFIG_MOUSE=y
|
||||
CONFIG_S3_RESUME=y
|
||||
CONFIG_VGAHOOKS=y
|
||||
# CONFIG_DISABLE_A20 is not set
|
||||
# CONFIG_WRITABLE_UPPERMEMORY is not set
|
||||
CONFIG_TCGBIOS=y
|
||||
|
||||
#
|
||||
# BIOS Tables
|
||||
#
|
||||
CONFIG_PIRTABLE=y
|
||||
CONFIG_MPTABLE=y
|
||||
# CONFIG_SMBIOS is not set
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_ACPI_DSDT=y
|
||||
CONFIG_FW_ROMFILE_LOAD=y
|
||||
|
||||
#
|
||||
# VGA ROM
|
||||
#
|
||||
# CONFIG_NO_VGABIOS is not set
|
||||
# CONFIG_VGA_STANDARD_VGA is not set
|
||||
# CONFIG_VGA_CIRRUS is not set
|
||||
CONFIG_VGA_BOCHS=y
|
||||
# CONFIG_VGA_GEODEGX2 is not set
|
||||
# CONFIG_VGA_GEODELX is not set
|
||||
# CONFIG_DISPLAY_BOCHS is not set
|
||||
# CONFIG_VGA_RAMFB is not set
|
||||
CONFIG_VGA_BOCHS_STDVGA=y
|
||||
# CONFIG_VGA_BOCHS_VMWARE is not set
|
||||
# CONFIG_VGA_BOCHS_QXL is not set
|
||||
# CONFIG_VGA_BOCHS_VIRTIO is not set
|
||||
CONFIG_BUILD_VGABIOS=y
|
||||
CONFIG_VGA_STDVGA_PORTS=y
|
||||
CONFIG_VGA_FIXUP_ASM=y
|
||||
CONFIG_VGA_ALLOCATE_EXTRA_STACK=y
|
||||
CONFIG_VGA_EXTRA_STACK_SIZE=512
|
||||
CONFIG_VGA_VBE=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_OVERRIDE_PCI_ID=y
|
||||
CONFIG_VGA_VID=0x1234
|
||||
CONFIG_VGA_DID=0x1111
|
||||
|
||||
#
|
||||
# Debugging
|
||||
#
|
||||
CONFIG_DEBUG_LEVEL=8
|
||||
# CONFIG_DEBUG_SERIAL is not set
|
||||
# CONFIG_DEBUG_SERIAL_MMIO is not set
|
||||
CONFIG_DEBUG_IO=y
|
BIN
bios/seabios.bin
BIN
bios/seabios.bin
Binary file not shown.
114
bios/seabios.config
Normal file
114
bios/seabios.config
Normal file
|
@ -0,0 +1,114 @@
|
|||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# SeaBIOS Configuration
|
||||
#
|
||||
|
||||
#
|
||||
# General Features
|
||||
#
|
||||
# CONFIG_COREBOOT is not set
|
||||
CONFIG_QEMU=y
|
||||
# CONFIG_CSM is not set
|
||||
CONFIG_QEMU_HARDWARE=y
|
||||
CONFIG_XEN=y
|
||||
CONFIG_THREADS=y
|
||||
# CONFIG_RELOCATE_INIT is not set
|
||||
# CONFIG_BOOTMENU is not set
|
||||
CONFIG_BOOTORDER=y
|
||||
CONFIG_MULTIBOOT=y
|
||||
CONFIG_ENTRY_EXTRASTACK=y
|
||||
CONFIG_MALLOC_UPPERMEMORY=y
|
||||
CONFIG_ROM_SIZE=0
|
||||
|
||||
#
|
||||
# Hardware support
|
||||
#
|
||||
CONFIG_ATA=y
|
||||
CONFIG_ATA_DMA=y
|
||||
CONFIG_ATA_PIO32=y
|
||||
CONFIG_AHCI=y
|
||||
CONFIG_SDCARD=y
|
||||
CONFIG_VIRTIO_BLK=y
|
||||
CONFIG_VIRTIO_SCSI=y
|
||||
CONFIG_PVSCSI=y
|
||||
CONFIG_ESP_SCSI=y
|
||||
CONFIG_LSI_SCSI=y
|
||||
CONFIG_MEGASAS=y
|
||||
CONFIG_MPT_SCSI=y
|
||||
CONFIG_FLOPPY=y
|
||||
CONFIG_FLASH_FLOPPY=y
|
||||
# CONFIG_NVME is not set
|
||||
CONFIG_PS2PORT=y
|
||||
# CONFIG_USB is not set
|
||||
CONFIG_SERIAL=y
|
||||
# CONFIG_SERCON is not set
|
||||
CONFIG_LPT=y
|
||||
CONFIG_RTC_TIMER=y
|
||||
CONFIG_HARDWARE_IRQ=y
|
||||
CONFIG_USE_SMM=y
|
||||
CONFIG_CALL32_SMM=y
|
||||
CONFIG_MTRR_INIT=y
|
||||
CONFIG_PMTIMER=y
|
||||
CONFIG_TSC_TIMER=y
|
||||
|
||||
#
|
||||
# BIOS interfaces
|
||||
#
|
||||
CONFIG_DRIVES=y
|
||||
CONFIG_CDROM_BOOT=y
|
||||
CONFIG_CDROM_EMU=y
|
||||
CONFIG_PCIBIOS=y
|
||||
CONFIG_APMBIOS=y
|
||||
CONFIG_PNPBIOS=y
|
||||
CONFIG_OPTIONROMS=y
|
||||
CONFIG_PMM=y
|
||||
CONFIG_BOOT=y
|
||||
CONFIG_KEYBOARD=y
|
||||
CONFIG_KBD_CALL_INT15_4F=y
|
||||
CONFIG_MOUSE=y
|
||||
CONFIG_S3_RESUME=y
|
||||
CONFIG_VGAHOOKS=y
|
||||
# CONFIG_DISABLE_A20 is not set
|
||||
# CONFIG_WRITABLE_UPPERMEMORY is not set
|
||||
CONFIG_TCGBIOS=y
|
||||
|
||||
#
|
||||
# BIOS Tables
|
||||
#
|
||||
CONFIG_PIRTABLE=y
|
||||
CONFIG_MPTABLE=y
|
||||
# CONFIG_SMBIOS is not set
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_ACPI_DSDT=y
|
||||
CONFIG_FW_ROMFILE_LOAD=y
|
||||
|
||||
#
|
||||
# VGA ROM
|
||||
#
|
||||
# CONFIG_NO_VGABIOS is not set
|
||||
# CONFIG_VGA_STANDARD_VGA is not set
|
||||
# CONFIG_VGA_CIRRUS is not set
|
||||
CONFIG_VGA_BOCHS=y
|
||||
# CONFIG_VGA_GEODEGX2 is not set
|
||||
# CONFIG_VGA_GEODELX is not set
|
||||
# CONFIG_DISPLAY_BOCHS is not set
|
||||
# CONFIG_VGA_RAMFB is not set
|
||||
CONFIG_VGA_BOCHS_STDVGA=y
|
||||
# CONFIG_VGA_BOCHS_VMWARE is not set
|
||||
# CONFIG_VGA_BOCHS_QXL is not set
|
||||
# CONFIG_VGA_BOCHS_VIRTIO is not set
|
||||
CONFIG_BUILD_VGABIOS=y
|
||||
CONFIG_VGA_STDVGA_PORTS=y
|
||||
CONFIG_VGA_FIXUP_ASM=y
|
||||
CONFIG_VGA_ALLOCATE_EXTRA_STACK=y
|
||||
CONFIG_VGA_EXTRA_STACK_SIZE=512
|
||||
CONFIG_VGA_VBE=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_OVERRIDE_PCI_ID=y
|
||||
CONFIG_VGA_VID=0x1234
|
||||
CONFIG_VGA_DID=0x1111
|
||||
|
||||
#
|
||||
# Debugging
|
||||
#
|
||||
CONFIG_DEBUG_LEVEL=0
|
Binary file not shown.
BIN
bios/vgabios.bin
BIN
bios/vgabios.bin
Binary file not shown.
138
debug.html
138
debug.html
|
@ -1,7 +1,7 @@
|
|||
<!doctype html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
|
||||
<title>Virtual x86</title>
|
||||
<title>Virtual x86 (debug)</title>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
|
||||
<script src="loader.js"></script>
|
||||
|
@ -11,22 +11,63 @@
|
|||
<div id="boot_options">
|
||||
<h4>Debugger</h4>
|
||||
<input type="button" value="ReactOS" id="start_reactos">
|
||||
<input type="button" value="Windows ME" id="start_windowsme">
|
||||
<input type="button" value="ReactOS (boot)" id="start_reactos-boot">
|
||||
<input type="button" value="Hiren's Boot CD" id="start_hirens">
|
||||
<br>
|
||||
|
||||
<input type="button" value="MS-DOS" id="start_msdos">
|
||||
<input type="button" value="FreeDOS" id="start_freedos">
|
||||
<input type="button" value="FreeDOS with FreeGEM" id="start_freegem">
|
||||
<input type="button" value="FreeDOS CD with games" id="start_fdgame">
|
||||
<input type="button" value="FreeDOS with QBasic" id="start_qbasic">
|
||||
<br>
|
||||
|
||||
<input type="button" value="Windows 1.01" id="start_windows1">
|
||||
<input type="button" value="Windows 3.0" id="start_windows30">
|
||||
<input type="button" value="Windows 3.1" id="start_windows31">
|
||||
<input type="button" value="Windows 95" id="start_windows95">
|
||||
<input type="button" value="FreeBSD" id="start_freebsd">
|
||||
<input type="button" value="Windows 95 (boot)" id="start_windows95-boot">
|
||||
<br>
|
||||
<input type="button" value="Windows 98" id="start_windows98">
|
||||
<input type="button" value="Windows 98 (boot)" id="start_windows98-boot">
|
||||
<input type="button" value="Windows 2000" id="start_windows2000">
|
||||
<input type="button" value="Windows Me" id="start_windowsme">
|
||||
<input type="button" value="Windows Me (2)" id="start_windowsme2">
|
||||
<br>
|
||||
|
||||
<input type="button" value="Linux 2.6 (Buildroot)" id="start_linux26">
|
||||
<input type="button" value="Linux 3.18 (Buildroot)" id="start_linux3">
|
||||
<input type="button" value="Linux 4.16 (Buildroot)" id="start_linux4">
|
||||
<input type="button" value="Linux 5.6 (Buildroot)" id="start_buildroot">
|
||||
<input type="button" value="Tiny Core" id="start_tinycore">
|
||||
<input type="button" value="OpenWRT" id="start_openwrt">
|
||||
<br>
|
||||
<input type="button" value="Arch Linux" id="start_archlinux">
|
||||
<input type="button" value="Arch Linux (boot)" id="start_archlinux-9p-cool">
|
||||
<input type="button" value="Damn Small Linux" id="start_dsl">
|
||||
<br>
|
||||
|
||||
<input type="button" value="FreeBSD" id="start_freebsd">
|
||||
<input type="button" value="FreeBSD (boot)" id="start_freebsd-boot">
|
||||
<input type="button" value="OpenBSD" id="start_openbsd">
|
||||
<input type="button" value="NetBSD" id="start_netbsd">
|
||||
<br>
|
||||
|
||||
<input type="button" value="Haiku" id="start_haiku">
|
||||
<input type="button" value="Haiku (boot)" id="start_haiku-boot">
|
||||
<input type="button" value="Minix" id="start_minix">
|
||||
<input type="button" value="SerenityOS" id="start_serenity">
|
||||
<input type="button" value="QNX" id="start_qnx">
|
||||
<input type="button" value="9front" id="start_9front">
|
||||
<input type="button" value="Plan 9" id="start_plan9">
|
||||
<br>
|
||||
|
||||
<input type="button" value="Oberon" id="start_oberon">
|
||||
<input type="button" value="KolibriOS" id="start_kolibrios">
|
||||
<input type="button" value="Linux 2.6" id="start_linux26">
|
||||
<input type="button" value="Linux 3.18" id="start_linux3">
|
||||
<input type="button" value="Windows 1.01" id="start_windows1">
|
||||
<input type="button" value="FreeDOS" id="start_freedos">
|
||||
<input type="button" value="MS-DOS" id="start_msdos">
|
||||
<input type="button" value="OpenBSD" id="start_openbsd">
|
||||
<input type="button" value="Solar OS" id="start_solos">
|
||||
<input type="button" value="Bootchess" id="start_bootchess">
|
||||
<input type="button" value="Test" id="start_test">
|
||||
<input type="button" value="HelenOS" id="start_helenos">
|
||||
<input type="button" value="MikeOS" id="start_mikeos">
|
||||
<br>
|
||||
|
||||
<!--
|
||||
|
@ -39,25 +80,19 @@
|
|||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="250">CD image</td>
|
||||
<td width="350"><label for="cd_image">CD image</label></td>
|
||||
<td>
|
||||
<!--
|
||||
<select>
|
||||
<option>None</option>
|
||||
<option>Local file</option>
|
||||
<option>External Server</option>
|
||||
</select>-->
|
||||
<input type="file" id="cd_image">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Floppy disk image</td>
|
||||
<td><label for="floppy_image">Floppy disk image</label></td>
|
||||
<td> <input type="file" id="floppy_image"><br></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Master Hard drive disk image</td>
|
||||
<td><label for="hda_image">Hard drive disk image</label></td>
|
||||
<td><input type="file" id="hda_image"><br></td>
|
||||
</tr>
|
||||
|
||||
|
@ -67,41 +102,59 @@
|
|||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Multiboot kernel image</td>
|
||||
<td><label for="multiboot_image">Multiboot kernel image (experimental)</label></td>
|
||||
<td><input type="file" id="multiboot_image"><br></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><hr></td>
|
||||
<td colspan="2"><small>Disk images are not uploaded to the server</small><hr></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Memory size</td>
|
||||
<td><label for="memory_size">Memory size</label></td>
|
||||
<td>
|
||||
<input id="memory_size" type="number" value="128" min="16" max="2048" step="16"> MB<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Video Memory size</td>
|
||||
<td><label for="video_memory_size">Video Memory size</label></td>
|
||||
<td>
|
||||
<input id="video_memory_size" type="number" value="8" min="1" max="128" step="1"> MB<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><label for="networking_proxy">Networking proxy (leave blank to disable)</label></td>
|
||||
<td>
|
||||
<input id="networking_proxy" type="text" value="wss://relay.widgetry.org/">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><hr></td>
|
||||
</tr>
|
||||
|
||||
<!--
|
||||
<tr>
|
||||
<td>Execution Cap </td>
|
||||
<td> <input type="number" value="100" min="5" max="100" step="5"> %<br> </td>
|
||||
<td><label for="disable_audio">Disable audio</label></td>
|
||||
<td>
|
||||
<input id="disable_audio" type="checkbox"><br>
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
|
||||
<tr>
|
||||
<td>Boot order</td>
|
||||
<td><label for="enable_acpi">Enable ACPI (experimental)</label></td>
|
||||
<td>
|
||||
<input id="enable_acpi" type="checkbox"><br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><hr></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><label for="boot_order">Boot order</label></td>
|
||||
<td>
|
||||
<select id="boot_order">
|
||||
<option value="213">CD / Floppy / Hard Disk</option>
|
||||
|
@ -117,25 +170,11 @@
|
|||
|
||||
<br>
|
||||
<button id="start_emulation">Start Emulation</button>
|
||||
<!--
|
||||
<br>
|
||||
<div id="setup_error">Error: Video size must be at least over 9000</div>
|
||||
-->
|
||||
<!--
|
||||
<br>
|
||||
<br>
|
||||
Link to this configuration: <a href="" id="config_link">http://copy.sh/v86/?a=b&c=d</a>
|
||||
-->
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div id="runtime_options" style="display: none">
|
||||
<input type="button" value="Step" id="step">
|
||||
<input type="button" value="Run until" id="run_until">
|
||||
<input type="button" value="Debugger" id="debugger">
|
||||
<input type="button" value="Dump Instructions" id="dump_instructions">
|
||||
<input type="button" value="Dump Instructions to file" id="dump_instructions_file">
|
||||
<input type="button" value="Dump Registers" id="dump_regs">
|
||||
<input type="button" value="Dump GDT/LDT" id="dump_gdt">
|
||||
<input type="button" value="Dump IDT" id="dump_idt">
|
||||
|
@ -155,7 +194,7 @@
|
|||
<input type="button" value="Get cdrom image" id="get_cdrom_image">
|
||||
<input type="button" value="Save State" id="save_state">
|
||||
<input type="button" value="Load State" id="load_state"> <input type="file" style="display: none" id="load_state_input">
|
||||
<input type="button" value="Memory Dump (raw)" id="memory_dump">
|
||||
<input type="button" value="Memory Dump" id="memory_dump">
|
||||
<input type="button" value="Disable mouse" id="toggle_mouse">
|
||||
<input type="button" value="Lock mouse" id="lock_mouse">
|
||||
<input type="button" value="Go fullscreen" id="fullscreen">
|
||||
|
@ -180,10 +219,7 @@
|
|||
</div>
|
||||
|
||||
<pre style="margin: 0" id="log_levels"></pre>
|
||||
<pre style="margin: 3px 0px 0px 0px" id="debug_infos"></pre>
|
||||
|
||||
<pre style="display: none" id="loading"></pre>
|
||||
|
||||
<br>
|
||||
</div>
|
||||
|
||||
|
@ -196,10 +232,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="runtime_infos" style="display: none">
|
||||
Running: <span id="running_time">0s</span> <br>
|
||||
Speed: <span id="speed">0</span>kIPS<br>
|
||||
Avg speed: <span id="avg_speed">0</span>kIPS<br>
|
||||
Speed: <span id="speed">0</span> mIPS<br>
|
||||
Avg speed: <span id="avg_speed">0</span> mIPS<br>
|
||||
<br>
|
||||
<div id="info_storage" style="display: none">
|
||||
<b>IDE device (HDA or CDROM)</b><br>
|
||||
|
@ -214,7 +251,7 @@
|
|||
<b>9p Filesystem</b><br>
|
||||
Bytes read: <span id="info_filesystem_bytes_read">0</span><br>
|
||||
Bytes written: <span id="info_filesystem_bytes_written">0</span><br>
|
||||
Last file: <span id="info_filesystem_last_file" style="word-wrap: break-word"></span><br>
|
||||
<div style="white-space: nowrap; overflow-x: hidden">Last file: <span id="info_filesystem_last_file"></span></div>
|
||||
Status: <span id="info_filesystem_status"></span><br>
|
||||
<br>
|
||||
</div>
|
||||
|
@ -230,7 +267,6 @@
|
|||
BPP: <span id="info_bpp">-</span><br>
|
||||
<br>
|
||||
Mouse: <span id="info_mouse_enabled">No</span><br>
|
||||
<!-- Keyboard: <span id="info_keyboard_enabled">-</span><br> -->
|
||||
</div>
|
||||
|
||||
<div id="filesystem_panel" style="display: none">
|
||||
|
@ -254,3 +290,5 @@
|
|||
|
||||
<textarea spellcheck="false" cols="40" rows="12" id="serial" style="display: none">
|
||||
</textarea>
|
||||
|
||||
<div id="terminal"></div>
|
||||
|
|
279
docs/api.md
279
docs/api.md
|
@ -1,279 +0,0 @@
|
|||
# V86Starter
|
||||
- [`run()`](#run)
|
||||
- [`stop()`](#stop)
|
||||
- [`restart()`](#restart)
|
||||
- [`add_listener(string event, function(*) listener)`](#add_listenerstring-event-function-listener)
|
||||
- [`remove_listener(string event, function(*) listener)`](#remove_listenerstring-event-function-listener)
|
||||
- [`restore_state(ArrayBuffer state)`](#restore_statearraybuffer-state)
|
||||
- [`save_state(function(Object, ArrayBuffer) callback)`](#save_statefunctionobject-arraybuffer-callback)
|
||||
- [`get_statistics() -> Object`](#get_statistics---object)
|
||||
- [`is_running() -> boolean`](#is_running---boolean)
|
||||
- [`keyboard_send_scancodes(Array.<number> codes)`](#keyboard_send_scancodesarraynumber-codes)
|
||||
- [`mouse_set_status(boolean enabled)`](#mouse_set_statusboolean-enabled)
|
||||
- [`keyboard_set_status(boolean enabled)`](#keyboard_set_statusboolean-enabled)
|
||||
- [`serial0_send(string data)`](#serial0_sendstring-data)
|
||||
- [`create_file(string file, Uint8Array data, function(Object) callback)`](#create_filestring-file-uint8array-data-functionobject-callback)
|
||||
- [`read_file(string file, function(Object, Uint8Array) callback)`](#read_filestring-file-functionobject-uint8array-callback)
|
||||
- [`lock_mouse()`](#lock_mouse)
|
||||
|
||||
***
|
||||
## `V86Starter`
|
||||
Constructor for emulator instances.
|
||||
|
||||
Usage: `var emulator = new V86Starter(options);`
|
||||
|
||||
Options can have the following properties (all optional, default in parenthesis):
|
||||
|
||||
- `memory_size number` (16 * 1024 * 1024) - The memory size in bytes, should
|
||||
be a power of 2.
|
||||
- `vga_memory_size number` (8 * 1024 * 1024) - VGA memory size in bytes.
|
||||
|
||||
- `autostart boolean` (false) - If emulation should be started when emulator
|
||||
is ready.
|
||||
|
||||
- `disable_keyboard boolean` (false) - If the keyboard should be disabled.
|
||||
- `disable_mouse boolean` (false) - If the mouse should be disabled.
|
||||
|
||||
- `network_relay_url string` (No network card) - The url of a server running
|
||||
websockproxy. See [networking.md](networking.md). Setting this will
|
||||
enable an emulated network card.
|
||||
|
||||
- `bios Object` (No bios) - Either a url pointing to a bios or an
|
||||
ArrayBuffer, see below.
|
||||
- `vga_bios Object` (No VGA bios) - VGA bios, see below.
|
||||
- `hda Object` (No hard drive) - First hard disk, see below.
|
||||
- `fda Object` (No floppy disk) - First floppy disk, see below.
|
||||
- `cdrom Object` (No CD) - See below.
|
||||
- `initial_state Object` (Normal boot) - An initial state to load, see
|
||||
[`restore_state`](#restore_statearraybuffer-state) and below.
|
||||
|
||||
- `filesystem Object` (No 9p filesystem) - A 9p filesystem, see
|
||||
[filesystem.md](filesystem.md).
|
||||
|
||||
- `serial_container HTMLTextAreaElement` (No serial terminal) - A textarea
|
||||
that will receive and send data to the emulated serial terminal.
|
||||
Alternatively the serial terminal can also be accessed programatically,
|
||||
see [serial.html](../examples/serial.html).
|
||||
|
||||
- `screen_container HTMLElement` (No screen) - An HTMLElement. This should
|
||||
have a certain structure, see [basic.html](../examples/basic.html).
|
||||
|
||||
***
|
||||
|
||||
There are two ways to load images (`bios`, `vga_bios`, `cdrom`, `hda`, ...):
|
||||
|
||||
- Pass an object that has a url. Optionally, `async: true` and `size:
|
||||
size_in_bytes` can be added to the object, so that sectors of the image
|
||||
are loaded on demand instead of being loaded before boot (slower, but
|
||||
strongly recommended for big files). In that case, the `Range: bytes=...`
|
||||
header must be supported on the server. Note: the python SimpleHTTPServer
|
||||
does not support this, so it won't work with the default webserver used
|
||||
by `make run`.
|
||||
|
||||
```javascript
|
||||
// download file before boot
|
||||
bios: {
|
||||
url: "bios/seabios.bin"
|
||||
}
|
||||
// download file sectors as requested, size is required
|
||||
hda: {
|
||||
url: "disk/linux.iso",
|
||||
async: true,
|
||||
size: 16 * 1024 * 1024
|
||||
}
|
||||
```
|
||||
|
||||
- Pass an `ArrayBuffer` or `File` object as `buffer` property.
|
||||
|
||||
```javascript
|
||||
// use <input type=file>
|
||||
bios: {
|
||||
buffer: document.all.hd_image.files[0]
|
||||
}
|
||||
// start with empty hard drive
|
||||
hda: {
|
||||
buffer: new ArrayBuffer(16 * 1024 * 1024)
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`Object`** options – Options to initialize the emulator with.
|
||||
|
||||
***
|
||||
#### `run()`
|
||||
Start emulation. Do nothing if emulator is running already. Can be
|
||||
asynchronous.
|
||||
|
||||
***
|
||||
#### `stop()`
|
||||
Stop emulation. Do nothing if emulator is not running. Can be asynchronous.
|
||||
|
||||
***
|
||||
#### `restart()`
|
||||
Restart (force a reboot).
|
||||
|
||||
***
|
||||
#### `lock_mouse()`
|
||||
Locks the mouse to the canvas the emulator was initated with
|
||||
|
||||
***
|
||||
#### `add_listener(string event, function(*) listener)`
|
||||
Add an event listener (the emulator is an event emitter). A list of events
|
||||
can be found at [events.md](events.md).
|
||||
|
||||
The callback function gets a single argument which depends on the event.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`string`** event – Name of the event.
|
||||
2. **`function(*)`** listener – The callback function.
|
||||
|
||||
***
|
||||
#### `remove_listener(string event, function(*) listener)`
|
||||
Remove an event listener.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`string`** event
|
||||
2. **`function(*)`** listener
|
||||
|
||||
***
|
||||
#### `restore_state(ArrayBuffer state)`
|
||||
Restore the emulator state from the given state, which must be an
|
||||
ArrayBuffer returned by
|
||||
[`save_state`](#save_statefunctionobject-arraybuffer-callback).
|
||||
|
||||
Note that the state can only be restored correctly if this constructor has
|
||||
been created with the same options as the original instance (e.g., same disk
|
||||
images, memory size, etc.).
|
||||
|
||||
Different versions of the emulator might use a different format for the
|
||||
state buffer.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`ArrayBuffer`** state
|
||||
|
||||
***
|
||||
#### `save_state(function(Object, ArrayBuffer) callback)`
|
||||
Asynchronously save the current state of the emulator. The first argument to
|
||||
the callback is an Error object if something went wrong and is null
|
||||
otherwise.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`function(Object, ArrayBuffer)`** callback
|
||||
|
||||
***
|
||||
#### `get_statistics() -> Object`
|
||||
|
||||
**Deprecated - Might be removed in a later release.**
|
||||
|
||||
Return an object with several statistics. Return value looks similar to
|
||||
(but can be subject to change in future versions or different
|
||||
configurations, so use defensively):
|
||||
|
||||
```javascript
|
||||
{
|
||||
"cpu": {
|
||||
"instruction_counter": 2821610069
|
||||
},
|
||||
"hda": {
|
||||
"sectors_read": 95240,
|
||||
"sectors_written": 952,
|
||||
"bytes_read": 48762880,
|
||||
"bytes_written": 487424,
|
||||
"loading": false
|
||||
},
|
||||
"cdrom": {
|
||||
"sectors_read": 0,
|
||||
"sectors_written": 0,
|
||||
"bytes_read": 0,
|
||||
"bytes_written": 0,
|
||||
"loading": false
|
||||
},
|
||||
"mouse": {
|
||||
"enabled": true
|
||||
},
|
||||
"vga": {
|
||||
"is_graphical": true,
|
||||
"res_x": 800,
|
||||
"res_y": 600,
|
||||
"bpp": 32
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
|
||||
* **`Object`**
|
||||
|
||||
***
|
||||
#### `is_running() -> boolean`
|
||||
|
||||
**Returns:**
|
||||
|
||||
* **`boolean`**
|
||||
|
||||
***
|
||||
#### `keyboard_send_scancodes(Array.<number> codes)`
|
||||
Send a sequence of scan codes to the emulated PS2 controller. A list of
|
||||
codes can be found at http://stanislavs.org/helppc/make_codes.html.
|
||||
Do nothing if there is no keyboard controller.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`Array.<number>`** codes
|
||||
|
||||
***
|
||||
#### `mouse_set_status(boolean enabled)`
|
||||
Enable or disable sending mouse events to the emulated PS2 controller.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`boolean`** enabled
|
||||
|
||||
***
|
||||
#### `keyboard_set_status(boolean enabled)`
|
||||
Enable or disable sending keyboard events to the emulated PS2 controller.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`boolean`** enabled
|
||||
|
||||
***
|
||||
#### `serial0_send(string data)`
|
||||
Send a string to the first emulated serial terminal.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`string`** data
|
||||
|
||||
***
|
||||
#### `create_file(string file, Uint8Array data, function(Object) callback)`
|
||||
Write to a file in the 9p filesystem. Nothing happens if no filesystem has
|
||||
been initialized. First argument to the callback is an error object if
|
||||
something went wrong and null otherwise.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`string`** file
|
||||
2. **`Uint8Array`** data
|
||||
3. **`function(Object)`** (optional) callback
|
||||
|
||||
***
|
||||
#### `read_file(string file, function(Object, Uint8Array) callback)`
|
||||
Read a file in the 9p filesystem. Nothing happens if no filesystem has been
|
||||
initialized.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
1. **`string`** file
|
||||
2. **`function(Object, Uint8Array)`** callback
|
||||
|
||||
<!-- ../src/browser/starter.js-->
|
||||
|
||||
<!-- vim: set tabstop=2 shiftwidth=2 softtabstop=2: -->
|
|
@ -1,16 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var markdox = require("markdox");
|
||||
|
||||
var options = {
|
||||
output: __dirname + "/api.md",
|
||||
template: __dirname + "/template.md.ejs"
|
||||
};
|
||||
|
||||
var files = [
|
||||
__dirname + "/../src/browser/starter.js",
|
||||
];
|
||||
|
||||
markdox.process(files, options, function() {
|
||||
console.log("Ok. %s written.", options.output);
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
Here is a list of events that can be listened to using
|
||||
[`add_listener`](api.md#add_listenerstring-event-function-listener). These
|
||||
can be used to programmatically control the emulator. Events cannot be sent to
|
||||
the emulator (although it is internally implemented that way), use the
|
||||
[API](api.md) methods for that.
|
||||
|
||||
### Serial terminal
|
||||
|
||||
See also: [serial.js](../src/browser/serial.js).
|
||||
|
||||
- `serial0-output-char` - `string chr`
|
||||
|
||||
### Network
|
||||
|
||||
See also: [network.js](../src/browser/network.js).
|
||||
|
||||
- `net0-receive` - `Uint8Array buffer`
|
||||
|
||||
### Screen
|
||||
|
||||
See also: [screen.js](../src/browser/screen.js).
|
||||
|
||||
- `screen-set-mode` - `boolean is_graphic`
|
||||
- `screen-put-char` - `[number row, number col, number chr, number bg_color, number fg_color]`
|
||||
- `screen-put-pixel-linear` - `[number addr, number value]`
|
||||
- `screen-put-pixel-linear32` - `[number addr, number value]`
|
||||
- `screen-set-size-text` - `[number cols_count, number rows_count]`
|
||||
- `screen-set-size-graphical` - `[number width, number height, number virtual_width, number virtual_height, number bpp]`
|
||||
- `screen-update-cursor` - `[number row, number col]`
|
||||
- `screen-update-cursor-scanline` - `[number cursor_scanline_start, number cursor_scanline_end]`
|
||||
|
||||
|
39
docs/sse-shifts.txt
Normal file
39
docs/sse-shifts.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
0F F1 PSLLW mm mm/m64 mmx Shift Packed Data Left Logical
|
||||
66 0F F1 PSLLW xmm xmm/m128 sse2 Shift Packed Data Left Logical
|
||||
0F F2 PSLLD mm mm/m64 mmx Shift Packed Data Left Logical
|
||||
66 0F F2 PSLLD xmm xmm/m128 sse2 Shift Packed Data Left Logical
|
||||
0F F3 PSLLQ mm mm/m64 mmx Shift Packed Data Left Logical
|
||||
66 0F F3 PSLLQ xmm xmm/m128 sse2 Shift Packed Data Left Logical
|
||||
|
||||
0F E1 PSRAW mm mm/m64 mmx Shift Packed Data Right Arithmetic
|
||||
66 0F E1 PSRAW xmm xmm/m128 sse2 Shift Packed Data Right Arithmetic
|
||||
0F E2 PSRAD mm mm/m64 mmx Shift Packed Data Right Arithmetic
|
||||
66 0F E2 PSRAD xmm xmm/m128 sse2 Shift Packed Data Right Arithmetic
|
||||
|
||||
0F D1 PSRLW mm mm/m64 mmx Shift Packed Data Right Logical
|
||||
66 0F D1 PSRLW xmm xmm/m128 sse2 Shift Packed Data Right Logical
|
||||
0F D2 PSRLD mm mm/m64 mmx Shift Packed Data Right Logical
|
||||
66 0F D2 PSRLD xmm xmm/m128 sse2 Shift Packed Data Right Logical
|
||||
0F D3 PSRLQ mm mm/m64 mmx Shift Packed Data Right Logical
|
||||
66 0F D3 PSRLQ xmm xmm/m128 sse2 Shift Packed Data Right Logical
|
||||
|
||||
0F 71 PSRLW mm imm8 mmx Shift Packed Data Right Logical
|
||||
66 0F 71 PSRLW xmm imm8 sse2 Shift Packed Data Right Logical
|
||||
0F 71 PSRAW mm imm8 mmx Shift Packed Data Right Arithmetic
|
||||
66 0F 71 PSRAW xmm imm8 sse2 Shift Packed Data Right Arithmetic
|
||||
0F 71 PSLLW mm imm8 mmx Shift Packed Data Left Logical
|
||||
66 0F 71 PSLLW xmm imm8 sse2 Shift Packed Data Left Logical
|
||||
|
||||
0F 72 PSRLD mm imm8 mmx Shift Double Quadword Right Logical
|
||||
66 0F 72 PSRLD xmm imm8 sse2 Shift Double Quadword Right Logical
|
||||
0F 72 PSRAD mm imm8 mmx Shift Packed Data Right Arithmetic
|
||||
66 0F 72 PSRAD xmm imm8 sse2 Shift Packed Data Right Arithmetic
|
||||
0F 72 PSLLD mm imm8 mmx Shift Packed Data Left Logical
|
||||
66 0F 72 PSLLD xmm imm8 sse2 Shift Packed Data Left Logical
|
||||
|
||||
0F 73 PSRLQ mm imm8 mmx Shift Packed Data Right Logical
|
||||
66 0F 73 PSRLQ xmm imm8 sse2 Shift Packed Data Right Logical
|
||||
66 0F 73 PSRLDQ xmm imm8 sse2 Shift Double Quadword Right Logical
|
||||
0F 73 PSLLQ mm imm8 mmx Shift Packed Data Left Logical
|
||||
66 0F 73 PSLLQ xmm imm8 sse2 Shift Packed Data Left Logical
|
||||
66 0F 73 PSLLDQ xmm imm8 sse2 Shift Double Quadword Left Logical
|
|
@ -1,107 +0,0 @@
|
|||
<?
|
||||
|
||||
function anchorify(str)
|
||||
{
|
||||
// how github creates the name attribute for anchors from headlines
|
||||
|
||||
str = str.replace(/[()<>.]/g, "");
|
||||
str = str.replace(/[^a-z0-9_-]+/gi, "-");
|
||||
str = str.toLowerCase();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
docfiles.forEach(function(doc)
|
||||
{
|
||||
doc.javadoc.forEach(function(comment)
|
||||
{
|
||||
var tags = comment.raw.tags;
|
||||
|
||||
comment.tagsByType = comment.raw.tags.reduce(function(result, tag)
|
||||
{
|
||||
result[tag.type] = tag;
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
comment.ignore = "ignore" in comment.tagsByType;
|
||||
|
||||
if(comment.name && !comment.ignore)
|
||||
{
|
||||
if(comment.isMethod || comment.isFunction)
|
||||
{
|
||||
var args = comment.paramTags.map(function(c)
|
||||
{
|
||||
return c.joinedTypes + " " + c.name;
|
||||
}).join(", ");
|
||||
|
||||
var returnVal = "";
|
||||
|
||||
if(comment.returnTags[0])
|
||||
{
|
||||
returnVal = " -> " + comment.returnTags[0].joinedTypes;
|
||||
}
|
||||
comment.args = args;
|
||||
comment.returnVal = returnVal;
|
||||
comment.longName = comment.name + "(" + comment.args + ")" + comment.returnVal;
|
||||
|
||||
?><?= "- [`" + comment.longName + "`](#" + anchorify(comment.longName) + ")\n" ?><?
|
||||
} else {
|
||||
?># <?= comment.name + "\n" ?><?
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
doc.javadoc.forEach(function(comment)
|
||||
{
|
||||
if(!comment.ignore)
|
||||
{
|
||||
?><?= "\n***\n" ?><?
|
||||
|
||||
if(comment.name)
|
||||
{
|
||||
if(comment.isMethod || comment.isFunction)
|
||||
{
|
||||
?><?= "#### `" + comment.longName + "`\n" ?><?
|
||||
}
|
||||
else
|
||||
{
|
||||
?><?= "## `" + comment.name + "`\n" ?><?
|
||||
}
|
||||
}
|
||||
|
||||
if(comment.deprecated)
|
||||
{
|
||||
?><?= "\n\n**Deprecated - Might be removed in a later release.**\n\n" ?><?
|
||||
}
|
||||
|
||||
?><?= comment.description + "\n" ?><?
|
||||
|
||||
|
||||
if(comment.paramTags.length)
|
||||
{
|
||||
?><?= "**Parameters:**\n\n" ?><?
|
||||
|
||||
comment.paramTags.forEach(function(paramTag, i)
|
||||
{
|
||||
?><?= (i + 1) + ". **`" + paramTag.joinedTypes + "`** " + (paramTag.optional ? "(optional) " : "") + paramTag.name + " " + (paramTag.description ? "– " + paramTag.description : "") + "\n" ?><?
|
||||
});
|
||||
}
|
||||
|
||||
if(comment.returnTags.length)
|
||||
{
|
||||
?><?= "**Returns:**\n\n" ?><?
|
||||
|
||||
comment.returnTags.forEach(function(returnTag)
|
||||
{
|
||||
?><?= "* **`" + returnTag.joinedTypes + "`** " + returnTag.description + "\n" ?><?
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
?><?= "\n<!-- " + doc.filename + "-->\n" ?><?
|
||||
});
|
||||
|
||||
?>
|
||||
|
||||
<!-- vim: set tabstop=2 shiftwidth=2 softtabstop=2: -->
|
|
@ -8,7 +8,8 @@
|
|||
window.onload = function()
|
||||
{
|
||||
var emulator = new V86Starter({
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 512 * 1024 * 1024,
|
||||
vga_memory_size: 8 * 1024 * 1024,
|
||||
screen_container: document.getElementById("screen_container"),
|
||||
bios: {
|
||||
|
@ -17,16 +18,17 @@ window.onload = function()
|
|||
vga_bios: {
|
||||
url: "../bios/vgabios.bin",
|
||||
},
|
||||
hda: {
|
||||
url: "http://localhost/v86-images/arch3.img",
|
||||
async: true,
|
||||
size: 8 * 1024 * 1024 * 1024,
|
||||
},
|
||||
filesystem: {
|
||||
baseurl: "http://localhost/v86-images/arch/",
|
||||
basefs: "http://localhost/v86-images/fs.json",
|
||||
baseurl: "../images/arch/",
|
||||
basefs: "../images/fs.json",
|
||||
},
|
||||
autostart: true,
|
||||
bzimage_initrd_from_filesystem: true,
|
||||
cmdline: [
|
||||
"rw",
|
||||
"root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose",
|
||||
"init=/usr/bin/init-openrc",
|
||||
].join(" "),
|
||||
});
|
||||
|
||||
document.getElementById("save_file").onclick = function()
|
||||
|
|
|
@ -14,6 +14,7 @@ window.onload = function()
|
|||
// is required if the server is on a different host
|
||||
|
||||
var emulator = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 64 * 1024 * 1024,
|
||||
vga_memory_size: 2 * 1024 * 1024,
|
||||
screen_container: document.getElementById("screen_container"),
|
||||
|
@ -24,7 +25,7 @@ window.onload = function()
|
|||
url: "../bios/vgabios.bin",
|
||||
},
|
||||
cdrom: {
|
||||
url: "https://dl.dropboxusercontent.com/u/61029208/dsl-4.11.rc2.iso",
|
||||
url: "../images/dsl-4.11.rc2.iso",
|
||||
async: true,
|
||||
|
||||
// size can be determined automatically, but costs an extra request
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
window.onload = function()
|
||||
{
|
||||
var emulator = window.emulator = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 32 * 1024 * 1024,
|
||||
vga_memory_size: 2 * 1024 * 1024,
|
||||
screen_container: document.getElementById("screen_container"),
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
window.onload = function()
|
||||
{
|
||||
var emulator = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 32 * 1024 * 1024,
|
||||
vga_memory_size: 2 * 1024 * 1024,
|
||||
screen_container: document.getElementById("screen_container"),
|
||||
|
|
|
@ -15,7 +15,8 @@ window.onload = function()
|
|||
}, 999);
|
||||
|
||||
var emulator = new V86Starter({
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 512 * 1024 * 1024,
|
||||
vga_memory_size: 8 * 1024 * 1024,
|
||||
screen_container: document.getElementById("screen_container"),
|
||||
bios: {
|
||||
|
@ -24,17 +25,11 @@ window.onload = function()
|
|||
vga_bios: {
|
||||
url: "../bios/vgabios.bin",
|
||||
},
|
||||
hda: {
|
||||
url: "http://localhost/v86-images/arch3.img",
|
||||
size: 8 * 1024 * 1024 * 1024,
|
||||
async: true,
|
||||
},
|
||||
initial_state: {
|
||||
url: "http://localhost/v86-images/v86state.bin",
|
||||
url: "../images/arch_state.bin.zst",
|
||||
},
|
||||
filesystem: {
|
||||
baseurl: "http://localhost/v86-images/arch/",
|
||||
basefs: "http://localhost/v86-images/fs.json",
|
||||
baseurl: "../images/arch/",
|
||||
},
|
||||
autostart: true,
|
||||
});
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
<!doctype html>
|
||||
<title>Interpreter 2</title>
|
||||
|
||||
<script src="../build/libv86.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
window.onload = function()
|
||||
{
|
||||
var start = Date.now();
|
||||
|
||||
document.getElementById("status").textContent = "Loading ...";
|
||||
|
||||
setInterval(function()
|
||||
{
|
||||
document.getElementById("time").textContent = Math.round((Date.now() - start) / 1000);
|
||||
}, 999);
|
||||
|
||||
if(location.host === "localhost")
|
||||
{
|
||||
var urlbase = "http://localhost/v86-images/";
|
||||
}
|
||||
else
|
||||
{
|
||||
var urlbase = "http://104.131.53.7:8086/";
|
||||
}
|
||||
|
||||
var emulator = new V86Starter({
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
vga_memory_size: 8 * 1024 * 1024,
|
||||
screen_container: document.getElementById("screen_container"),
|
||||
bios: {
|
||||
url: "../bios/seabios.bin",
|
||||
},
|
||||
vga_bios: {
|
||||
url: "../bios/vgabios.bin",
|
||||
},
|
||||
hda: {
|
||||
url: urlbase + "arch3.img",
|
||||
size: 8 * 1024 * 1024 * 1024,
|
||||
async: true,
|
||||
},
|
||||
initial_state: {
|
||||
url: urlbase + "v86state-node.bin",
|
||||
},
|
||||
filesystem: {
|
||||
baseurl: urlbase + "arch/",
|
||||
basefs: urlbase + "fs.json",
|
||||
},
|
||||
autostart: true,
|
||||
});
|
||||
|
||||
window.emulator = emulator;
|
||||
|
||||
emulator.add_listener("emulator-ready", function()
|
||||
{
|
||||
document.getElementById("status").textContent = "Running code ...";
|
||||
var code = "var fs = require('fs');\n" +
|
||||
"module.exports = function() {\n" +
|
||||
" fs.writeFileSync('/root/out.txt', 'The result is: ' + 2 * 3 * 4 * 5 * 6 * 7 * 8);\n" +
|
||||
"}\n";
|
||||
var buffer = new Uint8Array(code.length);
|
||||
|
||||
buffer.set(code.split("").map(function(chr) { return chr.charCodeAt(0); }));
|
||||
|
||||
emulator.create_file("/root/code.js", buffer, function(error)
|
||||
{
|
||||
if(error) throw error;
|
||||
|
||||
emulator.serial0_send('require("/root/code.js")()\n\n');
|
||||
});
|
||||
});
|
||||
|
||||
var interval = setInterval(function()
|
||||
{
|
||||
emulator.read_file("/root/out.txt", function(error, data)
|
||||
{
|
||||
if(error || !data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById("status").textContent = "Done!";
|
||||
document.getElementById("output").textContent = String.fromCharCode.apply(this, data);
|
||||
clearInterval(interval);
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
</script>
|
||||
|
||||
<pre><span id=time>0</span>s -- <span id=status></span></pre>
|
||||
<hr>
|
||||
|
||||
<pre id=output>
|
||||
|
||||
</pre>
|
||||
<hr>
|
||||
|
||||
<!-- A minimal structure for the ScreenAdapter defined in browser/screen.js -->
|
||||
<div id="screen_container">
|
||||
<div style="white-space: pre; font: 14px monospace; line-height: 14px"></div>
|
||||
<canvas style="display: none"></canvas>
|
||||
</div>
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
window.onload = function()
|
||||
{
|
||||
var emulator = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 32 * 1024 * 1024,
|
||||
vga_memory_size: 2 * 1024 * 1024,
|
||||
|
||||
|
@ -20,8 +21,8 @@ window.onload = function()
|
|||
vga_bios: {
|
||||
url: "../bios/vgabios.bin",
|
||||
},
|
||||
cdrom: {
|
||||
url: "../images/linux.iso",
|
||||
bzimage: {
|
||||
url: "../images/buildroot-bzimage.bin",
|
||||
},
|
||||
autostart: true,
|
||||
disable_keyboard: true,
|
||||
|
@ -36,12 +37,7 @@ window.onload = function()
|
|||
data += char;
|
||||
}
|
||||
|
||||
if(data.endsWith("login: "))
|
||||
{
|
||||
console.log("Do login");
|
||||
emulator.serial0_send("root\n");
|
||||
}
|
||||
else if(data.endsWith("/root% "))
|
||||
if(data.endsWith("~% "))
|
||||
{
|
||||
console.log("Now ready");
|
||||
document.getElementById("status").textContent = "Ready.\n";
|
||||
|
|
|
@ -10,15 +10,12 @@ function readfile(path)
|
|||
}
|
||||
|
||||
var bios = readfile(__dirname + "/../bios/seabios.bin");
|
||||
var linux = readfile(__dirname + "/../images/linux.iso");
|
||||
var linux = readfile(__dirname + "/../images/linux4.iso");
|
||||
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.resume();
|
||||
process.stdin.setEncoding("utf8");
|
||||
|
||||
var boot_start = Date.now();
|
||||
var booted = false;
|
||||
|
||||
console.log("Now booting, please stand by ...");
|
||||
|
||||
var emulator = new V86Starter({
|
||||
|
@ -29,14 +26,10 @@ var emulator = new V86Starter({
|
|||
|
||||
emulator.add_listener("serial0-output-char", function(chr)
|
||||
{
|
||||
if(!booted)
|
||||
if(chr <= "~")
|
||||
{
|
||||
var now = Date.now();
|
||||
console.log("Took %dms to boot", now - boot_start);
|
||||
booted = true;
|
||||
}
|
||||
|
||||
process.stdout.write(chr);
|
||||
}
|
||||
});
|
||||
|
||||
process.stdin.on("data", function(c)
|
||||
|
|
|
@ -12,7 +12,7 @@ function readfile(path)
|
|||
console.log("Use F2 to save the state and F3 to restore.");
|
||||
|
||||
var bios = readfile(__dirname + "/../bios/seabios.bin");
|
||||
var linux = readfile(__dirname + "/../images/linux.iso");
|
||||
var linux = readfile(__dirname + "/../images/linux4.iso");
|
||||
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.resume();
|
||||
|
@ -27,8 +27,11 @@ var emulator = new V86Starter({
|
|||
});
|
||||
|
||||
emulator.add_listener("serial0-output-char", function(chr)
|
||||
{
|
||||
if(chr <= "~")
|
||||
{
|
||||
process.stdout.write(chr);
|
||||
}
|
||||
});
|
||||
|
||||
var state;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
window.onload = function()
|
||||
{
|
||||
var emulator = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 32 * 1024 * 1024,
|
||||
vga_memory_size: 2 * 1024 * 1024,
|
||||
screen_container: document.getElementById("screen_container"),
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
window.onload = function()
|
||||
{
|
||||
var emulator = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
|
||||
// Uncomment to see what's going on
|
||||
//screen_container: document.getElementById("screen_container"),
|
||||
|
||||
|
@ -18,7 +20,7 @@ window.onload = function()
|
|||
url: "../bios/vgabios.bin",
|
||||
},
|
||||
cdrom: {
|
||||
url: "../images/linux.iso",
|
||||
url: "../images/linux4.iso",
|
||||
},
|
||||
autostart: true,
|
||||
disable_keyboard: true,
|
||||
|
|
|
@ -11,6 +11,7 @@ window.onload = function()
|
|||
var container2 = document.getElementById("screen_container2");
|
||||
|
||||
var emulator1 = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
screen_container: container1,
|
||||
bios: {
|
||||
url: "../bios/seabios.bin",
|
||||
|
@ -25,6 +26,7 @@ window.onload = function()
|
|||
});
|
||||
|
||||
var emulator2 = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
screen_container: container2,
|
||||
bios: {
|
||||
url: "../bios/seabios.bin",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
importScripts("../build/libv86.js");
|
||||
|
||||
var emulator = new V86Starter({
|
||||
wasm_path: "../build/v86.wasm",
|
||||
memory_size: 32 * 1024 * 1024,
|
||||
vga_memory_size: 2 * 1024 * 1024,
|
||||
bios: {
|
||||
|
@ -10,7 +11,7 @@ var emulator = new V86Starter({
|
|||
url: "../bios/vgabios.bin",
|
||||
},
|
||||
cdrom: {
|
||||
url: "../images/linux.iso",
|
||||
url: "../images/linux4.iso",
|
||||
},
|
||||
autostart: true,
|
||||
});
|
||||
|
|
486
gen/generate_analyzer.js
Executable file
486
gen/generate_analyzer.js
Executable file
|
@ -0,0 +1,486 @@
|
|||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const x86_table = require("./x86_table");
|
||||
const rust_ast = require("./rust_ast");
|
||||
const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table_rust } = require("./util");
|
||||
|
||||
const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/");
|
||||
|
||||
mkdirpSync(OUT_DIR);
|
||||
|
||||
const table_arg = get_switch_value("--table");
|
||||
const gen_all = get_switch_exist("--all");
|
||||
const to_generate = {
|
||||
analyzer: gen_all || table_arg === "analyzer",
|
||||
analyzer0f: gen_all || table_arg === "analyzer0f",
|
||||
};
|
||||
|
||||
assert(
|
||||
Object.keys(to_generate).some(k => to_generate[k]),
|
||||
"Pass --table [analyzer|analyzer0f] or --all to pick which tables to generate"
|
||||
);
|
||||
|
||||
gen_table();
|
||||
|
||||
function gen_read_imm_call(op, size_variant)
|
||||
{
|
||||
let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
|
||||
|
||||
if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
|
||||
{
|
||||
if(op.imm8)
|
||||
{
|
||||
return "cpu.read_imm8()";
|
||||
}
|
||||
else if(op.imm8s)
|
||||
{
|
||||
return "cpu.read_imm8s()";
|
||||
}
|
||||
else
|
||||
{
|
||||
if(op.immaddr)
|
||||
{
|
||||
// immaddr: depends on address size
|
||||
return "cpu.read_moffs()";
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(op.imm1632 || op.imm16 || op.imm32);
|
||||
|
||||
if(op.imm1632 && size === 16 || op.imm16)
|
||||
{
|
||||
return "cpu.read_imm16()";
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(op.imm1632 && size === 32 || op.imm32);
|
||||
return "cpu.read_imm32()";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function gen_call(name, args)
|
||||
{
|
||||
args = args || [];
|
||||
return `${name}(${args.join(", ")});`;
|
||||
}
|
||||
|
||||
/*
|
||||
* Current naming scheme:
|
||||
* instr(16|32|)_(66|F2|F3)?0F?[0-9a-f]{2}(_[0-7])?(_mem|_reg|)
|
||||
*/
|
||||
function make_instruction_name(encoding, size)
|
||||
{
|
||||
const suffix = encoding.os ? String(size) : "";
|
||||
const opcode_hex = hex(encoding.opcode & 0xFF, 2);
|
||||
const first_prefix = (encoding.opcode & 0xFF00) === 0 ? "" : hex(encoding.opcode >> 8 & 0xFF, 2);
|
||||
const second_prefix = (encoding.opcode & 0xFF0000) === 0 ? "" : hex(encoding.opcode >> 16 & 0xFF, 2);
|
||||
const fixed_g_suffix = encoding.fixed_g === undefined ? "" : `_${encoding.fixed_g}`;
|
||||
|
||||
assert(first_prefix === "" || first_prefix === "0F" || first_prefix === "F2" || first_prefix === "F3");
|
||||
assert(second_prefix === "" || second_prefix === "66" || second_prefix === "F2" || second_prefix === "F3");
|
||||
|
||||
return `instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`;
|
||||
}
|
||||
|
||||
function gen_instruction_body(encodings, size)
|
||||
{
|
||||
const encoding = encodings[0];
|
||||
|
||||
let has_66 = [];
|
||||
let has_F2 = [];
|
||||
let has_F3 = [];
|
||||
let no_prefix = [];
|
||||
|
||||
for(let e of encodings)
|
||||
{
|
||||
if((e.opcode >>> 16) === 0x66) has_66.push(e);
|
||||
else if((e.opcode >>> 8 & 0xFF) === 0xF2 || (e.opcode >>> 16) === 0xF2) has_F2.push(e);
|
||||
else if((e.opcode >>> 8 & 0xFF) === 0xF3 || (e.opcode >>> 16) === 0xF3) has_F3.push(e);
|
||||
else no_prefix.push(e);
|
||||
}
|
||||
|
||||
if(has_F2.length || has_F3.length)
|
||||
{
|
||||
assert((encoding.opcode & 0xFF0000) === 0 || (encoding.opcode & 0xFF00) === 0x0F00);
|
||||
}
|
||||
|
||||
if(has_66.length)
|
||||
{
|
||||
assert((encoding.opcode & 0xFF00) === 0x0F00);
|
||||
}
|
||||
|
||||
const code = [];
|
||||
|
||||
if(encoding.e)
|
||||
{
|
||||
code.push("let modrm_byte = cpu.read_imm8();");
|
||||
}
|
||||
|
||||
if(has_66.length || has_F2.length || has_F3.length)
|
||||
{
|
||||
const if_blocks = [];
|
||||
|
||||
if(has_66.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_66, size);
|
||||
if_blocks.push({ condition: "cpu.prefixes & ::prefix::PREFIX_66 != 0", body, });
|
||||
}
|
||||
if(has_F2.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_F2, size);
|
||||
if_blocks.push({ condition: "cpu.prefixes & ::prefix::PREFIX_F2 != 0", body, });
|
||||
}
|
||||
if(has_F3.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_F3, size);
|
||||
if_blocks.push({ condition: "cpu.prefixes & ::prefix::PREFIX_F3 != 0", body, });
|
||||
}
|
||||
|
||||
const else_block = {
|
||||
body: gen_instruction_body_after_prefix(no_prefix, size),
|
||||
};
|
||||
|
||||
return [].concat(
|
||||
code,
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks,
|
||||
else_block,
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
return [].concat(
|
||||
code,
|
||||
gen_instruction_body_after_prefix(encodings, size)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_instruction_body_after_prefix(encodings, size)
|
||||
{
|
||||
const encoding = encodings[0];
|
||||
|
||||
if(encoding.fixed_g !== undefined)
|
||||
{
|
||||
assert(encoding.e);
|
||||
|
||||
// instruction with modrm byte where the middle 3 bits encode the instruction
|
||||
|
||||
// group by opcode without prefix plus middle bits of modrm byte
|
||||
let cases = encodings.reduce((cases_by_opcode, case_) => {
|
||||
assert(typeof case_.fixed_g === "number");
|
||||
cases_by_opcode[case_.opcode & 0xFFFF | case_.fixed_g << 16] = case_;
|
||||
return cases_by_opcode;
|
||||
}, Object.create(null));
|
||||
cases = Object.values(cases).sort((e1, e2) => e1.fixed_g - e2.fixed_g);
|
||||
|
||||
return [
|
||||
{
|
||||
type: "switch",
|
||||
condition: "modrm_byte >> 3 & 7",
|
||||
cases: cases.map(case_ => {
|
||||
const fixed_g = case_.fixed_g;
|
||||
const body = gen_instruction_body_after_fixed_g(case_, size);
|
||||
|
||||
return {
|
||||
conditions: [fixed_g],
|
||||
body,
|
||||
};
|
||||
}),
|
||||
|
||||
default_case: {
|
||||
body: [
|
||||
"analysis.ty = ::analysis::AnalysisType::BlockBoundary;",
|
||||
"analysis.no_next_instruction = true;",
|
||||
],
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
else {
|
||||
assert(encodings.length === 1);
|
||||
return gen_instruction_body_after_fixed_g(encodings[0], size);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_instruction_body_after_fixed_g(encoding, size)
|
||||
{
|
||||
const imm_read = gen_read_imm_call(encoding, size);
|
||||
const instruction_postfix = [];
|
||||
|
||||
if(encoding.custom_sti) {
|
||||
instruction_postfix.push("analysis.ty = ::analysis::AnalysisType::STI;");
|
||||
}
|
||||
else if(
|
||||
encoding.block_boundary &&
|
||||
// jump_offset_imm: Is a block boundary, but gets a different type (Jump) below
|
||||
!encoding.jump_offset_imm || (!encoding.custom && encoding.e))
|
||||
{
|
||||
instruction_postfix.push("analysis.ty = ::analysis::AnalysisType::BlockBoundary;");
|
||||
}
|
||||
|
||||
if(encoding.no_next_instruction)
|
||||
{
|
||||
instruction_postfix.push("analysis.no_next_instruction = true;");
|
||||
}
|
||||
if(encoding.absolute_jump)
|
||||
{
|
||||
instruction_postfix.push("analysis.absolute_jump = true;");
|
||||
}
|
||||
|
||||
if(encoding.prefix)
|
||||
{
|
||||
const instruction_name = "::analysis::" + make_instruction_name(encoding, size) + "_analyze";
|
||||
const args = ["cpu", "analysis"];
|
||||
|
||||
assert(!imm_read);
|
||||
|
||||
return [].concat(
|
||||
gen_call(instruction_name, args),
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
else if(encoding.e)
|
||||
{
|
||||
// instruction with modrm byte where the middle 3 bits encode a register
|
||||
|
||||
const reg_postfix = [];
|
||||
const mem_postfix = [];
|
||||
|
||||
if(encoding.mem_ud)
|
||||
{
|
||||
mem_postfix.push(
|
||||
"analysis.ty = ::analysis::AnalysisType::BlockBoundary;"
|
||||
);
|
||||
}
|
||||
|
||||
if(encoding.reg_ud)
|
||||
{
|
||||
reg_postfix.push(
|
||||
"analysis.ty = ::analysis::AnalysisType::BlockBoundary;"
|
||||
);
|
||||
}
|
||||
|
||||
if(encoding.ignore_mod)
|
||||
{
|
||||
assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
|
||||
|
||||
// Has modrm byte, but the 2 mod bits are ignored and both
|
||||
// operands are always registers (0f20-0f24)
|
||||
|
||||
return instruction_postfix;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [].concat(
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks: [{
|
||||
condition: "modrm_byte < 0xC0",
|
||||
body: [].concat(
|
||||
gen_call("::analysis::modrm_analyze", ["cpu", "modrm_byte"]),
|
||||
mem_postfix,
|
||||
),
|
||||
}],
|
||||
else_block: {
|
||||
body: reg_postfix,
|
||||
},
|
||||
},
|
||||
imm_read ? [imm_read + ";"] : [],
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// instruction without modrm byte or prefix
|
||||
|
||||
const body = [];
|
||||
|
||||
if(imm_read)
|
||||
{
|
||||
if(encoding.jump_offset_imm)
|
||||
{
|
||||
body.push("let jump_offset = " + imm_read + ";");
|
||||
|
||||
if(encoding.conditional_jump)
|
||||
{
|
||||
assert(
|
||||
(encoding.opcode & ~0xF) === 0x70 ||
|
||||
(encoding.opcode & ~0xF) === 0x0F80 ||
|
||||
(encoding.opcode & ~0x3) === 0xE0
|
||||
);
|
||||
const condition_index = encoding.opcode & 0xFF;
|
||||
body.push(`analysis.ty = ::analysis::AnalysisType::Jump { offset: jump_offset as i32, condition: Some(0x${hex(condition_index, 2)}), is_32: cpu.osize_32() };`);
|
||||
}
|
||||
else
|
||||
{
|
||||
body.push(`analysis.ty = ::analysis::AnalysisType::Jump { offset: jump_offset as i32, condition: None, is_32: cpu.osize_32() };`);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
body.push(imm_read + ";");
|
||||
}
|
||||
}
|
||||
|
||||
if(encoding.extra_imm16)
|
||||
{
|
||||
assert(imm_read);
|
||||
body.push(gen_call("cpu.read_imm16"));
|
||||
}
|
||||
else if(encoding.extra_imm8)
|
||||
{
|
||||
assert(imm_read);
|
||||
body.push(gen_call("cpu.read_imm8"));
|
||||
}
|
||||
|
||||
return [].concat(
|
||||
body,
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_table()
|
||||
{
|
||||
let by_opcode = Object.create(null);
|
||||
let by_opcode0f = Object.create(null);
|
||||
|
||||
for(let o of x86_table)
|
||||
{
|
||||
let opcode = o.opcode;
|
||||
|
||||
if((opcode & 0xFF00) === 0x0F00)
|
||||
{
|
||||
opcode &= 0xFF;
|
||||
by_opcode0f[opcode] = by_opcode0f[opcode] || [];
|
||||
by_opcode0f[opcode].push(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
opcode &= 0xFF;
|
||||
by_opcode[opcode] = by_opcode[opcode] || [];
|
||||
by_opcode[opcode].push(o);
|
||||
}
|
||||
}
|
||||
|
||||
let cases = [];
|
||||
for(let opcode = 0; opcode < 0x100; opcode++)
|
||||
{
|
||||
let encoding = by_opcode[opcode];
|
||||
assert(encoding && encoding.length);
|
||||
|
||||
let opcode_hex = hex(opcode, 2);
|
||||
let opcode_high_hex = hex(opcode | 0x100, 2);
|
||||
|
||||
if(encoding[0].os)
|
||||
{
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_hex}`],
|
||||
body: gen_instruction_body(encoding, 16),
|
||||
});
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, 32),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, undefined),
|
||||
});
|
||||
}
|
||||
}
|
||||
const table = {
|
||||
type: "switch",
|
||||
condition: "opcode",
|
||||
cases,
|
||||
default_case: {
|
||||
body: ["dbg_assert!(false);"]
|
||||
},
|
||||
};
|
||||
|
||||
if(to_generate.analyzer)
|
||||
{
|
||||
const code = [
|
||||
"#[cfg_attr(rustfmt, rustfmt_skip)]",
|
||||
"pub fn analyzer(opcode: u32, cpu: &mut ::cpu_context::CpuContext, analysis: &mut ::analysis::Analysis) {",
|
||||
table,
|
||||
"}",
|
||||
];
|
||||
|
||||
finalize_table_rust(
|
||||
OUT_DIR,
|
||||
"analyzer.rs",
|
||||
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
|
||||
const cases0f = [];
|
||||
for(let opcode = 0; opcode < 0x100; opcode++)
|
||||
{
|
||||
let encoding = by_opcode0f[opcode];
|
||||
|
||||
assert(encoding && encoding.length);
|
||||
|
||||
let opcode_hex = hex(opcode, 2);
|
||||
let opcode_high_hex = hex(opcode | 0x100, 2);
|
||||
|
||||
if(encoding[0].os)
|
||||
{
|
||||
cases0f.push({
|
||||
conditions: [`0x${opcode_hex}`],
|
||||
body: gen_instruction_body(encoding, 16),
|
||||
});
|
||||
cases0f.push({
|
||||
conditions: [`0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, 32),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
let block = {
|
||||
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, undefined),
|
||||
};
|
||||
cases0f.push(block);
|
||||
}
|
||||
}
|
||||
|
||||
const table0f = {
|
||||
type: "switch",
|
||||
condition: "opcode",
|
||||
cases: cases0f,
|
||||
default_case: {
|
||||
body: ["dbg_assert!(false);"]
|
||||
},
|
||||
};
|
||||
|
||||
if(to_generate.analyzer0f)
|
||||
{
|
||||
const code = [
|
||||
"#![allow(unused)]",
|
||||
"#[cfg_attr(rustfmt, rustfmt_skip)]",
|
||||
"pub fn analyzer(opcode: u32, cpu: &mut ::cpu_context::CpuContext, analysis: &mut ::analysis::Analysis) {",
|
||||
table0f,
|
||||
"}"
|
||||
];
|
||||
|
||||
finalize_table_rust(
|
||||
OUT_DIR,
|
||||
"analyzer0f.rs",
|
||||
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
}
|
492
gen/generate_interpreter.js
Executable file
492
gen/generate_interpreter.js
Executable file
|
@ -0,0 +1,492 @@
|
|||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const x86_table = require("./x86_table");
|
||||
const rust_ast = require("./rust_ast");
|
||||
const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table_rust } = require("./util");
|
||||
|
||||
const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/");
|
||||
|
||||
mkdirpSync(OUT_DIR);
|
||||
|
||||
const table_arg = get_switch_value("--table");
|
||||
const gen_all = get_switch_exist("--all");
|
||||
const to_generate = {
|
||||
interpreter: gen_all || table_arg === "interpreter",
|
||||
interpreter0f: gen_all || table_arg === "interpreter0f",
|
||||
};
|
||||
|
||||
assert(
|
||||
Object.keys(to_generate).some(k => to_generate[k]),
|
||||
"Pass --table [interpreter|interpreter0f] or --all to pick which tables to generate"
|
||||
);
|
||||
|
||||
gen_table();
|
||||
|
||||
function wrap_imm_call(imm)
|
||||
{
|
||||
return `match ${imm} { Ok(o) => o, Err(()) => return }`;
|
||||
}
|
||||
|
||||
function gen_read_imm_call(op, size_variant)
|
||||
{
|
||||
let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
|
||||
|
||||
if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
|
||||
{
|
||||
if(op.imm8)
|
||||
{
|
||||
return wrap_imm_call("read_imm8()");
|
||||
}
|
||||
else if(op.imm8s)
|
||||
{
|
||||
return wrap_imm_call("read_imm8s()");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(op.immaddr)
|
||||
{
|
||||
// immaddr: depends on address size
|
||||
return wrap_imm_call("read_moffs()");
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(op.imm1632 || op.imm16 || op.imm32);
|
||||
|
||||
if(op.imm1632 && size === 16 || op.imm16)
|
||||
{
|
||||
return wrap_imm_call("read_imm16()");
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(op.imm1632 && size === 32 || op.imm32);
|
||||
return wrap_imm_call("read_imm32s()");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function gen_call(name, args)
|
||||
{
|
||||
args = args || [];
|
||||
return `${name}(${args.join(", ")});`;
|
||||
}
|
||||
|
||||
/*
|
||||
* Current naming scheme:
|
||||
* instr(16|32|)_(66|F2|F3)?0F?[0-9a-f]{2}(_[0-7])?(_mem|_reg|)
|
||||
*/
|
||||
function make_instruction_name(encoding, size)
|
||||
{
|
||||
const suffix = encoding.os ? String(size) : "";
|
||||
const opcode_hex = hex(encoding.opcode & 0xFF, 2);
|
||||
const first_prefix = (encoding.opcode & 0xFF00) === 0 ? "" : hex(encoding.opcode >> 8 & 0xFF, 2);
|
||||
const second_prefix = (encoding.opcode & 0xFF0000) === 0 ? "" : hex(encoding.opcode >> 16 & 0xFF, 2);
|
||||
const fixed_g_suffix = encoding.fixed_g === undefined ? "" : `_${encoding.fixed_g}`;
|
||||
const module = first_prefix === "0F" || second_prefix === "0F" ? "instructions_0f" : "instructions";
|
||||
|
||||
assert(first_prefix === "" || first_prefix === "0F" || first_prefix === "F2" || first_prefix === "F3");
|
||||
assert(second_prefix === "" || second_prefix === "66" || second_prefix === "F2" || second_prefix === "F3");
|
||||
|
||||
return `${module}::instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`;
|
||||
}
|
||||
|
||||
function gen_instruction_body(encodings, size)
|
||||
{
|
||||
const encoding = encodings[0];
|
||||
|
||||
let has_66 = [];
|
||||
let has_F2 = [];
|
||||
let has_F3 = [];
|
||||
let no_prefix = [];
|
||||
|
||||
for(let e of encodings)
|
||||
{
|
||||
if((e.opcode >>> 16) === 0x66) has_66.push(e);
|
||||
else if((e.opcode >>> 8 & 0xFF) === 0xF2 || (e.opcode >>> 16) === 0xF2) has_F2.push(e);
|
||||
else if((e.opcode >>> 8 & 0xFF) === 0xF3 || (e.opcode >>> 16) === 0xF3) has_F3.push(e);
|
||||
else no_prefix.push(e);
|
||||
}
|
||||
|
||||
if(has_F2.length || has_F3.length)
|
||||
{
|
||||
assert((encoding.opcode & 0xFF0000) === 0 || (encoding.opcode & 0xFF00) === 0x0F00);
|
||||
}
|
||||
|
||||
if(has_66.length)
|
||||
{
|
||||
assert((encoding.opcode & 0xFF00) === 0x0F00);
|
||||
}
|
||||
|
||||
const code = [];
|
||||
|
||||
if(encoding.e)
|
||||
{
|
||||
code.push(`let modrm_byte = ${wrap_imm_call("read_imm8()")};`);
|
||||
}
|
||||
|
||||
if(has_66.length || has_F2.length || has_F3.length)
|
||||
{
|
||||
const if_blocks = [];
|
||||
|
||||
if(has_66.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_66, size);
|
||||
if_blocks.push({ condition: "prefixes_ & PREFIX_66 != 0", body, });
|
||||
}
|
||||
if(has_F2.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_F2, size);
|
||||
if_blocks.push({ condition: "prefixes_ & PREFIX_F2 != 0", body, });
|
||||
}
|
||||
if(has_F3.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_F3, size);
|
||||
if_blocks.push({ condition: "prefixes_ & PREFIX_F3 != 0", body, });
|
||||
}
|
||||
|
||||
const check_prefixes = encoding.sse ? "(PREFIX_66 | PREFIX_F2 | PREFIX_F3)" : "(PREFIX_F2 | PREFIX_F3)";
|
||||
|
||||
const else_block = {
|
||||
body: [].concat(
|
||||
"dbg_assert!((prefixes_ & " + check_prefixes + ") == 0);",
|
||||
gen_instruction_body_after_prefix(no_prefix, size)
|
||||
)
|
||||
};
|
||||
|
||||
return [].concat(
|
||||
"let prefixes_ = *prefixes as i32;",
|
||||
code,
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks,
|
||||
else_block,
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
return [].concat(
|
||||
code,
|
||||
gen_instruction_body_after_prefix(encodings, size)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_instruction_body_after_prefix(encodings, size)
|
||||
{
|
||||
const encoding = encodings[0];
|
||||
|
||||
if(encoding.fixed_g !== undefined)
|
||||
{
|
||||
assert(encoding.e);
|
||||
|
||||
// instruction with modrm byte where the middle 3 bits encode the instruction
|
||||
|
||||
// group by opcode without prefix plus middle bits of modrm byte
|
||||
let cases = encodings.reduce((cases_by_opcode, case_) => {
|
||||
assert(typeof case_.fixed_g === "number");
|
||||
cases_by_opcode[case_.opcode & 0xFFFF | case_.fixed_g << 16] = case_;
|
||||
return cases_by_opcode;
|
||||
}, Object.create(null));
|
||||
cases = Object.values(cases).sort((e1, e2) => e1.fixed_g - e2.fixed_g);
|
||||
|
||||
return [
|
||||
{
|
||||
type: "switch",
|
||||
condition: "modrm_byte >> 3 & 7",
|
||||
cases: cases.map(case_ => {
|
||||
const fixed_g = case_.fixed_g;
|
||||
const body = gen_instruction_body_after_fixed_g(case_, size);
|
||||
|
||||
return {
|
||||
conditions: [fixed_g],
|
||||
body,
|
||||
};
|
||||
}),
|
||||
|
||||
default_case: {
|
||||
body: [
|
||||
`if DEBUG { panic!("Bad instruction at {:x}", *instruction_pointer); }`,
|
||||
"trigger_ud();",
|
||||
],
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
else {
|
||||
assert(encodings.length === 1);
|
||||
return gen_instruction_body_after_fixed_g(encodings[0], size);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_instruction_body_after_fixed_g(encoding, size)
|
||||
{
|
||||
const instruction_prefix = [];
|
||||
const instruction_postfix =
|
||||
(encoding.block_boundary && !encoding.no_block_boundary_in_interpreted) ||
|
||||
(!encoding.custom && encoding.e) ?
|
||||
["after_block_boundary();"] : [];
|
||||
|
||||
if(encoding.task_switch_test || encoding.sse)
|
||||
{
|
||||
instruction_prefix.push(
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks: [
|
||||
{
|
||||
condition: encoding.sse ? "!task_switch_test_mmx()" : "!task_switch_test()",
|
||||
body: ["return;"],
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const imm_read = gen_read_imm_call(encoding, size);
|
||||
const instruction_name = make_instruction_name(encoding, size);
|
||||
|
||||
if(encoding.e)
|
||||
{
|
||||
// instruction with modrm byte
|
||||
|
||||
const imm_read = gen_read_imm_call(encoding, size);
|
||||
|
||||
if(encoding.ignore_mod)
|
||||
{
|
||||
assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
|
||||
|
||||
// Has modrm byte, but the 2 mod bits are ignored and both
|
||||
// operands are always registers (0f20-0f24)
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
gen_call(instruction_name, ["modrm_byte & 7", "modrm_byte >> 3 & 7"]),
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
let mem_args;
|
||||
|
||||
if(encoding.custom_modrm_resolve)
|
||||
{
|
||||
// requires special handling around modrm_resolve
|
||||
mem_args = ["modrm_byte"];
|
||||
}
|
||||
else
|
||||
{
|
||||
mem_args = ["match modrm_resolve(modrm_byte) { Ok(a) => a, Err(()) => return }"];
|
||||
}
|
||||
|
||||
const reg_args = ["modrm_byte & 7"];
|
||||
|
||||
if(encoding.fixed_g === undefined)
|
||||
{
|
||||
mem_args.push("modrm_byte >> 3 & 7");
|
||||
reg_args.push("modrm_byte >> 3 & 7");
|
||||
}
|
||||
|
||||
if(imm_read)
|
||||
{
|
||||
mem_args.push(imm_read);
|
||||
reg_args.push(imm_read);
|
||||
}
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks: [
|
||||
{
|
||||
condition: "modrm_byte < 0xC0",
|
||||
body: [].concat(
|
||||
gen_call(`${instruction_name}_mem`, mem_args)
|
||||
),
|
||||
}
|
||||
],
|
||||
else_block: {
|
||||
body: [gen_call(`${instruction_name}_reg`, reg_args)],
|
||||
},
|
||||
},
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const args = [];
|
||||
|
||||
if(imm_read)
|
||||
{
|
||||
args.push(imm_read);
|
||||
}
|
||||
|
||||
if(encoding.extra_imm16)
|
||||
{
|
||||
assert(imm_read);
|
||||
args.push(wrap_imm_call("read_imm16()"));
|
||||
}
|
||||
else if(encoding.extra_imm8)
|
||||
{
|
||||
assert(imm_read);
|
||||
args.push(wrap_imm_call("read_imm8()"));
|
||||
}
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
gen_call(instruction_name, args),
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_table()
|
||||
{
|
||||
let by_opcode = Object.create(null);
|
||||
let by_opcode0f = Object.create(null);
|
||||
|
||||
for(let o of x86_table)
|
||||
{
|
||||
let opcode = o.opcode;
|
||||
|
||||
if((opcode & 0xFF00) === 0x0F00)
|
||||
{
|
||||
opcode &= 0xFF;
|
||||
by_opcode0f[opcode] = by_opcode0f[opcode] || [];
|
||||
by_opcode0f[opcode].push(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
opcode &= 0xFF;
|
||||
by_opcode[opcode] = by_opcode[opcode] || [];
|
||||
by_opcode[opcode].push(o);
|
||||
}
|
||||
}
|
||||
|
||||
let cases = [];
|
||||
for(let opcode = 0; opcode < 0x100; opcode++)
|
||||
{
|
||||
let encoding = by_opcode[opcode];
|
||||
assert(encoding && encoding.length);
|
||||
|
||||
let opcode_hex = hex(opcode, 2);
|
||||
let opcode_high_hex = hex(opcode | 0x100, 2);
|
||||
|
||||
if(encoding[0].os)
|
||||
{
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_hex}`],
|
||||
body: gen_instruction_body(encoding, 16),
|
||||
});
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, 32),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, undefined),
|
||||
});
|
||||
}
|
||||
}
|
||||
const table = {
|
||||
type: "switch",
|
||||
condition: "opcode",
|
||||
cases,
|
||||
default_case: {
|
||||
body: ["assert!(false);"]
|
||||
},
|
||||
};
|
||||
if(to_generate.interpreter)
|
||||
{
|
||||
const code = [
|
||||
"#![cfg_attr(rustfmt, rustfmt_skip)]",
|
||||
|
||||
"use cpu::cpu::{after_block_boundary, modrm_resolve};",
|
||||
"use cpu::cpu::{read_imm8, read_imm8s, read_imm16, read_imm32s, read_moffs};",
|
||||
"use cpu::cpu::{task_switch_test, trigger_ud, DEBUG, PREFIX_F2, PREFIX_F3};",
|
||||
"use cpu::instructions;",
|
||||
"use cpu::global_pointers::{instruction_pointer, prefixes};",
|
||||
|
||||
"pub unsafe fn run(opcode: u32) {",
|
||||
table,
|
||||
"}",
|
||||
];
|
||||
|
||||
finalize_table_rust(
|
||||
OUT_DIR,
|
||||
"interpreter.rs",
|
||||
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
|
||||
const cases0f = [];
|
||||
for(let opcode = 0; opcode < 0x100; opcode++)
|
||||
{
|
||||
let encoding = by_opcode0f[opcode];
|
||||
|
||||
assert(encoding && encoding.length);
|
||||
|
||||
let opcode_hex = hex(opcode, 2);
|
||||
let opcode_high_hex = hex(opcode | 0x100, 2);
|
||||
|
||||
if(encoding[0].os)
|
||||
{
|
||||
cases0f.push({
|
||||
conditions: [`0x${opcode_hex}`],
|
||||
body: gen_instruction_body(encoding, 16),
|
||||
});
|
||||
cases0f.push({
|
||||
conditions: [`0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, 32),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
let block = {
|
||||
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, undefined),
|
||||
};
|
||||
cases0f.push(block);
|
||||
}
|
||||
}
|
||||
|
||||
const table0f = {
|
||||
type: "switch",
|
||||
condition: "opcode",
|
||||
cases: cases0f,
|
||||
default_case: {
|
||||
body: ["assert!(false);"]
|
||||
},
|
||||
};
|
||||
|
||||
if(to_generate.interpreter0f)
|
||||
{
|
||||
const code = [
|
||||
"#![cfg_attr(rustfmt, rustfmt_skip)]",
|
||||
|
||||
"use cpu::cpu::{after_block_boundary, modrm_resolve};",
|
||||
"use cpu::cpu::{read_imm8, read_imm16, read_imm32s};",
|
||||
"use cpu::cpu::{task_switch_test, task_switch_test_mmx, trigger_ud};",
|
||||
"use cpu::cpu::{DEBUG, PREFIX_66, PREFIX_F2, PREFIX_F3};",
|
||||
"use cpu::instructions_0f;",
|
||||
"use cpu::global_pointers::{instruction_pointer, prefixes};",
|
||||
|
||||
"pub unsafe fn run(opcode: u32) {",
|
||||
table0f,
|
||||
"}",
|
||||
];
|
||||
|
||||
finalize_table_rust(
|
||||
OUT_DIR,
|
||||
"interpreter0f.rs",
|
||||
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
}
|
563
gen/generate_jit.js
Executable file
563
gen/generate_jit.js
Executable file
|
@ -0,0 +1,563 @@
|
|||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const x86_table = require("./x86_table");
|
||||
const rust_ast = require("./rust_ast");
|
||||
const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table_rust } = require("./util");
|
||||
|
||||
const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/");
|
||||
|
||||
mkdirpSync(OUT_DIR);
|
||||
|
||||
const table_arg = get_switch_value("--table");
|
||||
const gen_all = get_switch_exist("--all");
|
||||
const to_generate = {
|
||||
jit: gen_all || table_arg === "jit",
|
||||
jit0f: gen_all || table_arg === "jit0f",
|
||||
};
|
||||
|
||||
assert(
|
||||
Object.keys(to_generate).some(k => to_generate[k]),
|
||||
"Pass --table [jit|jit0f] or --all to pick which tables to generate"
|
||||
);
|
||||
|
||||
gen_table();
|
||||
|
||||
function gen_read_imm_call(op, size_variant)
|
||||
{
|
||||
let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
|
||||
|
||||
if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
|
||||
{
|
||||
if(op.imm8)
|
||||
{
|
||||
return "ctx.cpu.read_imm8()";
|
||||
}
|
||||
else if(op.imm8s)
|
||||
{
|
||||
return "ctx.cpu.read_imm8s()";
|
||||
}
|
||||
else
|
||||
{
|
||||
if(op.immaddr)
|
||||
{
|
||||
// immaddr: depends on address size
|
||||
return "ctx.cpu.read_moffs()";
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(op.imm1632 || op.imm16 || op.imm32);
|
||||
|
||||
if(op.imm1632 && size === 16 || op.imm16)
|
||||
{
|
||||
return "ctx.cpu.read_imm16()";
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(op.imm1632 && size === 32 || op.imm32);
|
||||
return "ctx.cpu.read_imm32()";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function gen_call(name, args)
|
||||
{
|
||||
args = args || [];
|
||||
return `${name}(${args.join(", ")});`;
|
||||
}
|
||||
|
||||
/*
|
||||
* Current naming scheme:
|
||||
* instr(16|32|)_(66|F2|F3)?0F?[0-9a-f]{2}(_[0-7])?(_mem|_reg|)
|
||||
*/
|
||||
function make_instruction_name(encoding, size)
|
||||
{
|
||||
const suffix = encoding.os ? String(size) : "";
|
||||
const opcode_hex = hex(encoding.opcode & 0xFF, 2);
|
||||
const first_prefix = (encoding.opcode & 0xFF00) === 0 ? "" : hex(encoding.opcode >> 8 & 0xFF, 2);
|
||||
const second_prefix = (encoding.opcode & 0xFF0000) === 0 ? "" : hex(encoding.opcode >> 16 & 0xFF, 2);
|
||||
const fixed_g_suffix = encoding.fixed_g === undefined ? "" : `_${encoding.fixed_g}`;
|
||||
|
||||
assert(first_prefix === "" || first_prefix === "0F" || first_prefix === "F2" || first_prefix === "F3");
|
||||
assert(second_prefix === "" || second_prefix === "66" || second_prefix === "F2" || second_prefix === "F3");
|
||||
|
||||
return `instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`;
|
||||
}
|
||||
|
||||
function gen_instruction_body(encodings, size)
|
||||
{
|
||||
const encoding = encodings[0];
|
||||
|
||||
let has_66 = [];
|
||||
let has_F2 = [];
|
||||
let has_F3 = [];
|
||||
let no_prefix = [];
|
||||
|
||||
for(let e of encodings)
|
||||
{
|
||||
if((e.opcode >>> 16) === 0x66) has_66.push(e);
|
||||
else if((e.opcode >>> 8 & 0xFF) === 0xF2 || (e.opcode >>> 16) === 0xF2) has_F2.push(e);
|
||||
else if((e.opcode >>> 8 & 0xFF) === 0xF3 || (e.opcode >>> 16) === 0xF3) has_F3.push(e);
|
||||
else no_prefix.push(e);
|
||||
}
|
||||
|
||||
if(has_F2.length || has_F3.length)
|
||||
{
|
||||
assert((encoding.opcode & 0xFF0000) === 0 || (encoding.opcode & 0xFF00) === 0x0F00);
|
||||
}
|
||||
|
||||
if(has_66.length)
|
||||
{
|
||||
assert((encoding.opcode & 0xFF00) === 0x0F00);
|
||||
}
|
||||
|
||||
const code = [];
|
||||
|
||||
if(encoding.e)
|
||||
{
|
||||
code.push("let modrm_byte = ctx.cpu.read_imm8();");
|
||||
}
|
||||
|
||||
if(has_66.length || has_F2.length || has_F3.length)
|
||||
{
|
||||
const if_blocks = [];
|
||||
|
||||
if(has_66.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_66, size);
|
||||
if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_66 != 0", body, });
|
||||
}
|
||||
if(has_F2.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_F2, size);
|
||||
if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F2 != 0", body, });
|
||||
}
|
||||
if(has_F3.length) {
|
||||
const body = gen_instruction_body_after_prefix(has_F3, size);
|
||||
if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F3 != 0", body, });
|
||||
}
|
||||
|
||||
const else_block = {
|
||||
body: gen_instruction_body_after_prefix(no_prefix, size),
|
||||
};
|
||||
|
||||
return [].concat(
|
||||
code,
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks,
|
||||
else_block,
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
return [].concat(
|
||||
code,
|
||||
gen_instruction_body_after_prefix(encodings, size)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_instruction_body_after_prefix(encodings, size)
|
||||
{
|
||||
const encoding = encodings[0];
|
||||
|
||||
if(encoding.fixed_g !== undefined)
|
||||
{
|
||||
assert(encoding.e);
|
||||
|
||||
// instruction with modrm byte where the middle 3 bits encode the instruction
|
||||
|
||||
// group by opcode without prefix plus middle bits of modrm byte
|
||||
let cases = encodings.reduce((cases_by_opcode, case_) => {
|
||||
assert(typeof case_.fixed_g === "number");
|
||||
cases_by_opcode[case_.opcode & 0xFFFF | case_.fixed_g << 16] = case_;
|
||||
return cases_by_opcode;
|
||||
}, Object.create(null));
|
||||
cases = Object.values(cases).sort((e1, e2) => e1.fixed_g - e2.fixed_g);
|
||||
|
||||
return [
|
||||
{
|
||||
type: "switch",
|
||||
condition: "modrm_byte >> 3 & 7",
|
||||
cases: cases.map(case_ => {
|
||||
const fixed_g = case_.fixed_g;
|
||||
const body = gen_instruction_body_after_fixed_g(case_, size);
|
||||
|
||||
return {
|
||||
conditions: [fixed_g],
|
||||
body,
|
||||
};
|
||||
}),
|
||||
|
||||
default_case: {
|
||||
body: [].concat(
|
||||
gen_call(`::codegen::gen_trigger_ud`, ["ctx"]),
|
||||
"*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
|
||||
),
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
else {
|
||||
assert(encodings.length === 1);
|
||||
return gen_instruction_body_after_fixed_g(encodings[0], size);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_instruction_body_after_fixed_g(encoding, size)
|
||||
{
|
||||
const instruction_postfix = [];
|
||||
|
||||
if(encoding.block_boundary || (!encoding.custom && encoding.e))
|
||||
{
|
||||
instruction_postfix.push("*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;");
|
||||
}
|
||||
|
||||
const instruction_prefix = [];
|
||||
|
||||
if(encoding.task_switch_test || encoding.sse)
|
||||
{
|
||||
instruction_prefix.push(
|
||||
gen_call(encoding.sse ? "::codegen::gen_task_switch_test_mmx" : "::codegen::gen_task_switch_test", ["ctx"])
|
||||
);
|
||||
}
|
||||
|
||||
const imm_read = gen_read_imm_call(encoding, size);
|
||||
const imm_read_bindings = [];
|
||||
if(imm_read)
|
||||
{
|
||||
imm_read_bindings.push(`let imm = ${imm_read} as u32;`);
|
||||
}
|
||||
|
||||
const instruction_name = make_instruction_name(encoding, size);
|
||||
|
||||
if(!encoding.prefix)
|
||||
{
|
||||
if(encoding.custom)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
instruction_prefix.push(
|
||||
gen_call("::codegen::gen_move_registers_from_locals_to_memory", ["ctx"])
|
||||
);
|
||||
instruction_postfix.push(
|
||||
gen_call("::codegen::gen_move_registers_from_memory_to_locals", ["ctx"])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(encoding.e)
|
||||
{
|
||||
const reg_postfix = [];
|
||||
const mem_postfix = [];
|
||||
|
||||
if(encoding.mem_ud)
|
||||
{
|
||||
mem_postfix.push(
|
||||
"*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
|
||||
);
|
||||
}
|
||||
|
||||
if(encoding.reg_ud)
|
||||
{
|
||||
reg_postfix.push(
|
||||
"*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
|
||||
);
|
||||
}
|
||||
|
||||
if(encoding.ignore_mod)
|
||||
{
|
||||
assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
|
||||
|
||||
// Has modrm byte, but the 2 mod bits are ignored and both
|
||||
// operands are always registers (0f20-0f24)
|
||||
const args = ["ctx.builder", `"${instruction_name}"`, "(modrm_byte & 7) as u32", "(modrm_byte >> 3 & 7) as u32"];
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
|
||||
reg_postfix,
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
else if(encoding.custom)
|
||||
{
|
||||
const mem_args = ["ctx", "addr"];
|
||||
const reg_args = ["ctx", "(modrm_byte & 7) as u32"];
|
||||
|
||||
if(encoding.fixed_g === undefined)
|
||||
{
|
||||
mem_args.push("(modrm_byte >> 3 & 7) as u32");
|
||||
reg_args.push("(modrm_byte >> 3 & 7) as u32");
|
||||
}
|
||||
|
||||
if(imm_read)
|
||||
{
|
||||
mem_args.push("imm");
|
||||
reg_args.push("imm");
|
||||
}
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks: [{
|
||||
condition: "modrm_byte < 0xC0",
|
||||
body: [].concat(
|
||||
"let addr = ::modrm::decode(ctx.cpu, modrm_byte);",
|
||||
imm_read_bindings,
|
||||
gen_call(`::jit_instructions::${instruction_name}_mem_jit`, mem_args),
|
||||
mem_postfix
|
||||
),
|
||||
}],
|
||||
else_block: {
|
||||
body: [].concat(
|
||||
imm_read_bindings,
|
||||
gen_call(`::jit_instructions::${instruction_name}_reg_jit`, reg_args),
|
||||
reg_postfix
|
||||
),
|
||||
},
|
||||
},
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
const mem_args = ["ctx.builder", `"${instruction_name}_mem"`];
|
||||
const reg_args = ["ctx.builder", `"${instruction_name}_reg"`, "(modrm_byte & 7) as u32"];
|
||||
|
||||
if(encoding.fixed_g === undefined)
|
||||
{
|
||||
mem_args.push("(modrm_byte >> 3 & 7) as u32");
|
||||
reg_args.push("(modrm_byte >> 3 & 7) as u32");
|
||||
}
|
||||
|
||||
if(imm_read)
|
||||
{
|
||||
mem_args.push("imm");
|
||||
reg_args.push("imm");
|
||||
}
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
{
|
||||
type: "if-else",
|
||||
if_blocks: [{
|
||||
condition: "modrm_byte < 0xC0",
|
||||
body: [].concat(
|
||||
"let addr = ::modrm::decode(ctx.cpu, modrm_byte);",
|
||||
gen_call(`::codegen::gen_modrm_resolve`, ["ctx", "addr"]),
|
||||
imm_read_bindings,
|
||||
gen_call(`::codegen::gen_modrm_fn${mem_args.length - 2}`, mem_args),
|
||||
mem_postfix
|
||||
),
|
||||
}],
|
||||
else_block: {
|
||||
body: [].concat(
|
||||
imm_read_bindings,
|
||||
gen_call(`::codegen::gen_fn${reg_args.length - 2}_const`, reg_args),
|
||||
reg_postfix
|
||||
),
|
||||
},
|
||||
},
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
}
|
||||
else if(encoding.prefix || encoding.custom)
|
||||
{
|
||||
// custom, but not modrm
|
||||
|
||||
const args = ["ctx"];
|
||||
|
||||
if(imm_read)
|
||||
{
|
||||
args.push("imm");
|
||||
}
|
||||
|
||||
if(encoding.prefix)
|
||||
{
|
||||
args.push("instr_flags");
|
||||
}
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
imm_read_bindings,
|
||||
gen_call(`::jit_instructions::${instruction_name}_jit`, args),
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// instruction without modrm byte or prefix
|
||||
|
||||
const args = ["ctx.builder", `"${instruction_name}"`];
|
||||
|
||||
if(imm_read)
|
||||
{
|
||||
args.push("imm");
|
||||
}
|
||||
|
||||
if(encoding.extra_imm16)
|
||||
{
|
||||
assert(imm_read);
|
||||
imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm16() as u32;`);
|
||||
args.push("imm2");
|
||||
}
|
||||
else if(encoding.extra_imm8)
|
||||
{
|
||||
assert(imm_read);
|
||||
imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm8() as u32;`);
|
||||
args.push("imm2");
|
||||
}
|
||||
|
||||
return [].concat(
|
||||
instruction_prefix,
|
||||
imm_read_bindings,
|
||||
gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
|
||||
instruction_postfix
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function gen_table()
|
||||
{
|
||||
let by_opcode = Object.create(null);
|
||||
let by_opcode0f = Object.create(null);
|
||||
|
||||
for(let o of x86_table)
|
||||
{
|
||||
let opcode = o.opcode;
|
||||
|
||||
if((opcode & 0xFF00) === 0x0F00)
|
||||
{
|
||||
opcode &= 0xFF;
|
||||
by_opcode0f[opcode] = by_opcode0f[opcode] || [];
|
||||
by_opcode0f[opcode].push(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
opcode &= 0xFF;
|
||||
by_opcode[opcode] = by_opcode[opcode] || [];
|
||||
by_opcode[opcode].push(o);
|
||||
}
|
||||
}
|
||||
|
||||
let cases = [];
|
||||
for(let opcode = 0; opcode < 0x100; opcode++)
|
||||
{
|
||||
let encoding = by_opcode[opcode];
|
||||
assert(encoding && encoding.length);
|
||||
|
||||
let opcode_hex = hex(opcode, 2);
|
||||
let opcode_high_hex = hex(opcode | 0x100, 2);
|
||||
|
||||
if(encoding[0].os)
|
||||
{
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_hex}`],
|
||||
body: gen_instruction_body(encoding, 16),
|
||||
});
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, 32),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
cases.push({
|
||||
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, undefined),
|
||||
});
|
||||
}
|
||||
}
|
||||
const table = {
|
||||
type: "switch",
|
||||
condition: "opcode",
|
||||
cases,
|
||||
default_case: {
|
||||
body: ["assert!(false);"]
|
||||
},
|
||||
};
|
||||
|
||||
if(to_generate.jit)
|
||||
{
|
||||
const code = [
|
||||
"#[cfg_attr(rustfmt, rustfmt_skip)]",
|
||||
"pub fn jit(opcode: u32, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
|
||||
table,
|
||||
"}",
|
||||
];
|
||||
|
||||
finalize_table_rust(
|
||||
OUT_DIR,
|
||||
"jit.rs",
|
||||
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
|
||||
const cases0f = [];
|
||||
for(let opcode = 0; opcode < 0x100; opcode++)
|
||||
{
|
||||
let encoding = by_opcode0f[opcode];
|
||||
|
||||
assert(encoding && encoding.length);
|
||||
|
||||
let opcode_hex = hex(opcode, 2);
|
||||
let opcode_high_hex = hex(opcode | 0x100, 2);
|
||||
|
||||
if(encoding[0].os)
|
||||
{
|
||||
cases0f.push({
|
||||
conditions: [`0x${opcode_hex}`],
|
||||
body: gen_instruction_body(encoding, 16),
|
||||
});
|
||||
cases0f.push({
|
||||
conditions: [`0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, 32),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
let block = {
|
||||
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
||||
body: gen_instruction_body(encoding, undefined),
|
||||
};
|
||||
cases0f.push(block);
|
||||
}
|
||||
}
|
||||
|
||||
const table0f = {
|
||||
type: "switch",
|
||||
condition: "opcode",
|
||||
cases: cases0f,
|
||||
default_case: {
|
||||
body: ["assert!(false);"]
|
||||
},
|
||||
};
|
||||
|
||||
if(to_generate.jit0f)
|
||||
{
|
||||
const code = [
|
||||
"#[cfg_attr(rustfmt, rustfmt_skip)]",
|
||||
"pub fn jit(opcode: u32, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
|
||||
table0f,
|
||||
"}",
|
||||
];
|
||||
|
||||
finalize_table_rust(
|
||||
OUT_DIR,
|
||||
"jit0f.rs",
|
||||
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
}
|
90
gen/rust_ast.js
Normal file
90
gen/rust_ast.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
"use strict";
|
||||
|
||||
const assert = require("assert").strict;
|
||||
|
||||
function repeat(s, n)
|
||||
{
|
||||
let out = "";
|
||||
for(let i = 0; i < n; i++) out += s;
|
||||
return out;
|
||||
}
|
||||
|
||||
function indent(lines, how_much)
|
||||
{
|
||||
return lines.map(line => repeat(" ", how_much) + line);
|
||||
}
|
||||
|
||||
function print_syntax_tree(statements)
|
||||
{
|
||||
let code = [];
|
||||
|
||||
for(let statement of statements)
|
||||
{
|
||||
if(typeof statement === "string")
|
||||
{
|
||||
code.push(statement);
|
||||
}
|
||||
else if(statement.type === "switch")
|
||||
{
|
||||
assert(statement.condition);
|
||||
|
||||
const cases = [];
|
||||
|
||||
for(let case_ of statement.cases)
|
||||
{
|
||||
assert(case_.conditions.length >= 1);
|
||||
|
||||
cases.push(case_.conditions.join(" | ") + " => {");
|
||||
cases.push.apply(cases, indent(print_syntax_tree(case_.body), 4));
|
||||
cases.push(`},`);
|
||||
}
|
||||
|
||||
if(statement.default_case)
|
||||
{
|
||||
cases.push(`_ => {`);
|
||||
cases.push.apply(cases, indent(print_syntax_tree(statement.default_case.body), 4));
|
||||
cases.push(`}`);
|
||||
}
|
||||
|
||||
code.push(`match ${statement.condition} {`);
|
||||
code.push.apply(code, indent(cases, 4));
|
||||
code.push(`}`);
|
||||
}
|
||||
else if(statement.type === "if-else")
|
||||
{
|
||||
assert(statement.if_blocks.length >= 1);
|
||||
|
||||
let first_if_block = statement.if_blocks[0];
|
||||
|
||||
code.push(`if ${first_if_block.condition} {`);
|
||||
code.push.apply(code, indent(print_syntax_tree(first_if_block.body), 4));
|
||||
code.push(`}`);
|
||||
|
||||
for(let i = 1; i < statement.if_blocks.length; i++)
|
||||
{
|
||||
let if_block = statement.if_blocks[i];
|
||||
|
||||
code.push(`else if ${if_block.condition} {`);
|
||||
code.push.apply(code, indent(print_syntax_tree(if_block.body), 4));
|
||||
code.push(`}`);
|
||||
}
|
||||
|
||||
if(statement.else_block)
|
||||
{
|
||||
code.push(`else {`);
|
||||
code.push.apply(code, indent(print_syntax_tree(statement.else_block.body), 4));
|
||||
code.push(`}`);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, "Unexpected type: " + statement.type, "In:", statement);
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
print_syntax_tree,
|
||||
};
|
54
gen/util.js
Normal file
54
gen/util.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
"use strict";
|
||||
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const process = require("process");
|
||||
const child_process = require("child_process");
|
||||
|
||||
const CYAN_FMT = "\x1b[36m%s\x1b[0m";
|
||||
|
||||
function hex(n, pad)
|
||||
{
|
||||
pad = pad || 0;
|
||||
let s = n.toString(16).toUpperCase();
|
||||
while(s.length < pad) s = "0" + s;
|
||||
return s;
|
||||
}
|
||||
|
||||
function mkdirpSync(dir)
|
||||
{
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
function get_switch_value(arg_switch)
|
||||
{
|
||||
const argv = process.argv;
|
||||
const switch_i = argv.indexOf(arg_switch);
|
||||
const val_i = switch_i + 1;
|
||||
if(switch_i > -1 && val_i < argv.length)
|
||||
{
|
||||
return argv[switch_i + 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function get_switch_exist(arg_switch)
|
||||
{
|
||||
return process.argv.includes(arg_switch);
|
||||
}
|
||||
|
||||
function finalize_table_rust(out_dir, name, contents)
|
||||
{
|
||||
const file_path = path.join(out_dir, name);
|
||||
fs.writeFileSync(file_path, contents);
|
||||
console.log(CYAN_FMT, `[+] Wrote table ${name}.`);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hex,
|
||||
mkdirpSync,
|
||||
get_switch_value,
|
||||
get_switch_exist,
|
||||
finalize_table_rust,
|
||||
};
|
977
gen/x86_table.js
Normal file
977
gen/x86_table.js
Normal file
|
@ -0,0 +1,977 @@
|
|||
"use strict";
|
||||
|
||||
const { hex } = require("./util");
|
||||
|
||||
// http://ref.x86asm.net/coder32.html
|
||||
|
||||
const zf = 1 << 6;
|
||||
const of = 1 << 11;
|
||||
const cf = 1 << 0;
|
||||
const af = 1 << 4;
|
||||
const pf = 1 << 2;
|
||||
const sf = 1 << 7;
|
||||
|
||||
// === Types of instructions
|
||||
//
|
||||
// create entry | check for compiled code | instruction
|
||||
// -------------+-------------------------+-----------------------------------------------------------
|
||||
// 1 | optional | pop ds (may change cpu state)
|
||||
// | | trigger_ud, div (exception that doesn't generate conditional return from BB)
|
||||
// | | port io, popf, sti (may call interrupt or continue at next instruction)
|
||||
// | | hlt
|
||||
// -------------+-------------------------+-----------------------------------------------------------
|
||||
// 1 | 1 | call [eax], jmp [eax], int, iret, ret, jmpf, callf, sysenter, sysexit
|
||||
// | | Special case: normal instruction with fallthough to next page
|
||||
// | | Special case: after execution of compiled code
|
||||
// | | -> may create redundant entry points depending on last instruction?
|
||||
// -------------+-------------------------+-----------------------------------------------------------
|
||||
// 1 | 0 | rep movs, rep lods, rep stos, rep cmps, rep scas
|
||||
// | | -> Executed as follows:
|
||||
// | | - Upto including the first call in compiled mode
|
||||
// | | - Back to main loop and repeated in interpreted mode (as entry point is after instruction, not on)
|
||||
// | | - When finished entry pointer *after* instruction is hit and execution continues in compiled mode
|
||||
// -------------+-------------------------+-----------------------------------------------------------
|
||||
// 0 | optional | jmp foo, jnz foo
|
||||
// | | (foo is in the same page as the instruction)
|
||||
// -------------+-------------------------+-----------------------------------------------------------
|
||||
// 1 | 1 | call foo
|
||||
// | | (foo is in the same page as the instruction)
|
||||
// | | -> The entry point is not created for jumps within
|
||||
// | | this page, but speculatively for calls from
|
||||
// | | other pages to the function in this page
|
||||
// -------------+-------------------------+-----------------------------------------------------------
|
||||
// 1 | 1 | call foo, jmp foo, jnz foo
|
||||
// | | (foo is in a different page than the instruction)
|
||||
|
||||
|
||||
// e: a modrm byte follows the operand
|
||||
// os: the instruction behaves differently depending on the operand size
|
||||
// fixed_g: the reg field of the modrm byte selects an instruction
|
||||
// skip: skip automatically generated tests (nasmtests)
|
||||
// mask_flags: flags bits to mask in generated tests
|
||||
// prefix: is a prefix instruction
|
||||
// imm8, imm8s, imm16, imm1632, immaddr, extra_imm8, extra_imm16: one or two immediate bytes follows the instruction
|
||||
// custom: will callback jit to generate custom code
|
||||
// block_boundary: may change eip in a way not handled by the jit
|
||||
// no_next_instruction: jit will stop analysing after instruction (e.g., unconditional jump, ret)
|
||||
const encodings = [
|
||||
{ opcode: 0x00, custom: 1, e: 1, },
|
||||
{ opcode: 0x01, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x02, custom: 1, e: 1, },
|
||||
{ opcode: 0x03, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x08, custom: 1, e: 1, },
|
||||
{ opcode: 0x09, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x0A, custom: 1, e: 1, },
|
||||
{ opcode: 0x0B, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x10, custom: 1, e: 1, },
|
||||
{ opcode: 0x11, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x12, custom: 1, e: 1, },
|
||||
{ opcode: 0x13, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x18, custom: 1, e: 1, },
|
||||
{ opcode: 0x19, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x1A, custom: 1, e: 1, },
|
||||
{ opcode: 0x1B, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x20, custom: 1, e: 1, },
|
||||
{ opcode: 0x21, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x22, custom: 1, e: 1, },
|
||||
{ opcode: 0x23, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x28, custom: 1, e: 1, },
|
||||
{ opcode: 0x29, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x2A, custom: 1, e: 1, },
|
||||
{ opcode: 0x2B, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x30, custom: 1, e: 1, },
|
||||
{ opcode: 0x31, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x32, custom: 1, e: 1, },
|
||||
{ opcode: 0x33, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x38, custom: 1, e: 1, },
|
||||
{ opcode: 0x39, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x3A, custom: 1, e: 1, },
|
||||
{ opcode: 0x3B, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x06, os: 1, custom: 1 },
|
||||
{ opcode: 0x07, os: 1, skip: 1, block_boundary: 1, }, // pop es: block_boundary since it uses non-raising cpu exceptions
|
||||
{ opcode: 0x0E, os: 1, custom: 1 },
|
||||
{ opcode: 0x0F, os: 1, prefix: 1, },
|
||||
{ opcode: 0x16, os: 1, custom: 1 },
|
||||
{ opcode: 0x17, block_boundary: 1, os: 1, skip: 1, }, // pop ss
|
||||
{ opcode: 0x1E, os: 1, custom: 1 },
|
||||
{ opcode: 0x1F, block_boundary: 1, os: 1, skip: 1, }, // pop ds
|
||||
{ opcode: 0x26, prefix: 1, },
|
||||
{ opcode: 0x27, mask_flags: of, },
|
||||
{ opcode: 0x2E, prefix: 1, },
|
||||
{ opcode: 0x2F, mask_flags: of, },
|
||||
{ opcode: 0x36, prefix: 1, },
|
||||
{ opcode: 0x37, mask_flags: of | sf | pf | zf, },
|
||||
{ opcode: 0x3E, prefix: 1, },
|
||||
{ opcode: 0x3F, mask_flags: of | sf | pf | zf, },
|
||||
|
||||
{ opcode: 0x40, os: 1, custom: 1 },
|
||||
{ opcode: 0x41, os: 1, custom: 1 },
|
||||
{ opcode: 0x42, os: 1, custom: 1 },
|
||||
{ opcode: 0x43, os: 1, custom: 1 },
|
||||
{ opcode: 0x44, os: 1, custom: 1 },
|
||||
{ opcode: 0x45, os: 1, custom: 1 },
|
||||
{ opcode: 0x46, os: 1, custom: 1 },
|
||||
{ opcode: 0x47, os: 1, custom: 1 },
|
||||
|
||||
{ opcode: 0x48, os: 1, custom: 1 },
|
||||
{ opcode: 0x49, os: 1, custom: 1 },
|
||||
{ opcode: 0x4A, os: 1, custom: 1 },
|
||||
{ opcode: 0x4B, os: 1, custom: 1 },
|
||||
{ opcode: 0x4C, os: 1, custom: 1 },
|
||||
{ opcode: 0x4D, os: 1, custom: 1 },
|
||||
{ opcode: 0x4E, os: 1, custom: 1 },
|
||||
{ opcode: 0x4F, os: 1, custom: 1 },
|
||||
|
||||
{ opcode: 0x50, custom: 1, os: 1 },
|
||||
{ opcode: 0x51, custom: 1, os: 1 },
|
||||
{ opcode: 0x52, custom: 1, os: 1 },
|
||||
{ opcode: 0x53, custom: 1, os: 1 },
|
||||
{ opcode: 0x54, custom: 1, os: 1 },
|
||||
{ opcode: 0x55, custom: 1, os: 1 },
|
||||
{ opcode: 0x56, custom: 1, os: 1 },
|
||||
{ opcode: 0x57, custom: 1, os: 1 },
|
||||
|
||||
{ opcode: 0x58, custom: 1, os: 1, },
|
||||
{ opcode: 0x59, custom: 1, os: 1, },
|
||||
{ opcode: 0x5A, custom: 1, os: 1, },
|
||||
{ opcode: 0x5B, custom: 1, os: 1, },
|
||||
{ opcode: 0x5C, custom: 1, os: 1, },
|
||||
{ opcode: 0x5D, custom: 1, os: 1, },
|
||||
{ opcode: 0x5E, custom: 1, os: 1, },
|
||||
{ opcode: 0x5F, custom: 1, os: 1, },
|
||||
|
||||
{ opcode: 0x60, os: 1, },
|
||||
{ opcode: 0x61, os: 1, },
|
||||
{ opcode: 0x62, e: 1, skip: 1, },
|
||||
{ opcode: 0x63, e: 1, block_boundary: 1, }, // arpl
|
||||
{ opcode: 0x64, prefix: 1, },
|
||||
{ opcode: 0x65, prefix: 1, },
|
||||
{ opcode: 0x66, prefix: 1, },
|
||||
{ opcode: 0x67, prefix: 1, },
|
||||
|
||||
{ opcode: 0x68, custom: 1, os: 1, imm1632: 1 },
|
||||
{ opcode: 0x69, os: 1, e: 1, custom: 1, imm1632: 1, mask_flags: af, }, // zf?
|
||||
{ opcode: 0x6A, custom: 1, os: 1, imm8s: 1 },
|
||||
{ opcode: 0x6B, os: 1, e: 1, custom: 1, imm8s: 1, mask_flags: af, }, // zf?
|
||||
|
||||
{ opcode: 0x6C, block_boundary: 1, custom: 1, is_string: 1, skip: 1, }, // ins
|
||||
{ opcode: 0xF26C, block_boundary: 1, custom: 1, is_string: 1, skip: 1, },
|
||||
{ opcode: 0xF36C, block_boundary: 1, custom: 1, is_string: 1, skip: 1, },
|
||||
{ opcode: 0x6D, block_boundary: 1, custom: 1, is_string: 1, os: 1, skip: 1, },
|
||||
{ opcode: 0xF26D, block_boundary: 1, custom: 1, is_string: 1, os: 1, skip: 1, },
|
||||
{ opcode: 0xF36D, block_boundary: 1, custom: 1, is_string: 1, os: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0x6E, block_boundary: 1, custom: 1, is_string: 1, skip: 1, }, // outs
|
||||
{ opcode: 0xF26E, block_boundary: 1, custom: 1, is_string: 1, skip: 1, },
|
||||
{ opcode: 0xF36E, block_boundary: 1, custom: 1, is_string: 1, skip: 1, },
|
||||
{ opcode: 0x6F, block_boundary: 1, custom: 1, is_string: 1, os: 1, skip: 1, },
|
||||
{ opcode: 0xF26F, block_boundary: 1, custom: 1, is_string: 1, os: 1, skip: 1, },
|
||||
{ opcode: 0xF36F, block_boundary: 1, custom: 1, is_string: 1, os: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0x84, custom: 1, e: 1, },
|
||||
{ opcode: 0x85, custom: 1, e: 1, os: 1, },
|
||||
{ opcode: 0x86, custom: 1, e: 1, },
|
||||
{ opcode: 0x87, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x88, custom: 1, e: 1, },
|
||||
{ opcode: 0x89, custom: 1, os: 1, e: 1, },
|
||||
{ opcode: 0x8A, custom: 1, e: 1, },
|
||||
{ opcode: 0x8B, custom: 1, os: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x8C, os: 1, e: 1, custom: 1 }, // mov reg, sreg
|
||||
{ opcode: 0x8D, reg_ud: 1, os: 1, e: 1, custom_modrm_resolve: 1, custom: 1, }, // lea
|
||||
{ opcode: 0x8E, block_boundary: 1, e: 1, skip: 1, }, // mov sreg
|
||||
{ opcode: 0x8F, os: 1, e: 1, fixed_g: 0, custom_modrm_resolve: 1, custom: 1, block_boundary: 1, }, // pop r/m
|
||||
|
||||
{ opcode: 0x90, custom: 1, },
|
||||
{ opcode: 0x91, custom: 1, os: 1, },
|
||||
{ opcode: 0x92, custom: 1, os: 1, },
|
||||
{ opcode: 0x93, custom: 1, os: 1, },
|
||||
{ opcode: 0x94, custom: 1, os: 1, },
|
||||
{ opcode: 0x95, custom: 1, os: 1, },
|
||||
{ opcode: 0x96, custom: 1, os: 1, },
|
||||
{ opcode: 0x97, custom: 1, os: 1, },
|
||||
|
||||
{ opcode: 0x98, os: 1, custom: 1 },
|
||||
{ opcode: 0x99, os: 1, custom: 1 },
|
||||
{ opcode: 0x9A, os: 1, imm1632: 1, extra_imm16: 1, skip: 1, block_boundary: 1, }, // callf
|
||||
{ opcode: 0x9B, block_boundary: 1, skip: 1, }, // fwait: block_boundary since it uses non-raising cpu exceptions
|
||||
{ opcode: 0x9C, os: 1, custom: 1 },
|
||||
{ opcode: 0x9D, os: 1, skip: 1, custom: 1, },
|
||||
{ opcode: 0x9E, custom: 1 },
|
||||
{ opcode: 0x9F, custom: 1 },
|
||||
|
||||
{ opcode: 0xA0, custom: 1, immaddr: 1 },
|
||||
{ opcode: 0xA1, custom: 1, os: 1, immaddr: 1 },
|
||||
{ opcode: 0xA2, custom: 1, immaddr: 1 },
|
||||
{ opcode: 0xA3, custom: 1, os: 1, immaddr: 1 },
|
||||
|
||||
// string instructions aren't jumps, but they modify eip due to how they're implemented
|
||||
// TODO: The block_boundary on the non-rep instructions can be removed once they're custom
|
||||
{ opcode: 0xA4, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF2A4, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF3A4, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xA5, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF2A5, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF3A5, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
|
||||
{ opcode: 0xA6, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF2A6, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF3A6, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xA7, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF2A7, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF3A7, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
|
||||
{ opcode: 0xA8, custom: 1, imm8: 1, },
|
||||
{ opcode: 0xA9, custom: 1, os: 1, imm1632: 1, },
|
||||
|
||||
{ opcode: 0xAA, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF2AA, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF3AA, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xAB, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF2AB, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF3AB, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
|
||||
{ opcode: 0xAC, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF2AC, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF3AC, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xAD, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF2AD, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF3AD, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
|
||||
{ opcode: 0xAE, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF2AE, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xF3AE, block_boundary: 1, custom: 1, is_string: 1, },
|
||||
{ opcode: 0xAF, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF2AF, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
{ opcode: 0xF3AF, block_boundary: 1, custom: 1, is_string: 1, os: 1, },
|
||||
|
||||
{ opcode: 0xC2, custom: 1, block_boundary: 1, no_next_instruction: 1, os: 1, absolute_jump: 1, imm16: 1, skip: 1, }, // ret
|
||||
{ opcode: 0xC3, custom: 1, block_boundary: 1, no_next_instruction: 1, os: 1, absolute_jump: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0xC4, block_boundary: 1, os: 1, e: 1, skip: 1, }, // les
|
||||
{ opcode: 0xC5, block_boundary: 1, os: 1, e: 1, skip: 1, }, // lds
|
||||
|
||||
{ opcode: 0xC6, custom: 1, e: 1, fixed_g: 0, imm8: 1 },
|
||||
{ opcode: 0xC7, custom: 1, os: 1, e: 1, fixed_g: 0, imm1632: 1 },
|
||||
|
||||
// XXX: Temporary block boundary
|
||||
{ opcode: 0xC8, os: 1, imm16: 1, extra_imm8: 1, block_boundary: 1, }, // enter
|
||||
{ opcode: 0xC9, custom: 1, os: 1, skip: 1 }, // leave
|
||||
|
||||
{ opcode: 0xCA, block_boundary: 1, no_next_instruction: 1, os: 1, imm16: 1, skip: 1, }, // retf
|
||||
{ opcode: 0xCB, block_boundary: 1, no_next_instruction: 1, os: 1, skip: 1, },
|
||||
{ opcode: 0xCC, block_boundary: 1, skip: 1, }, // int
|
||||
{ opcode: 0xCD, block_boundary: 1, skip: 1, imm8: 1, },
|
||||
{ opcode: 0xCE, block_boundary: 1, skip: 1, },
|
||||
{ opcode: 0xCF, block_boundary: 1, no_next_instruction: 1, os: 1, skip: 1, }, // iret
|
||||
|
||||
{ opcode: 0xD4, imm8: 1, block_boundary: 1, }, // aam, may trigger #de
|
||||
{ opcode: 0xD5, imm8: 1, mask_flags: of | cf | af, },
|
||||
{ opcode: 0xD6, },
|
||||
|
||||
{ opcode: 0xD7, skip: 1, custom: 1, },
|
||||
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 0, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 1, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 2, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 3, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 4, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 6, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xD8, e: 1, fixed_g: 7, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 0, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 1, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 2, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 3, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 4, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, skip_mem: 1, }, // fldenv (mem)
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 6, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, skip: 1, }, // fstenv (mem), fprem (reg)
|
||||
{ opcode: 0xD9, e: 1, fixed_g: 7, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, skip_reg: 1, }, // fprem, fyl2xp1 (precision issues)
|
||||
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 0, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 1, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 2, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 3, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 4, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 6, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDA, e: 1, fixed_g: 7, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 0, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 1, custom: 0, is_fpu: 1, task_switch_test: 1, skip_mem: 1, }, // unimplemented: fisttp (sse3)
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 2, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 3, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 4, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 6, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDB, e: 1, fixed_g: 7, custom: 0, is_fpu: 1, task_switch_test: 1, },
|
||||
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 0, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 1, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 2, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 3, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 4, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 6, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDC, e: 1, fixed_g: 7, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 0, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 1, custom: 0, is_fpu: 1, task_switch_test: 1, os: 1, skip_mem: 1, }, // unimplemented: fisttp (sse3)
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 2, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 3, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 4, custom: 0, is_fpu: 1, task_switch_test: 1, os: 1, skip_mem: 1 }, // frstor
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 6, custom: 0, is_fpu: 1, task_switch_test: 1, os: 1, skip_mem: 1 }, // fsave
|
||||
{ opcode: 0xDD, e: 1, fixed_g: 7, custom: 0, is_fpu: 1, task_switch_test: 1, os: 1, },
|
||||
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 0, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 1, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 2, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 3, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 4, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 6, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDE, e: 1, fixed_g: 7, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 0, custom: 0, is_fpu: 1, task_switch_test: 1 },
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 1, custom: 0, is_fpu: 1, task_switch_test: 1, skip_mem: 1 }, // unimplemented: fisttp (sse3)
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 2, custom: 1, is_fpu: 1, task_switch_test: 1 },
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 3, custom: 1, is_fpu: 1, task_switch_test: 1 },
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 4, custom: 1, is_fpu: 1, task_switch_test: 1, skip_mem: 1 }, // unimplemented: Binary Coded Decimals
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 5, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 6, custom: 1, is_fpu: 1, task_switch_test: 1, skip_mem: 1 }, // unimplemented: Binary Coded Decimals
|
||||
{ opcode: 0xDF, e: 1, fixed_g: 7, custom: 1, is_fpu: 1, task_switch_test: 1, },
|
||||
|
||||
// loop, jcxz, etc.
|
||||
{ opcode: 0xE0, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
|
||||
{ opcode: 0xE1, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
|
||||
{ opcode: 0xE2, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
|
||||
{ opcode: 0xE3, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
|
||||
|
||||
// port functions aren't jumps, but they may modify eip due to how they are implemented
|
||||
{ opcode: 0xE4, block_boundary: 1, imm8: 1, skip: 1, }, // in
|
||||
{ opcode: 0xE5, block_boundary: 1, os: 1, imm8: 1, skip: 1, },
|
||||
{ opcode: 0xE6, block_boundary: 1, imm8: 1, skip: 1, }, // out
|
||||
{ opcode: 0xE7, block_boundary: 1, os: 1, imm8: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0xE8, block_boundary: 1, jump_offset_imm: 1, os: 1, imm1632: 1, custom: 1, skip: 1, }, // call
|
||||
{ opcode: 0xE9, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, no_next_instruction: 1, os: 1, imm1632: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0xEA, block_boundary: 1, no_next_instruction: 1, os: 1, imm1632: 1, extra_imm16: 1, skip: 1, }, // jmpf
|
||||
{ opcode: 0xEB, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, no_next_instruction: 1, os: 1, imm8s: 1, custom: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0xEC, block_boundary: 1, skip: 1, }, // in
|
||||
{ opcode: 0xED, block_boundary: 1, os: 1, skip: 1, },
|
||||
{ opcode: 0xEE, block_boundary: 1, skip: 1, }, // out
|
||||
{ opcode: 0xEF, block_boundary: 1, os: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0xF0, prefix: 1, },
|
||||
{ opcode: 0xF1, skip: 1, },
|
||||
{ opcode: 0xF2, prefix: 1, },
|
||||
{ opcode: 0xF3, prefix: 1, },
|
||||
{ opcode: 0xF4, block_boundary: 1, no_next_instruction: 1, skip: 1, }, // hlt
|
||||
{ opcode: 0xF5, },
|
||||
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 0, imm8: 1, custom: 1 },
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 1, imm8: 1, custom: 1 },
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 2, },
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 3, },
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 4, mask_flags: af | zf, },
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 5, mask_flags: af | zf, },
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 6, block_boundary: 1, }, // div/idiv: Not a block boundary, but doesn't use control flow exceptions
|
||||
{ opcode: 0xF6, e: 1, fixed_g: 7, block_boundary: 1, },
|
||||
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 0, imm1632: 1, custom: 1 },
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 1, imm1632: 1, custom: 1 },
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 2, custom: 1 },
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 3, custom: 1 },
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 4, mask_flags: zf | af, custom: 1 },
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 5, mask_flags: zf | af, custom: 1 },
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 6, custom: 1 },
|
||||
{ opcode: 0xF7, os: 1, e: 1, fixed_g: 7, custom: 1 },
|
||||
|
||||
{ opcode: 0xF8, custom: 1 },
|
||||
{ opcode: 0xF9, custom: 1 },
|
||||
{ opcode: 0xFA, custom: 1, skip: 1 },
|
||||
// STI: Note: Has special handling in jit in order to call handle_irqs safely
|
||||
{ opcode: 0xFB, custom: 1, custom_sti: 1, skip: 1, },
|
||||
{ opcode: 0xFC, custom: 1, },
|
||||
{ opcode: 0xFD, custom: 1, },
|
||||
|
||||
{ opcode: 0xFE, e: 1, fixed_g: 0, custom: 1 },
|
||||
{ opcode: 0xFE, e: 1, fixed_g: 1, custom: 1 },
|
||||
{ opcode: 0xFF, os: 1, e: 1, fixed_g: 0, custom: 1, },
|
||||
{ opcode: 0xFF, os: 1, e: 1, fixed_g: 1, custom: 1, },
|
||||
{ opcode: 0xFF, os: 1, e: 1, fixed_g: 2, custom: 1, block_boundary: 1, absolute_jump: 1, skip: 1, },
|
||||
{ opcode: 0xFF, os: 1, e: 1, fixed_g: 3, block_boundary: 1, skip: 1, },
|
||||
{ opcode: 0xFF, os: 1, e: 1, fixed_g: 4, custom: 1, block_boundary: 1, absolute_jump: 1, no_next_instruction: 1, skip: 1, },
|
||||
{ opcode: 0xFF, os: 1, e: 1, fixed_g: 5, block_boundary: 1, no_next_instruction: 1, skip: 1, },
|
||||
{ opcode: 0xFF, custom: 1, os: 1, e: 1, fixed_g: 6, },
|
||||
|
||||
{ opcode: 0x0F00, fixed_g: 0, e: 1, skip: 1, block_boundary: 1, os: 1, }, // sldt, ...
|
||||
{ opcode: 0x0F00, fixed_g: 1, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F00, fixed_g: 2, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F00, fixed_g: 3, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F00, fixed_g: 4, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F00, fixed_g: 5, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
|
||||
{ opcode: 0x0F01, fixed_g: 0, e: 1, skip: 1, block_boundary: 1, os: 1, }, // sgdt, ...
|
||||
{ opcode: 0x0F01, fixed_g: 1, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F01, fixed_g: 2, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F01, fixed_g: 3, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F01, fixed_g: 4, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F01, fixed_g: 6, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
{ opcode: 0x0F01, fixed_g: 7, e: 1, skip: 1, block_boundary: 1, os: 1, },
|
||||
|
||||
{ opcode: 0x0F02, os: 1, e: 1, skip: 1, block_boundary: 1, }, // lar
|
||||
{ opcode: 0x0F03, os: 1, e: 1, skip: 1, block_boundary: 1, }, // lsl
|
||||
{ opcode: 0x0F04, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F05, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F06, skip: 1, block_boundary: 1, }, // clts
|
||||
{ opcode: 0x0F07, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F08, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F09, skip: 1, block_boundary: 1, }, // wbinvd
|
||||
{ opcode: 0x0F0A, skip: 1, block_boundary: 1, },
|
||||
// ud2
|
||||
// Technically has a next instruction, but Linux uses this for assertions
|
||||
// and embeds the assertion message after this instruction, which is likely
|
||||
// the most common use case of ud2
|
||||
{ opcode: 0x0F0B, skip: 1, block_boundary: 1, no_next_instruction: 1, },
|
||||
{ opcode: 0x0F0C, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F0D, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F0E, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F0F, skip: 1, block_boundary: 1, },
|
||||
|
||||
{ opcode: 0x0F18, e: 1, custom: 1 },
|
||||
{ opcode: 0x0F19, custom: 1, e: 1, },
|
||||
{ opcode: 0x0F1A, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F1B, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F1C, custom: 1, e: 1, },
|
||||
{ opcode: 0x0F1D, custom: 1, e: 1, },
|
||||
{ opcode: 0x0F1E, custom: 1, e: 1, },
|
||||
{ opcode: 0x0F1F, custom: 1, e: 1, },
|
||||
|
||||
{ opcode: 0x0F20, ignore_mod: 1, e: 1, skip: 1, block_boundary: 1, }, // mov reg, creg
|
||||
{ opcode: 0x0F21, ignore_mod: 1, e: 1, skip: 1, block_boundary: 1, }, // mov reg, dreg
|
||||
{ opcode: 0x0F22, ignore_mod: 1, e: 1, skip: 1, block_boundary: 1, }, // mov creg, reg
|
||||
{ opcode: 0x0F23, ignore_mod: 1, e: 1, skip: 1, block_boundary: 1, }, // mov dreg, reg
|
||||
{ opcode: 0x0F24, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F25, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F26, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F27, skip: 1, block_boundary: 1, },
|
||||
|
||||
{ opcode: 0x0F30, skip: 1, block_boundary: 1, }, // wrmsr
|
||||
{ opcode: 0x0F31, skip: 1, custom: 1, }, // rdtsc
|
||||
{ opcode: 0x0F32, skip: 1, block_boundary: 1, }, // rdmsr
|
||||
{ opcode: 0x0F33, skip: 1, block_boundary: 1, }, // rdpmc
|
||||
{ opcode: 0x0F34, skip: 1, block_boundary: 1, no_next_instruction: 1, }, // sysenter
|
||||
{ opcode: 0x0F35, skip: 1, block_boundary: 1, no_next_instruction: 1, }, // sysexit
|
||||
|
||||
{ opcode: 0x0F36, skip: 1, block_boundary: 1, }, // ud
|
||||
{ opcode: 0x0F37, skip: 1, block_boundary: 1, }, // getsec
|
||||
|
||||
// sse3+
|
||||
{ opcode: 0x0F38, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F39, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F3A, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F3B, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F3C, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F3D, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F3E, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F3F, skip: 1, block_boundary: 1, },
|
||||
|
||||
{ opcode: 0x0F40, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F41, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F42, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F43, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F44, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F45, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F46, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F47, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F48, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F49, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F4A, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F4B, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F4C, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F4D, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F4E, e: 1, os: 1, custom: 1, },
|
||||
{ opcode: 0x0F4F, e: 1, os: 1, custom: 1, },
|
||||
|
||||
{ opcode: 0x0F80, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F81, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F82, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F83, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F84, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F85, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F86, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F87, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F88, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F89, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F8A, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F8B, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F8C, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F8D, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F8E, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x0F8F, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, imm1632: 1, os: 1, custom: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0x0F90, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F91, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F92, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F93, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F94, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F95, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F96, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F97, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F98, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F99, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F9A, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F9B, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F9C, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F9D, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F9E, e: 1, custom: 1, },
|
||||
{ opcode: 0x0F9F, e: 1, custom: 1, },
|
||||
|
||||
{ opcode: 0x0FA0, os: 1, custom: 1, },
|
||||
{ opcode: 0x0FA1, os: 1, block_boundary: 1, skip: 1, }, // pop fs: block_boundary since it uses non-raising cpu exceptions
|
||||
|
||||
{ opcode: 0x0FA2, skip: 1, },
|
||||
|
||||
{ opcode: 0x0FA8, os: 1, custom: 1, },
|
||||
{ opcode: 0x0FA9, os: 1, block_boundary: 1, skip: 1, }, // pop gs
|
||||
|
||||
{ opcode: 0x0FA3, os: 1, e: 1, custom: 1, skip_mem: 1 }, // bt (can also index memory, but not supported by test right now)
|
||||
{ opcode: 0x0FAB, os: 1, e: 1, custom: 1, skip_mem: 1 },
|
||||
{ opcode: 0x0FB3, os: 1, e: 1, custom: 1, skip_mem: 1 },
|
||||
{ opcode: 0x0FBB, os: 1, e: 1, custom: 1, skip_mem: 1 },
|
||||
|
||||
{ opcode: 0x0FBA, os: 1, e: 1, fixed_g: 4, imm8: 1, custom: 1 }, // bt
|
||||
{ opcode: 0x0FBA, os: 1, e: 1, fixed_g: 5, imm8: 1, custom: 1 },
|
||||
{ opcode: 0x0FBA, os: 1, e: 1, fixed_g: 6, imm8: 1, custom: 1 },
|
||||
{ opcode: 0x0FBA, os: 1, e: 1, fixed_g: 7, imm8: 1, custom: 1 },
|
||||
|
||||
{ opcode: 0x0FBC, os: 1, e: 1, mask_flags: af, custom: 1 }, // bsf
|
||||
{ opcode: 0x0FBD, os: 1, e: 1, mask_flags: af, custom: 1 },
|
||||
|
||||
// note: overflow flag only undefined if shift is > 1
|
||||
{ opcode: 0x0FA4, os: 1, e: 1, custom: 1, imm8: 1, mask_flags: af | of, }, // shld
|
||||
{ opcode: 0x0FA5, os: 1, e: 1, custom: 1, mask_flags: af | of, },
|
||||
{ opcode: 0x0FAC, os: 1, e: 1, custom: 1, imm8: 1, mask_flags: af | of, },
|
||||
{ opcode: 0x0FAD, os: 1, e: 1, custom: 1, mask_flags: af | of, },
|
||||
|
||||
{ opcode: 0x0FA6, skip: 1, block_boundary: 1, }, // ud
|
||||
{ opcode: 0x0FA7, skip: 1, block_boundary: 1, }, // ud
|
||||
|
||||
{ opcode: 0x0FAA, skip: 1 },
|
||||
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 0, reg_ud: 1, task_switch_test: 1, skip: 1, block_boundary: 1, }, // fxsave
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 1, reg_ud: 1, task_switch_test: 1, skip: 1, block_boundary: 1, }, // fxrstor
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 2, reg_ud: 1, sse: 1, skip: 1, block_boundary: 1, }, // ldmxcsr
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 3, reg_ud: 1, sse: 1, skip: 1, block_boundary: 1, }, // stmxcsr
|
||||
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 4, reg_ud: 1, skip: 1, block_boundary: 1, }, // xsave (mem, not implemented)
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 5, skip: 1, custom: 1 }, // lfence (reg, only 0), xrstor (mem, not implemented)
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 6, skip: 1, block_boundary: 1, }, // mfence (reg, only 0), xsaveopt (mem, not implemented)
|
||||
{ opcode: 0x0FAE, e: 1, fixed_g: 7, skip: 1, block_boundary: 1, }, // sfence (reg, only 0), clflush (mem)
|
||||
|
||||
{ opcode: 0x0FAF, os: 1, e: 1, mask_flags: af | zf, custom: 1, }, // imul
|
||||
|
||||
{ opcode: 0x0FB0, e: 1 }, // cmxchg
|
||||
{ opcode: 0x0FB1, os: 1, e: 1, custom: 1 },
|
||||
{ opcode: 0x0FC7, e: 1, fixed_g: 1, os: 1, reg_ud: 1, custom: 1 }, // cmpxchg8b (memory)
|
||||
{ opcode: 0x0FC7, e: 1, fixed_g: 6, os: 1, mem_ud: 1, skip: 1, }, // rdrand
|
||||
|
||||
{ opcode: 0x0FB2, block_boundary: 1, os: 1, e: 1, skip: 1, }, // lss
|
||||
{ opcode: 0x0FB4, block_boundary: 1, os: 1, e: 1, skip: 1, }, // lfs
|
||||
{ opcode: 0x0FB5, block_boundary: 1, os: 1, e: 1, skip: 1, }, // lgs
|
||||
|
||||
{ opcode: 0x0FB6, os: 1, e: 1, custom: 1 }, // movzx
|
||||
{ opcode: 0x0FB7, os: 1, e: 1, custom: 1 },
|
||||
|
||||
{ opcode: 0xF30FB8, os: 1, e: 1, custom: 1 }, // popcnt
|
||||
{ opcode: 0x0FB8, os: 1, e: 1, block_boundary: 1, }, // ud
|
||||
|
||||
{ opcode: 0x0FB9, block_boundary: 1, }, // ud2
|
||||
|
||||
{ opcode: 0x0FBE, os: 1, e: 1, custom: 1 }, // movsx
|
||||
{ opcode: 0x0FBF, os: 1, e: 1, custom: 1 },
|
||||
|
||||
{ opcode: 0x0FC0, e: 1, }, // xadd
|
||||
{ opcode: 0x0FC1, os: 1, e: 1, custom: 1 },
|
||||
|
||||
{ opcode: 0x0FC8, custom: 1 }, // bswap
|
||||
{ opcode: 0x0FC9, custom: 1 },
|
||||
{ opcode: 0x0FCA, custom: 1 },
|
||||
{ opcode: 0x0FCB, custom: 1 },
|
||||
{ opcode: 0x0FCC, custom: 1 },
|
||||
{ opcode: 0x0FCD, custom: 1 },
|
||||
{ opcode: 0x0FCE, custom: 1 },
|
||||
{ opcode: 0x0FCF, custom: 1 },
|
||||
|
||||
|
||||
// mmx, sse
|
||||
// - skipped or missing are sse3+
|
||||
|
||||
{ sse: 1, opcode: 0x0F10, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F10, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F10, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF20F10, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F11, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F11, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F11, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF20F11, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F12, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F12, reg_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0xF20F12, e: 1, skip: 1, block_boundary: 1, }, // sse3
|
||||
{ sse: 1, opcode: 0xF30F12, e: 1, skip: 1, block_boundary: 1, }, // sse3
|
||||
{ sse: 1, opcode: 0x0F13, reg_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F13, reg_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F14, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F14, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F15, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F15, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F16, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F16, reg_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0xF30F16, skip: 1, e: 1, block_boundary: 1, }, // sse3
|
||||
{ sse: 1, opcode: 0x0F17, reg_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F17, reg_ud: 1, e: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F28, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F28, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F29, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F29, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F2A, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F2A, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F2A, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F2A, e: 1, },
|
||||
{ sse: 1, opcode: 0x0F2B, reg_ud: 1, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F2B, reg_ud: 1, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F2C, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F2C, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F2C, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F2C, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F2D, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F2D, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F2D, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F2D, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F2E, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F2E, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F2F, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F2F, e: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F50, mem_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F50, mem_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F51, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F51, e: 1 },
|
||||
{ sse: 1, opcode: 0xF20F51, e: 1 },
|
||||
{ sse: 1, opcode: 0xF30F51, e: 1 },
|
||||
|
||||
// approximation of 1/sqrt(x). Skipped because our approximation doesn't match intel's
|
||||
{ sse: 1, opcode: 0x0F52, e: 1, skip: 1, },
|
||||
{ sse: 1, opcode: 0xF30F52, e: 1, skip: 1, },
|
||||
|
||||
// reciprocal: approximation of 1/x. Skipped because our approximation doesn't match intel's
|
||||
{ sse: 1, opcode: 0x0F53, e: 1, skip: 1, },
|
||||
{ sse: 1, opcode: 0xF30F53, e: 1, skip: 1, },
|
||||
|
||||
{ sse: 1, opcode: 0x0F54, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F54, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F55, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F55, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F56, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F56, e: 1 },
|
||||
{ sse: 1, opcode: 0x0F57, e: 1 },
|
||||
{ sse: 1, opcode: 0x660F57, e: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F58, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F58, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F58, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F58, e: 1, },
|
||||
{ sse: 1, opcode: 0x0F59, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F59, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F59, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F59, e: 1, },
|
||||
|
||||
{ sse: 1, opcode: 0x0F5A, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F5A, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F5A, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F5A, e: 1, },
|
||||
{ sse: 1, opcode: 0x0F5B, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F5B, e: 1, },
|
||||
// no F2 variant
|
||||
{ sse: 1, opcode: 0xF30F5B, e: 1, },
|
||||
|
||||
{ sse: 1, opcode: 0x0F5C, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F5C, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F5C, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F5C, e: 1, },
|
||||
{ sse: 1, opcode: 0x0F5D, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F5D, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F5D, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F5D, e: 1, },
|
||||
|
||||
{ sse: 1, opcode: 0x0F5E, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F5E, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F5E, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F5E, e: 1, },
|
||||
{ sse: 1, opcode: 0x0F5F, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F5F, e: 1, },
|
||||
{ sse: 1, opcode: 0xF20F5F, e: 1, },
|
||||
{ sse: 1, opcode: 0xF30F5F, e: 1, },
|
||||
|
||||
{ sse: 1, opcode: 0x660F60, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F60, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F61, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F61, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F62, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F62, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F63, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F63, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F64, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F64, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F65, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F65, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F66, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F66, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F67, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F67, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x660F68, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F68, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F69, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F69, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F6A, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F6A, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F6B, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F6B, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F6C, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F6C, e: 1, block_boundary: 1, }, // ud
|
||||
{ sse: 1, opcode: 0x660F6D, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F6D, e: 1, block_boundary: 1, }, // ud
|
||||
{ sse: 1, opcode: 0x660F6E, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F6E, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F6F, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F6F, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F6F, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F70, e: 1, imm8: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F70, e: 1, imm8: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF20F70, e: 1, imm8: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F70, e: 1, imm8: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F71, e: 1, fixed_g: 2, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F71, e: 1, fixed_g: 2, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F71, e: 1, fixed_g: 4, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F71, e: 1, fixed_g: 4, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F71, e: 1, fixed_g: 6, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F71, e: 1, fixed_g: 6, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F72, e: 1, fixed_g: 2, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F72, e: 1, fixed_g: 2, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F72, e: 1, fixed_g: 4, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F72, e: 1, fixed_g: 4, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F72, e: 1, fixed_g: 6, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F72, e: 1, fixed_g: 6, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F73, e: 1, fixed_g: 2, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F73, e: 1, fixed_g: 2, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F73, e: 1, fixed_g: 3, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F73, e: 1, fixed_g: 6, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F73, e: 1, fixed_g: 6, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F73, e: 1, fixed_g: 7, imm8: 1, mem_ud: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0F74, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F74, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F75, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F75, e: 1, },
|
||||
{ sse: 1, opcode: 0x0F76, e: 1, },
|
||||
{ sse: 1, opcode: 0x660F76, e: 1, },
|
||||
{ sse: 1, opcode: 0x0F77, skip: 1 }, // emms (skip as it breaks gdb printing of float registers)
|
||||
|
||||
// vmx instructions
|
||||
{ opcode: 0x0F78, skip: 1, block_boundary: 1, },
|
||||
{ opcode: 0x0F79, skip: 1, block_boundary: 1, },
|
||||
|
||||
{ opcode: 0x0F7A, skip: 1, block_boundary: 1, }, // ud
|
||||
{ opcode: 0x0F7B, skip: 1, block_boundary: 1, }, // ud
|
||||
{ sse: 1, opcode: 0x0F7C, skip: 1, block_boundary: 1, }, // sse3
|
||||
{ sse: 1, opcode: 0x0F7D, skip: 1, block_boundary: 1, }, // sse3
|
||||
|
||||
{ sse: 1, opcode: 0x0F7E, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F7E, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F7E, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0F7F, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660F7F, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30F7F, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0FC2, e: 1, imm8: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FC2, e: 1, imm8: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF20FC2, e: 1, imm8: 1 },
|
||||
{ sse: 1, opcode: 0xF30FC2, e: 1, imm8: 1 },
|
||||
|
||||
{ opcode: 0x0FC3, e: 1, custom: 1, reg_ud: 1, }, // movnti: Uses normal registers, hence not marked as sse
|
||||
|
||||
{ sse: 1, opcode: 0x0FC4, e: 1, imm8: 1 },
|
||||
{ sse: 1, opcode: 0x660FC4, e: 1, imm8: 1 },
|
||||
{ sse: 1, opcode: 0x0FC5, e: 1, mem_ud: 1, imm8: 1 },
|
||||
{ sse: 1, opcode: 0x660FC5, e: 1, mem_ud: 1, imm8: 1, },
|
||||
|
||||
{ sse: 1, opcode: 0x0FC6, e: 1, imm8: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FC6, e: 1, imm8: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0FD0, skip: 1, block_boundary: 1, }, // sse3
|
||||
|
||||
{ sse: 1, opcode: 0x0FD1, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD1, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FD2, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD2, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FD3, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD3, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FD4, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD4, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FD5, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD5, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x660FD6, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF20FD6, mem_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0xF30FD6, mem_ud: 1, e: 1 },
|
||||
{ sse: 1, opcode: 0x0FD6, e: 1, block_boundary: 1, }, // ud
|
||||
|
||||
{ sse: 1, opcode: 0x0FD7, e: 1, mem_ud: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD7, e: 1, mem_ud: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0FD8, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD8, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FD9, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FD9, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FDA, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FDA, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FDB, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FDB, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FDC, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FDC, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FDD, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FDD, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FDE, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FDE, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FDF, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FDF, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0FE0, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE0, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FE1, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE1, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FE2, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE2, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FE3, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE3, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FE4, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE4, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FE5, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE5, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x660FE6, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF20FE6, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0xF30FE6, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FE6, e: 1, block_boundary: 1, }, // ud
|
||||
{ sse: 1, opcode: 0x0FE7, e: 1, reg_ud: 1 },
|
||||
{ sse: 1, opcode: 0x660FE7, e: 1, reg_ud: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0FE8, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE8, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FE9, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FE9, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FEA, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FEA, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FEB, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FEB, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FEC, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FEC, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FED, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FED, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FEE, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FEE, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FEF, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FEF, e: 1, custom: 1 },
|
||||
|
||||
{ sse: 1, opcode: 0x0FF0, skip: 1, block_boundary: 1, }, // sse3
|
||||
|
||||
{ sse: 1, opcode: 0x0FF1, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF1, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FF2, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF2, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FF3, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF3, e: 1, custom: 1, },
|
||||
{ sse: 1, opcode: 0x0FF4, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF4, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FF5, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF5, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FF6, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF6, e: 1, custom: 1 },
|
||||
// maskmovq (0FF7), maskmovdqu (660FF7) tested manually
|
||||
// Generated tests don't setup EDI as required (yet)
|
||||
{ sse: 1, opcode: 0x0FF7, mem_ud: 1, e: 1, custom: 1, skip: 1, },
|
||||
{ sse: 1, opcode: 0x660FF7, mem_ud: 1, e: 1, custom: 1, skip: 1, },
|
||||
|
||||
{ sse: 1, opcode: 0x0FF8, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF8, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FF9, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FF9, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FFA, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FFA, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FFB, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FFB, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FFC, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FFC, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FFD, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FFD, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x0FFE, e: 1, custom: 1 },
|
||||
{ sse: 1, opcode: 0x660FFE, e: 1, custom: 1 },
|
||||
|
||||
{ opcode: 0x0FFF, block_boundary: 1, }, // ud
|
||||
];
|
||||
|
||||
for(let i = 0; i < 8; i++)
|
||||
{
|
||||
encodings.push.apply(encodings, [
|
||||
{ opcode: 0x04 | i << 3, custom: 1, imm8: 1, },
|
||||
{ opcode: 0x05 | i << 3, custom: 1, os: 1, imm1632: 1, },
|
||||
|
||||
{ opcode: 0x70 | i, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, os: 1, imm8s: 1, custom: 1, skip: 1, },
|
||||
{ opcode: 0x78 | i, block_boundary: 1, no_block_boundary_in_interpreted: 1, jump_offset_imm: 1, conditional_jump: 1, os: 1, imm8s: 1, custom: 1, skip: 1, },
|
||||
|
||||
{ opcode: 0x80, e: 1, fixed_g: i, imm8: 1, custom: 1, },
|
||||
{ opcode: 0x81, os: 1, e: 1, fixed_g: i, imm1632: 1, custom: 1, },
|
||||
{ opcode: 0x82, e: 1, fixed_g: i, imm8: 1, custom: 1, },
|
||||
{ opcode: 0x83, os: 1, e: 1, fixed_g: i, imm8s: 1, custom: 1, },
|
||||
|
||||
{ opcode: 0xB0 | i, custom: 1, imm8: 1, },
|
||||
{ opcode: 0xB8 | i, custom: 1, os: 1, imm1632: 1, },
|
||||
|
||||
// note: overflow flag only undefined if shift is > 1
|
||||
// note: the adjust flag is undefined for shifts > 0 and unaffected by rotates
|
||||
{ opcode: 0xC0, e: 1, fixed_g: i, imm8: 1, mask_flags: of | af, custom: 1, },
|
||||
{ opcode: 0xC1, os: 1, e: 1, fixed_g: i, imm8: 1, mask_flags: of | af, custom: 1, },
|
||||
{ opcode: 0xD0, e: 1, fixed_g: i, mask_flags: af, custom: 1 },
|
||||
{ opcode: 0xD1, os: 1, e: 1, fixed_g: i, mask_flags: af, custom: 1, },
|
||||
{ opcode: 0xD2, e: 1, fixed_g: i, mask_flags: of | af, custom: 1 },
|
||||
{ opcode: 0xD3, os: 1, e: 1, fixed_g: i, mask_flags: of | af, custom: 1, },
|
||||
]);
|
||||
}
|
||||
|
||||
encodings.sort((e1, e2) => {
|
||||
let o1 = (e1.opcode & 0xFF00) === 0x0F00 ? e1.opcode & 0xFFFF : e1.opcode & 0xFF;
|
||||
let o2 = (e2.opcode & 0xFF00) === 0x0F00 ? e2.opcode & 0xFFFF : e2.opcode & 0xFF;
|
||||
return o1 - o2 || e1.fixed_g - e2.fixed_g;
|
||||
});
|
||||
|
||||
module.exports = Object.freeze(encodings.map(entry => Object.freeze(entry)));
|
139
index.html
139
index.html
|
@ -3,97 +3,133 @@
|
|||
|
||||
<title>Virtual x86</title>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<meta name="description" content="Run KolibriOS, Linux or Windows 98 in your browser">
|
||||
|
||||
<script src="build/v86_all.js"></script>
|
||||
<link rel="stylesheet" href="v86.css">
|
||||
|
||||
<div>
|
||||
<div id="boot_options">
|
||||
<h4>Quickstart</h4>
|
||||
<input type="button" value="ReactOS (32 MB)" id="start_reactos">
|
||||
- Restored from snapshot<br>
|
||||
<input type="button" value="Windows 95 (6.7 MB)" id="start_windows95">
|
||||
- Restored from snapshot<br>
|
||||
<input type="button" value="FreeBSD 10.2 (13.0 MB)" id="start_freebsd">
|
||||
- Restored from snapshot<br>
|
||||
<input type="button" value="Oberon (16.0 MB)" id="start_oberon">
|
||||
- Native Oberon 2.3.6 (<a href="https://lists.inf.ethz.ch/pipermail/oberon/2013/006844.html">via</a>)<br>
|
||||
<input type="button" value="Windows 98 (12.0 MB)" id="start_windows98">
|
||||
- Including Minesweeper and audio, additional sectors are loaded as needed<br>
|
||||
<input type="button" value="Arch Linux (10.1 MB)" id="start_archlinux">
|
||||
- A complete Arch Linux restored from a snapshot, additional files are loaded as needed<br>
|
||||
<input type="button" value="KolibriOS (1.4 MB)" id="start_kolibrios">
|
||||
- Graphical OS, takes about 60 seconds to boot<br>
|
||||
<input type="button" value="Linux 2.6 (5.4 MB)" id="start_linux26">
|
||||
- With busybox, Lua interpreter and test cases, takes about 20 seconds to boot<br>
|
||||
<input type="button" value="Linux 3.18 (8.3 MB)" id="start_linux3">
|
||||
- With internet access, telnet, ping, wget and links. Takes about 60 seconds to boot. Run <code>udhcpc</code> for networking. Exchange files through <code>/mnt/</code>.<br>
|
||||
<input type="button" value="Windows 1.01 (1.4 MB)" id="start_windows1">
|
||||
- Takes 1 second to boot<br>
|
||||
<input type="button" value="MS-DOS 6.22 (3.4 MB)" id="start_msdos">
|
||||
- Takes 10 seconds to boot. With Enhanced Tools, QBasic and everything from the FreeDOS image<br>
|
||||
<input type="button" value="FreeDOS (0.7 MB)" id="start_freedos">
|
||||
- With nasm, vim, debug.com, some games and demos, takes 1 second to boot<br>
|
||||
<input type="button" value="OpenBSD (1.4 MB)" id="start_openbsd">
|
||||
- Random boot floppy, takes about 60 seconds<br>
|
||||
<input type="button" value="Solar OS (1.4 MB)" id="start_solos">
|
||||
- Simple graphical OS<br>
|
||||
<input type="button" value="Bootchess (0.2 MB)" id="start_bootchess">
|
||||
- A tiny chess program written in the boot sector
|
||||
<h4>Select profile</h4>
|
||||
<table id="oses">
|
||||
<tr id="start_archlinux"><td><a href="?profile=archlinux">Arch Linux</a> <small>12 MB</small></td><td>
|
||||
A complete Arch Linux restored from a snapshot, additional files are loaded as needed</td></tr>
|
||||
<tr id="start_dsl"><td><a href="?profile=dsl">Damn Small Linux</a> <small>50 MB</small></td><td>
|
||||
Graphical Linux with 2.4 kernel, Firefox 2.0 and more. Takes 1 minute to boot.</td></tr>
|
||||
<tr id="start_buildroot"><td><a href="?profile=buildroot">Buildroot Linux</a> <small>5.0 MB</small></td><td>
|
||||
Minimal Linux with busybox, Lua, tests, internet access, ping, telnet and curl. Exchange files through <code>/mnt/</code>.</td></tr>
|
||||
|
||||
<tr id="start_reactos"><td><a href="?profile=reactos">ReactOS</a> <small>18 MB</small></td><td>
|
||||
Windows-compatible OS. Restored from snapshot</td></tr>
|
||||
<tr id="start_windows98"><td><a href="?profile=windows98">Windows 98</a> <small>9.7 MB</small></td><td>
|
||||
Including Minesweeper and Internet Explorer with internet access. Additional sectors are loaded as needed.</td></tr>
|
||||
<tr id="start_windows95"><td><a href="?profile=windows95">Windows 95</a> <small>4.6 MB</small></td><td>
|
||||
Restored from snapshot</td></tr>
|
||||
<tr id="start_windows1"><td><a href="?profile=windows1">Windows 1.01</a> <small>0.6 MB</small></td><td>
|
||||
Takes 1 second to boot</td></tr>
|
||||
<tr id="start_msdos"><td><a href="?profile=msdos">MS-DOS 6.22</a> <small>4.4 MB</small></td><td>
|
||||
With Enhanced Tools, QBasic, vim, games and demos.</td></tr>
|
||||
<tr id="start_freedos"><td><a href="?profile=freedos">FreeDOS</a> <small>0.5 MB</small></td><td>
|
||||
With nasm, vim, debug.com, Rogue, some games and demos.</td></tr>
|
||||
|
||||
<tr id="start_freebsd"><td><a href="?profile=freebsd">FreeBSD</a> <small>17 MB</small></td><td>
|
||||
FreeBSD 12.0 base install. Restored from snapshot.</td></tr>
|
||||
<tr id="start_openbsd"><td><a href="?profile=openbsd">OpenBSD</a> <small>12 MB</small></td><td>
|
||||
OpenBSD 6.6 base install. Restored from snapshot.</td></tr>
|
||||
<tr id="start_9front"><td><a href="?profile=9front">9front</a> <small>4.4 MB</small></td><td>
|
||||
A Plan 9 fork.</td></tr>
|
||||
<tr id="start_haiku"><td><a href="?profile=haiku">Haiku</a> <small>46 MB</small></td><td>
|
||||
Restored from snapshot. Includes network support.</td></tr>
|
||||
|
||||
<tr id="start_oberon"><td><a href="?profile=oberon">Oberon</a> <small>1.2 MB</small></td><td>
|
||||
Native Oberon 2.3.6</td></tr>
|
||||
<tr id="start_kolibrios"><td><a href="?profile=kolibrios">KolibriOS</a> <small>1.4 MB</small></td><td>
|
||||
Fast graphical OS written in Assembly</td></tr>
|
||||
<tr id="start_qnx"><td><a href="?profile=qnx">QNX</a> <small>1.3 MB</small></td><td>
|
||||
QNX 4.05 Demo disk (no networking)</td></tr>
|
||||
<tr id="start_solos"><td><a href="?profile=solos">Solar OS</a> <small>0.3 MB</small></td><td>
|
||||
Simple graphical OS</td></tr>
|
||||
<tr id="start_bootchess"><td><a href="?profile=bootchess">Bootchess</a> <small>0.1 MB</small></td><td>
|
||||
A tiny chess program written in the boot sector</td></tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
<hr>
|
||||
<h4>Setup</h4>
|
||||
<table>
|
||||
<tr>
|
||||
<td width="350">CD image</td>
|
||||
<td width="350"><label for="cd_image">CD image</label></td>
|
||||
<td>
|
||||
<input type="file" id="cd_image">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Floppy disk image</td>
|
||||
<td><label for="floppy_image">Floppy disk image</label></td>
|
||||
<td> <input type="file" id="floppy_image"><br></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Hard drive disk image</td>
|
||||
<td><label for="hda_image">Hard drive disk image</label></td>
|
||||
<td><input type="file" id="hda_image"><br></td>
|
||||
</tr>
|
||||
|
||||
<!--
|
||||
<tr>
|
||||
<td>Multiboot kernel image (experimental)</td>
|
||||
<td><label for="multiboot_image">Multiboot kernel image (experimental)</td>
|
||||
<td><input type="file" id="multiboot_image"><br></td>
|
||||
</tr>
|
||||
-->
|
||||
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><small><small>Disk images are not uploaded to the server</small></small><hr></td>
|
||||
<td colspan="2"><small>Disk images are not uploaded to the server</small><hr></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Memory size</td>
|
||||
<td><label for="memory_size">Memory size</label></td>
|
||||
<td>
|
||||
<input id="memory_size" type="number" value="128" min="16" max="2048" step="16"> MB<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Video Memory size</td>
|
||||
<td><label for="video_memory_size">Video Memory size</label></td>
|
||||
<td>
|
||||
<input id="video_memory_size" type="number" value="8" min="1" max="128" step="1"> MB<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><label for="networking_proxy">Networking proxy (leave blank to disable)</label></td>
|
||||
<td>
|
||||
<input id="networking_proxy" type="text" value="wss://relay.widgetry.org/">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><hr></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Boot order</td>
|
||||
<td><label for="disable_audio">Disable audio</label></td>
|
||||
<td>
|
||||
<input id="disable_audio" type="checkbox"><br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><label for="enable_acpi">Enable ACPI (experimental)</label></td>
|
||||
<td>
|
||||
<input id="enable_acpi" type="checkbox"><br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><hr></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><label for="boot_order">Boot order</label></td>
|
||||
<td>
|
||||
<select id="boot_order">
|
||||
<option value="213">CD / Floppy / Hard Disk</option>
|
||||
|
@ -152,7 +188,6 @@
|
|||
<pre style="display: none" id="loading"></pre>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="screen_container" style="display: none">
|
||||
<div id="screen"></div>
|
||||
<canvas id="vga"></canvas>
|
||||
|
@ -164,8 +199,8 @@
|
|||
|
||||
<div id="runtime_infos" style="display: none">
|
||||
Running: <span id="running_time">0s</span> <br>
|
||||
Speed: <span id="speed">0</span>kIPS<br>
|
||||
Avg speed: <span id="avg_speed">0</span>kIPS<br>
|
||||
Speed: <span id="speed">0</span> mIPS<br>
|
||||
Avg speed: <span id="avg_speed">0</span> mIPS<br>
|
||||
<br>
|
||||
<div id="info_storage" style="display: none">
|
||||
<b>IDE device (HDA or CDROM)</b><br>
|
||||
|
@ -180,7 +215,7 @@
|
|||
<b>9p Filesystem</b><br>
|
||||
Bytes read: <span id="info_filesystem_bytes_read">0</span><br>
|
||||
Bytes written: <span id="info_filesystem_bytes_written">0</span><br>
|
||||
Last file: <span id="info_filesystem_last_file" style="word-wrap: break-word"></span><br>
|
||||
<div style="white-space: nowrap; overflow-x: hidden">Last file: <span id="info_filesystem_last_file"></span></div>
|
||||
Status: <span id="info_filesystem_status"></span><br>
|
||||
<br>
|
||||
</div>
|
||||
|
@ -196,13 +231,12 @@
|
|||
BPP: <span id="info_bpp">-</span><br>
|
||||
<br>
|
||||
Mouse: <span id="info_mouse_enabled">No</span><br>
|
||||
<!-- Keyboard: <span id="info_keyboard_enabled">-</span><br> -->
|
||||
|
||||
<div id="description" style="display: none"></div>
|
||||
<div id="description" style="display: none"><br></div>
|
||||
</div>
|
||||
|
||||
<div id="filesystem_panel" style="display: none">
|
||||
<label title="Files will appear in / of the 9p filesystem">
|
||||
<label>
|
||||
Send files to emulator<br>
|
||||
<input type="file" id="filesystem_send_file" multiple>
|
||||
</label>
|
||||
|
@ -215,13 +249,10 @@
|
|||
|
||||
<br style="clear: both"><br>
|
||||
|
||||
<textarea cols="40" rows="12" id="serial" style="display: none">This is the serial console. Whatever you type or paste here will be sent to COM1.
|
||||
|
||||
In Linux it can be accessed with `cat /dev/ttyS0`
|
||||
</textarea>
|
||||
<div id="terminal"></div>
|
||||
|
||||
<br style="clear: both">
|
||||
<code>Version: <a href="https://github.com/copy/v86/commits/53caefc">53caefc</a> (Jan 22, 2016 23:01)</a></code>
|
||||
<code>Version: <a href="https://github.com/copy/v86/commits/b40c8ad2">b40c8ad2</a> (Apr 23, 2020 01:04)</code>
|
||||
|
||||
<hr>
|
||||
<a href="debug.html">Enable debug</a>
|
||||
|
@ -231,7 +262,3 @@ In Linux it can be accessed with `cat /dev/ttyS0`
|
|||
<a href="https://github.com/copy/v86">Project on Github</a>
|
||||
—
|
||||
<a href="https://github.com/copy/v86#compatibility">Compatibility</a>
|
||||
—
|
||||
<a href="./screenshots/">Screenshots</a>
|
||||
|
||||
|
||||
|
|
504
lib/9p.js
504
lib/9p.js
|
@ -6,17 +6,21 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Feature bit (bit position) for mount tag.
|
||||
const VIRTIO_9P_F_MOUNT_TAG = 0;
|
||||
// Assumed max tag length in bytes.
|
||||
const VIRTIO_9P_MAX_TAGLEN = 254;
|
||||
|
||||
// TODO
|
||||
// flush
|
||||
// lock?
|
||||
// correct hard links
|
||||
|
||||
var EPERM = 1; /* Operation not permitted */
|
||||
var ENOENT = 2; /* No such file or directory */
|
||||
var EEXIST = 17; /* File exists */
|
||||
var EINVAL = 22; /* Invalid argument */
|
||||
var ENOTSUPP = 524; /* Operation is not supported */
|
||||
var EOPNOTSUPP = 95; /* Operation is not supported */
|
||||
var ENOTEMPTY = 39; /* Directory not empty */
|
||||
var EPROTO = 71 /* Protocol error */
|
||||
var EPROTO = 71; /* Protocol error */
|
||||
|
||||
var P9_SETATTR_MODE = 0x00000001;
|
||||
var P9_SETATTR_UID = 0x00000002;
|
||||
|
@ -43,6 +47,19 @@ var P9_STAT_MODE_SETUID = 0x00080000;
|
|||
var P9_STAT_MODE_SETGID = 0x00040000;
|
||||
var P9_STAT_MODE_SETVTX = 0x00010000;
|
||||
|
||||
const P9_LOCK_TYPE_RDLCK = 0;
|
||||
const P9_LOCK_TYPE_WRLCK = 1;
|
||||
const P9_LOCK_TYPE_UNLCK = 2;
|
||||
const P9_LOCK_TYPES = Object.freeze(["shared", "exclusive", "unlock"]);
|
||||
|
||||
const P9_LOCK_FLAGS_BLOCK = 1;
|
||||
const P9_LOCK_FLAGS_RECLAIM = 2;
|
||||
|
||||
const P9_LOCK_SUCCESS = 0;
|
||||
const P9_LOCK_BLOCKED = 1;
|
||||
const P9_LOCK_ERROR = 2;
|
||||
const P9_LOCK_GRACE = 3;
|
||||
|
||||
var FID_NONE = -1;
|
||||
var FID_INODE = 1;
|
||||
var FID_XATTR = 2;
|
||||
|
@ -51,21 +68,19 @@ var FID_XATTR = 2;
|
|||
* @constructor
|
||||
*
|
||||
* @param {FS} filesystem
|
||||
* @param {CPU} cpu
|
||||
*/
|
||||
function Virtio9p(filesystem, bus) {
|
||||
/** @const @type {FS} */
|
||||
function Virtio9p(filesystem, cpu, bus) {
|
||||
/** @type {FS} */
|
||||
this.fs = filesystem;
|
||||
|
||||
/** @const @type {BusConnector} */
|
||||
this.bus = bus;
|
||||
|
||||
this.SendReply = function(x, y) {};
|
||||
this.deviceid = 0x9; // 9p filesystem
|
||||
this.hostfeature = 0x1; // mountpoint
|
||||
//this.configspace = [0x0, 0x4, 0x68, 0x6F, 0x73, 0x74]; // length of string and "host" string
|
||||
//this.configspace = [0x0, 0x9, 0x2F, 0x64, 0x65, 0x76, 0x2F, 0x72, 0x6F, 0x6F, 0x74 ]; // length of string and "/dev/root" string
|
||||
|
||||
this.configspace = new Uint8Array([0x6, 0x0, 0x68, 0x6F, 0x73, 0x74, 0x39, 0x70]); // length of string and "host9p" string
|
||||
this.configspace_tagname = [0x68, 0x6F, 0x73, 0x74, 0x39, 0x70]; // "host9p" string
|
||||
this.configspace_taglen = this.configspace_tagname.length; // num bytes
|
||||
this.VERSION = "9P2000.L";
|
||||
this.BLOCKSIZE = 8192; // Let's define one page.
|
||||
this.msize = 8192; // maximum message size
|
||||
|
@ -73,48 +88,145 @@ function Virtio9p(filesystem, bus) {
|
|||
this.replybuffersize = 0;
|
||||
|
||||
this.fids = [];
|
||||
|
||||
/** @type {VirtIO} */
|
||||
this.virtio = new VirtIO(cpu,
|
||||
{
|
||||
name: "virtio-9p",
|
||||
pci_id: 0x06 << 3,
|
||||
device_id: 0x1049,
|
||||
subsystem_device_id: 9,
|
||||
common:
|
||||
{
|
||||
initial_port: 0xA800,
|
||||
queues:
|
||||
[
|
||||
{
|
||||
size_supported: 32,
|
||||
notify_offset: 0,
|
||||
},
|
||||
],
|
||||
features:
|
||||
[
|
||||
VIRTIO_9P_F_MOUNT_TAG,
|
||||
VIRTIO_F_VERSION_1,
|
||||
VIRTIO_F_RING_EVENT_IDX,
|
||||
VIRTIO_F_RING_INDIRECT_DESC,
|
||||
],
|
||||
on_driver_ok: () => {},
|
||||
},
|
||||
notification:
|
||||
{
|
||||
initial_port: 0xA900,
|
||||
single_handler: false,
|
||||
handlers:
|
||||
[
|
||||
(queue_id) =>
|
||||
{
|
||||
if(queue_id !== 0)
|
||||
{
|
||||
dbg_assert(false, "Virtio9P Notified for non-existent queue: " + queue_id +
|
||||
" (expected queue_id of 0)");
|
||||
return;
|
||||
}
|
||||
while(this.virtqueue.has_request())
|
||||
{
|
||||
const bufchain = this.virtqueue.pop_request();
|
||||
this.ReceiveRequest(bufchain);
|
||||
}
|
||||
this.virtqueue.notify_me_after(0);
|
||||
// Don't flush replies here: async replies are not completed yet.
|
||||
},
|
||||
],
|
||||
},
|
||||
isr_status:
|
||||
{
|
||||
initial_port: 0xA700,
|
||||
},
|
||||
device_specific:
|
||||
{
|
||||
initial_port: 0xA600,
|
||||
struct:
|
||||
[
|
||||
{
|
||||
bytes: 2,
|
||||
name: "mount tag length",
|
||||
read: () => this.configspace_taglen,
|
||||
write: data => { /* read only */ },
|
||||
},
|
||||
].concat(v86util.range(VIRTIO_9P_MAX_TAGLEN).map(index =>
|
||||
({
|
||||
bytes: 1,
|
||||
name: "mount tag name " + index,
|
||||
// Note: configspace_tagname may have changed after set_state
|
||||
read: () => this.configspace_tagname[index] || 0,
|
||||
write: data => { /* read only */ },
|
||||
})
|
||||
)),
|
||||
},
|
||||
});
|
||||
this.virtqueue = this.virtio.queues[0];
|
||||
}
|
||||
|
||||
Virtio9p.prototype.get_state = function()
|
||||
{
|
||||
var state = [];
|
||||
|
||||
state[0] = this.deviceid;
|
||||
state[1] = this.hostfeature;
|
||||
state[2] = this.configspace;
|
||||
state[0] = this.configspace_tagname;
|
||||
state[1] = this.configspace_taglen;
|
||||
state[2] = this.virtio;
|
||||
state[3] = this.VERSION;
|
||||
state[4] = this.BLOCKSIZE;
|
||||
state[5] = this.msize;
|
||||
state[6] = this.replybuffer;
|
||||
state[7] = this.replybuffersize;
|
||||
state[8] = this.fids.map(function(f) { return [f.inodeid, f.type, f.uid] });
|
||||
state[8] = this.fids.map(function(f) { return [f.inodeid, f.type, f.uid, f.dbg_name]; });
|
||||
state[9] = this.fs;
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
Virtio9p.prototype.set_state = function(state)
|
||||
{
|
||||
this.deviceid = state[0];
|
||||
this.hostfeature = state[1];
|
||||
this.configspace = state[2];
|
||||
this.configspace_tagname = state[0];
|
||||
this.configspace_taglen = state[1];
|
||||
this.virtio.set_state(state[2]);
|
||||
this.virtqueue = this.virtio.queues[0];
|
||||
this.VERSION = state[3];
|
||||
this.BLOCKSIZE = state[4];
|
||||
this.msize = state[5];
|
||||
this.replybuffer = state[6];
|
||||
this.replybuffersize = state[7];
|
||||
this.fids = state[8].map(function(f) { return { inodeid: f[0], type: f[1], uid: f[2] } });
|
||||
this.fids = state[8].map(function(f)
|
||||
{
|
||||
return { inodeid: f[0], type: f[1], uid: f[2], dbg_name: f[3] };
|
||||
});
|
||||
this.fs.set_state(state[9]);
|
||||
};
|
||||
|
||||
Virtio9p.prototype.Createfid = function(inode, type, uid) {
|
||||
return {inodeid: inode, type: type, uid: uid};
|
||||
// Note: dbg_name is only used for debugging messages and may not be the same as the filename,
|
||||
// since it is not synchronised with renames done outside of 9p. Hard-links, linking and unlinking
|
||||
// operations also mean that having a single filename no longer makes sense.
|
||||
// Set TRACK_FILENAMES = true (in config.js) to sync dbg_name during 9p renames.
|
||||
Virtio9p.prototype.Createfid = function(inodeid, type, uid, dbg_name) {
|
||||
return {inodeid, type, uid, dbg_name};
|
||||
};
|
||||
|
||||
Virtio9p.prototype.update_dbg_name = function(idx, newname)
|
||||
{
|
||||
for(const fid of this.fids)
|
||||
{
|
||||
if(fid.inodeid === idx) fid.dbg_name = newname;
|
||||
}
|
||||
};
|
||||
|
||||
Virtio9p.prototype.Reset = function() {
|
||||
this.fids = [];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Virtio9p.prototype.BuildReply = function(id, tag, payloadsize) {
|
||||
dbg_assert(payloadsize >= 0, "9P: Negative payload size");
|
||||
marshall.Marshall(["w", "b", "h"], [payloadsize+7, id+1, tag], this.replybuffer, 0);
|
||||
if ((payloadsize+7) >= this.replybuffer.length) {
|
||||
message.Debug("Error in 9p: payloadsize exceeds maximum length");
|
||||
|
@ -123,16 +235,28 @@ Virtio9p.prototype.BuildReply = function(id, tag, payloadsize) {
|
|||
// this.replybuffer[7+i] = payload[i];
|
||||
this.replybuffersize = payloadsize+7;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
Virtio9p.prototype.SendError = function (tag, errormsg, errorcode) {
|
||||
//var size = marshall.Marshall(["s", "w"], [errormsg, errorcode], this.replybuffer, 7);
|
||||
var size = marshall.Marshall(["w"], [errorcode], this.replybuffer, 7);
|
||||
this.BuildReply(6, tag, size);
|
||||
}
|
||||
};
|
||||
|
||||
Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
||||
var header = marshall.Unmarshall2(["w", "b", "h"], GetByte);
|
||||
Virtio9p.prototype.SendReply = function (bufchain) {
|
||||
dbg_assert(this.replybuffersize >= 0, "9P: Negative replybuffersize");
|
||||
bufchain.set_next_blob(this.replybuffer.subarray(0, this.replybuffersize));
|
||||
this.virtqueue.push_reply(bufchain);
|
||||
this.virtqueue.flush_replies();
|
||||
};
|
||||
|
||||
Virtio9p.prototype.ReceiveRequest = async function (bufchain) {
|
||||
// TODO: split into header + data blobs to avoid unnecessary copying.
|
||||
const buffer = new Uint8Array(bufchain.length_readable);
|
||||
bufchain.get_next_blob(buffer);
|
||||
|
||||
const state = { offset : 0 };
|
||||
var header = marshall.Unmarshall(["w", "b", "h"], buffer, state);
|
||||
var size = header[0];
|
||||
var id = header[1];
|
||||
var tag = header[2];
|
||||
|
@ -149,70 +273,70 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
req[2] = Math.floor(space/req[1]); // free blocks
|
||||
req[3] = req[2] - Math.floor(size/req[1]); // free blocks in fs
|
||||
req[4] = req[2] - Math.floor(size/req[1]); // free blocks avail to non-superuser
|
||||
req[5] = this.fs.inodes.length; // total number of inodes
|
||||
req[6] = 1024*1024;
|
||||
req[5] = this.fs.CountUsedInodes(); // total number of inodes
|
||||
req[6] = this.fs.CountFreeInodes();
|
||||
req[7] = 0; // file system id?
|
||||
req[8] = 256; // maximum length of filenames
|
||||
|
||||
size = marshall.Marshall(["w", "w", "d", "d", "d", "d", "d", "d", "w"], req, this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, size);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 112: // topen
|
||||
case 12: // tlopen
|
||||
var req = marshall.Unmarshall2(["w", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var mode = req[1];
|
||||
message.Debug("[open] fid=" + fid + ", mode=" + mode);
|
||||
var idx = this.fids[fid].inodeid;
|
||||
var inode = this.fs.GetInode(idx);
|
||||
message.Debug("file open " + inode.name);
|
||||
message.Debug("file open " + this.fids[fid].dbg_name);
|
||||
//if (inode.status == STATUS_LOADING) return;
|
||||
var ret = this.fs.OpenInode(idx, mode);
|
||||
|
||||
this.fs.AddEvent(this.fids[fid].inodeid,
|
||||
function() {
|
||||
message.Debug("file opened " + inode.name + " tag:"+tag);
|
||||
message.Debug("file opened " + this.fids[fid].dbg_name + " tag:"+tag);
|
||||
var req = [];
|
||||
req[0] = inode.qid;
|
||||
req[1] = this.msize - 24;
|
||||
marshall.Marshall(["Q", "w"], req, this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 13+4);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
}.bind(this)
|
||||
);
|
||||
break;
|
||||
|
||||
case 70: // link (just copying)
|
||||
var req = marshall.Unmarshall2(["w", "w", "s"], GetByte);
|
||||
case 70: // link
|
||||
var req = marshall.Unmarshall(["w", "w", "s"], buffer, state);
|
||||
var dfid = req[0];
|
||||
var fid = req[1];
|
||||
var name = req[2];
|
||||
message.Debug("[link] dfid=" + dfid + ", name=" + name);
|
||||
var inode = this.fs.CreateInode();
|
||||
var inodetarget = this.fs.GetInode(this.fids[fid].inodeid);
|
||||
var targetdata = this.fs.inodedata[this.fids[fid].inodeid];
|
||||
//inode = inodetarget;
|
||||
inode.mode = inodetarget.mode;
|
||||
inode.size = inodetarget.size;
|
||||
inode.symlink = inodetarget.symlink;
|
||||
var data = this.fs.inodedata[this.fs.inodes.length] = new Uint8Array(inode.size);
|
||||
for(var i=0; i<inode.size; i++) {
|
||||
data[i] = targetdata[i];
|
||||
}
|
||||
inode.name = name;
|
||||
inode.parentid = this.fids[dfid].inodeid;
|
||||
this.fs.PushInode(inode);
|
||||
|
||||
//inode.uid = inodetarget.uid;
|
||||
//inode.gid = inodetarget.gid;
|
||||
//inode.mode = inodetarget.mode | S_IFLNK;
|
||||
var ret = this.fs.Link(this.fids[dfid].inodeid, this.fids[fid].inodeid, name);
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
let error_message = "";
|
||||
if(ret === -EPERM) error_message = "Operation not permitted";
|
||||
else
|
||||
{
|
||||
error_message = "Unknown error: " + (-ret);
|
||||
dbg_assert(false, "[link]: Unexpected error code: " + (-ret));
|
||||
}
|
||||
this.SendError(tag, error_message, -ret);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 16: // symlink
|
||||
var req = marshall.Unmarshall2(["w", "s", "s", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "s", "s", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var name = req[1];
|
||||
var symgt = req[2];
|
||||
|
@ -224,11 +348,11 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
inode.gid = gid;
|
||||
marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 13);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 18: // mknod
|
||||
var req = marshall.Unmarshall2(["w", "s", "w", "w", "w", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "s", "w", "w", "w", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var name = req[1];
|
||||
var mode = req[2];
|
||||
|
@ -243,23 +367,23 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
inode.gid = gid;
|
||||
marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 13);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
|
||||
case 22: // TREADLINK
|
||||
var req = marshall.Unmarshall2(["w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w"], buffer, state);
|
||||
var fid = req[0];
|
||||
message.Debug("[readlink] fid=" + fid);
|
||||
var inode = this.fs.GetInode(this.fids[fid].inodeid);
|
||||
message.Debug("[readlink] fid=" + fid + " name=" + this.fids[fid].dbg_name + " target=" + inode.symlink);
|
||||
size = marshall.Marshall(["s"], [inode.symlink], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, size);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
|
||||
case 72: // tmkdir
|
||||
var req = marshall.Unmarshall2(["w", "s", "w", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "s", "w", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var name = req[1];
|
||||
var mode = req[2];
|
||||
|
@ -272,51 +396,85 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
inode.gid = gid;
|
||||
marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 13);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 14: // tlcreate
|
||||
var req = marshall.Unmarshall2(["w", "s", "w", "w", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "s", "w", "w", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var name = req[1];
|
||||
var flags = req[2];
|
||||
var mode = req[3];
|
||||
var gid = req[4];
|
||||
this.bus.send("9p-create", [name, this.fids[fid].inodeid]);
|
||||
message.Debug("[create] fid=" + fid + ", name=" + name + ", flags=" + flags + ", mode=" + mode + ", gid=" + gid);
|
||||
var idx = this.fs.CreateFile(name, this.fids[fid].inodeid);
|
||||
this.fids[fid].inodeid = idx;
|
||||
this.fids[fid].type = FID_INODE;
|
||||
this.fids[fid].dbg_name = name;
|
||||
var inode = this.fs.GetInode(idx);
|
||||
inode.uid = this.fids[fid].uid;
|
||||
inode.gid = gid;
|
||||
inode.mode = mode;
|
||||
marshall.Marshall(["Q", "w"], [inode.qid, this.msize - 24], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 13+4);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 52: // lock always suceed
|
||||
message.Debug("lock file\n");
|
||||
marshall.Marshall(["w"], [0], this.replybuffer, 7);
|
||||
case 52: // lock
|
||||
var req = marshall.Unmarshall(["w", "b", "w", "d", "d", "w", "s"], buffer, state);
|
||||
var fid = req[0];
|
||||
var flags = req[2];
|
||||
var lock_length = req[4] === 0 ? Infinity : req[4];
|
||||
var lock_request = this.fs.DescribeLock(req[1], req[3], lock_length, req[5], req[6]);
|
||||
message.Debug("[lock] fid=" + fid +
|
||||
", type=" + P9_LOCK_TYPES[lock_request.type] + ", start=" + lock_request.start +
|
||||
", length=" + lock_request.length + ", proc_id=" + lock_request.proc_id);
|
||||
|
||||
var ret = this.fs.Lock(this.fids[fid].inodeid, lock_request, flags);
|
||||
|
||||
marshall.Marshall(["b"], [ret], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 1);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
/*
|
||||
case 54: // getlock
|
||||
var req = marshall.Unmarshall(["w", "b", "d", "d", "w", "s"], buffer, state);
|
||||
var fid = req[0];
|
||||
var lock_length = req[3] === 0 ? Infinity : req[3];
|
||||
var lock_request = this.fs.DescribeLock(req[1], req[2], lock_length, req[4], req[5]);
|
||||
message.Debug("[getlock] fid=" + fid +
|
||||
", type=" + P9_LOCK_TYPES[lock_request.type] + ", start=" + lock_request.start +
|
||||
", length=" + lock_request.length + ", proc_id=" + lock_request.proc_id);
|
||||
|
||||
var ret = this.fs.GetLock(this.fids[fid].inodeid, lock_request);
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
ret = lock_request;
|
||||
ret.type = P9_LOCK_TYPE_UNLCK;
|
||||
}
|
||||
|
||||
var ret_length = ret.length === Infinity ? 0 : ret.length;
|
||||
|
||||
size = marshall.Marshall(["b", "d", "d", "w", "s"],
|
||||
[ret.type, ret.start, ret_length, ret.proc_id, ret.client_id],
|
||||
this.replybuffer, 7);
|
||||
|
||||
this.BuildReply(id, tag, size);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
*/
|
||||
|
||||
case 24: // getattr
|
||||
var req = marshall.Unmarshall2(["w", "d"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "d"], buffer, state);
|
||||
var fid = req[0];
|
||||
var inode = this.fs.GetInode(this.fids[fid].inodeid);
|
||||
message.Debug("[getattr]: fid=" + fid + " name=" + inode.name + " request mask=" + req[1]);
|
||||
message.Debug("[getattr]: fid=" + fid + " name=" + this.fids[fid].dbg_name + " request mask=" + req[1]);
|
||||
if(!inode || inode.status === STATUS_UNLINKED)
|
||||
{
|
||||
message.Debug("getattr: unlinked");
|
||||
this.SendError(tag, "No such file or directory", ENOENT);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
req[0] |= 0x1000; // P9_STATS_GEN
|
||||
|
@ -332,7 +490,7 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
req[6] = (inode.major<<8) | (inode.minor); // device id low
|
||||
req[7] = inode.size; // size low
|
||||
req[8] = this.BLOCKSIZE;
|
||||
req[9] = Math.floor(inode.size/512+1);; // blk size low
|
||||
req[9] = Math.floor(inode.size/512+1); // blk size low
|
||||
req[10] = inode.atime; // atime
|
||||
req[11] = 0x0;
|
||||
req[12] = inode.mtime; // mtime
|
||||
|
@ -356,20 +514,20 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
"d", "d",
|
||||
], req, this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 8 + 13 + 4 + 4+ 4 + 8*15);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 26: // setattr
|
||||
var req = marshall.Unmarshall2(["w", "w",
|
||||
var req = marshall.Unmarshall(["w", "w",
|
||||
"w", // mode
|
||||
"w", "w", // uid, gid
|
||||
"d", // size
|
||||
"d", "d", // atime
|
||||
"d", "d"] // mtime
|
||||
, GetByte);
|
||||
"d", "d", // mtime
|
||||
], buffer, state);
|
||||
var fid = req[0];
|
||||
var inode = this.fs.GetInode(this.fids[fid].inodeid);
|
||||
message.Debug("[setattr]: fid=" + fid + " request mask=" + req[1] + " name=" +inode.name);
|
||||
message.Debug("[setattr]: fid=" + fid + " request mask=" + req[1] + " name=" + this.fids[fid].dbg_name);
|
||||
if (req[1] & P9_SETATTR_MODE) {
|
||||
inode.mode = req[2];
|
||||
}
|
||||
|
@ -395,33 +553,33 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
inode.mtime = req[8];
|
||||
}
|
||||
if (req[1] & P9_SETATTR_SIZE) {
|
||||
this.fs.ChangeSize(this.fids[fid].inodeid, req[5]);
|
||||
await this.fs.ChangeSize(this.fids[fid].inodeid, req[5]);
|
||||
}
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 50: // fsync
|
||||
var req = marshall.Unmarshall2(["w", "d"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "d"], buffer, state);
|
||||
var fid = req[0];
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 40: // TREADDIR
|
||||
case 116: // read
|
||||
var req = marshall.Unmarshall2(["w", "d", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "d", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var offset = req[1];
|
||||
var count = req[2];
|
||||
var inode = this.fs.GetInode(this.fids[fid].inodeid);
|
||||
if (id == 40) message.Debug("[treaddir]: fid=" + fid + " offset=" + offset + " count=" + count);
|
||||
if (id == 116) message.Debug("[read]: fid=" + fid + " (" + inode.name + ") offset=" + offset + " count=" + count + " fidtype=" + this.fids[fid].type);
|
||||
if (id == 116) message.Debug("[read]: fid=" + fid + " (" + this.fids[fid].dbg_name + ") offset=" + offset + " count=" + count + " fidtype=" + this.fids[fid].type);
|
||||
if(!inode || inode.status === STATUS_UNLINKED)
|
||||
{
|
||||
message.Debug("read/treaddir: unlinked");
|
||||
this.SendError(tag, "No such file or directory", ENOENT);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
if (this.fids[fid].type == FID_XATTR) {
|
||||
|
@ -430,65 +588,101 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
this.replybuffer[7+4+i] = inode.caps[offset+i];
|
||||
marshall.Marshall(["w"], [count], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 4 + count);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
} else {
|
||||
var file = this.fs.inodes[this.fids[fid].inodeid];
|
||||
this.bus.send("9p-read-start");
|
||||
|
||||
this.fs.OpenInode(this.fids[fid].inodeid, undefined);
|
||||
this.fs.AddEvent(this.fids[fid].inodeid,
|
||||
function() {
|
||||
this.bus.send("9p-read-end", [file.name, count]);
|
||||
const inodeid = this.fids[fid].inodeid;
|
||||
|
||||
if (inode.size < offset+count) count = inode.size - offset;
|
||||
var data = this.fs.inodedata[this.fids[fid].inodeid];
|
||||
else if(id == 40)
|
||||
{
|
||||
// for directories, return whole number of dir-entries.
|
||||
count = this.fs.RoundToDirentry(inodeid, offset + count) - offset;
|
||||
}
|
||||
if(offset > inode.size)
|
||||
{
|
||||
// offset can be greater than available - should return count of zero.
|
||||
// See http://ericvh.github.io/9p-rfc/rfc9p2000.html#anchor30
|
||||
count = 0;
|
||||
}
|
||||
|
||||
this.bus.send("9p-read-start", [this.fids[fid].dbg_name]);
|
||||
|
||||
const data = await this.fs.Read(inodeid, offset, count);
|
||||
|
||||
this.bus.send("9p-read-end", [this.fids[fid].dbg_name, count]);
|
||||
|
||||
if(data) {
|
||||
for(var i=0; i<count; i++)
|
||||
this.replybuffer[7+4+i] = data[offset+i];
|
||||
this.replybuffer.set(data, 7 + 4);
|
||||
}
|
||||
marshall.Marshall(["w"], [count], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 4 + count);
|
||||
this.SendReply(0, index);
|
||||
}.bind(this)
|
||||
);
|
||||
this.SendReply(bufchain);
|
||||
}
|
||||
break;
|
||||
|
||||
case 118: // write
|
||||
var req = marshall.Unmarshall2(["w", "d", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "d", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var offset = req[1];
|
||||
var count = req[2];
|
||||
message.Debug("[write]: fid=" + fid + " (" + this.fs.inodes[this.fids[fid].inodeid].name + ") offset=" + offset + " count=" + count);
|
||||
this.fs.Write(this.fids[fid].inodeid, offset, count, GetByte);
|
||||
|
||||
var file = this.fs.inodes[this.fids[fid].inodeid];
|
||||
this.bus.send("9p-write-end", [file.name, count]);
|
||||
const filename = this.fids[fid].dbg_name;
|
||||
|
||||
message.Debug("[write]: fid=" + fid + " (" + filename + ") offset=" + offset + " count=" + count + " fidtype=" + this.fids[fid].type);
|
||||
if(this.fids[fid].type === FID_XATTR)
|
||||
{
|
||||
// XXX: xattr not supported yet. Ignore write.
|
||||
this.SendError(tag, "Setxattr not supported", EOPNOTSUPP);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// XXX: Size of the subarray is unchecked
|
||||
await this.fs.Write(this.fids[fid].inodeid, offset, count, buffer.subarray(state.offset));
|
||||
}
|
||||
|
||||
this.bus.send("9p-write-end", [filename, count]);
|
||||
|
||||
marshall.Marshall(["w"], [count], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 4);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 74: // RENAMEAT
|
||||
var req = marshall.Unmarshall2(["w", "s", "w", "s"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "s", "w", "s"], buffer, state);
|
||||
var olddirfid = req[0];
|
||||
var oldname = req[1];
|
||||
var newdirfid = req[2];
|
||||
var newname = req[3];
|
||||
message.Debug("[renameat]: oldname=" + oldname + " newname=" + newname);
|
||||
var ret = this.fs.Rename(this.fids[olddirfid].inodeid, oldname, this.fids[newdirfid].inodeid, newname);
|
||||
if (ret == false) {
|
||||
this.SendError(tag, "No such file or directory", ENOENT);
|
||||
this.SendReply(0, index);
|
||||
var ret = await this.fs.Rename(this.fids[olddirfid].inodeid, oldname, this.fids[newdirfid].inodeid, newname);
|
||||
if (ret < 0) {
|
||||
let error_message = "";
|
||||
if(ret === -ENOENT) error_message = "No such file or directory";
|
||||
else if(ret === -EPERM) error_message = "Operation not permitted";
|
||||
else if(ret === -ENOTEMPTY) error_message = "Directory not empty";
|
||||
else
|
||||
{
|
||||
error_message = "Unknown error: " + (-ret);
|
||||
dbg_assert(false, "[renameat]: Unexpected error code: " + (-ret));
|
||||
}
|
||||
this.SendError(tag, error_message, -ret);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
if(TRACK_FILENAMES)
|
||||
{
|
||||
const newidx = this.fs.Search(this.fids[newdirfid].inodeid, newname);
|
||||
this.update_dbg_name(newidx, newname);
|
||||
}
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 76: // TUNLINKAT
|
||||
var req = marshall.Unmarshall2(["w", "s", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "s", "w"], buffer, state);
|
||||
var dirfd = req[0];
|
||||
var name = req[1];
|
||||
var flags = req[2];
|
||||
|
@ -496,75 +690,83 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
var fid = this.fs.Search(this.fids[dirfd].inodeid, name);
|
||||
if (fid == -1) {
|
||||
this.SendError(tag, "No such file or directory", ENOENT);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
var ret = this.fs.Unlink(fid);
|
||||
if (!ret) {
|
||||
this.SendError(tag, "Directory not empty", ENOTEMPTY);
|
||||
this.SendReply(0, index);
|
||||
var ret = this.fs.Unlink(this.fids[dirfd].inodeid, name);
|
||||
if (ret < 0) {
|
||||
let error_message = "";
|
||||
if(ret === -ENOTEMPTY) error_message = "Directory not empty";
|
||||
else if(ret === -EPERM) error_message = "Operation not permitted";
|
||||
else
|
||||
{
|
||||
error_message = "Unknown error: " + (-ret);
|
||||
dbg_assert(false, "[unlink]: Unexpected error code: " + (-ret));
|
||||
}
|
||||
this.SendError(tag, error_message, -ret);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 100: // version
|
||||
var version = marshall.Unmarshall2(["w", "s"], GetByte);
|
||||
var version = marshall.Unmarshall(["w", "s"], buffer, state);
|
||||
message.Debug("[version]: msize=" + version[0] + " version=" + version[1]);
|
||||
this.msize = version[0];
|
||||
size = marshall.Marshall(["w", "s"], [this.msize, this.VERSION], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, size);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 104: // attach
|
||||
// return root directorie's QID
|
||||
var req = marshall.Unmarshall2(["w", "w", "s", "s", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "w", "s", "s", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var uid = req[4];
|
||||
message.Debug("[attach]: fid=" + fid + " afid=" + hex8(req[1]) + " uname=" + req[2] + " aname=" + req[3]);
|
||||
this.fids[fid] = this.Createfid(0, FID_INODE, uid);
|
||||
this.fids[fid] = this.Createfid(0, FID_INODE, uid, "");
|
||||
var inode = this.fs.GetInode(this.fids[fid].inodeid);
|
||||
marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 13);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 108: // tflush
|
||||
var req = marshall.Unmarshall2(["h"], GetByte);
|
||||
var req = marshall.Unmarshall(["h"], buffer, state);
|
||||
var oldtag = req[0];
|
||||
message.Debug("[flush] " + tag);
|
||||
//marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
|
||||
case 110: // walk
|
||||
var req = marshall.Unmarshall2(["w", "w", "h"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "w", "h"], buffer, state);
|
||||
var fid = req[0];
|
||||
var nwfid = req[1];
|
||||
var nwname = req[2];
|
||||
message.Debug("[walk]: fid=" + req[0] + " nwfid=" + req[1] + " nwname=" + nwname);
|
||||
if (nwname == 0) {
|
||||
this.fids[nwfid] = this.Createfid(this.fids[fid].inodeid, FID_INODE, this.fids[fid].uid);
|
||||
this.fids[nwfid] = this.Createfid(this.fids[fid].inodeid, FID_INODE, this.fids[fid].uid, this.fids[fid].dbg_name);
|
||||
//this.fids[nwfid].inodeid = this.fids[fid].inodeid;
|
||||
marshall.Marshall(["h"], [0], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 2);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
var wnames = [];
|
||||
for(var i=0; i<nwname; i++) {
|
||||
wnames.push("s");
|
||||
}
|
||||
var walk = marshall.Unmarshall2(wnames, GetByte);
|
||||
var walk = marshall.Unmarshall(wnames, buffer, state);
|
||||
var idx = this.fids[fid].inodeid;
|
||||
var offset = 7+2;
|
||||
var nwidx = 0;
|
||||
//console.log(idx, this.fs.inodes[idx]);
|
||||
message.Debug("walk in dir " + this.fs.inodes[idx].name + " to: " + walk.toString());
|
||||
//console.log(idx, this.fs.GetInode(idx));
|
||||
message.Debug("walk in dir " + this.fids[fid].dbg_name + " to: " + walk.toString());
|
||||
for(var i=0; i<nwname; i++) {
|
||||
idx = this.fs.Search(idx, walk[i]);
|
||||
|
||||
|
@ -572,50 +774,59 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
message.Debug("Could not find: " + walk[i]);
|
||||
break;
|
||||
}
|
||||
offset += marshall.Marshall(["Q"], [this.fs.inodes[idx].qid], this.replybuffer, offset);
|
||||
offset += marshall.Marshall(["Q"], [this.fs.GetInode(idx).qid], this.replybuffer, offset);
|
||||
nwidx++;
|
||||
//message.Debug(this.fids[nwfid].inodeid);
|
||||
//this.fids[nwfid].inodeid = idx;
|
||||
//this.fids[nwfid].type = FID_INODE;
|
||||
this.fids[nwfid] = this.Createfid(idx, FID_INODE, this.fids[fid].uid);
|
||||
this.fids[nwfid] = this.Createfid(idx, FID_INODE, this.fids[fid].uid, walk[i]);
|
||||
}
|
||||
marshall.Marshall(["h"], [nwidx], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, offset-7);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 120: // clunk
|
||||
var req = marshall.Unmarshall2(["w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w"], buffer, state);
|
||||
message.Debug("[clunk]: fid=" + req[0]);
|
||||
if (this.fids[req[0]] && this.fids[req[0]].inodeid >= 0) {
|
||||
this.fs.CloseInode(this.fids[req[0]].inodeid);
|
||||
await this.fs.CloseInode(this.fids[req[0]].inodeid);
|
||||
this.fids[req[0]].inodeid = -1;
|
||||
this.fids[req[0]].type = FID_NONE;
|
||||
}
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 32: // txattrcreate
|
||||
var req = marshall.Unmarshall2(["w", "s", "d", "w"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "s", "d", "w"], buffer, state);
|
||||
var fid = req[0];
|
||||
var name = req[1];
|
||||
var attr_size = req[2];
|
||||
var flags = req[3];
|
||||
message.Debug("[txattrcreate]: fid=" + fid + " name=" + name + " attr_size=" + attr_size + " flags=" + flags);
|
||||
|
||||
// XXX: xattr not supported yet. E.g. checks corresponding to the flags needed.
|
||||
this.fids[fid].type = FID_XATTR;
|
||||
|
||||
this.BuildReply(id, tag, 0);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
//this.SendError(tag, "Operation i not supported", EINVAL);
|
||||
//this.SendReply(0, index);
|
||||
//this.SendReply(bufchain);
|
||||
break;
|
||||
|
||||
case 30: // xattrwalk
|
||||
var req = marshall.Unmarshall2(["w", "w", "s"], GetByte);
|
||||
var req = marshall.Unmarshall(["w", "w", "s"], buffer, state);
|
||||
var fid = req[0];
|
||||
var newfid = req[1];
|
||||
var name = req[2];
|
||||
message.Debug("[xattrwalk]: fid=" + req[0] + " newfid=" + req[1] + " name=" + req[2]);
|
||||
this.fids[newfid] = this.Createfid(this.fids[fid].inodeid, FID_NONE, this.fids[fid].uid);
|
||||
|
||||
// Workaround for Linux restarts writes until full blocksize
|
||||
this.SendError(tag, "Setxattr not supported", EOPNOTSUPP);
|
||||
this.SendReply(bufchain);
|
||||
/*
|
||||
this.fids[newfid] = this.Createfid(this.fids[fid].inodeid, FID_NONE, this.fids[fid].uid, this.fids[fid].dbg_name);
|
||||
//this.fids[newfid].inodeid = this.fids[fid].inodeid;
|
||||
//this.fids[newfid].type = FID_NONE;
|
||||
var length = 0;
|
||||
|
@ -625,17 +836,18 @@ Virtio9p.prototype.ReceiveRequest = function (index, GetByte) {
|
|||
}
|
||||
marshall.Marshall(["d"], [length], this.replybuffer, 7);
|
||||
this.BuildReply(id, tag, 8);
|
||||
this.SendReply(0, index);
|
||||
this.SendReply(bufchain);
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
message.Debug("Error in Virtio9p: Unknown id " + id + " received");
|
||||
message.Abort();
|
||||
//this.SendError(tag, "Operation i not supported", ENOTSUPP);
|
||||
//this.SendReply(0, index);
|
||||
//this.SendError(tag, "Operation i not supported", EOPNOTSUPP);
|
||||
//this.SendReply(bufchain);
|
||||
break;
|
||||
}
|
||||
|
||||
//consistency checks if there are problems with the filesystem
|
||||
//this.fs.Check();
|
||||
}
|
||||
};
|
||||
|
|
2
lib/esprima-min.js
vendored
2
lib/esprima-min.js
vendored
File diff suppressed because one or more lines are too long
3960
lib/esprima.js
3960
lib/esprima.js
File diff suppressed because one or more lines are too long
1915
lib/filesystem.js
1915
lib/filesystem.js
File diff suppressed because it is too large
Load diff
12
lib/jor1k.js
12
lib/jor1k.js
|
@ -40,15 +40,15 @@ var message = {};
|
|||
message.Debug = function(log)
|
||||
{
|
||||
dbg_log([].slice.apply(arguments).join(" "), LOG_9P);
|
||||
}
|
||||
};
|
||||
|
||||
message.Abort = function()
|
||||
{
|
||||
if(DEBUG)
|
||||
{
|
||||
throw "abort";
|
||||
}
|
||||
throw new Error("message.Abort()");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// XXX: Should go through emulator interface
|
||||
|
@ -85,7 +85,7 @@ if(typeof XMLHttpRequest !== "undefined")
|
|||
};
|
||||
*/
|
||||
req.send(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -100,8 +100,8 @@ else
|
|||
}
|
||||
else
|
||||
{
|
||||
OnSuccess(new Uint8Array(data).buffer);
|
||||
OnSuccess(data.buffer);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// -------------------------------------------------
|
||||
// helper functions for virtio and 9p.
|
||||
|
||||
"use strict";
|
||||
|
||||
var marshall = {};
|
||||
|
||||
|
||||
|
@ -46,8 +48,8 @@ marshall.Marshall = function(typelist, input, struct, offset) {
|
|||
struct[offset++] = 0; // set the length later
|
||||
struct[offset++] = 0;
|
||||
size += 2;
|
||||
for (var j in item) {
|
||||
var utf8 = UnicodeToUTF8Stream(item.charCodeAt(j));
|
||||
for (var j of item) {
|
||||
var utf8 = UnicodeToUTF8Stream(j.charCodeAt(0));
|
||||
utf8.forEach( function(c) {
|
||||
struct[offset++] = c;
|
||||
size += 1;
|
||||
|
@ -58,7 +60,7 @@ marshall.Marshall = function(typelist, input, struct, offset) {
|
|||
struct[lengthoffset+1] = (length >> 8) & 0xFF;
|
||||
break;
|
||||
case "Q":
|
||||
marshall.Marshall(["b", "w", "d"], [item.type, item.version, item.path], struct, offset)
|
||||
marshall.Marshall(["b", "w", "d"], [item.type, item.version, item.path], struct, offset);
|
||||
offset += 13;
|
||||
size += 13;
|
||||
break;
|
||||
|
@ -72,7 +74,8 @@ marshall.Marshall = function(typelist, input, struct, offset) {
|
|||
|
||||
|
||||
// Extracts data from a byte aligned struct in memory to an array
|
||||
marshall.Unmarshall = function(typelist, struct, offset) {
|
||||
marshall.Unmarshall = function(typelist, struct, state) {
|
||||
let offset = state.offset;
|
||||
var output = [];
|
||||
for (var i=0; i < typelist.length; i++) {
|
||||
switch (typelist[i]) {
|
||||
|
@ -104,17 +107,28 @@ marshall.Unmarshall = function(typelist, struct, offset) {
|
|||
var str = '';
|
||||
var utf8converter = new UTF8StreamToUnicode();
|
||||
for (var j=0; j < len; j++) {
|
||||
var c = utf8converter.Put(struct[offset++])
|
||||
var c = utf8converter.Put(struct[offset++]);
|
||||
if (c == -1) continue;
|
||||
str += String.fromCharCode(c);
|
||||
}
|
||||
output.push(str);
|
||||
break;
|
||||
case "Q":
|
||||
state.offset = offset;
|
||||
const qid = marshall.Unmarshall(["b", "w", "d"], struct, state);
|
||||
offset = state.offset;
|
||||
output.push({
|
||||
type: qid[0],
|
||||
version: qid[1],
|
||||
path: qid[2],
|
||||
});
|
||||
break;
|
||||
default:
|
||||
message.Debug("Error in Unmarshall: Unknown type=" + typelist[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.offset = offset;
|
||||
return output;
|
||||
};
|
||||
|
||||
|
@ -152,7 +166,7 @@ marshall.Unmarshall2 = function(typelist, GetByte) {
|
|||
var str = '';
|
||||
var utf8converter = new UTF8StreamToUnicode();
|
||||
for (var j=0; j < len; j++) {
|
||||
var c = utf8converter.Put(GetByte())
|
||||
var c = utf8converter.Put(GetByte());
|
||||
if (c == -1) continue;
|
||||
str += String.fromCharCode(c);
|
||||
}
|
||||
|
@ -165,4 +179,3 @@ marshall.Unmarshall2 = function(typelist, GetByte) {
|
|||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
|
|
32501
lib/softfloat/softfloat.c
Normal file
32501
lib/softfloat/softfloat.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var UTF8 = {}
|
||||
var UTF8 = {};
|
||||
|
||||
/** @constructor */
|
||||
function UTF8StreamToUnicode() {
|
||||
|
@ -43,7 +43,7 @@ function UTF8StreamToUnicode() {
|
|||
//break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function UnicodeToUTF8Stream(key)
|
||||
|
@ -60,4 +60,4 @@ UTF8.UTF8Length = function(s)
|
|||
length += c<128?1:2;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
};
|
||||
|
|
1
lib/walk-min.js
vendored
1
lib/walk-min.js
vendored
File diff suppressed because one or more lines are too long
330
lib/walk.js
330
lib/walk.js
|
@ -1,330 +0,0 @@
|
|||
// AST walker module for Mozilla Parser API compatible trees
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
|
||||
if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
|
||||
mod((this.acorn || (this.acorn = {})).walk = {}); // Plain browser env
|
||||
})(function(exports) {
|
||||
"use strict";
|
||||
|
||||
// A simple walk is one where you simply specify callbacks to be
|
||||
// called on specific nodes. The last two arguments are optional. A
|
||||
// simple use would be
|
||||
//
|
||||
// walk.simple(myTree, {
|
||||
// Expression: function(node) { ... }
|
||||
// });
|
||||
//
|
||||
// to do something with all expressions. All Parser API node types
|
||||
// can be used to identify node types, as well as Expression,
|
||||
// Statement, and ScopeBody, which denote categories of nodes.
|
||||
//
|
||||
// The base argument can be used to pass a custom (recursive)
|
||||
// walker, and state can be used to give this walked an initial
|
||||
// state.
|
||||
exports.simple = function(node, visitors, base, state) {
|
||||
if (!base) base = exports.base;
|
||||
function c(node, st, override) {
|
||||
var type = override || node.type, found = visitors[type];
|
||||
base[type](node, st, c);
|
||||
if (found) found(node, st);
|
||||
}
|
||||
c(node, state);
|
||||
};
|
||||
|
||||
// An ancestor walk builds up an array of ancestor nodes (including
|
||||
// the current node) and passes them to the callback as the state parameter.
|
||||
exports.ancestor = function(node, visitors, base, state) {
|
||||
if (!base) base = exports.base;
|
||||
if (!state) state = [];
|
||||
function c(node, st, override) {
|
||||
var type = override || node.type, found = visitors[type];
|
||||
if (node != st[st.length - 1]) {
|
||||
st = st.slice();
|
||||
st.push(node);
|
||||
}
|
||||
base[type](node, st, c);
|
||||
if (found) found(node, st);
|
||||
}
|
||||
c(node, state);
|
||||
};
|
||||
|
||||
// A recursive walk is one where your functions override the default
|
||||
// walkers. They can modify and replace the state parameter that's
|
||||
// threaded through the walk, and can opt how and whether to walk
|
||||
// their child nodes (by calling their third argument on these
|
||||
// nodes).
|
||||
exports.recursive = function(node, state, funcs, base) {
|
||||
var visitor = funcs ? exports.make(funcs, base) : base;
|
||||
function c(node, st, override) {
|
||||
visitor[override || node.type](node, st, c);
|
||||
}
|
||||
c(node, state);
|
||||
};
|
||||
|
||||
function makeTest(test) {
|
||||
if (typeof test == "string")
|
||||
return function(type) { return type == test; };
|
||||
else if (!test)
|
||||
return function() { return true; };
|
||||
else
|
||||
return test;
|
||||
}
|
||||
|
||||
function Found(node, state) { this.node = node; this.state = state; }
|
||||
|
||||
// Find a node with a given start, end, and type (all are optional,
|
||||
// null can be used as wildcard). Returns a {node, state} object, or
|
||||
// undefined when it doesn't find a matching node.
|
||||
exports.findNodeAt = function(node, start, end, test, base, state) {
|
||||
test = makeTest(test);
|
||||
try {
|
||||
if (!base) base = exports.base;
|
||||
var c = function(node, st, override) {
|
||||
var type = override || node.type;
|
||||
if ((start == null || node.start <= start) &&
|
||||
(end == null || node.end >= end))
|
||||
base[type](node, st, c);
|
||||
if (test(type, node) &&
|
||||
(start == null || node.start == start) &&
|
||||
(end == null || node.end == end))
|
||||
throw new Found(node, st);
|
||||
};
|
||||
c(node, state);
|
||||
} catch (e) {
|
||||
if (e instanceof Found) return e;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
// Find the innermost node of a given type that contains the given
|
||||
// position. Interface similar to findNodeAt.
|
||||
exports.findNodeAround = function(node, pos, test, base, state) {
|
||||
test = makeTest(test);
|
||||
try {
|
||||
if (!base) base = exports.base;
|
||||
var c = function(node, st, override) {
|
||||
var type = override || node.type;
|
||||
if (node.start > pos || node.end < pos) return;
|
||||
base[type](node, st, c);
|
||||
if (test(type, node)) throw new Found(node, st);
|
||||
};
|
||||
c(node, state);
|
||||
} catch (e) {
|
||||
if (e instanceof Found) return e;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
// Find the outermost matching node after a given position.
|
||||
exports.findNodeAfter = function(node, pos, test, base, state) {
|
||||
test = makeTest(test);
|
||||
try {
|
||||
if (!base) base = exports.base;
|
||||
var c = function(node, st, override) {
|
||||
if (node.end < pos) return;
|
||||
var type = override || node.type;
|
||||
if (node.start >= pos && test(type, node)) throw new Found(node, st);
|
||||
base[type](node, st, c);
|
||||
};
|
||||
c(node, state);
|
||||
} catch (e) {
|
||||
if (e instanceof Found) return e;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
// Find the outermost matching node before a given position.
|
||||
exports.findNodeBefore = function(node, pos, test, base, state) {
|
||||
test = makeTest(test);
|
||||
if (!base) base = exports.base;
|
||||
var max;
|
||||
var c = function(node, st, override) {
|
||||
if (node.start > pos) return;
|
||||
var type = override || node.type;
|
||||
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
|
||||
max = new Found(node, st);
|
||||
base[type](node, st, c);
|
||||
};
|
||||
c(node, state);
|
||||
return max;
|
||||
};
|
||||
|
||||
// Used to create a custom walker. Will fill in all missing node
|
||||
// type properties with the defaults.
|
||||
exports.make = function(funcs, base) {
|
||||
if (!base) base = exports.base;
|
||||
var visitor = {};
|
||||
for (var type in base) visitor[type] = base[type];
|
||||
for (var type in funcs) visitor[type] = funcs[type];
|
||||
return visitor;
|
||||
};
|
||||
|
||||
function skipThrough(node, st, c) { c(node, st); }
|
||||
function ignore(_node, _st, _c) {}
|
||||
|
||||
// Node walkers.
|
||||
|
||||
var base = exports.base = {};
|
||||
base.Program = base.BlockStatement = function(node, st, c) {
|
||||
for (var i = 0; i < node.body.length; ++i)
|
||||
c(node.body[i], st, "Statement");
|
||||
};
|
||||
base.Statement = skipThrough;
|
||||
base.EmptyStatement = ignore;
|
||||
base.ExpressionStatement = function(node, st, c) {
|
||||
c(node.expression, st, "Expression");
|
||||
};
|
||||
base.IfStatement = function(node, st, c) {
|
||||
c(node.test, st, "Expression");
|
||||
c(node.consequent, st, "Statement");
|
||||
if (node.alternate) c(node.alternate, st, "Statement");
|
||||
};
|
||||
base.LabeledStatement = function(node, st, c) {
|
||||
c(node.body, st, "Statement");
|
||||
};
|
||||
base.BreakStatement = base.ContinueStatement = ignore;
|
||||
base.WithStatement = function(node, st, c) {
|
||||
c(node.object, st, "Expression");
|
||||
c(node.body, st, "Statement");
|
||||
};
|
||||
base.SwitchStatement = function(node, st, c) {
|
||||
c(node.discriminant, st, "Expression");
|
||||
for (var i = 0; i < node.cases.length; ++i) {
|
||||
var cs = node.cases[i];
|
||||
if (cs.test) c(cs.test, st, "Expression");
|
||||
for (var j = 0; j < cs.consequent.length; ++j)
|
||||
c(cs.consequent[j], st, "Statement");
|
||||
}
|
||||
};
|
||||
base.ReturnStatement = function(node, st, c) {
|
||||
if (node.argument) c(node.argument, st, "Expression");
|
||||
};
|
||||
base.ThrowStatement = function(node, st, c) {
|
||||
c(node.argument, st, "Expression");
|
||||
};
|
||||
base.TryStatement = function(node, st, c) {
|
||||
c(node.block, st, "Statement");
|
||||
if (node.handler) c(node.handler.body, st, "ScopeBody");
|
||||
if (node.finalizer) c(node.finalizer, st, "Statement");
|
||||
};
|
||||
base.WhileStatement = function(node, st, c) {
|
||||
c(node.test, st, "Expression");
|
||||
c(node.body, st, "Statement");
|
||||
};
|
||||
base.DoWhileStatement = base.WhileStatement;
|
||||
base.ForStatement = function(node, st, c) {
|
||||
if (node.init) c(node.init, st, "ForInit");
|
||||
if (node.test) c(node.test, st, "Expression");
|
||||
if (node.update) c(node.update, st, "Expression");
|
||||
c(node.body, st, "Statement");
|
||||
};
|
||||
base.ForInStatement = function(node, st, c) {
|
||||
c(node.left, st, "ForInit");
|
||||
c(node.right, st, "Expression");
|
||||
c(node.body, st, "Statement");
|
||||
};
|
||||
base.ForInit = function(node, st, c) {
|
||||
if (node.type == "VariableDeclaration") c(node, st);
|
||||
else c(node, st, "Expression");
|
||||
};
|
||||
base.DebuggerStatement = ignore;
|
||||
|
||||
base.FunctionDeclaration = function(node, st, c) {
|
||||
c(node, st, "Function");
|
||||
};
|
||||
base.VariableDeclaration = function(node, st, c) {
|
||||
for (var i = 0; i < node.declarations.length; ++i) {
|
||||
var decl = node.declarations[i];
|
||||
if (decl.init) c(decl.init, st, "Expression");
|
||||
}
|
||||
};
|
||||
|
||||
base.Function = function(node, st, c) {
|
||||
c(node.body, st, "ScopeBody");
|
||||
};
|
||||
base.ScopeBody = function(node, st, c) {
|
||||
c(node, st, "Statement");
|
||||
};
|
||||
|
||||
base.Expression = skipThrough;
|
||||
base.ThisExpression = ignore;
|
||||
base.ArrayExpression = function(node, st, c) {
|
||||
for (var i = 0; i < node.elements.length; ++i) {
|
||||
var elt = node.elements[i];
|
||||
if (elt) c(elt, st, "Expression");
|
||||
}
|
||||
};
|
||||
base.ObjectExpression = function(node, st, c) {
|
||||
for (var i = 0; i < node.properties.length; ++i)
|
||||
c(node.properties[i].value, st, "Expression");
|
||||
};
|
||||
base.FunctionExpression = base.FunctionDeclaration;
|
||||
base.SequenceExpression = function(node, st, c) {
|
||||
for (var i = 0; i < node.expressions.length; ++i)
|
||||
c(node.expressions[i], st, "Expression");
|
||||
};
|
||||
base.UnaryExpression = base.UpdateExpression = function(node, st, c) {
|
||||
c(node.argument, st, "Expression");
|
||||
};
|
||||
base.BinaryExpression = base.AssignmentExpression = base.LogicalExpression = function(node, st, c) {
|
||||
c(node.left, st, "Expression");
|
||||
c(node.right, st, "Expression");
|
||||
};
|
||||
base.ConditionalExpression = function(node, st, c) {
|
||||
c(node.test, st, "Expression");
|
||||
c(node.consequent, st, "Expression");
|
||||
c(node.alternate, st, "Expression");
|
||||
};
|
||||
base.NewExpression = base.CallExpression = function(node, st, c) {
|
||||
c(node.callee, st, "Expression");
|
||||
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
|
||||
c(node.arguments[i], st, "Expression");
|
||||
};
|
||||
base.MemberExpression = function(node, st, c) {
|
||||
c(node.object, st, "Expression");
|
||||
if (node.computed) c(node.property, st, "Expression");
|
||||
};
|
||||
base.Identifier = base.Literal = ignore;
|
||||
|
||||
// A custom walker that keeps track of the scope chain and the
|
||||
// variables defined in it.
|
||||
function makeScope(prev, isCatch) {
|
||||
return {vars: Object.create(null), prev: prev, isCatch: isCatch};
|
||||
}
|
||||
function normalScope(scope) {
|
||||
while (scope.isCatch) scope = scope.prev;
|
||||
return scope;
|
||||
}
|
||||
exports.scopeVisitor = exports.make({
|
||||
Function: function(node, scope, c) {
|
||||
var inner = makeScope(scope);
|
||||
for (var i = 0; i < node.params.length; ++i)
|
||||
inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
|
||||
if (node.id) {
|
||||
var decl = node.type == "FunctionDeclaration";
|
||||
(decl ? normalScope(scope) : inner).vars[node.id.name] =
|
||||
{type: decl ? "function" : "function name", node: node.id};
|
||||
}
|
||||
c(node.body, inner, "ScopeBody");
|
||||
},
|
||||
TryStatement: function(node, scope, c) {
|
||||
c(node.block, scope, "Statement");
|
||||
if (node.handler) {
|
||||
var inner = makeScope(scope, true);
|
||||
inner.vars[node.handler.param.name] = {type: "catch clause", node: node.handler.param};
|
||||
c(node.handler.body, inner, "ScopeBody");
|
||||
}
|
||||
if (node.finalizer) c(node.finalizer, scope, "Statement");
|
||||
},
|
||||
VariableDeclaration: function(node, scope, c) {
|
||||
var target = normalScope(scope);
|
||||
for (var i = 0; i < node.declarations.length; ++i) {
|
||||
var decl = node.declarations[i];
|
||||
target.vars[decl.id.name] = {type: "var", node: decl.id};
|
||||
if (decl.init) c(decl.init, scope, "Expression");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
13526
lib/zstd/zstddeclib.c
Normal file
13526
lib/zstd/zstddeclib.c
Normal file
File diff suppressed because it is too large
Load diff
19
loader.js
19
loader.js
|
@ -5,23 +5,25 @@
|
|||
"use strict";
|
||||
|
||||
var CORE_FILES =
|
||||
"const.js config.js log.js cpu.js debug.js translate.js modrm.js string.js arith.js misc_instr.js instructions.js " +
|
||||
"io.js main.js lib.js ide.js fpu.js pci.js floppy.js " +
|
||||
"const.js config.js log.js lib.js cpu.js debug.js " +
|
||||
"io.js main.js ide.js pci.js floppy.js " +
|
||||
"memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js acpi.js apic.js ioapic.js hpet.js sb16.js " +
|
||||
"ne2k.js state.js virtio.js bus.js elf.js";
|
||||
"ne2k.js state.js virtio.js bus.js elf.js kernel.js";
|
||||
|
||||
var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js speaker.js serial.js lib.js network.js starter.js worker_bus.js";
|
||||
//var LIB_FILES = "esprima.js walk.js";
|
||||
var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js speaker.js serial.js lib.js network.js starter.js worker_bus.js print_stats.js filestorage.js";
|
||||
var LIB_FILES = "";
|
||||
|
||||
// jor1k stuff
|
||||
LIB_FILES += " jor1k.js 9p.js filesystem.js marshall.js utf8.js";
|
||||
|
||||
var BUILD_FILES = "capstone-x86.min.js libwabt.js";
|
||||
|
||||
var to_load = [];
|
||||
|
||||
load_scripts(CORE_FILES, "src/");
|
||||
load_scripts(BROWSER_FILES, "src/browser/");
|
||||
load_scripts(LIB_FILES, "lib/");
|
||||
load_scripts(BUILD_FILES, "build/");
|
||||
|
||||
function load_scripts(resp, path)
|
||||
{
|
||||
|
@ -42,7 +44,7 @@
|
|||
|
||||
function load_next()
|
||||
{
|
||||
var s = to_load.shift();
|
||||
let s = to_load.shift();
|
||||
|
||||
if(!s)
|
||||
{
|
||||
|
@ -52,6 +54,11 @@
|
|||
var script = document.createElement("script");
|
||||
script.src = s;
|
||||
script.onload = load_next;
|
||||
script.onerror = function() {
|
||||
dbg_log("Warning: trying to ignore script " + s + ", which failed to load");
|
||||
|
||||
load_next();
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
})();
|
||||
|
|
11
package.json
11
package.json
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"name": "v86",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/copy/v86"
|
||||
},
|
||||
"license": "BSD-2-Clause",
|
||||
"scripts": {
|
||||
"test": "node tests/full/run.js"
|
||||
}
|
||||
}
|
45
src/acpi.js
45
src/acpi.js
|
@ -3,7 +3,7 @@
|
|||
// http://www.uefi.org/sites/default/files/resources/ACPI_6_1.pdf
|
||||
|
||||
/** @const */
|
||||
var PMTIMER_FREQ = 3579545;
|
||||
var PMTIMER_FREQ_SECONDS = 3579545;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
|
@ -31,6 +31,9 @@ function ACPI(cpu)
|
|||
// 00:07.0 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
|
||||
cpu.devices.pci.register_device(acpi);
|
||||
|
||||
this.timer_last_value = 0;
|
||||
this.timer_imprecision_offset = 0;
|
||||
|
||||
this.status = 1;
|
||||
this.pm1_status = 0;
|
||||
this.pm1_enable = 0;
|
||||
|
@ -145,7 +148,43 @@ ACPI.prototype.timer = function(now)
|
|||
|
||||
ACPI.prototype.get_timer = function(now)
|
||||
{
|
||||
return now * (PMTIMER_FREQ / 1000) | 0;
|
||||
const t = Math.round(now * (PMTIMER_FREQ_SECONDS / 1000));
|
||||
|
||||
// Due to the low precision of JavaScript's time functions we increment the
|
||||
// returned timer value every time it is read
|
||||
|
||||
if(t === this.timer_last_value)
|
||||
{
|
||||
// don't go past 1ms
|
||||
|
||||
if(this.timer_imprecision_offset < PMTIMER_FREQ_SECONDS / 1000)
|
||||
{
|
||||
this.timer_imprecision_offset++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(t > this.timer_last_value);
|
||||
|
||||
const previous_timer = this.timer_last_value + this.timer_imprecision_offset;
|
||||
|
||||
// don't go back in time
|
||||
|
||||
if(previous_timer <= t)
|
||||
{
|
||||
this.timer_imprecision_offset = 0;
|
||||
this.timer_last_value = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Warning: Overshot pmtimer, waiting;" +
|
||||
" current=" + t +
|
||||
" last=" + this.timer_last_value +
|
||||
" offset=" + this.timer_imprecision_offset, LOG_ACPI);
|
||||
}
|
||||
}
|
||||
|
||||
return this.timer_last_value + this.timer_imprecision_offset;
|
||||
};
|
||||
|
||||
ACPI.prototype.get_state = function()
|
||||
|
@ -154,6 +193,7 @@ ACPI.prototype.get_state = function()
|
|||
state[0] = this.status;
|
||||
state[1] = this.pm1_status;
|
||||
state[2] = this.pm1_enable;
|
||||
state[3] = this.gpe;
|
||||
return state;
|
||||
};
|
||||
|
||||
|
@ -162,4 +202,5 @@ ACPI.prototype.set_state = function(state)
|
|||
this.status = state[0];
|
||||
this.pm1_status = state[1];
|
||||
this.pm1_enable = state[2];
|
||||
this.gpe = state[3];
|
||||
};
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
|
||||
/** @constructor */
|
||||
function ScreenAdapter()
|
||||
{
|
||||
this.put_pixel_linear = function() {};
|
||||
this.put_pixel_linear32 = function() {};
|
||||
this.put_char = function() {};
|
||||
this.set_mode = function() {};
|
||||
this.set_size_graphical = function() {};
|
||||
this.set_size_text = function() {};
|
||||
this.update_cursor = function() {};
|
||||
this.update_cursor_scanline = function() {};
|
||||
this.timer = function() {};
|
||||
}
|
10
src/apic.js
10
src/apic.js
|
@ -394,9 +394,13 @@ APIC.prototype.timer = function(now)
|
|||
|
||||
if(mode === APIC_TIMER_MODE_PERIODIC)
|
||||
{
|
||||
// This isn't exact, because timer_current_count might already be
|
||||
// negative at this point since timer() fires late
|
||||
this.timer_current_count = this.timer_initial_count;
|
||||
this.timer_current_count = this.timer_current_count % this.timer_initial_count;
|
||||
|
||||
if(this.timer_current_count <= 0)
|
||||
{
|
||||
this.timer_current_count += this.timer_initial_count;
|
||||
}
|
||||
dbg_assert(this.timer_current_count !== 0);
|
||||
|
||||
if((this.lvt_timer & IOAPIC_CONFIG_MASKED) === 0)
|
||||
{
|
||||
|
|
1625
src/arith.js
1625
src/arith.js
File diff suppressed because it is too large
Load diff
160
src/browser/filestorage.js
Normal file
160
src/browser/filestorage.js
Normal file
|
@ -0,0 +1,160 @@
|
|||
"use strict";
|
||||
|
||||
/** @interface */
|
||||
function FileStorageInterface() {}
|
||||
|
||||
/**
|
||||
* Read a portion of a file.
|
||||
* @param {string} sha256sum
|
||||
* @param {number} offset
|
||||
* @param {number} count
|
||||
* @return {!Promise<Uint8Array>} null if file does not exist.
|
||||
*/
|
||||
FileStorageInterface.prototype.read = function(sha256sum, offset, count) {};
|
||||
|
||||
/**
|
||||
* Add a read-only file to the filestorage.
|
||||
* @param {string} sha256sum
|
||||
* @param {!Uint8Array} data
|
||||
* @return {!Promise}
|
||||
*/
|
||||
FileStorageInterface.prototype.cache = function(sha256sum, data) {};
|
||||
|
||||
/**
|
||||
* Call this when the file won't be used soon, e.g. when a file closes or when this immutable
|
||||
* version is already out of date. It is used to help prevent accumulation of unused files in
|
||||
* memory in the long run for some FileStorage mediums.
|
||||
*/
|
||||
FileStorageInterface.prototype.uncache = function(sha256sum) {};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {FileStorageInterface}
|
||||
*/
|
||||
function MemoryFileStorage()
|
||||
{
|
||||
/**
|
||||
* From sha256sum to file data.
|
||||
* @type {Map<string,Uint8Array>}
|
||||
*/
|
||||
this.filedata = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} sha256sum
|
||||
* @param {number} offset
|
||||
* @param {number} count
|
||||
* @return {!Promise<Uint8Array>} null if file does not exist.
|
||||
*/
|
||||
MemoryFileStorage.prototype.read = async function(sha256sum, offset, count)
|
||||
{
|
||||
dbg_assert(sha256sum, "MemoryFileStorage read: sha256sum should be a non-empty string");
|
||||
const data = this.filedata.get(sha256sum);
|
||||
|
||||
if(!data)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return data.subarray(offset, offset + count);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sha256sum
|
||||
* @param {!Uint8Array} data
|
||||
*/
|
||||
MemoryFileStorage.prototype.cache = async function(sha256sum, data)
|
||||
{
|
||||
dbg_assert(sha256sum, "MemoryFileStorage cache: sha256sum should be a non-empty string");
|
||||
this.filedata.set(sha256sum, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sha256sum
|
||||
*/
|
||||
MemoryFileStorage.prototype.uncache = function(sha256sum)
|
||||
{
|
||||
this.filedata.delete(sha256sum);
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {FileStorageInterface}
|
||||
* @param {FileStorageInterface} file_storage
|
||||
* @param {string} baseurl
|
||||
*/
|
||||
function ServerFileStorageWrapper(file_storage, baseurl)
|
||||
{
|
||||
dbg_assert(baseurl, "ServerMemoryFileStorage: baseurl should not be empty");
|
||||
|
||||
this.storage = file_storage;
|
||||
this.baseurl = baseurl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} sha256sum
|
||||
* @return {!Promise<Uint8Array>}
|
||||
*/
|
||||
ServerFileStorageWrapper.prototype.load_from_server = function(sha256sum)
|
||||
{
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
v86util.load_file(this.baseurl + sha256sum, { done: buffer =>
|
||||
{
|
||||
const data = new Uint8Array(buffer);
|
||||
this.cache(sha256sum, data).then(() => resolve(data));
|
||||
}});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sha256sum
|
||||
* @param {number} offset
|
||||
* @param {number} count
|
||||
* @return {!Promise<Uint8Array>}
|
||||
*/
|
||||
ServerFileStorageWrapper.prototype.read = async function(sha256sum, offset, count)
|
||||
{
|
||||
const data = await this.storage.read(sha256sum, offset, count);
|
||||
if(!data)
|
||||
{
|
||||
const full_file = await this.load_from_server(sha256sum);
|
||||
return full_file.subarray(offset, offset + count);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sha256sum
|
||||
* @param {!Uint8Array} data
|
||||
*/
|
||||
ServerFileStorageWrapper.prototype.cache = async function(sha256sum, data)
|
||||
{
|
||||
return await this.storage.cache(sha256sum, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sha256sum
|
||||
*/
|
||||
ServerFileStorageWrapper.prototype.uncache = function(sha256sum)
|
||||
{
|
||||
this.storage.uncache(sha256sum);
|
||||
};
|
||||
|
||||
// Closure Compiler's way of exporting
|
||||
if(typeof window !== "undefined")
|
||||
{
|
||||
window["MemoryFileStorage"] = MemoryFileStorage;
|
||||
window["ServerFileStorageWrapper"] = ServerFileStorageWrapper;
|
||||
}
|
||||
else if(typeof module !== "undefined" && typeof module.exports !== "undefined")
|
||||
{
|
||||
module.exports["MemoryFileStorage"] = MemoryFileStorage;
|
||||
module.exports["ServerFileStorageWrapper"] = ServerFileStorageWrapper;
|
||||
}
|
||||
else if(typeof importScripts === "function")
|
||||
{
|
||||
// web worker
|
||||
self["MemoryFileStorage"] = MemoryFileStorage;
|
||||
self["ServerFileStorageWrapper"] = ServerFileStorageWrapper;
|
||||
}
|
|
@ -111,7 +111,7 @@ function KeyboardAdapter(bus)
|
|||
* ascii -> javascript event code (US layout)
|
||||
* @const
|
||||
*/
|
||||
var asciimap = {10: 13, 32: 32, 39: 222, 44: 188, 45: 189, 46: 190, 47: 191, 48: 48, 49: 49, 50: 50, 51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 57: 57, 59: 186, 61: 187, 91: 219, 92: 220, 93: 221, 96: 192, 97: 65, 98: 66, 99: 67, 100: 68, 101: 69, 102: 70, 103: 71, 104: 72, 105: 73, 106: 74, 107: 75, 108: 76, 109: 77, 110: 78, 111: 79, 112: 80, 113: 81, 114: 82, 115: 83, 116: 84, 117: 85, 118: 86, 119: 87, 120: 88, 121: 89, 122: 90};
|
||||
var asciimap = {8: 8, 10: 13, 32: 32, 39: 222, 44: 188, 45: 189, 46: 190, 47: 191, 48: 48, 49: 49, 50: 50, 51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 57: 57, 59: 186, 61: 187, 91: 219, 92: 220, 93: 221, 96: 192, 97: 65, 98: 66, 99: 67, 100: 68, 101: 69, 102: 70, 103: 71, 104: 72, 105: 73, 106: 74, 107: 75, 108: 76, 109: 77, 110: 78, 111: 79, 112: 80, 113: 81, 114: 82, 115: 83, 116: 84, 117: 85, 118: 86, 119: 87, 120: 88, 121: 89, 122: 90};
|
||||
var asciimap_shift = {33: 49, 34: 222, 35: 51, 36: 52, 37: 53, 38: 55, 40: 57, 41: 48, 42: 56, 43: 187, 58: 186, 60: 188, 62: 190, 63: 191, 64: 50, 65: 65, 66: 66, 67: 67, 68: 68, 69: 69, 70: 70, 71: 71, 72: 72, 73: 73, 74: 74, 75: 75, 76: 76, 77: 77, 78: 78, 79: 79, 80: 80, 81: 81, 82: 82, 83: 83, 84: 84, 85: 85, 86: 86, 87: 87, 88: 88, 89: 89, 90: 90, 94: 54, 95: 189, 123: 219, 124: 220, 125: 221, 126: 192};
|
||||
|
||||
// From:
|
||||
|
@ -284,7 +284,7 @@ function KeyboardAdapter(bus)
|
|||
|
||||
function may_handle(e)
|
||||
{
|
||||
if(e.shiftKey && e.ctrlKey && (e.keyCode === 74 || e.keyCode === 75))
|
||||
if(e.shiftKey && e.ctrlKey && (e.keyCode === 73 || e.keyCode === 74 || e.keyCode === 75))
|
||||
{
|
||||
// don't prevent opening chromium dev tools
|
||||
// maybe add other important combinations here, too
|
||||
|
@ -383,7 +383,7 @@ function KeyboardAdapter(bus)
|
|||
|
||||
if(!code)
|
||||
{
|
||||
console.log("Missing char in map: " + e.keyCode.toString(16));
|
||||
console.log("Missing char in map: keyCode=" + (e.keyCode || -1).toString(16) + " code=" + e.code);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,18 @@ var ASYNC_SAFE = false;
|
|||
}
|
||||
|
||||
v86util.AsyncXHRBuffer = AsyncXHRBuffer;
|
||||
v86util.AsyncXHRPartfileBuffer = AsyncXHRPartfileBuffer;
|
||||
v86util.AsyncFileBuffer = AsyncFileBuffer;
|
||||
v86util.SyncFileBuffer = SyncFileBuffer;
|
||||
|
||||
// Reads len characters at offset from Memory object mem as a JS string
|
||||
v86util.read_sized_string_from_mem = function read_sized_string_from_mem(mem, offset, len)
|
||||
{
|
||||
offset >>>= 0;
|
||||
len >>>= 0;
|
||||
return String.fromCharCode(...new Uint8Array(mem.buffer, offset, len));
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
* @param {Object} options
|
||||
|
@ -28,7 +37,11 @@ var ASYNC_SAFE = false;
|
|||
|
||||
http.open(options.method || "get", filename, true);
|
||||
|
||||
if(!options.as_text)
|
||||
if(options.as_json)
|
||||
{
|
||||
http.responseType = "json";
|
||||
}
|
||||
else
|
||||
{
|
||||
http.responseType = "arraybuffer";
|
||||
}
|
||||
|
@ -49,6 +62,16 @@ var ASYNC_SAFE = false;
|
|||
let start = options.range.start;
|
||||
let end = start + options.range.length - 1;
|
||||
http.setRequestHeader("Range", "bytes=" + start + "-" + end);
|
||||
|
||||
// Abort if server responds with complete file in response to range
|
||||
// request, to prevent downloading large files from broken http servers
|
||||
http.onreadystatechange = function()
|
||||
{
|
||||
if(http.status === 200)
|
||||
{
|
||||
http.abort();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
http.onload = function(e)
|
||||
|
@ -83,14 +106,14 @@ var ASYNC_SAFE = false;
|
|||
|
||||
if(options.range)
|
||||
{
|
||||
dbg_assert(!options.as_text);
|
||||
dbg_assert(!options.as_json);
|
||||
|
||||
fs["open"](filename, "r", (err, fd) =>
|
||||
{
|
||||
if(err) throw err;
|
||||
|
||||
let length = options.range.length;
|
||||
var buffer = new global["Buffer"](length);
|
||||
var buffer = Buffer.allocUnsafe(length);
|
||||
|
||||
fs["read"](fd, buffer, 0, length, options.range.start, (err, bytes_read) =>
|
||||
{
|
||||
|
@ -108,7 +131,7 @@ var ASYNC_SAFE = false;
|
|||
else
|
||||
{
|
||||
var o = {
|
||||
encoding: options.as_text ? "utf-8" : null,
|
||||
encoding: options.as_json ? "utf-8" : null,
|
||||
};
|
||||
|
||||
fs["readFile"](filename, o, function(err, data)
|
||||
|
@ -121,7 +144,11 @@ var ASYNC_SAFE = false;
|
|||
{
|
||||
var result = data;
|
||||
|
||||
if(!options.as_text)
|
||||
if(options.as_json)
|
||||
{
|
||||
result = JSON.parse(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new Uint8Array(result).buffer;
|
||||
}
|
||||
|
@ -165,18 +192,12 @@ var ASYNC_SAFE = false;
|
|||
}
|
||||
else
|
||||
{
|
||||
cb({ header });
|
||||
const error = "`Range: bytes=...` header not supported (Got `" + header + "`)";
|
||||
cb(error);
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
Range: "bytes=0-0",
|
||||
|
||||
//"Accept-Encoding": "",
|
||||
|
||||
// Added by Chromium, but can cause the whole file to be sent
|
||||
// Settings this to empty also causes problems and Chromium
|
||||
// doesn't seem to create this header any more
|
||||
//"If-Range": "",
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -198,7 +219,7 @@ var ASYNC_SAFE = false;
|
|||
this.block_size = 256;
|
||||
this.byteLength = size;
|
||||
|
||||
this.loaded_blocks = {};
|
||||
this.loaded_blocks = Object.create(null);
|
||||
|
||||
this.onload = undefined;
|
||||
this.onprogress = undefined;
|
||||
|
@ -208,7 +229,7 @@ var ASYNC_SAFE = false;
|
|||
{
|
||||
if(this.byteLength !== undefined)
|
||||
{
|
||||
this.onload && this.onload({});
|
||||
this.onload && this.onload(Object.create(null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -218,15 +239,13 @@ var ASYNC_SAFE = false;
|
|||
{
|
||||
if(error)
|
||||
{
|
||||
console.assert(false,
|
||||
"Cannot use: " + this.filename + ". " +
|
||||
"`Range: bytes=...` header not supported (Got `" + error.header + "`)");
|
||||
console.assert(false, "Cannot use: " + this.filename + ". " + error);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(size >= 0);
|
||||
this.byteLength = size;
|
||||
this.onload && this.onload({});
|
||||
this.onload && this.onload(Object.create(null));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -306,7 +325,7 @@ var ASYNC_SAFE = false;
|
|||
/**
|
||||
* Relies on this.byteLength, this.loaded_blocks and this.block_size
|
||||
*
|
||||
* @this {AsyncFileBuffer|AsyncXHRBuffer}
|
||||
* @this {AsyncFileBuffer|AsyncXHRBuffer|AsyncXHRPartfileBuffer}
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {!Uint8Array} data
|
||||
|
@ -344,7 +363,7 @@ var ASYNC_SAFE = false;
|
|||
};
|
||||
|
||||
/**
|
||||
* @this {AsyncFileBuffer|AsyncXHRBuffer}
|
||||
* @this {AsyncFileBuffer|AsyncXHRBuffer|AsyncXHRPartfileBuffer}
|
||||
* @param {number} offset
|
||||
* @param {number} len
|
||||
* @param {!Uint8Array} block
|
||||
|
@ -381,17 +400,13 @@ var ASYNC_SAFE = false;
|
|||
|
||||
AsyncXHRBuffer.prototype.get_written_blocks = function()
|
||||
{
|
||||
var count = 0;
|
||||
for(var _ in this.loaded_blocks)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
var count = Object.keys(this.loaded_blocks).length;
|
||||
|
||||
var buffer = new Uint8Array(count * this.block_size);
|
||||
var indices = [];
|
||||
|
||||
var i = 0;
|
||||
for(var index in this.loaded_blocks)
|
||||
for(var index of Object.keys(this.loaded_blocks))
|
||||
{
|
||||
var block = this.loaded_blocks[index];
|
||||
dbg_assert(block.length === this.block_size);
|
||||
|
@ -411,6 +426,122 @@ var ASYNC_SAFE = false;
|
|||
};
|
||||
};
|
||||
|
||||
AsyncXHRBuffer.prototype.get_state = function()
|
||||
{
|
||||
const state = [];
|
||||
const loaded_blocks = [];
|
||||
|
||||
for(let [index, block] of Object.entries(this.loaded_blocks))
|
||||
{
|
||||
dbg_assert(isFinite(+index));
|
||||
loaded_blocks.push([+index, block]);
|
||||
}
|
||||
|
||||
state[0] = loaded_blocks;
|
||||
return state;
|
||||
};
|
||||
|
||||
AsyncXHRBuffer.prototype.set_state = function(state)
|
||||
{
|
||||
const loaded_blocks = state[0];
|
||||
this.loaded_blocks = Object.create(null);
|
||||
|
||||
for(let [index, block] of Object.values(loaded_blocks))
|
||||
{
|
||||
this.loaded_blocks[index] = block;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronous access to ArrayBuffer, loading blocks lazily as needed,
|
||||
* downloading files named filename-\d-\d.ext.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} filename Name of the file to download
|
||||
* @param {number|undefined} size
|
||||
*/
|
||||
function AsyncXHRPartfileBuffer(filename, size)
|
||||
{
|
||||
const parts = filename.match(/(.*)(\..*)/);
|
||||
|
||||
if(parts)
|
||||
{
|
||||
this.basename = parts[1];
|
||||
this.extension = parts[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
this.basename = filename;
|
||||
this.extension = "";
|
||||
}
|
||||
|
||||
/** @const */
|
||||
this.block_size = 256;
|
||||
this.byteLength = size;
|
||||
|
||||
this.loaded_blocks = Object.create(null);
|
||||
|
||||
this.onload = undefined;
|
||||
this.onprogress = undefined;
|
||||
}
|
||||
|
||||
AsyncXHRPartfileBuffer.prototype.load = function()
|
||||
{
|
||||
if(this.byteLength !== undefined)
|
||||
{
|
||||
this.onload && this.onload(Object.create(null));
|
||||
return;
|
||||
}
|
||||
dbg_assert(false);
|
||||
this.onload && this.onload(Object.create(null));
|
||||
};
|
||||
AsyncXHRPartfileBuffer.prototype.get_from_cache = AsyncXHRBuffer.prototype.get_from_cache;
|
||||
|
||||
/**
|
||||
* @param {number} offset
|
||||
* @param {number} len
|
||||
* @param {function(!Uint8Array)} fn
|
||||
*/
|
||||
AsyncXHRPartfileBuffer.prototype.get = function(offset, len, fn)
|
||||
{
|
||||
console.assert(offset + len <= this.byteLength);
|
||||
console.assert(offset % this.block_size === 0);
|
||||
console.assert(len % this.block_size === 0);
|
||||
console.assert(len);
|
||||
|
||||
var block = this.get_from_cache(offset, len, fn);
|
||||
if(block)
|
||||
{
|
||||
if(ASYNC_SAFE)
|
||||
{
|
||||
setTimeout(fn.bind(this, block), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
fn(block);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const part_filename = this.basename + "-" + offset + "-" + (offset + len) + this.extension;
|
||||
|
||||
v86util.load_file(part_filename, {
|
||||
done: function done(buffer)
|
||||
{
|
||||
dbg_assert(buffer.byteLength === len);
|
||||
var block = new Uint8Array(buffer);
|
||||
this.handle_read(offset, len, block);
|
||||
fn(block);
|
||||
}.bind(this),
|
||||
});
|
||||
};
|
||||
|
||||
AsyncXHRPartfileBuffer.prototype.set = AsyncXHRBuffer.prototype.set;
|
||||
AsyncXHRPartfileBuffer.prototype.handle_read = AsyncXHRBuffer.prototype.handle_read;
|
||||
AsyncXHRPartfileBuffer.prototype.get_written_blocks = AsyncXHRBuffer.prototype.get_written_blocks;
|
||||
AsyncXHRPartfileBuffer.prototype.get_state = AsyncXHRBuffer.prototype.get_state;
|
||||
AsyncXHRPartfileBuffer.prototype.set_state = AsyncXHRBuffer.prototype.set_state;
|
||||
|
||||
/**
|
||||
* Synchronous access to File, loading blocks from the input type=file
|
||||
* The whole file is loaded into memory during initialisation
|
||||
|
@ -505,6 +636,20 @@ var ASYNC_SAFE = false;
|
|||
fn(this.buffer);
|
||||
};
|
||||
|
||||
SyncFileBuffer.prototype.get_state = function()
|
||||
{
|
||||
const state = [];
|
||||
state[0] = this.byteLength;
|
||||
state[1] = new Uint8Array(this.buffer);
|
||||
return state;
|
||||
};
|
||||
|
||||
SyncFileBuffer.prototype.set_state = function(state)
|
||||
{
|
||||
this.byteLength = state[0];
|
||||
this.buffer = state[1].slice().buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronous access to File, loading blocks from the input type=file
|
||||
*
|
||||
|
@ -517,7 +662,7 @@ var ASYNC_SAFE = false;
|
|||
|
||||
/** @const */
|
||||
this.block_size = 256;
|
||||
this.loaded_blocks = {};
|
||||
this.loaded_blocks = Object.create(null);
|
||||
|
||||
this.onload = undefined;
|
||||
this.onprogress = undefined;
|
||||
|
@ -525,7 +670,7 @@ var ASYNC_SAFE = false;
|
|||
|
||||
AsyncFileBuffer.prototype.load = function()
|
||||
{
|
||||
this.onload && this.onload({});
|
||||
this.onload && this.onload(Object.create(null));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
var ON_LOCALHOST = !location.hostname.endsWith("copy.sh");
|
||||
|
||||
/** @const */
|
||||
var HOST = ON_LOCALHOST ? "" : "//i.copy.sh/";
|
||||
var HOST = ON_LOCALHOST ? "images/" : "//k.copy.sh/";
|
||||
|
||||
/**
|
||||
* @return {Object.<string, string>}
|
||||
|
@ -19,7 +19,7 @@
|
|||
for(var i = 0; i < query.length; i++)
|
||||
{
|
||||
var param = query[i].split("=");
|
||||
parameters[param[0]] = decodeURIComponent(param[1]);
|
||||
parameters[param[0]] = decodeURIComponent(param.slice(1).join("="));
|
||||
}
|
||||
|
||||
return parameters;
|
||||
|
@ -28,6 +28,8 @@
|
|||
function set_title(text)
|
||||
{
|
||||
document.title = text + " - Virtual x86" + (DEBUG ? " - debug" : "");
|
||||
const description = document.querySelector("meta[name=description]");
|
||||
description && (description.content = "Running " + text);
|
||||
}
|
||||
|
||||
function format_timestamp(time)
|
||||
|
@ -67,6 +69,13 @@
|
|||
var el = $("loading");
|
||||
el.style.display = "block";
|
||||
|
||||
if(e.file_name.endsWith(".wasm"))
|
||||
{
|
||||
const parts = e.file_name.split("/");
|
||||
el.textContent = "Fetching " + parts[parts.length - 1] + " ...";
|
||||
return;
|
||||
}
|
||||
|
||||
if(e.file_index === e.file_count - 1 && e.loaded >= e.total - 2048)
|
||||
{
|
||||
// last file is (almost) loaded
|
||||
|
@ -102,25 +111,22 @@
|
|||
|
||||
function $(id)
|
||||
{
|
||||
var el = document.getElementById(id);
|
||||
|
||||
if(!el)
|
||||
{
|
||||
dbg_log("Element with id `" + id + "` not found");
|
||||
}
|
||||
|
||||
return el;
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function onload()
|
||||
{
|
||||
if(!("responseType" in new XMLHttpRequest))
|
||||
if(!window.WebAssembly)
|
||||
{
|
||||
alert("Your browser is not supported " +
|
||||
"because it doesn't have XMLHttpRequest.responseType");
|
||||
alert("Your browser is not supported because it doesn't support WebAssembly");
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = "build/xterm.js";
|
||||
script.async = true;
|
||||
document.body.appendChild(script);
|
||||
|
||||
var settings = {};
|
||||
|
||||
$("start_emulation").onclick = function()
|
||||
|
@ -182,37 +188,40 @@
|
|||
debug_onload(settings);
|
||||
}
|
||||
|
||||
// Abandonware OS images are from https://winworldpc.com/library/operating-systems
|
||||
var oses = [
|
||||
{
|
||||
id: "archlinux",
|
||||
state: {
|
||||
"url": HOST + "images/v86state.bin",
|
||||
"size": 142770436,
|
||||
},
|
||||
name: "Arch Linux",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
memory_size: 512 * 1024 * 1024,
|
||||
vga_memory_size: 8 * 1024 * 1024,
|
||||
|
||||
// required for restoring state, should not be used when booted on 9p
|
||||
hda: {
|
||||
"url": HOST + "images/arch3.img",
|
||||
"size": 16 * 1024 * 1024 * 1024,
|
||||
"async": true,
|
||||
state: {
|
||||
"url": HOST + "arch_state.bin.zst",
|
||||
},
|
||||
|
||||
filesystem: {
|
||||
"basefs": {
|
||||
"url": HOST + "images/fs.json",
|
||||
"size": 10232633,
|
||||
},
|
||||
"baseurl": HOST + "arch/",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "haiku",
|
||||
memory_size: 512 * 1024 * 1024,
|
||||
hda: {
|
||||
url: HOST + "haiku.img",
|
||||
async: true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
size: 1 * 1024 * 1024 * 1024,
|
||||
},
|
||||
state: {
|
||||
url: HOST + "haiku_state.bin.zst",
|
||||
},
|
||||
name: "Haiku",
|
||||
},
|
||||
{
|
||||
id: "msdos",
|
||||
hda: {
|
||||
"url": HOST + "images/msdos.img",
|
||||
"url": HOST + "msdos.img",
|
||||
"size": 8 * 1024 * 1024,
|
||||
"async": false,
|
||||
},
|
||||
boot_order: 0x132,
|
||||
name: "MS-DOS",
|
||||
|
@ -220,152 +229,372 @@
|
|||
{
|
||||
id: "freedos",
|
||||
fda: {
|
||||
"url": HOST + "images/freedos722.img",
|
||||
"url": HOST + "freedos722.img",
|
||||
"size": 737280,
|
||||
"async": false,
|
||||
},
|
||||
name: "FreeDOS",
|
||||
},
|
||||
{
|
||||
id: "oberon",
|
||||
fda: {
|
||||
"url": HOST + "images/oberon-boot.dsk",
|
||||
"size": 1440 * 1024,
|
||||
},
|
||||
hda: {
|
||||
"url": HOST + "images/oberon.dsk",
|
||||
"size": 41943040,
|
||||
"url": HOST + "oberon.img",
|
||||
"size": 24 * 1024 * 1024,
|
||||
"async": false,
|
||||
},
|
||||
name: "Oberon",
|
||||
},
|
||||
{
|
||||
id: "windows1",
|
||||
fda: {
|
||||
"url": HOST + "images/windows101.img",
|
||||
"url": HOST + "windows101.img",
|
||||
"size": 1474560,
|
||||
"async": false,
|
||||
},
|
||||
name: "Windows",
|
||||
},
|
||||
{
|
||||
id: "linux26",
|
||||
cdrom: {
|
||||
"url": HOST + "images/linux.iso",
|
||||
"size": 5666816,
|
||||
"url": HOST + "linux.iso",
|
||||
"size": 6547456,
|
||||
"async": false,
|
||||
},
|
||||
name: "Linux",
|
||||
},
|
||||
{
|
||||
id: "linux3",
|
||||
cdrom: {
|
||||
"url": HOST + "images/linux3.iso",
|
||||
"url": HOST + "linux3.iso",
|
||||
"size": 8624128,
|
||||
"async": false,
|
||||
},
|
||||
name: "Linux",
|
||||
},
|
||||
{
|
||||
id: "linux4",
|
||||
cdrom: {
|
||||
"url": HOST + "linux4.iso",
|
||||
"size": 7731200,
|
||||
"async": false,
|
||||
},
|
||||
name: "Linux",
|
||||
filesystem: {},
|
||||
},
|
||||
{
|
||||
id: "buildroot",
|
||||
bzimage: {
|
||||
url: HOST + "buildroot-bzimage.bin",
|
||||
size: 5166352,
|
||||
async: false,
|
||||
},
|
||||
name: "Buildroot Linux",
|
||||
filesystem: {},
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",
|
||||
},
|
||||
{
|
||||
id: "dsl",
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
cdrom: {
|
||||
url: HOST + "dsl-4.11.rc2.iso",
|
||||
size: 52824064,
|
||||
async: false,
|
||||
},
|
||||
name: "Damn Small Linux",
|
||||
homepage: "http://www.damnsmalllinux.org/",
|
||||
},
|
||||
{
|
||||
id: "minix",
|
||||
name: "Minix",
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
cdrom: {
|
||||
url: HOST + "minix-3.3.0.iso",
|
||||
size: 605581312,
|
||||
async: true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "kolibrios",
|
||||
fda: {
|
||||
"url": ON_LOCALHOST ?
|
||||
"images/kolibri.img" :
|
||||
HOST + "kolibri.img" :
|
||||
"//builds.kolibrios.org/eng/data/data/kolibri.img",
|
||||
"size": 1474560,
|
||||
"async": false,
|
||||
},
|
||||
name: "KolibriOS",
|
||||
homepage: "https://kolibrios.org/en/",
|
||||
},
|
||||
{
|
||||
id: "kolibrios-fallback",
|
||||
fda: {
|
||||
"url": HOST + "images/kolibri.img",
|
||||
"url": HOST + "kolibri.img",
|
||||
"size": 1474560,
|
||||
"async": false,
|
||||
},
|
||||
name: "KolibriOS",
|
||||
},
|
||||
{
|
||||
id: "openbsd",
|
||||
fda: {
|
||||
"url": HOST + "images/openbsd.img",
|
||||
"size": 1474560,
|
||||
hda: {
|
||||
"url": HOST + "openbsd.img",
|
||||
async: true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
size: 1073741824,
|
||||
},
|
||||
state: {
|
||||
url: HOST + "openbsd_state.bin.zst",
|
||||
},
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
name: "OpenBSD",
|
||||
},
|
||||
{
|
||||
id: "openbsd-boot",
|
||||
hda: {
|
||||
url: HOST + "openbsd.img",
|
||||
async: true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
size: 1073741824,
|
||||
},
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
name: "OpenBSD",
|
||||
//acpi: true, // doesn't seem to work
|
||||
},
|
||||
{
|
||||
id: "solos",
|
||||
fda: {
|
||||
"url": HOST + "images/os8.dsk",
|
||||
"url": HOST + "os8.img",
|
||||
"async": false,
|
||||
"size": 1474560,
|
||||
},
|
||||
name: "Sol OS",
|
||||
},
|
||||
{
|
||||
id: "dexos",
|
||||
cdrom: {
|
||||
"url": HOST + "images/DexOSv6.iso",
|
||||
"size": 1837056,
|
||||
},
|
||||
name: "DexOS",
|
||||
homepage: "http://oby.ro/os/",
|
||||
},
|
||||
{
|
||||
id: "bootchess",
|
||||
fda: {
|
||||
"url": HOST + "images/bootchess.img",
|
||||
"url": HOST + "bootchess.img",
|
||||
"async": false,
|
||||
"size": 1474560,
|
||||
},
|
||||
name: "Bootchess",
|
||||
homepage: "http://www.pouet.net/prod.php?which=64962",
|
||||
},
|
||||
{
|
||||
id: "windows98",
|
||||
memory_size: 64 * 1024 * 1024,
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "images/windows98.img",
|
||||
"url": HOST + "windows98.img",
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
"size": 300 * 1024 * 1024,
|
||||
},
|
||||
name: "Windows 98",
|
||||
state: {
|
||||
"url": HOST + "images/windows98_state.bin",
|
||||
"size": 75705744,
|
||||
"url": HOST + "windows98_state.bin.zst",
|
||||
},
|
||||
preserve_mac_from_state_image: true,
|
||||
},
|
||||
{
|
||||
id: "windows98-boot",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "windows98.img",
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
"size": 300 * 1024 * 1024,
|
||||
},
|
||||
name: "Windows 98",
|
||||
},
|
||||
{
|
||||
id: "windows95",
|
||||
memory_size: 32 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "images/W95.IMG",
|
||||
"url": HOST + "w95.img",
|
||||
"size": 242049024,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
name: "Windows 95",
|
||||
state: {
|
||||
"url": HOST + "images/windows95_state.bin",
|
||||
"size": 42151316,
|
||||
"url": HOST + "windows95_state.bin.zst",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "freebsd",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
state: {
|
||||
"url": HOST + "images/freebsd_state.bin",
|
||||
"size": 142815292,
|
||||
},
|
||||
id: "windows95-boot",
|
||||
memory_size: 32 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "images/freebsd3.img",
|
||||
"size": 17179869184,
|
||||
"url": HOST + "w95.img",
|
||||
"size": 242049024,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
name: "Windows 95",
|
||||
},
|
||||
{
|
||||
id: "windows30",
|
||||
memory_size: 64 * 1024 * 1024,
|
||||
cdrom: {
|
||||
"url": HOST + "Win30.iso",
|
||||
"async": false,
|
||||
},
|
||||
name: "Windows 3.0",
|
||||
},
|
||||
{
|
||||
id: "freebsd",
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "freebsd.img",
|
||||
"size": 2147483648,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
state: {
|
||||
"url": HOST + "freebsd_state.bin.zst",
|
||||
},
|
||||
name: "FreeBSD",
|
||||
},
|
||||
{
|
||||
id: "reactos",
|
||||
id: "freebsd-boot",
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
cdrom: {
|
||||
"url": HOST + "images/ReactOS-0.4.9-RC-Live.iso",
|
||||
hda: {
|
||||
"url": HOST + "freebsd.img",
|
||||
"size": 2147483648,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
state: {
|
||||
"url": HOST + "images/reactos_state.bin",
|
||||
"size": 276971224,
|
||||
name: "FreeBSD",
|
||||
},
|
||||
{
|
||||
id: "reactos-livecd",
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "reactos-livecd-0.4.15-dev-73-g03c09c9-x86-gcc-lin-dbg.iso",
|
||||
"size": 250609664,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
name: "ReactOS",
|
||||
description: 'Running <a href="https://reactos.org/">ReactOS</a>',
|
||||
homepage: "https://reactos.org/",
|
||||
},
|
||||
{
|
||||
id: "reactos",
|
||||
memory_size: 512 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "reactos.img",
|
||||
"size": 500 * 1024 * 1024,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
state: {
|
||||
"url": HOST + "reactos_state.bin.zst",
|
||||
},
|
||||
preserve_mac_from_state_image: true,
|
||||
name: "ReactOS",
|
||||
homepage: "https://reactos.org/",
|
||||
},
|
||||
{
|
||||
id: "reactos-boot",
|
||||
memory_size: 512 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "reactos.img",
|
||||
"size": 500 * 1024 * 1024,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
name: "ReactOS",
|
||||
homepage: "https://reactos.org/",
|
||||
},
|
||||
{
|
||||
id: "skift",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
cdrom: {
|
||||
"url": HOST + "skift-20200910.iso",
|
||||
"size": 64452608,
|
||||
"async": false,
|
||||
},
|
||||
name: "Skift",
|
||||
homepage: "https://skiftos.org/",
|
||||
},
|
||||
{
|
||||
id: "openwrt",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "openwrt-18.06.1-x86-legacy-combined-squashfs.img",
|
||||
"size": 19846474,
|
||||
"async": false,
|
||||
},
|
||||
name: "OpenWrt",
|
||||
},
|
||||
{
|
||||
id: "qnx",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
fda: {
|
||||
url: HOST + "qnx-demo-network-4.05.img",
|
||||
size: 1474560,
|
||||
async: false
|
||||
},
|
||||
name: "QNX 4.05",
|
||||
},
|
||||
{
|
||||
id: "9front",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
hda: {
|
||||
url: HOST + "9front-7781.38dcaeaa222c.386.iso",
|
||||
size: 496388096,
|
||||
async: true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
state: {
|
||||
"url": HOST + "9front_state.bin.zst",
|
||||
},
|
||||
acpi: true,
|
||||
name: "9front",
|
||||
},
|
||||
{
|
||||
id: "9front-boot",
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
hda: {
|
||||
url: HOST + "9front-7781.38dcaeaa222c.386.iso",
|
||||
size: 496388096,
|
||||
async: true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
acpi: true,
|
||||
name: "9front",
|
||||
},
|
||||
{
|
||||
id: "mobius",
|
||||
memory_size: 64 * 1024 * 1024,
|
||||
fda: {
|
||||
"url": HOST + "mobius-fd-release5.img",
|
||||
"size": 1474560,
|
||||
"async": false,
|
||||
},
|
||||
name: "Mobius",
|
||||
},
|
||||
{
|
||||
id: "android",
|
||||
memory_size: 512 * 1024 * 1024,
|
||||
cdrom: {
|
||||
"url": HOST + "android-x86-1.6-r2.iso",
|
||||
"size": 54661120,
|
||||
"async": true,
|
||||
use_parts: !ON_LOCALHOST,
|
||||
},
|
||||
name: "Android",
|
||||
},
|
||||
{
|
||||
id: "tinycore",
|
||||
memory_size: 256 * 1024 * 1024,
|
||||
hda: {
|
||||
"url": HOST + "TinyCore-11.0.iso",
|
||||
"async": false,
|
||||
},
|
||||
name: "Tinycore",
|
||||
homepage: "http://www.tinycorelinux.net/",
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -408,6 +637,14 @@
|
|||
var query_args = get_query_arguments();
|
||||
var profile = query_args["profile"];
|
||||
|
||||
if(!profile && !DEBUG)
|
||||
{
|
||||
const link = document.createElement("link");
|
||||
link.rel = "prefetch";
|
||||
link.href = "build/v86.wasm";
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
if(query_args["use_bochs_bios"])
|
||||
{
|
||||
settings.use_bochs_bios = true;
|
||||
|
@ -427,8 +664,9 @@
|
|||
|
||||
if(element)
|
||||
{
|
||||
element.onclick = function(infos, element)
|
||||
element.onclick = function(infos, element, e)
|
||||
{
|
||||
e.preventDefault();
|
||||
set_profile(infos.id);
|
||||
element.blur();
|
||||
|
||||
|
@ -491,7 +729,13 @@
|
|||
settings.cdrom = infos.cdrom;
|
||||
settings.hda = infos.hda;
|
||||
settings.multiboot = infos.multiboot;
|
||||
settings.bzimage = infos.bzimage;
|
||||
settings.initrd = infos.initrd;
|
||||
settings.cmdline = infos.cmdline;
|
||||
settings.bzimage_initrd_from_filesystem = infos.bzimage_initrd_from_filesystem;
|
||||
settings.preserve_mac_from_state_image = infos.preserve_mac_from_state_image;
|
||||
|
||||
settings.acpi = infos.acpi;
|
||||
settings.memory_size = infos.memory_size;
|
||||
settings.vga_memory_size = infos.vga_memory_size;
|
||||
|
||||
|
@ -502,10 +746,15 @@
|
|||
settings.boot_order = infos.boot_order;
|
||||
}
|
||||
|
||||
if(!DEBUG && infos.description)
|
||||
if(!DEBUG && infos.homepage)
|
||||
{
|
||||
$("description").style.display = "block";
|
||||
$("description").innerHTML = "<br>" + infos.description;
|
||||
const link = document.createElement("a");
|
||||
link.href = infos.homepage;
|
||||
link.textContent = infos.name;
|
||||
link.target = "_blank";
|
||||
$("description").appendChild(document.createTextNode("Running "));
|
||||
$("description").appendChild(link);
|
||||
}
|
||||
|
||||
start_emulation(settings, done);
|
||||
|
@ -528,21 +777,10 @@
|
|||
{
|
||||
// called on window.onload, in debug mode
|
||||
|
||||
//settings.filesystem = {
|
||||
// baseurl: "http://localhost/v86-images/arch/",
|
||||
// basefs: "http://localhost/v86-images/fs.json",
|
||||
//};
|
||||
|
||||
//$("restore_state").onchange = function()
|
||||
//{
|
||||
//};
|
||||
|
||||
//$("start_test").onclick = function()
|
||||
//{
|
||||
//};
|
||||
|
||||
var log_levels = $("log_levels");
|
||||
|
||||
if(log_levels)
|
||||
{
|
||||
for(var i = 0; i < LOG_NAMES.length; i++)
|
||||
{
|
||||
var mask = LOG_NAMES[i][0];
|
||||
|
@ -590,9 +828,7 @@
|
|||
|
||||
target.blur();
|
||||
};
|
||||
|
||||
var debug_infos = $("debug_infos");
|
||||
debug_infos.textContent = "ACPI: " + (ENABLE_ACPI ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", onload, false);
|
||||
|
@ -654,6 +890,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
const networking_proxy = $("networking_proxy").value;
|
||||
const disable_audio = $("disable_audio").checked;
|
||||
const enable_acpi = settings.acpi === undefined ? $("enable_acpi").checked : settings.acpi;
|
||||
|
||||
/** @const */
|
||||
var BIOSPATH = "bios/";
|
||||
|
||||
|
@ -666,13 +906,8 @@
|
|||
{
|
||||
var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";
|
||||
var vgabiosfile = DEBUG ? "vgabios-debug.bin" : "vgabios.bin";
|
||||
//var biosfile = DEBUG ? "seabios-ultradebug.bin" : "seabios.bin";
|
||||
//var vgabiosfile = DEBUG ? "vgabios-ultradebug.bin" : "vgabios.bin";
|
||||
}
|
||||
|
||||
//var biosfile = "seabios-qemu.bin";
|
||||
//var vgabiosfile = "vgabios-qemu.bin";
|
||||
|
||||
var bios;
|
||||
var vga_bios;
|
||||
|
||||
|
@ -692,12 +927,11 @@
|
|||
"vga_memory_size": vga_memory_size,
|
||||
|
||||
"screen_container": $("screen_container"),
|
||||
"serial_container": $("serial"),
|
||||
"serial_container_xtermjs": $("terminal"),
|
||||
|
||||
"boot_order": settings.boot_order || parseInt($("boot_order").value, 16) || 0,
|
||||
|
||||
"network_relay_url": "wss://relay.widgetry.org/",
|
||||
//"network_relay_url": "ws://localhost:8001/",
|
||||
"network_relay_url": ON_LOCALHOST ? "ws://localhost:8080/" : networking_proxy,
|
||||
|
||||
"bios": bios,
|
||||
"vga_bios": vga_bios,
|
||||
|
@ -708,9 +942,16 @@
|
|||
"cdrom": settings.cdrom,
|
||||
|
||||
"multiboot": settings.multiboot,
|
||||
"bzimage": settings.bzimage,
|
||||
"initrd": settings.initrd,
|
||||
"cmdline": settings.cmdline,
|
||||
"bzimage_initrd_from_filesystem": settings.bzimage_initrd_from_filesystem,
|
||||
|
||||
"acpi": enable_acpi,
|
||||
"initial_state": settings.initial_state,
|
||||
"filesystem": settings.filesystem || {},
|
||||
"disable_speaker": disable_audio,
|
||||
"preserve_mac_from_state_image": settings.preserve_mac_from_state_image,
|
||||
|
||||
"autostart": true,
|
||||
});
|
||||
|
@ -724,6 +965,43 @@
|
|||
debug_start(emulator);
|
||||
}
|
||||
|
||||
if(emulator.v86.cpu.wm.exports["profiler_is_enabled"]())
|
||||
{
|
||||
const CLEAR_STATS = false;
|
||||
|
||||
var panel = document.createElement("pre");
|
||||
document.body.appendChild(panel);
|
||||
|
||||
setInterval(function()
|
||||
{
|
||||
if(!emulator.is_running())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const text = print_stats.stats_to_string(emulator.v86.cpu);
|
||||
panel.textContent = text;
|
||||
|
||||
CLEAR_STATS && emulator.v86.cpu.clear_opstats();
|
||||
}, CLEAR_STATS ? 5000 : 1000);
|
||||
}
|
||||
|
||||
if(settings.id === "dsl")
|
||||
{
|
||||
setTimeout(() => {
|
||||
// hack: Start automatically
|
||||
emulator.keyboard_send_text("\n");
|
||||
}, 3000);
|
||||
}
|
||||
else if(settings.id == "android")
|
||||
{
|
||||
setTimeout(() => {
|
||||
// hack: select vesa mode and start automatically
|
||||
emulator.keyboard_send_scancodes([0xe050, 0xe050 | 0x80]);
|
||||
emulator.keyboard_send_text("\n");
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
init_ui(settings, emulator);
|
||||
|
||||
done && done(emulator);
|
||||
|
@ -808,24 +1086,32 @@
|
|||
var last_tick = 0;
|
||||
var running_time = 0;
|
||||
var last_instr_counter = 0;
|
||||
var interval;
|
||||
var interval = null;
|
||||
var os_uses_mouse = false;
|
||||
var total_instructions = 0;
|
||||
|
||||
function update_info()
|
||||
{
|
||||
var now = Date.now();
|
||||
|
||||
var instruction_counter = emulator.get_instruction_counter();
|
||||
var last_ips = instruction_counter - last_instr_counter;
|
||||
|
||||
if(instruction_counter < last_instr_counter)
|
||||
{
|
||||
// 32-bit wrap-around
|
||||
last_instr_counter -= 0x100000000;
|
||||
}
|
||||
|
||||
var last_ips = instruction_counter - last_instr_counter;
|
||||
last_instr_counter = instruction_counter;
|
||||
total_instructions += last_ips;
|
||||
|
||||
var delta_time = now - last_tick;
|
||||
running_time += delta_time;
|
||||
last_tick = now;
|
||||
|
||||
$("speed").textContent = last_ips / delta_time | 0;
|
||||
$("avg_speed").textContent = instruction_counter / running_time | 0;
|
||||
$("speed").textContent = (last_ips / 1000 / delta_time).toFixed(1);
|
||||
$("avg_speed").textContent = (total_instructions / 1000 / running_time).toFixed(1);
|
||||
$("running_time").textContent = format_timestamp(running_time / 1000 | 0);
|
||||
}
|
||||
|
||||
|
@ -838,33 +1124,52 @@
|
|||
emulator.add_listener("emulator-stopped", function()
|
||||
{
|
||||
update_info();
|
||||
if(interval !== null)
|
||||
{
|
||||
clearInterval(interval);
|
||||
}
|
||||
});
|
||||
|
||||
var stats_9p = {
|
||||
read: 0,
|
||||
write: 0,
|
||||
files: [],
|
||||
};
|
||||
|
||||
emulator.add_listener("9p-read-start", function()
|
||||
emulator.add_listener("9p-read-start", function(args)
|
||||
{
|
||||
const file = args[0];
|
||||
stats_9p.files.push(file);
|
||||
$("info_filesystem").style.display = "block";
|
||||
$("info_filesystem_status").textContent = "Loading ...";
|
||||
$("info_filesystem_last_file").textContent = file;
|
||||
});
|
||||
emulator.add_listener("9p-read-end", function(args)
|
||||
{
|
||||
stats_9p.read += args[1];
|
||||
|
||||
$("info_filesystem_status").textContent = "Idle";
|
||||
$("info_filesystem_last_file").textContent = args[0];
|
||||
$("info_filesystem_bytes_read").textContent = stats_9p.read;
|
||||
|
||||
const file = args[0];
|
||||
stats_9p.files = stats_9p.files.filter(f => f !== file);
|
||||
|
||||
if(stats_9p.files[0])
|
||||
{
|
||||
$("info_filesystem_last_file").textContent = stats_9p.files[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$("info_filesystem_status").textContent = "Idle";
|
||||
}
|
||||
});
|
||||
emulator.add_listener("9p-write-end", function(args)
|
||||
{
|
||||
stats_9p.write += args[1];
|
||||
|
||||
$("info_filesystem_last_file").textContent = args[0];
|
||||
$("info_filesystem_bytes_written").textContent = stats_9p.write;
|
||||
|
||||
if(!stats_9p.files[0])
|
||||
{
|
||||
$("info_filesystem_last_file").textContent = args[0];
|
||||
}
|
||||
});
|
||||
|
||||
var stats_storage = {
|
||||
|
@ -997,7 +1302,8 @@
|
|||
|
||||
$("memory_dump").onclick = function()
|
||||
{
|
||||
dump_file(emulator.v86.cpu.mem8, "v86memory.bin");
|
||||
const mem8 = emulator.v86.cpu.mem8;
|
||||
dump_file(new Uint8Array(mem8.buffer, mem8.byteOffset, mem8.length), "v86memory.bin");
|
||||
$("memory_dump").blur();
|
||||
};
|
||||
|
||||
|
@ -1172,6 +1478,7 @@
|
|||
|
||||
$("screen_container").addEventListener("mousedown", (e) =>
|
||||
{
|
||||
e.preventDefault();
|
||||
phone_keyboard.focus();
|
||||
}, false);
|
||||
|
||||
|
@ -1182,8 +1489,6 @@
|
|||
$("take_screenshot").blur();
|
||||
};
|
||||
|
||||
$("serial").style.display = "block";
|
||||
|
||||
window.addEventListener("keydown", ctrl_w_rescue, false);
|
||||
window.addEventListener("keyup", ctrl_w_rescue, false);
|
||||
window.addEventListener("blur", ctrl_w_rescue, false);
|
||||
|
@ -1267,31 +1572,14 @@
|
|||
// called as soon as soon as emulation is started, in debug mode
|
||||
var debug = emulator.v86.cpu.debug;
|
||||
|
||||
var debug_infos = $("debug_infos");
|
||||
debug_infos.textContent += " | logging ops: " +
|
||||
(debug.step_mode || debug.trace_all ? "yes" : "no");
|
||||
|
||||
$("step").onclick = debug.step.bind(debug);
|
||||
$("run_until").onclick = debug.run_until.bind(debug);
|
||||
$("dump_gdt").onclick = debug.dump_gdt_ldt.bind(debug);
|
||||
$("dump_idt").onclick = debug.dump_idt.bind(debug);
|
||||
$("dump_regs").onclick = debug.dump_regs.bind(debug);
|
||||
$("dump_pt").onclick = debug.dump_page_directory.bind(debug);
|
||||
$("dump_instructions").onclick = debug.dump_instructions.bind(debug);
|
||||
|
||||
$("dump_log").onclick = function()
|
||||
{
|
||||
dump_file(log_data, "v86.log");
|
||||
};
|
||||
|
||||
$("dump_instructions_file").onclick = function()
|
||||
{
|
||||
var ins = debug.get_instructions();
|
||||
|
||||
if(ins)
|
||||
{
|
||||
dump_file(ins, "trace.txt");
|
||||
}
|
||||
dump_file(log_data.join(""), "v86.log");
|
||||
};
|
||||
|
||||
var cpu = emulator.v86.cpu;
|
||||
|
@ -1301,6 +1589,8 @@
|
|||
{
|
||||
$("debug_panel").textContent =
|
||||
cpu.debug.get_regs_short().join("\n") + "\n" + cpu.debug.get_state();
|
||||
|
||||
$("dump_log").value = "Dump log" + (log_data.length ? " (" + log_data.length + " lines)" : "");
|
||||
}, 1000);
|
||||
|
||||
// helps debugging
|
||||
|
|
|
@ -13,7 +13,7 @@ function ModemAdapter()
|
|||
this.enabled = true;
|
||||
this.socket = new WebSocket("ws://localhost:2080");
|
||||
|
||||
this.socket.onopen = this.onopen.bind(this);;
|
||||
this.socket.onopen = this.onopen.bind(this);
|
||||
this.socket.onmessage = this.onmessage.bind(this);
|
||||
this.socket.onclose = this.onclose.bind(this);
|
||||
this.socket.onerror = this.onerror.bind(this);
|
||||
|
@ -60,4 +60,4 @@ ModemAdapter.prototype.put_chr = function(chr)
|
|||
{
|
||||
this.socket.send(chr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -34,6 +34,10 @@ function MouseAdapter(bus, screen_container)
|
|||
|
||||
this.destroy = function()
|
||||
{
|
||||
if(typeof window === "undefined")
|
||||
{
|
||||
return;
|
||||
}
|
||||
window.removeEventListener("touchstart", touch_start_handler, false);
|
||||
window.removeEventListener("touchend", touch_end_handler, false);
|
||||
window.removeEventListener("touchmove", mousemove_handler, false);
|
||||
|
@ -41,7 +45,7 @@ function MouseAdapter(bus, screen_container)
|
|||
window.removeEventListener("mousedown", mousedown_handler, false);
|
||||
window.removeEventListener("mouseup", mouseup_handler, false);
|
||||
window.removeEventListener("DOMMouseScroll", mousewheel_handler, false);
|
||||
window.removeEventListener("mousewheel", mousewheel_handler, false);
|
||||
window.removeEventListener("mousewheel", mousewheel_handler, { passive: false });
|
||||
};
|
||||
|
||||
this.init = function()
|
||||
|
@ -59,7 +63,7 @@ function MouseAdapter(bus, screen_container)
|
|||
window.addEventListener("mousedown", mousedown_handler, false);
|
||||
window.addEventListener("mouseup", mouseup_handler, false);
|
||||
window.addEventListener("DOMMouseScroll", mousewheel_handler, false);
|
||||
window.addEventListener("mousewheel", mousewheel_handler, false);
|
||||
window.addEventListener("mousewheel", mousewheel_handler, { passive: false });
|
||||
};
|
||||
this.init();
|
||||
|
||||
|
@ -84,6 +88,15 @@ function MouseAdapter(bus, screen_container)
|
|||
return false;
|
||||
}
|
||||
|
||||
const MOVE_MOUSE_WHEN_OVER_SCREEN_ONLY = true;
|
||||
|
||||
if(MOVE_MOUSE_WHEN_OVER_SCREEN_ONLY)
|
||||
{
|
||||
var parent = screen_container || document.body;
|
||||
return document.pointerLockElement || is_child(e.target, parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(e.type === "mousemove" || e.type === "touchmove")
|
||||
{
|
||||
return true;
|
||||
|
@ -91,12 +104,12 @@ function MouseAdapter(bus, screen_container)
|
|||
|
||||
if(e.type === "mousewheel" || e.type === "DOMMouseScroll")
|
||||
{
|
||||
var parent = screen_container || document.body;
|
||||
return is_child(e.target, parent);
|
||||
}
|
||||
|
||||
return !e.target || e.target.nodeName !== "INPUT" && e.target.nodeName !== "TEXTAREA";
|
||||
}
|
||||
}
|
||||
|
||||
function touch_start_handler(e)
|
||||
{
|
||||
|
@ -239,7 +252,7 @@ function MouseAdapter(bus, screen_container)
|
|||
}
|
||||
else
|
||||
{
|
||||
console.log("Unknown event.which: " + e.which);
|
||||
dbg_log("Unknown event.which: " + e.which);
|
||||
}
|
||||
mouse.bus.send("mouse-click", [left_down, middle_down, right_down]);
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
*/
|
||||
function NetworkAdapter(url, bus)
|
||||
{
|
||||
this.send_data = function(x) {};
|
||||
|
||||
this.bus = bus;
|
||||
this.socket = undefined;
|
||||
|
||||
|
@ -75,6 +73,11 @@ NetworkAdapter.prototype.destroy = function()
|
|||
|
||||
NetworkAdapter.prototype.connect = function()
|
||||
{
|
||||
if(typeof WebSocket === "undefined")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.socket)
|
||||
{
|
||||
var state = this.socket.readyState;
|
||||
|
@ -95,19 +98,10 @@ NetworkAdapter.prototype.connect = function()
|
|||
|
||||
this.last_connect_attempt = Date.now();
|
||||
|
||||
try
|
||||
{
|
||||
this.socket = new WebSocket(this.url);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
this.handle_close(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
this.socket.binaryType = "arraybuffer";
|
||||
|
||||
this.socket.onopen = this.handle_open.bind(this);;
|
||||
this.socket.onopen = this.handle_open.bind(this);
|
||||
this.socket.onmessage = this.handle_message.bind(this);
|
||||
this.socket.onclose = this.handle_close.bind(this);
|
||||
this.socket.onerror = this.handle_error.bind(this);
|
||||
|
@ -132,4 +126,17 @@ NetworkAdapter.prototype.send = function(data)
|
|||
{
|
||||
this.socket.send(data);
|
||||
}
|
||||
};
|
||||
|
||||
NetworkAdapter.prototype.change_proxy = function(url)
|
||||
{
|
||||
this.url = url;
|
||||
|
||||
if(this.socket)
|
||||
{
|
||||
this.socket.onclose = function() {};
|
||||
this.socket.onerror = function() {};
|
||||
this.socket.close();
|
||||
this.socket = undefined;
|
||||
}
|
||||
};
|
||||
|
|
280
src/browser/print_stats.js
Normal file
280
src/browser/print_stats.js
Normal file
|
@ -0,0 +1,280 @@
|
|||
"use strict";
|
||||
|
||||
const print_stats = {
|
||||
stats_to_string: function(cpu)
|
||||
{
|
||||
return print_stats.print_misc_stats(cpu) +
|
||||
print_stats.print_instruction_counts(cpu);
|
||||
},
|
||||
|
||||
print_misc_stats: function(cpu)
|
||||
{
|
||||
let text = "";
|
||||
|
||||
const stat_names = [
|
||||
"COMPILE",
|
||||
"COMPILE_SKIPPED_NO_NEW_ENTRY_POINTS",
|
||||
"COMPILE_SUCCESS",
|
||||
"COMPILE_WRONG_ADDRESS_SPACE",
|
||||
"COMPILE_CUT_OFF_AT_END_OF_PAGE",
|
||||
"COMPILE_WITH_LOOP_SAFETY",
|
||||
"COMPILE_PAGE",
|
||||
"COMPILE_PAGE/COMPILE_SUCCESS",
|
||||
"COMPILE_PAGE_SKIPPED_NO_NEW_ENTRY_POINTS",
|
||||
"COMPILE_BASIC_BLOCK",
|
||||
"COMPILE_DUPLICATED_BASIC_BLOCK",
|
||||
"COMPILE_WASM_BLOCK",
|
||||
"COMPILE_WASM_LOOP",
|
||||
"COMPILE_DISPATCHER",
|
||||
"COMPILE_ENTRY_POINT",
|
||||
"COMPILE_WASM_TOTAL_BYTES",
|
||||
"COMPILE_WASM_TOTAL_BYTES/COMPILE_PAGE",
|
||||
"JIT_CACHE_OVERRIDE",
|
||||
"JIT_CACHE_OVERRIDE_DIFFERENT_STATE_FLAGS",
|
||||
"RUN_INTERPRETED",
|
||||
"RUN_INTERPRETED_PENDING",
|
||||
"RUN_INTERPRETED_NEAR_END_OF_PAGE",
|
||||
"RUN_INTERPRETED_DIFFERENT_STATE",
|
||||
"RUN_INTERPRETED_MISSED_COMPILED_ENTRY_RUN_INTERPRETED",
|
||||
"RUN_INTERPRETED_MISSED_COMPILED_ENTRY_LOOKUP",
|
||||
"RUN_INTERPRETED_STEPS",
|
||||
"RUN_FROM_CACHE",
|
||||
"RUN_FROM_CACHE_STEPS",
|
||||
"RUN_FROM_CACHE_STEPS/RUN_FROM_CACHE",
|
||||
"RUN_FROM_CACHE_STEPS/RUN_INTERPRETED_STEPS",
|
||||
"DIRECT_EXIT",
|
||||
"INDIRECT_JUMP",
|
||||
"INDIRECT_JUMP_NO_ENTRY",
|
||||
"NORMAL_PAGE_CHANGE",
|
||||
"NORMAL_FALLTHRU",
|
||||
"NORMAL_FALLTHRU_WITH_TARGET_BLOCK",
|
||||
"NORMAL_BRANCH",
|
||||
"NORMAL_BRANCH_WITH_TARGET_BLOCK",
|
||||
"CONDITIONAL_JUMP",
|
||||
"CONDITIONAL_JUMP_PAGE_CHANGE",
|
||||
"CONDITIONAL_JUMP_EXIT",
|
||||
"CONDITIONAL_JUMP_FALLTHRU",
|
||||
"CONDITIONAL_JUMP_FALLTHRU_WITH_TARGET_BLOCK",
|
||||
"CONDITIONAL_JUMP_BRANCH",
|
||||
"CONDITIONAL_JUMP_BRANCH_WITH_TARGET_BLOCK",
|
||||
"DISPATCHER_SMALL",
|
||||
"DISPATCHER_LARGE",
|
||||
"LOOP",
|
||||
"LOOP_SAFETY",
|
||||
"CONDITION_OPTIMISED",
|
||||
"CONDITION_UNOPTIMISED",
|
||||
"FAILED_PAGE_CHANGE",
|
||||
"SAFE_READ_FAST",
|
||||
"SAFE_READ_SLOW_PAGE_CROSSED",
|
||||
"SAFE_READ_SLOW_NOT_VALID",
|
||||
"SAFE_READ_SLOW_NOT_USER",
|
||||
"SAFE_READ_SLOW_IN_MAPPED_RANGE",
|
||||
"SAFE_WRITE_FAST",
|
||||
"SAFE_WRITE_SLOW_PAGE_CROSSED",
|
||||
"SAFE_WRITE_SLOW_NOT_VALID",
|
||||
"SAFE_WRITE_SLOW_NOT_USER",
|
||||
"SAFE_WRITE_SLOW_IN_MAPPED_RANGE",
|
||||
"SAFE_WRITE_SLOW_READ_ONLY",
|
||||
"SAFE_WRITE_SLOW_HAS_CODE",
|
||||
"SAFE_READ_WRITE_FAST",
|
||||
"SAFE_READ_WRITE_SLOW_PAGE_CROSSED",
|
||||
"SAFE_READ_WRITE_SLOW_NOT_VALID",
|
||||
"SAFE_READ_WRITE_SLOW_NOT_USER",
|
||||
"SAFE_READ_WRITE_SLOW_IN_MAPPED_RANGE",
|
||||
"SAFE_READ_WRITE_SLOW_READ_ONLY",
|
||||
"SAFE_READ_WRITE_SLOW_HAS_CODE",
|
||||
"PAGE_FAULT",
|
||||
"TLB_MISS",
|
||||
"DO_RUN",
|
||||
"DO_MANY_CYCLES",
|
||||
"CYCLE_INTERNAL",
|
||||
"INVALIDATE_ALL_MODULES_NO_FREE_WASM_INDICES",
|
||||
"INVALIDATE_MODULE_WRITTEN_WHILE_COMPILED",
|
||||
"INVALIDATE_MODULE_UNUSED_AFTER_OVERWRITE",
|
||||
"INVALIDATE_MODULE_DIRTY_PAGE",
|
||||
"INVALIDATE_PAGE_HAD_CODE",
|
||||
"INVALIDATE_PAGE_HAD_ENTRY_POINTS",
|
||||
"DIRTY_PAGE_DID_NOT_HAVE_CODE",
|
||||
"RUN_FROM_CACHE_EXIT_SAME_PAGE",
|
||||
"RUN_FROM_CACHE_EXIT_NEAR_END_OF_PAGE",
|
||||
"RUN_FROM_CACHE_EXIT_DIFFERENT_PAGE",
|
||||
"CLEAR_TLB",
|
||||
"FULL_CLEAR_TLB",
|
||||
"TLB_FULL",
|
||||
"TLB_GLOBAL_FULL",
|
||||
"MODRM_SIMPLE_REG",
|
||||
"MODRM_SIMPLE_REG_WITH_OFFSET",
|
||||
"MODRM_SIMPLE_CONST_OFFSET",
|
||||
"MODRM_COMPLEX",
|
||||
"SEG_OFFSET_OPTIMISED",
|
||||
"SEG_OFFSET_NOT_OPTIMISED",
|
||||
];
|
||||
|
||||
let j = 0;
|
||||
const stat_values = {};
|
||||
for(let i = 0; i < stat_names.length; i++)
|
||||
{
|
||||
const name = stat_names[i];
|
||||
let value;
|
||||
if(name.includes("/"))
|
||||
{
|
||||
j++; // skip profiler_stat_get
|
||||
const [left, right] = name.split("/");
|
||||
value = stat_values[left] / stat_values[right];
|
||||
}
|
||||
else
|
||||
{
|
||||
let stat = stat_values[name] = cpu.wm.exports["profiler_stat_get"](i - j);
|
||||
value = stat >= 100e6 ? Math.round(stat / 1e6) + "m" : stat >= 100e3 ? Math.round(stat / 1e3) + "k" : stat;
|
||||
}
|
||||
text += name + "=" + value + "\n";
|
||||
}
|
||||
|
||||
text += "\n";
|
||||
|
||||
const tlb_entries = cpu.wm.exports["get_valid_tlb_entries_count"]();
|
||||
const global_tlb_entries = cpu.wm.exports["get_valid_global_tlb_entries_count"]();
|
||||
const nonglobal_tlb_entries = tlb_entries - global_tlb_entries;
|
||||
|
||||
text += "TLB_ENTRIES=" + tlb_entries + " (" + global_tlb_entries + " global, " + nonglobal_tlb_entries + " non-global)\n";
|
||||
text += "WASM_TABLE_FREE=" + cpu.wm.exports["jit_get_wasm_table_index_free_list_count"]() + "\n";
|
||||
text += "JIT_CACHE_SIZE=" + cpu.wm.exports["jit_get_cache_size"]() + "\n";
|
||||
text += "FLAT_SEGMENTS=" + cpu.wm.exports["has_flat_segmentation"]() + "\n";
|
||||
|
||||
text += "do_many_cycles avg: " + (do_many_cycles_total / do_many_cycles_count || 0) + "\n";
|
||||
text += "wasm memory size: " + (cpu.wasm_memory.buffer.byteLength >> 20) + "m\n";
|
||||
|
||||
text += "Config:\n";
|
||||
text += "MAX_PAGES=" + cpu.wm.exports["get_config"](0) + "\n";
|
||||
text += "JIT_USE_LOOP_SAFETY=" + cpu.wm.exports["get_config"](1) + "\n";
|
||||
text += "MAX_EXTRA_BASIC_BLOCKS=" + cpu.wm.exports["get_config"](2) + "\n";
|
||||
|
||||
return text;
|
||||
},
|
||||
|
||||
print_instruction_counts: function(cpu)
|
||||
{
|
||||
return [
|
||||
print_stats.print_instruction_counts_offset(cpu, false, false, false, false),
|
||||
print_stats.print_instruction_counts_offset(cpu, true, false, false, false),
|
||||
print_stats.print_instruction_counts_offset(cpu, false, true, false, false),
|
||||
print_stats.print_instruction_counts_offset(cpu, false, false, true, false),
|
||||
print_stats.print_instruction_counts_offset(cpu, false, false, false, true),
|
||||
].join("\n\n");
|
||||
},
|
||||
|
||||
print_instruction_counts_offset: function(cpu, compiled, jit_exit, unguarded_register, wasm_size)
|
||||
{
|
||||
let text = "";
|
||||
|
||||
const counts = [];
|
||||
|
||||
const label =
|
||||
compiled ? "compiled" :
|
||||
jit_exit ? "jit exit" :
|
||||
unguarded_register ? "unguarded register" :
|
||||
wasm_size ? "wasm size" :
|
||||
"executed";
|
||||
|
||||
for(let opcode = 0; opcode < 0x100; opcode++)
|
||||
{
|
||||
for(let fixed_g = 0; fixed_g < 8; fixed_g++)
|
||||
{
|
||||
for(let is_mem of [false, true])
|
||||
{
|
||||
const count = cpu.wm.exports["get_opstats_buffer"](compiled, jit_exit, unguarded_register, wasm_size, opcode, false, is_mem, fixed_g);
|
||||
counts.push({ opcode, count, is_mem, fixed_g });
|
||||
|
||||
const count_0f = cpu.wm.exports["get_opstats_buffer"](compiled, jit_exit, unguarded_register, wasm_size, opcode, true, is_mem, fixed_g);
|
||||
counts.push({ opcode: 0x0f00 | opcode, count: count_0f, is_mem, fixed_g });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let total = 0;
|
||||
const prefixes = new Set([
|
||||
0x26, 0x2E, 0x36, 0x3E,
|
||||
0x64, 0x65, 0x66, 0x67,
|
||||
0xF0, 0xF2, 0xF3,
|
||||
]);
|
||||
for(let { count, opcode } of counts)
|
||||
{
|
||||
if(!prefixes.has(opcode))
|
||||
{
|
||||
total += count;
|
||||
}
|
||||
}
|
||||
|
||||
if(total === 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
const per_opcode = new Uint32Array(0x100);
|
||||
const per_opcode0f = new Uint32Array(0x100);
|
||||
|
||||
for(let { opcode, count } of counts)
|
||||
{
|
||||
if((opcode & 0xFF00) == 0x0F00)
|
||||
{
|
||||
per_opcode0f[opcode & 0xFF] += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
per_opcode[opcode & 0xFF] += count;
|
||||
}
|
||||
}
|
||||
|
||||
text += "------------------\n";
|
||||
text += "Total: " + total + "\n";
|
||||
|
||||
const factor = total > 1e7 ? 1000 : 1;
|
||||
|
||||
const max_count = Math.max.apply(Math,
|
||||
counts.map(({ count }) => Math.round(count / factor))
|
||||
);
|
||||
const pad_length = String(max_count).length;
|
||||
|
||||
text += `Instruction counts ${label} (in ${factor}):\n`;
|
||||
|
||||
for(let i = 0; i < 0x100; i++)
|
||||
{
|
||||
text += h(i, 2).slice(2) + ":" + v86util.pads(Math.round(per_opcode[i] / factor), pad_length);
|
||||
|
||||
if(i % 16 == 15)
|
||||
text += "\n";
|
||||
else
|
||||
text += " ";
|
||||
}
|
||||
|
||||
text += "\n";
|
||||
text += `Instruction counts ${label} (0f, in ${factor}):\n`;
|
||||
|
||||
for(let i = 0; i < 0x100; i++)
|
||||
{
|
||||
text += h(i & 0xFF, 2).slice(2) + ":" + v86util.pads(Math.round(per_opcode0f[i] / factor), pad_length);
|
||||
|
||||
if(i % 16 == 15)
|
||||
text += "\n";
|
||||
else
|
||||
text += " ";
|
||||
}
|
||||
text += "\n";
|
||||
|
||||
const top_counts = counts.filter(({ count }) => count).sort(({ count: count1 }, { count: count2 }) => count2 - count1);
|
||||
|
||||
for(let { opcode, is_mem, fixed_g, count } of top_counts.slice(0, 200))
|
||||
{
|
||||
let opcode_description = opcode.toString(16) + "_" + fixed_g + (is_mem ? "_m" : "_r");
|
||||
text += opcode_description + ":" + (count / total * 100).toFixed(2) + " ";
|
||||
}
|
||||
text += "\n";
|
||||
|
||||
return text;
|
||||
},
|
||||
};
|
||||
|
||||
if(typeof module !== "undefined" && typeof module.exports !== "undefined")
|
||||
{
|
||||
module.exports["print_stats"] = print_stats;
|
||||
}
|
|
@ -1,13 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
if(typeof window !== "undefined" && !window.requestAnimationFrame)
|
||||
{
|
||||
window.requestAnimationFrame =
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapter to use visual screen in browsers (in contrast to node)
|
||||
* @constructor
|
||||
|
@ -20,7 +12,7 @@ function ScreenAdapter(screen_container, bus)
|
|||
|
||||
var
|
||||
graphic_screen = screen_container.getElementsByTagName("canvas")[0],
|
||||
graphic_context = graphic_screen.getContext("2d"),
|
||||
graphic_context = graphic_screen.getContext("2d", { alpha: false }),
|
||||
|
||||
text_screen = screen_container.getElementsByTagName("div")[0],
|
||||
cursor_element = document.createElement("div");
|
||||
|
@ -42,6 +34,8 @@ function ScreenAdapter(screen_container, bus)
|
|||
/** @type {number} */
|
||||
scale_y = 1,
|
||||
|
||||
base_scale = 1,
|
||||
|
||||
graphical_mode_width,
|
||||
graphical_mode_height,
|
||||
|
||||
|
@ -193,7 +187,10 @@ function ScreenAdapter(screen_container, bus)
|
|||
this.make_screenshot = function()
|
||||
{
|
||||
try {
|
||||
window.open(graphic_screen.toDataURL());
|
||||
const image = new Image();
|
||||
image.src = graphic_screen.toDataURL("image/png");
|
||||
const w = window.open("");
|
||||
w.document.write(image.outerHTML);
|
||||
}
|
||||
catch(e) {}
|
||||
};
|
||||
|
@ -331,6 +328,16 @@ function ScreenAdapter(screen_container, bus)
|
|||
graphical_mode_width = width;
|
||||
graphical_mode_height = height;
|
||||
|
||||
// add some scaling to tiny resolutions
|
||||
if(graphical_mode_width <= 640)
|
||||
{
|
||||
base_scale = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
base_scale = 1;
|
||||
}
|
||||
|
||||
this.bus.send("screen-tell-buffer", [graphic_buffer32], [graphic_buffer32.buffer]);
|
||||
update_scale_graphic();
|
||||
};
|
||||
|
@ -352,7 +359,7 @@ function ScreenAdapter(screen_container, bus)
|
|||
|
||||
function update_scale_graphic()
|
||||
{
|
||||
elem_set_scale(graphic_screen, scale_x, scale_y, false);
|
||||
elem_set_scale(graphic_screen, scale_x * base_scale, scale_y * base_scale, false);
|
||||
}
|
||||
|
||||
function elem_set_scale(elem, scale_x, scale_y, use_scale)
|
||||
|
@ -362,7 +369,7 @@ function ScreenAdapter(screen_container, bus)
|
|||
|
||||
if(use_scale)
|
||||
{
|
||||
elem.style.transform = elem.style.webkitTransform = elem.style.MozTransform = "";
|
||||
elem.style.transform = "";
|
||||
}
|
||||
|
||||
var rectangle = elem.getBoundingClientRect();
|
||||
|
@ -374,18 +381,15 @@ function ScreenAdapter(screen_container, bus)
|
|||
scale_str += scale_x === 1 ? "" : " scaleX(" + scale_x + ")";
|
||||
scale_str += scale_y === 1 ? "" : " scaleY(" + scale_y + ")";
|
||||
|
||||
elem.style.transform = elem.style.webkitTransform = elem.style.MozTransform = scale_str;
|
||||
elem.style.transform = scale_str;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unblur non-fractional scales
|
||||
if(scale_x % 1 === 0 && scale_y % 1 === 0)
|
||||
{
|
||||
graphic_screen.style.imageRendering = "-moz-crisp-edges";
|
||||
graphic_screen.style.imageRendering = "moz-crisp-edges";
|
||||
graphic_screen.style.imageRendering = "webkit-optimize-contrast";
|
||||
graphic_screen.style.imageRendering = "o-crisp-edges";
|
||||
graphic_screen.style.imageRendering = "pixelated";
|
||||
graphic_screen.style["imageRendering"] = "crisp-edges"; // firefox
|
||||
graphic_screen.style["imageRendering"] = "pixelated";
|
||||
graphic_screen.style["-ms-interpolation-mode"] = "nearest-neighbor";
|
||||
}
|
||||
else
|
||||
|
@ -547,5 +551,3 @@ function ScreenAdapter(screen_container, bus)
|
|||
|
||||
this.init();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ function SerialAdapter(element, bus)
|
|||
{
|
||||
this.destroy();
|
||||
|
||||
element.style.display = "block";
|
||||
element.addEventListener("keypress", keypress_handler, false);
|
||||
element.addEventListener("keydown", keydown_handler, false);
|
||||
element.addEventListener("paste", paste_handler, false);
|
||||
|
@ -79,7 +80,7 @@ function SerialAdapter(element, bus)
|
|||
this.update_timer = setTimeout(() => {
|
||||
this.update_timer = undefined;
|
||||
var now = Date.now();
|
||||
dbg_assert(now - this.last_update >= 16);
|
||||
dbg_assert(now - this.last_update >= 15);
|
||||
this.last_update = now;
|
||||
this.render();
|
||||
}, 16 - delta);
|
||||
|
@ -107,7 +108,7 @@ function SerialAdapter(element, bus)
|
|||
this.text_new_line = false;
|
||||
element.scrollTop = 1e9;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} chr_code
|
||||
|
@ -192,3 +193,51 @@ function SerialAdapter(element, bus)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {BusConnector} bus
|
||||
*/
|
||||
function SerialRecordingAdapter(bus)
|
||||
{
|
||||
var serial = this;
|
||||
this.text = "";
|
||||
|
||||
bus.register("serial0-output-char", function(chr)
|
||||
{
|
||||
this.text += chr;
|
||||
}, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {BusConnector} bus
|
||||
*/
|
||||
function SerialAdapterXtermJS(element, bus)
|
||||
{
|
||||
this.element = element;
|
||||
|
||||
if(!window["Terminal"])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var term = this.term = new window["Terminal"]();
|
||||
term["setOption"]("logLevel", "off");
|
||||
term.write("This is the serial console. Whatever you type or paste here will be sent to COM1");
|
||||
|
||||
term["onData"](function(data) {
|
||||
bus.send("serial0-input", data.charCodeAt(0));
|
||||
});
|
||||
|
||||
bus.register("serial0-output-char", function(chr)
|
||||
{
|
||||
term.write(chr);
|
||||
}, this);
|
||||
}
|
||||
|
||||
SerialAdapterXtermJS.prototype.show = function()
|
||||
{
|
||||
this.term && this.term.open(this.element);
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ function SpeakerAdapter(bus)
|
|||
this.bus = bus;
|
||||
|
||||
/** @const */
|
||||
this.audio_context = new (window.AudioContext || window["webkitAudioContext"])();
|
||||
this.audio_context = new AudioContext();
|
||||
|
||||
/** @const */
|
||||
this.mixer = new SpeakerMixer(bus, this.audio_context);
|
||||
|
@ -765,9 +765,11 @@ function SpeakerWorkletDAC(bus, audio_context, mixer)
|
|||
|
||||
this.node_processor = new AudioWorkletNode(this.audio_context, "dac-processor",
|
||||
{
|
||||
"numberOfInputs": 0,
|
||||
"numberOfOutputs": 1,
|
||||
"outputChannelCount": [2],
|
||||
numberOfInputs: 0,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [2],
|
||||
parameterData: {},
|
||||
processorOptions: {},
|
||||
});
|
||||
|
||||
this.node_processor.port.postMessage(
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
* - `hda Object` (No hard drive) - First hard disk, see below.
|
||||
* - `fda Object` (No floppy disk) - First floppy disk, see below.
|
||||
* - `cdrom Object` (No CD) - See below.
|
||||
*
|
||||
* - `bzimage Object` - A Linux kernel image to boot (only bzimage format), see below.
|
||||
* - `initrd Object` - A Linux ramdisk image, see below.
|
||||
* - `bzimage_initrd_from_filesystem boolean` - Automatically fetch bzimage and
|
||||
* initrd from the specified `filesystem`.
|
||||
*
|
||||
* - `initial_state Object` (Normal boot) - An initial state to load, see
|
||||
* [`restore_state`](#restore_statearraybuffer-state) and below.
|
||||
*
|
||||
|
@ -89,11 +95,144 @@ function V86Starter(options)
|
|||
|
||||
this.cpu_is_running = false;
|
||||
|
||||
var bus = Bus.create();
|
||||
var adapter_bus = this.bus = bus[0];
|
||||
const bus = Bus.create();
|
||||
const adapter_bus = this.bus = bus[0];
|
||||
this.emulator_bus = bus[1];
|
||||
var emulator = this.v86 = new v86(this.emulator_bus);
|
||||
|
||||
var cpu;
|
||||
var wasm_memory;
|
||||
|
||||
const wasm_table = new WebAssembly.Table({ element: "anyfunc", "initial": WASM_TABLE_SIZE + WASM_TABLE_OFFSET });
|
||||
|
||||
const wasm_shared_funcs = {
|
||||
"cpu_exception_hook": (n) => {
|
||||
return this["cpu_exception_hook"] && this["cpu_exception_hook"](n);
|
||||
},
|
||||
"hlt_op": function() { return cpu.hlt_op(); },
|
||||
"abort": function() { dbg_assert(false); },
|
||||
"logop": function(eip, op) { return cpu.debug.logop(eip, op); },
|
||||
"microtick": v86.microtick,
|
||||
"get_rand_int": function() { return v86util.get_rand_int(); },
|
||||
|
||||
"pic_acknowledge": function() { cpu.pic_acknowledge(); },
|
||||
|
||||
"io_port_read8": function(addr) { return cpu.io.port_read8(addr); },
|
||||
"io_port_read16": function(addr) { return cpu.io.port_read16(addr); },
|
||||
"io_port_read32": function(addr) { return cpu.io.port_read32(addr); },
|
||||
"io_port_write8": function(addr, value) { cpu.io.port_write8(addr, value); },
|
||||
"io_port_write16": function(addr, value) { cpu.io.port_write16(addr, value); },
|
||||
"io_port_write32": function(addr, value) { cpu.io.port_write32(addr, value); },
|
||||
|
||||
"mmap_read8": function(addr) { return cpu.mmap_read8(addr); },
|
||||
"mmap_read16": function(addr) { return cpu.mmap_read16(addr); },
|
||||
"mmap_read32": function(addr) { return cpu.mmap_read32(addr); },
|
||||
"mmap_write8": function(addr, value) { cpu.mmap_write8(addr, value); },
|
||||
"mmap_write16": function(addr, value) { cpu.mmap_write16(addr, value); },
|
||||
"mmap_write32": function(addr, value) { cpu.mmap_write32(addr, value); },
|
||||
"mmap_write64": function(addr, value0, value1) { cpu.mmap_write64(addr, value0, value1); },
|
||||
"mmap_write128": function(addr, value0, value1, value2, value3) {
|
||||
cpu.mmap_write128(addr, value0, value1, value2, value3);
|
||||
},
|
||||
|
||||
"cpuid": function() { return cpu.cpuid(); },
|
||||
|
||||
"load_ldt": function() { return cpu.load_ldt.apply(cpu, arguments); },
|
||||
"load_tr": function() { return cpu.load_tr.apply(cpu, arguments); },
|
||||
|
||||
"log_from_wasm": function(offset, len) {
|
||||
const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
|
||||
dbg_log(str, LOG_CPU);
|
||||
},
|
||||
"console_log_from_wasm": function(offset, len) {
|
||||
const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
|
||||
console.error(str);
|
||||
},
|
||||
"dbg_trace_from_wasm": function() {
|
||||
dbg_trace();
|
||||
},
|
||||
|
||||
"codegen_finalize": (wasm_table_index, start, state_flags, ptr, len) => {
|
||||
cpu.codegen_finalize(wasm_table_index, start, state_flags, ptr, len);
|
||||
},
|
||||
"jit_clear_func": (wasm_table_index) => cpu.jit_clear_func(wasm_table_index),
|
||||
"jit_clear_all_funcs": () => cpu.jit_clear_all_funcs(),
|
||||
"do_task_switch": (selector, has_error_code, error_code) => {
|
||||
cpu.do_task_switch(selector, has_error_code, error_code);
|
||||
},
|
||||
|
||||
"__indirect_function_table": wasm_table,
|
||||
};
|
||||
|
||||
let v86_bin = DEBUG ? "v86-debug.wasm" : "v86.wasm";
|
||||
let v86_bin_fallback = "v86-fallback.wasm";
|
||||
|
||||
if(options["wasm_path"])
|
||||
{
|
||||
v86_bin = options["wasm_path"];
|
||||
}
|
||||
else if(typeof window === "undefined" && typeof __dirname === "string")
|
||||
{
|
||||
v86_bin = __dirname + "/" + v86_bin;
|
||||
v86_bin_fallback = __dirname + "/" + v86_bin_fallback;
|
||||
}
|
||||
else
|
||||
{
|
||||
v86_bin = "build/" + v86_bin;
|
||||
v86_bin_fallback = "build/" + v86_bin_fallback;
|
||||
}
|
||||
|
||||
v86util.load_file(v86_bin, {
|
||||
done: bytes =>
|
||||
{
|
||||
WebAssembly
|
||||
.instantiate(bytes, { "env": wasm_shared_funcs })
|
||||
.then(({ instance }) => {
|
||||
const imports = wasm_shared_funcs;
|
||||
const exports = instance["exports"];
|
||||
wasm_memory = exports.memory;
|
||||
exports["rust_init"]();
|
||||
|
||||
const emulator = this.v86 = new v86(this.emulator_bus, { exports, wasm_table });
|
||||
cpu = emulator.cpu;
|
||||
|
||||
this.continue_init(emulator, options);
|
||||
}, err => {
|
||||
v86util.load_file(v86_bin_fallback, {
|
||||
done: bytes => {
|
||||
WebAssembly
|
||||
.instantiate(bytes, { "env": wasm_shared_funcs })
|
||||
.then(({ instance }) => {
|
||||
const imports = wasm_shared_funcs;
|
||||
const exports = instance["exports"];
|
||||
wasm_memory = exports.memory;
|
||||
exports["rust_init"]();
|
||||
|
||||
const emulator = this.v86 = new v86(this.emulator_bus, { exports, wasm_table });
|
||||
cpu = emulator.cpu;
|
||||
|
||||
this.continue_init(emulator, options);
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
progress: e =>
|
||||
{
|
||||
this.emulator_bus.send("download-progress", {
|
||||
file_index: 0,
|
||||
file_count: 1,
|
||||
file_name: v86_bin,
|
||||
|
||||
lengthComputable: e.lengthComputable,
|
||||
total: e.total,
|
||||
loaded: e.loaded,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
V86Starter.prototype.continue_init = async function(emulator, options)
|
||||
{
|
||||
this.bus.register("emulator-stopped", function()
|
||||
{
|
||||
this.cpu_is_running = false;
|
||||
|
@ -114,49 +253,62 @@ function V86Starter(options)
|
|||
"cdrom": undefined,
|
||||
};
|
||||
|
||||
settings.acpi = options["acpi"];
|
||||
settings.load_devices = true;
|
||||
settings.log_level = options["log_level"];
|
||||
settings.memory_size = options["memory_size"] || 64 * 1024 * 1024;
|
||||
settings.vga_memory_size = options["vga_memory_size"] || 8 * 1024 * 1024;
|
||||
settings.boot_order = options["boot_order"] || 0x213;
|
||||
settings.fastboot = options["fastboot"] || false;
|
||||
settings.fda = undefined;
|
||||
settings.fdb = undefined;
|
||||
settings.uart1 = options["uart1"] || false;
|
||||
settings.uart2 = options["uart2"] || false;
|
||||
settings.uart3 = options["uart3"] || false;
|
||||
settings.uart1 = options["uart1"];
|
||||
settings.uart2 = options["uart2"];
|
||||
settings.uart3 = options["uart3"];
|
||||
settings.cmdline = options["cmdline"];
|
||||
settings.preserve_mac_from_state_image = options["preserve_mac_from_state_image"];
|
||||
|
||||
if(options["network_relay_url"])
|
||||
{
|
||||
this.network_adapter = new NetworkAdapter(options["network_relay_url"], adapter_bus);
|
||||
settings.enable_ne2k = true;
|
||||
this.network_adapter = new NetworkAdapter(options["network_relay_url"], this.bus);
|
||||
}
|
||||
|
||||
// Enable unconditionally, so that state images don't miss hardware
|
||||
// TODO: Should be properly fixed in restore_state
|
||||
settings.enable_ne2k = true;
|
||||
|
||||
if(!options["disable_keyboard"])
|
||||
{
|
||||
this.keyboard_adapter = new KeyboardAdapter(adapter_bus);
|
||||
this.keyboard_adapter = new KeyboardAdapter(this.bus);
|
||||
}
|
||||
if(!options["disable_mouse"])
|
||||
{
|
||||
this.mouse_adapter = new MouseAdapter(adapter_bus, options["screen_container"]);
|
||||
this.mouse_adapter = new MouseAdapter(this.bus, options["screen_container"]);
|
||||
}
|
||||
|
||||
if(options["screen_container"])
|
||||
{
|
||||
this.screen_adapter = new ScreenAdapter(options["screen_container"], adapter_bus);
|
||||
this.screen_adapter = new ScreenAdapter(options["screen_container"], this.bus);
|
||||
}
|
||||
else if(options["screen_dummy"])
|
||||
{
|
||||
this.screen_adapter = new DummyScreenAdapter(adapter_bus);
|
||||
this.screen_adapter = new DummyScreenAdapter(this.bus);
|
||||
}
|
||||
|
||||
if(options["serial_container"])
|
||||
{
|
||||
this.serial_adapter = new SerialAdapter(options["serial_container"], adapter_bus);
|
||||
this.serial_adapter = new SerialAdapter(options["serial_container"], this.bus);
|
||||
//this.recording_adapter = new SerialRecordingAdapter(this.bus);
|
||||
}
|
||||
|
||||
if(options["serial_container_xtermjs"])
|
||||
{
|
||||
this.serial_adapter = new SerialAdapterXtermJS(options["serial_container_xtermjs"], this.bus);
|
||||
}
|
||||
|
||||
if(!options["disable_speaker"])
|
||||
{
|
||||
this.speaker_adapter = new SpeakerAdapter(adapter_bus);
|
||||
this.speaker_adapter = new SpeakerAdapter(this.bus);
|
||||
}
|
||||
|
||||
// ugly, but required for closure compiler compilation
|
||||
|
@ -181,7 +333,13 @@ function V86Starter(options)
|
|||
break;
|
||||
|
||||
case "multiboot":
|
||||
settings.multiboot = this.disk_images["multiboot"] = buffer;
|
||||
settings.multiboot = this.disk_images["multiboot"] = buffer.buffer;
|
||||
break;
|
||||
case "bzimage":
|
||||
settings.bzimage = this.disk_images["bzimage"] = buffer.buffer;
|
||||
break;
|
||||
case "initrd":
|
||||
settings.initrd = this.disk_images["initrd"] = buffer.buffer;
|
||||
break;
|
||||
|
||||
case "bios":
|
||||
|
@ -194,7 +352,7 @@ function V86Starter(options)
|
|||
settings.initial_state = buffer.buffer;
|
||||
break;
|
||||
case "fs9p_json":
|
||||
settings.fs9p_json = buffer.buffer;
|
||||
settings.fs9p_json = buffer;
|
||||
break;
|
||||
default:
|
||||
dbg_assert(false, name);
|
||||
|
@ -226,10 +384,12 @@ function V86Starter(options)
|
|||
async: file["async"],
|
||||
url: file["url"],
|
||||
size: file["size"],
|
||||
use_parts: file.use_parts,
|
||||
};
|
||||
|
||||
if(name === "bios" || name === "vga_bios" ||
|
||||
name === "initial_state" || name === "multiboot")
|
||||
name === "initial_state" || name === "multiboot" ||
|
||||
name === "bzimage" || name === "initrd")
|
||||
{
|
||||
// Ignore async for these because they must be available before boot.
|
||||
// This should make result.buffer available after the object is loaded
|
||||
|
@ -279,7 +439,17 @@ function V86Starter(options)
|
|||
{
|
||||
if(file.async)
|
||||
{
|
||||
var buffer = new v86util.AsyncXHRBuffer(file.url, file.size);
|
||||
let buffer;
|
||||
|
||||
if(file.use_parts)
|
||||
{
|
||||
buffer = new v86util.AsyncXHRPartfileBuffer(file.url, file.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = new v86util.AsyncXHRBuffer(file.url, file.size);
|
||||
}
|
||||
|
||||
files_to_load.push({
|
||||
name: name,
|
||||
loadable: buffer,
|
||||
|
@ -300,10 +470,16 @@ function V86Starter(options)
|
|||
}
|
||||
}
|
||||
|
||||
if(options["state"])
|
||||
{
|
||||
console.warn("Warning: Unknown option 'state'. Did you mean 'initial_state'?");
|
||||
}
|
||||
|
||||
var image_names = [
|
||||
"bios", "vga_bios",
|
||||
"cdrom", "hda", "hdb", "fda", "fdb",
|
||||
"initial_state", "multiboot",
|
||||
"bzimage", "initrd",
|
||||
];
|
||||
|
||||
for(var i = 0; i < image_names.length; i++)
|
||||
|
@ -316,8 +492,13 @@ function V86Starter(options)
|
|||
var fs_url = options["filesystem"]["basefs"];
|
||||
var base_url = options["filesystem"]["baseurl"];
|
||||
|
||||
this.fs9p = new FS(base_url);
|
||||
settings.fs9p = this.fs9p;
|
||||
let file_storage = new MemoryFileStorage();
|
||||
|
||||
if(base_url)
|
||||
{
|
||||
file_storage = new ServerFileStorageWrapper(file_storage, base_url);
|
||||
}
|
||||
settings.fs9p = this.fs9p = new FS(file_storage);
|
||||
|
||||
if(fs_url)
|
||||
{
|
||||
|
@ -336,7 +517,7 @@ function V86Starter(options)
|
|||
name: "fs9p_json",
|
||||
url: fs_url,
|
||||
size: size,
|
||||
as_text: true,
|
||||
as_json: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +549,7 @@ function V86Starter(options)
|
|||
v86util.load_file(f.url, {
|
||||
done: function(result)
|
||||
{
|
||||
put_on_settings.call(this, f.name, new SyncBuffer(result));
|
||||
put_on_settings.call(this, f.name, f.as_json ? result : new SyncBuffer(result));
|
||||
cont(index + 1);
|
||||
}.bind(this),
|
||||
progress: function progress(e)
|
||||
|
@ -395,7 +576,7 @@ function V86Starter(options)
|
|||
});
|
||||
}
|
||||
},
|
||||
as_text: f.as_text,
|
||||
as_json: f.as_json,
|
||||
});
|
||||
}
|
||||
}.bind(this);
|
||||
|
@ -403,36 +584,111 @@ function V86Starter(options)
|
|||
|
||||
function done()
|
||||
{
|
||||
if(settings.initial_state)
|
||||
//if(settings.initial_state)
|
||||
//{
|
||||
// // avoid large allocation now, memory will be restored later anyway
|
||||
// settings.memory_size = 0;
|
||||
//}
|
||||
|
||||
if(settings.fs9p && settings.fs9p_json)
|
||||
{
|
||||
// avoid large allocation now, memory will be restored later anyway
|
||||
settings.memory_size = 0;
|
||||
if(!settings.initial_state)
|
||||
{
|
||||
settings.fs9p.load_from_json(settings.fs9p_json);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Filesystem basefs ignored: Overridden by state image");
|
||||
}
|
||||
|
||||
if(options["bzimage_initrd_from_filesystem"])
|
||||
{
|
||||
const { bzimage, initrd } = this.get_bzimage_initrd_from_filesystem(settings.fs9p);
|
||||
|
||||
dbg_log("Found bzimage: " + bzimage + " and initrd: " + initrd);
|
||||
|
||||
Promise.all([
|
||||
settings.fs9p.read_file(initrd),
|
||||
settings.fs9p.read_file(bzimage),
|
||||
]).then(([initrd, bzimage]) => {
|
||||
put_on_settings.call(this, "initrd", new SyncBuffer(initrd.buffer));
|
||||
put_on_settings.call(this, "bzimage", new SyncBuffer(bzimage.buffer));
|
||||
finish.call(this);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
finish.call(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
console.assert(
|
||||
!options["bzimage_initrd_from_filesystem"],
|
||||
"bzimage_initrd_from_filesystem: Requires a filesystem");
|
||||
finish.call(this);
|
||||
}
|
||||
|
||||
function finish()
|
||||
{
|
||||
this.serial_adapter && this.serial_adapter.show && this.serial_adapter.show();
|
||||
|
||||
this.bus.send("cpu-init", settings);
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
if(settings.initial_state)
|
||||
{
|
||||
emulator.restore_state(settings.initial_state);
|
||||
}
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
if(settings.fs9p && settings.fs9p_json)
|
||||
{
|
||||
settings.fs9p.OnJSONLoaded(settings.fs9p_json);
|
||||
// The GC can't free settings, since it is referenced from
|
||||
// several closures. This isn't needed anymore, so we delete it
|
||||
// here
|
||||
settings.initial_state = undefined;
|
||||
}
|
||||
|
||||
if(options["autostart"])
|
||||
{
|
||||
this.bus.send("cpu-run");
|
||||
}
|
||||
}.bind(this), 0);
|
||||
}.bind(this), 0);
|
||||
|
||||
this.emulator_bus.send("emulator-loaded");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
V86Starter.prototype.get_bzimage_initrd_from_filesystem = function(filesystem)
|
||||
{
|
||||
const root = (filesystem.read_dir("/") || []).map(x => "/" + x);
|
||||
const boot = (filesystem.read_dir("/boot/") || []).map(x => "/boot/" + x);
|
||||
|
||||
let initrd;
|
||||
let bzimage;
|
||||
|
||||
for(let f of [].concat(root, boot))
|
||||
{
|
||||
const old = /old/i.test(f) || /fallback/i.test(f);
|
||||
const is_bzimage = /vmlinuz/i.test(f) || /bzimage/i.test(f);
|
||||
const is_initrd = /initrd/i.test(f) || /initramfs/i.test(f);
|
||||
|
||||
if(is_bzimage && (!bzimage || !old))
|
||||
{
|
||||
bzimage = f;
|
||||
}
|
||||
|
||||
if(is_initrd && (!initrd || !old))
|
||||
{
|
||||
initrd = f;
|
||||
}
|
||||
}
|
||||
|
||||
if(!initrd || !bzimage)
|
||||
{
|
||||
console.log("Failed to find bzimage or initrd in filesystem. Files:");
|
||||
console.log(root.join(" "));
|
||||
console.log(boot.join(" "));
|
||||
}
|
||||
|
||||
return { initrd, bzimage };
|
||||
};
|
||||
|
||||
/**
|
||||
* Start emulation. Do nothing if emulator is running already. Can be
|
||||
|
@ -643,7 +899,7 @@ V86Starter.prototype.get_instruction_counter = function()
|
|||
{
|
||||
if(this.v86)
|
||||
{
|
||||
return this.v86.cpu.timestamp_counter;
|
||||
return this.v86.cpu.instruction_counter[0] >>> 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -851,6 +1107,59 @@ V86Starter.prototype.serial_send_bytes = function(serial, data)
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mount another filesystem to the current filesystem.
|
||||
* @param {string} path Path for the mount point
|
||||
* @param {string|undefined} baseurl
|
||||
* @param {string|undefined} basefs As a JSON string
|
||||
* @param {function(Object)=} callback
|
||||
* @export
|
||||
*/
|
||||
V86Starter.prototype.mount_fs = async function(path, baseurl, basefs, callback)
|
||||
{
|
||||
let file_storage = new MemoryFileStorage();
|
||||
|
||||
if(baseurl)
|
||||
{
|
||||
file_storage = new ServerFileStorageWrapper(file_storage, baseurl);
|
||||
}
|
||||
const newfs = new FS(file_storage, this.fs9p.qidcounter);
|
||||
const mount = () =>
|
||||
{
|
||||
const idx = this.fs9p.Mount(path, newfs);
|
||||
if(!callback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(idx === -ENOENT)
|
||||
{
|
||||
callback(new FileNotFoundError());
|
||||
}
|
||||
else if(idx === -EEXIST)
|
||||
{
|
||||
callback(new FileExistsError());
|
||||
}
|
||||
else if(idx < 0)
|
||||
{
|
||||
dbg_assert(false, "Unexpected error code: " + (-idx));
|
||||
callback(new Error("Failed to mount. Error number: " + (-idx)));
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(null);
|
||||
}
|
||||
};
|
||||
if(baseurl)
|
||||
{
|
||||
dbg_assert(typeof basefs === "object", "Filesystem: basefs must be a JSON object");
|
||||
newfs.load_from_json(basefs, () => mount());
|
||||
}
|
||||
else
|
||||
{
|
||||
mount();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Write to a file in the 9p filesystem. Nothing happens if no filesystem has
|
||||
* been initialized. First argument to the callback is an error object if
|
||||
|
@ -863,6 +1172,8 @@ V86Starter.prototype.serial_send_bytes = function(serial, data)
|
|||
*/
|
||||
V86Starter.prototype.create_file = function(file, data, callback)
|
||||
{
|
||||
callback = callback || function() {};
|
||||
|
||||
var fs = this.fs9p;
|
||||
|
||||
if(!fs)
|
||||
|
@ -879,21 +1190,14 @@ V86Starter.prototype.create_file = function(file, data, callback)
|
|||
|
||||
if(!not_found)
|
||||
{
|
||||
fs.CreateBinaryFile(filename, parent_id, data);
|
||||
}
|
||||
|
||||
if(callback)
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
if(not_found)
|
||||
{
|
||||
callback(new FileNotFoundError());
|
||||
fs.CreateBinaryFile(filename, parent_id, data)
|
||||
.then(() => callback(null));
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(null);
|
||||
}
|
||||
setTimeout(function()
|
||||
{
|
||||
callback(new FileNotFoundError());
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
@ -915,35 +1219,96 @@ V86Starter.prototype.read_file = function(file, callback)
|
|||
return;
|
||||
}
|
||||
|
||||
var path_infos = fs.SearchPath(file);
|
||||
var id = path_infos.id;
|
||||
|
||||
if(id === -1)
|
||||
fs.read_file(file).then((result) => {
|
||||
if(result)
|
||||
{
|
||||
callback(new FileNotFoundError(), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.OpenInode(id, undefined);
|
||||
fs.AddEvent(
|
||||
id,
|
||||
function()
|
||||
{
|
||||
var data = fs.inodedata[id];
|
||||
|
||||
if(data)
|
||||
{
|
||||
callback(null, data.subarray(0, fs.inodes[id].size));
|
||||
callback(null, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(new FileNotFoundError(), null);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
V86Starter.prototype.automatically = function(steps)
|
||||
{
|
||||
const run = (steps) =>
|
||||
{
|
||||
const step = steps[0];
|
||||
|
||||
if(!step)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const remaining_steps = steps.slice(1);
|
||||
|
||||
if(step.sleep)
|
||||
{
|
||||
setTimeout(() => run(remaining_steps), step.sleep * 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
if(step.vga_text)
|
||||
{
|
||||
const screen = this.screen_adapter.get_text_screen();
|
||||
|
||||
for(let line of screen)
|
||||
{
|
||||
if(line.includes(step.vga_text))
|
||||
{
|
||||
run(remaining_steps);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => run(steps), 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
if(step.keyboard_send)
|
||||
{
|
||||
if(step.keyboard_send instanceof Array)
|
||||
{
|
||||
this.keyboard_send_scancodes(step.keyboard_send);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(typeof step.keyboard_send === "string");
|
||||
this.keyboard_send_text(step.keyboard_send);
|
||||
}
|
||||
|
||||
run(remaining_steps);
|
||||
return;
|
||||
}
|
||||
|
||||
if(step.call)
|
||||
{
|
||||
step.call();
|
||||
run(remaining_steps);
|
||||
return;
|
||||
}
|
||||
|
||||
console.assert(false, step);
|
||||
};
|
||||
|
||||
run(steps);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @constructor
|
||||
*
|
||||
* @param {string=} message
|
||||
*/
|
||||
function FileExistsError(message)
|
||||
{
|
||||
this.message = message || "File already exists";
|
||||
}
|
||||
FileExistsError.prototype = Error.prototype;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @constructor
|
||||
|
|
|
@ -20,13 +20,22 @@ var LOG_ALL_IO = false;
|
|||
|
||||
/**
|
||||
* @const
|
||||
* Enables logging of page faults, quite verbose
|
||||
*/
|
||||
var LOG_PAGE_FAULTS = false;
|
||||
var DUMP_GENERATED_WASM = false;
|
||||
|
||||
/**
|
||||
* @const
|
||||
*/
|
||||
var DUMP_UNCOMPILED_ASSEMBLY = false;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* More accurate filenames in 9p debug messages at the cost of performance.
|
||||
*/
|
||||
var TRACK_FILENAMES = false;
|
||||
|
||||
var LOG_LEVEL = LOG_ALL & ~LOG_PS2 & ~LOG_PIT & ~LOG_VIRTIO & ~LOG_9P & ~LOG_PIC &
|
||||
~LOG_DMA & ~LOG_SERIAL & ~LOG_NET & ~LOG_FLOPPY & ~LOG_DISK;
|
||||
~LOG_DMA & ~LOG_SERIAL & ~LOG_NET & ~LOG_FLOPPY & ~LOG_DISK & ~LOG_VGA;
|
||||
|
||||
/**
|
||||
* @const
|
||||
|
@ -38,19 +47,6 @@ var DEBUG_SCREEN_LAYERS = DEBUG && false;
|
|||
/** @const */
|
||||
var ENABLE_HPET = DEBUG && false;
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
* Overridden by closure compiler
|
||||
*/
|
||||
var ENABLE_ACPI = false;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* How many cycles the CPU does at a time before running hardware timers
|
||||
*/
|
||||
var LOOP_COUNTER = 11001;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* How often, in milliseconds, to yield to the browser for rendering and
|
||||
|
@ -62,7 +58,7 @@ var TIME_PER_FRAME = 1;
|
|||
* @const
|
||||
* How many ticks the TSC does per millisecond
|
||||
*/
|
||||
var TSC_RATE = 8 * 1024;
|
||||
var TSC_RATE = 1 * 1000 * 1000;
|
||||
|
||||
|
||||
/** @const */
|
||||
|
|
105
src/const.js
105
src/const.js
|
@ -61,13 +61,6 @@ var LOG_NAMES = [
|
|||
[LOG_SB16, "SB16"]
|
||||
];
|
||||
|
||||
var
|
||||
/** @const */ TLB_SYSTEM_READ = 1,
|
||||
/** @const */ TLB_SYSTEM_WRITE = 2,
|
||||
/** @const */ TLB_USER_READ = 4,
|
||||
/** @const */ TLB_USER_WRITE = 8;
|
||||
|
||||
|
||||
var
|
||||
|
||||
// flags register bitflags
|
||||
|
@ -95,36 +88,6 @@ var
|
|||
*/
|
||||
flags_default = 1 << 1,
|
||||
|
||||
/**
|
||||
* bitmask to select non-reserved flags bits
|
||||
* @const
|
||||
*/
|
||||
flags_mask =
|
||||
flag_carry | flag_parity | flag_adjust | flag_zero | flag_sign | flag_trap | flag_interrupt |
|
||||
flag_direction | flag_overflow | flag_iopl | flag_nt | flag_rf | flag_vm | flag_ac |
|
||||
flag_vif | flag_vip | flag_id,
|
||||
|
||||
|
||||
/**
|
||||
* all arithmetic flags
|
||||
* @const
|
||||
*/
|
||||
flags_all = flag_carry | flag_parity | flag_adjust | flag_zero | flag_sign | flag_overflow,
|
||||
|
||||
|
||||
/**
|
||||
* opsizes used by get flag functions
|
||||
*
|
||||
* @const
|
||||
*/
|
||||
OPSIZE_8 = 7,
|
||||
/** @const */
|
||||
OPSIZE_16 = 15,
|
||||
/** @const */
|
||||
OPSIZE_32 = 31,
|
||||
|
||||
/** @const */
|
||||
PSE_ENABLED = 128,
|
||||
|
||||
/** @const */ reg_eax = 0,
|
||||
/** @const */ reg_ecx = 1,
|
||||
|
@ -135,25 +98,6 @@ PSE_ENABLED = 128,
|
|||
/** @const */ reg_esi = 6,
|
||||
/** @const */ reg_edi = 7,
|
||||
|
||||
/** @const */ reg_ax = 0,
|
||||
/** @const */ reg_cx = 2,
|
||||
/** @const */ reg_dx = 4,
|
||||
/** @const */ reg_bx = 6,
|
||||
/** @const */ reg_sp = 8,
|
||||
/** @const */ reg_bp = 10,
|
||||
/** @const */ reg_si = 12,
|
||||
/** @const */ reg_di = 14,
|
||||
|
||||
/** @const */ reg_al = 0,
|
||||
/** @const */ reg_cl = 4,
|
||||
/** @const */ reg_dl = 8,
|
||||
/** @const */ reg_bl = 12,
|
||||
/** @const */ reg_ah = 1,
|
||||
/** @const */ reg_ch = 5,
|
||||
/** @const */ reg_dh = 9,
|
||||
/** @const */ reg_bh = 13,
|
||||
|
||||
|
||||
/** @const */ reg_es = 0,
|
||||
/** @const */ reg_cs = 1,
|
||||
/** @const */ reg_ss = 2,
|
||||
|
@ -176,15 +120,6 @@ var
|
|||
/** @const */
|
||||
MMAP_BLOCK_SIZE = 1 << MMAP_BLOCK_BITS;
|
||||
|
||||
|
||||
/** @const */
|
||||
var MEM_PAGE_WRITTEN = 1;
|
||||
|
||||
|
||||
/** @const */
|
||||
var MAGIC_CPU_EXCEPTION = 0xDEADBEE;
|
||||
|
||||
|
||||
var
|
||||
/** @const */
|
||||
REPEAT_STRING_PREFIX_NONE = 0,
|
||||
|
@ -207,6 +142,8 @@ var
|
|||
/** @const */
|
||||
CR0_WP = 1 << 16,
|
||||
/** @const */
|
||||
CR0_AM = 1 << 18,
|
||||
/** @const */
|
||||
CR0_NW = 1 << 29,
|
||||
/** @const */
|
||||
CR0_CD = 1 << 30,
|
||||
|
@ -320,36 +257,32 @@ var IA32_APIC_BASE_EN = 1 << 11;
|
|||
/** @const */ var TSR_LDT = 0x60;
|
||||
|
||||
|
||||
// https://github.com/qemu/seabios/blob/14221cd86eadba82255fdc55ed174d401c7a0a04/src/fw/paravirt.c#L205-L219
|
||||
|
||||
/** @const */ var FW_CFG_SIGNATURE = 0x00;
|
||||
/** @const */ var FW_CFG_ID = 0x01;
|
||||
/** @const */ var FW_CFG_RAM_SIZE = 0x03;
|
||||
/** @const */ var FW_CFG_NB_CPUS = 0x05;
|
||||
/** @const */ var FW_CFG_MAX_CPUS = 0x0F;
|
||||
/** @const */ var FW_CFG_NUMA = 0x0D;
|
||||
/** @const */ var FW_CFG_FILE_DIR = 0x19;
|
||||
|
||||
/** @const */ var FW_CFG_CUSTOM_START = 0x8000;
|
||||
// This value is specific to v86, choosen to hopefully not collide with other indexes
|
||||
/** @const */ var FW_CFG_FILE_START = 0xC000;
|
||||
|
||||
/** @const */
|
||||
var PREFIX_MASK_REP = 0b11000;
|
||||
/** @const */
|
||||
var PREFIX_REPZ = 0b01000;
|
||||
/** @const */
|
||||
var PREFIX_REPNZ = 0b10000;
|
||||
|
||||
/** @const */
|
||||
var PREFIX_MASK_SEGMENT = 0b111;
|
||||
|
||||
/** @const */
|
||||
var PREFIX_MASK_OPSIZE = 0b100000;
|
||||
/** @const */
|
||||
var PREFIX_MASK_ADDRSIZE = 0b1000000;
|
||||
|
||||
/** @const */
|
||||
var PREFIX_F2 = PREFIX_REPNZ; // alias
|
||||
/** @const */
|
||||
var PREFIX_F3 = PREFIX_REPZ; // alias
|
||||
/** @const */
|
||||
var PREFIX_66 = PREFIX_MASK_OPSIZE; // alias
|
||||
/** @const */ var FW_CFG_SIGNATURE_QEMU = 0x554D4551;
|
||||
|
||||
/** @const */
|
||||
var MXCSR_MASK = (0xFFFF & ~(1 << 6));
|
||||
|
||||
// See same constant in jit.rs
|
||||
/** @const */
|
||||
var WASM_TABLE_SIZE = 900;
|
||||
|
||||
/** @const */
|
||||
var WASM_TABLE_OFFSET = 1024;
|
||||
|
||||
|
||||
/** @const */
|
||||
var MIXER_CHANNEL_LEFT = 0;
|
||||
|
|
4325
src/cpu.js
4325
src/cpu.js
File diff suppressed because it is too large
Load diff
150
src/debug.js
150
src/debug.js
|
@ -130,7 +130,7 @@ CPU.prototype.debug_init = function()
|
|||
|
||||
cpu.running = false;
|
||||
var a = parseInt(prompt("input hex", ""), 16);
|
||||
if(a) while(cpu.instruction_pointer != a) step();
|
||||
if(a) while(cpu.instruction_pointer[0] != a) step();
|
||||
}
|
||||
|
||||
// http://ref.x86asm.net/x86reference.xml
|
||||
|
@ -177,6 +177,8 @@ CPU.prototype.debug_init = function()
|
|||
return;
|
||||
}
|
||||
|
||||
_ip = _ip >>> 0;
|
||||
|
||||
if(debug.trace_all && debug.all_ops)
|
||||
{
|
||||
debug.all_ops.push(_ip, op);
|
||||
|
@ -215,15 +217,17 @@ CPU.prototype.debug_init = function()
|
|||
|
||||
function get_state(where)
|
||||
{
|
||||
var vm = (cpu.flags & flag_vm) ? 1 : 0;
|
||||
var mode = cpu.protected_mode ? vm ? "vm86" : "prot" : "real";
|
||||
if(!DEBUG) return;
|
||||
|
||||
var mode = cpu.protected_mode[0] ? "prot" : "real";
|
||||
var vm = (cpu.flags[0] & flag_vm) ? 1 : 0;
|
||||
var flags = cpu.get_eflags();
|
||||
var iopl = cpu.getiopl();
|
||||
var cpl = cpu.cpl;
|
||||
var cpl = cpu.cpl[0];
|
||||
var cs_eip = h(cpu.sreg[reg_cs], 4) + ":" + h(cpu.get_real_eip() >>> 0, 8);
|
||||
var ss_esp = h(cpu.sreg[reg_ss], 4) + ":" + h(cpu.get_stack_reg() >>> 0, 8);
|
||||
var op_size = cpu.is_32 ? "32" : "16";
|
||||
var if_ = (cpu.flags & flag_interrupt) ? 1 : 0;
|
||||
var ss_esp = h(cpu.sreg[reg_ss], 4) + ":" + h(cpu.reg32[reg_es] >>> 0, 8);
|
||||
var op_size = cpu.is_32[0] ? "32" : "16";
|
||||
var if_ = (cpu.flags[0] & flag_interrupt) ? 1 : 0;
|
||||
|
||||
var flag_names = {
|
||||
[flag_carry]: "c",
|
||||
|
@ -253,12 +257,12 @@ CPU.prototype.debug_init = function()
|
|||
}
|
||||
}
|
||||
|
||||
return ("mode=" + mode + "/" + op_size + " paging=" + (+cpu.paging) +
|
||||
return ("mode=" + mode + "/" + op_size + " paging=" + (+((cpu.cr[0] & CR0_PG) !== 0)) +
|
||||
" iopl=" + iopl + " cpl=" + cpl + " if=" + if_ + " cs:eip=" + cs_eip +
|
||||
" cs_off=" + h(cpu.get_seg(reg_cs) >>> 0, 8) +
|
||||
" cs_off=" + h(cpu.get_seg_cs() >>> 0, 8) +
|
||||
" flgs=" + h(cpu.get_eflags() >>> 0, 6) + " (" + flag_string + ")" +
|
||||
" ss:esp=" + ss_esp +
|
||||
" ssize=" + (+cpu.stack_size_32) +
|
||||
" ssize=" + (+cpu.stack_size_32[0]) +
|
||||
(where ? " in " + where : ""));
|
||||
}
|
||||
|
||||
|
@ -283,8 +287,8 @@ CPU.prototype.debug_init = function()
|
|||
|
||||
for(var i = 0; i < 4; i++)
|
||||
{
|
||||
line1 += r32_names[i] + "=" + h(cpu.reg32[r32[r32_names[i]]], 8) + " ";
|
||||
line2 += r32_names[i+4] + "=" + h(cpu.reg32[r32[r32_names[i+4]]], 8) + " ";
|
||||
line1 += r32_names[i] + "=" + h(cpu.reg32[r32[r32_names[i]]] >>> 0, 8) + " ";
|
||||
line2 += r32_names[i+4] + "=" + h(cpu.reg32[r32[r32_names[i+4]]] >>> 0, 8) + " ";
|
||||
}
|
||||
|
||||
//line1 += " eip=" + h(cpu.get_real_eip() >>> 0, 8);
|
||||
|
@ -357,8 +361,8 @@ CPU.prototype.debug_init = function()
|
|||
{
|
||||
if(!DEBUG) return;
|
||||
|
||||
dbg_log("gdt: (len = " + h(cpu.gdtr_size) + ")");
|
||||
dump_table(cpu.translate_address_system_read(cpu.gdtr_offset), cpu.gdtr_size);
|
||||
dbg_log("gdt: (len = " + h(cpu.gdtr_size[0]) + ")");
|
||||
dump_table(cpu.translate_address_system_read(cpu.gdtr_offset[0]), cpu.gdtr_size[0]);
|
||||
|
||||
dbg_log("\nldt: (len = " + h(cpu.segment_limits[reg_ldtr]) + ")");
|
||||
dump_table(cpu.translate_address_system_read(cpu.segment_offsets[reg_ldtr]), cpu.segment_limits[reg_ldtr]);
|
||||
|
@ -439,9 +443,9 @@ CPU.prototype.debug_init = function()
|
|||
{
|
||||
if(!DEBUG) return;
|
||||
|
||||
for(var i = 0; i < cpu.idtr_size; i += 8)
|
||||
for(var i = 0; i < cpu.idtr_size[0]; i += 8)
|
||||
{
|
||||
var addr = cpu.translate_address_system_read(cpu.idtr_offset + i),
|
||||
var addr = cpu.translate_address_system_read(cpu.idtr_offset[0] + i),
|
||||
base = cpu.read16(addr) | cpu.read16(addr + 6) << 16,
|
||||
selector = cpu.read16(addr + 2),
|
||||
type = cpu.read8(addr + 5),
|
||||
|
@ -529,6 +533,7 @@ CPU.prototype.debug_init = function()
|
|||
|
||||
if(!entry)
|
||||
{
|
||||
dbg_log("Not present: " + h((i << 22) >>> 0, 8));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -583,7 +588,7 @@ CPU.prototype.debug_init = function()
|
|||
if(start === undefined)
|
||||
{
|
||||
start = 0;
|
||||
count = cpu.memory_size;
|
||||
count = cpu.memory_size[0];
|
||||
}
|
||||
else if(count === undefined)
|
||||
{
|
||||
|
@ -630,7 +635,7 @@ CPU.prototype.debug_init = function()
|
|||
|
||||
var width = 0x80,
|
||||
height = 0x10,
|
||||
block_size = cpu.memory_size / width / height | 0,
|
||||
block_size = cpu.memory_size[0] / width / height | 0,
|
||||
row;
|
||||
|
||||
for(var i = 0; i < height; i++)
|
||||
|
@ -707,4 +712,113 @@ CPU.prototype.debug_init = function()
|
|||
// this.debug.dump_regs_short();
|
||||
//}
|
||||
};
|
||||
|
||||
let cs;
|
||||
let capstone_decoder;
|
||||
|
||||
debug.dump_code = function(is_32, buffer, start)
|
||||
{
|
||||
if(!capstone_decoder)
|
||||
{
|
||||
if(cs === undefined)
|
||||
{
|
||||
if(typeof require === "function")
|
||||
{
|
||||
cs = require("./capstone-x86.min.js");
|
||||
}
|
||||
else
|
||||
{
|
||||
cs = window.cs;
|
||||
}
|
||||
|
||||
if(cs === undefined)
|
||||
{
|
||||
dbg_log("Warning: Missing capstone library, disassembly not available");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
capstone_decoder = [
|
||||
new cs.Capstone(cs.ARCH_X86, cs.MODE_16),
|
||||
new cs.Capstone(cs.ARCH_X86, cs.MODE_32),
|
||||
];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const instructions = capstone_decoder[is_32].disasm(buffer, start);
|
||||
|
||||
instructions.forEach(function (instr) {
|
||||
dbg_log(h(instr.address >>> 0) + ": " +
|
||||
v86util.pads(instr.bytes.map(x => h(x, 2).slice(-2)).join(" "), 20) + " " +
|
||||
instr.mnemonic + " " + instr.op_str);
|
||||
});
|
||||
dbg_log("");
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
dbg_log("Could not disassemble: " + Array.from(buffer).map(x => h(x, 2)).join(" "));
|
||||
}
|
||||
};
|
||||
|
||||
function dump_file(ab, name)
|
||||
{
|
||||
var blob = new Blob([ab]);
|
||||
|
||||
var a = document.createElement("a");
|
||||
a["download"] = name;
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.dataset["downloadurl"] = ["application/octet-stream", a["download"], a.href].join(":");
|
||||
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(a.src);
|
||||
}
|
||||
|
||||
let wabt;
|
||||
|
||||
debug.dump_wasm = function(buffer)
|
||||
{
|
||||
if(wabt === undefined)
|
||||
{
|
||||
if(typeof require === "function")
|
||||
{
|
||||
wabt = require("./libwabt.js");
|
||||
}
|
||||
else
|
||||
{
|
||||
wabt = new window.WabtModule;
|
||||
}
|
||||
|
||||
if(wabt === undefined)
|
||||
{
|
||||
dbg_log("Warning: Missing libwabt, wasm dump not available");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to make a small copy otherwise libwabt goes nuts trying to copy
|
||||
// the whole underlying buffer
|
||||
buffer = buffer.slice();
|
||||
|
||||
try
|
||||
{
|
||||
var module = wabt.readWasm(buffer, { readDebugNames: false });
|
||||
module.generateNames();
|
||||
module.applyNames();
|
||||
const result = module.toText({ foldExprs: true, inlineExport: true });
|
||||
dbg_log(result);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
dump_file(buffer, "failed.wasm");
|
||||
console.log(e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(module)
|
||||
{
|
||||
module.destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -104,11 +104,11 @@ function read_elf(buffer)
|
|||
|
||||
if(DEBUG)
|
||||
{
|
||||
for(let key in header)
|
||||
for(let key of Object.keys(header))
|
||||
{
|
||||
console.log(key + ": 0x" + header[key].toString(16));
|
||||
dbg_log(key + ": 0x" + header[key].toString(16));
|
||||
}
|
||||
console.log(header);
|
||||
dbg_log(header);
|
||||
}
|
||||
|
||||
console.assert(header.magic === ELF_MAGIC, "Bad magic");
|
||||
|
@ -137,7 +137,7 @@ function read_elf(buffer)
|
|||
SectionHeader,
|
||||
header.shnum);
|
||||
|
||||
if(DEBUG)
|
||||
if(DEBUG && LOG_LEVEL)
|
||||
{
|
||||
console.log("%d program headers:", program_headers.length);
|
||||
for(let program of program_headers)
|
||||
|
|
|
@ -1,69 +1,56 @@
|
|||
"use strict";
|
||||
|
||||
var performance = {};
|
||||
|
||||
|
||||
|
||||
var global = {};
|
||||
var require = function(module) {};
|
||||
var process = {};
|
||||
var process = { hrtime: function() {} };
|
||||
var __dirname = "";
|
||||
|
||||
var esprima = { tokenize: {}, parse: {} };
|
||||
var acorn = { walk: { simple: {} } };
|
||||
|
||||
var exports = {};
|
||||
var define = {};
|
||||
var module = {};
|
||||
|
||||
// New Web Audio API
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {AudioNode}
|
||||
* @param {Object=} options
|
||||
*/
|
||||
var AudioWorkletNode = function(context, name, options)
|
||||
{
|
||||
this.port =
|
||||
{
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Object=} transfer
|
||||
*/
|
||||
postMessage: function(data, transfer) {}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var AudioWorkletProcessor = function()
|
||||
{
|
||||
this.port =
|
||||
{
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Object=} transfer
|
||||
*/
|
||||
postMessage: function(data, transfer) {}
|
||||
};
|
||||
}
|
||||
|
||||
var AudioWorklet = function() {};
|
||||
|
||||
AudioContext.prototype.audioWorklet =
|
||||
{
|
||||
/** @return {Promise} */
|
||||
addModule: function(file) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {function()} processor
|
||||
*/
|
||||
var registerProcessor = function(name, processor) {}
|
||||
var registerProcessor = function(name, processor) {};
|
||||
|
||||
/** @const */
|
||||
var currentTime = 0;
|
||||
|
||||
/** @const */
|
||||
var sampleRate = 0;
|
||||
|
||||
|
||||
var WebAssembly = {
|
||||
Memory() {},
|
||||
Table() {},
|
||||
instantiate() { return { instance: null, module: null }; },
|
||||
compile() {},
|
||||
Instance() {},
|
||||
Module() {},
|
||||
};
|
||||
WebAssembly.Module.customSections = function(module, section) {};
|
||||
|
||||
var WabtModule = {
|
||||
readWasm: function(buf, opt) {},
|
||||
generateNames: function() {},
|
||||
applyNames: function() {},
|
||||
toText: function() {},
|
||||
};
|
||||
var cs = {
|
||||
Capstone: function() {},
|
||||
ARCH_X86: 0,
|
||||
MODE_16: 0,
|
||||
MODE_32: 0,
|
||||
disasm: { bytes: "", mnemonic: "", op_str: "", },
|
||||
};
|
||||
|
||||
const Buffer = {
|
||||
allocUnsafe : function(length) {},
|
||||
from : function(arrayBuffer, byteOffset, length) {},
|
||||
};
|
||||
|
|
|
@ -189,7 +189,7 @@ FloppyController.prototype.port3F7_read = function()
|
|||
{
|
||||
dbg_log("3F7 read", LOG_FLOPPY);
|
||||
return 0x00;
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.port3F5_read = function()
|
||||
{
|
||||
|
@ -294,7 +294,7 @@ FloppyController.prototype.port3F2_read = function()
|
|||
{
|
||||
dbg_log("read 3F2: DOR", LOG_FLOPPY);
|
||||
return this.dor;
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.port3F2_write = function(value)
|
||||
{
|
||||
|
@ -311,7 +311,7 @@ FloppyController.prototype.port3F2_write = function(value)
|
|||
dbg_log("DOR = " + h(value), LOG_FLOPPY);
|
||||
|
||||
this.dor = value;
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.check_drive_status = function(args)
|
||||
{
|
||||
|
@ -320,7 +320,7 @@ FloppyController.prototype.check_drive_status = function(args)
|
|||
this.response_index = 0;
|
||||
this.response_length = 1;
|
||||
this.response_data[0] = 1 << 5;
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.seek = function(args)
|
||||
{
|
||||
|
@ -331,14 +331,14 @@ FloppyController.prototype.seek = function(args)
|
|||
this.last_head = args[0] >> 2 & 1;
|
||||
|
||||
this.raise_irq();
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.calibrate = function(args)
|
||||
{
|
||||
dbg_log("floppy calibrate", LOG_FLOPPY);
|
||||
|
||||
this.raise_irq();
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.check_interrupt_status = function()
|
||||
{
|
||||
|
@ -350,7 +350,7 @@ FloppyController.prototype.check_interrupt_status = function()
|
|||
|
||||
this.response_data[0] = 1 << 5;
|
||||
this.response_data[1] = this.last_cylinder;
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.do_sector = function(is_write, args)
|
||||
{
|
||||
|
@ -424,12 +424,12 @@ FloppyController.prototype.done = function(args, cylinder, head, sector, error)
|
|||
this.response_data[6] = args[4];
|
||||
|
||||
this.raise_irq();
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.fix_drive_data = function(args)
|
||||
{
|
||||
dbg_log("floppy fix drive data " + args, LOG_FLOPPY);
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.read_sector_id = function(args)
|
||||
{
|
||||
|
@ -447,7 +447,7 @@ FloppyController.prototype.read_sector_id = function(args)
|
|||
this.response_data[6] = 0;
|
||||
|
||||
this.raise_irq();
|
||||
}
|
||||
};
|
||||
|
||||
FloppyController.prototype.raise_irq = function()
|
||||
{
|
||||
|
|
1628
src/fpu.js
1628
src/fpu.js
File diff suppressed because it is too large
Load diff
96
src/ide.js
96
src/ide.js
|
@ -47,7 +47,7 @@ var HD_SECTOR_SIZE = 512;
|
|||
function IDEDevice(cpu, master_buffer, slave_buffer, is_cd, nr, bus)
|
||||
{
|
||||
this.master = new IDEInterface(this, cpu, master_buffer, is_cd, nr, 0, bus);
|
||||
this.slave = new IDEInterface(this, cpu, slave_buffer, is_cd, nr, 1, bus);
|
||||
this.slave = new IDEInterface(this, cpu, slave_buffer, false, nr, 1, bus);
|
||||
|
||||
this.current_interface = this.master;
|
||||
|
||||
|
@ -83,8 +83,8 @@ function IDEDevice(cpu, master_buffer, slave_buffer, is_cd, nr, bus)
|
|||
this.pci_space = [
|
||||
0x86, 0x80, 0x10, 0x70, 0x05, 0x00, 0xA0, 0x02,
|
||||
0x00, 0x80, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
this.ata_port & 0xFF | 1, this.ata_port >> 8, 0x00, 0x00,
|
||||
this.ata_port_high & 0xFF | 1, this.ata_port_high >> 8, 0x00, 0x00,
|
||||
0 | 1, 0, 0x00, 0x00,
|
||||
0 | 1, 0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, // second device
|
||||
0x00, 0x00, 0x00, 0x00, // second device
|
||||
this.master_port & 0xFF | 1, this.master_port >> 8, 0x00, 0x00,
|
||||
|
@ -151,7 +151,7 @@ function IDEDevice(cpu, master_buffer, slave_buffer, is_cd, nr, bus)
|
|||
{
|
||||
dbg_log("Read error: " + h(this.current_interface.error & 0xFF) +
|
||||
" slave=" + (this.current_interface === this.slave), LOG_DISK);
|
||||
return this.current_interface.error;
|
||||
return this.current_interface.error & 0xFF;
|
||||
});
|
||||
cpu.io.register_read(this.ata_port | 2, this, function()
|
||||
{
|
||||
|
@ -177,7 +177,7 @@ function IDEDevice(cpu, master_buffer, slave_buffer, is_cd, nr, bus)
|
|||
cpu.io.register_read(this.ata_port | 6, this, function()
|
||||
{
|
||||
dbg_log("Read 1F6", LOG_DISK);
|
||||
return this.current_interface.drive_head;
|
||||
return this.current_interface.drive_head & 0xFF;
|
||||
});
|
||||
|
||||
cpu.io.register_write(this.ata_port | 0, this, function(data)
|
||||
|
@ -433,8 +433,8 @@ IDEDevice.prototype.get_state = function()
|
|||
|
||||
IDEDevice.prototype.set_state = function(state)
|
||||
{
|
||||
this.master = state[0];
|
||||
this.slave = state[1];
|
||||
this.master.set_state(state[0]);
|
||||
this.slave.set_state(state[1]);
|
||||
this.ata_port = state[2];
|
||||
this.irq = state[3];
|
||||
this.pci_id = state[4];
|
||||
|
@ -617,6 +617,11 @@ function IDEInterface(device, cpu, buffer, is_cd, device_nr, interface_nr, bus)
|
|||
/** @type {number} */
|
||||
this.write_dest = 0;
|
||||
|
||||
// cancellation support
|
||||
this.last_io_id = 0;
|
||||
this.in_progress_io_ids = new Set();
|
||||
this.cancelled_io_ids = new Set();
|
||||
|
||||
Object.seal(this);
|
||||
}
|
||||
|
||||
|
@ -642,6 +647,8 @@ IDEInterface.prototype.device_reset = function()
|
|||
this.cylinder_low = 0; // lba_mid
|
||||
this.cylinder_high = 0; // lba_high
|
||||
}
|
||||
|
||||
this.cancel_io_operations();
|
||||
};
|
||||
|
||||
IDEInterface.prototype.push_irq = function()
|
||||
|
@ -1183,7 +1190,7 @@ IDEInterface.prototype.atapi_read = function(cmd)
|
|||
this.status = 0x50 | 0x80;
|
||||
this.report_read_start();
|
||||
|
||||
this.buffer.get(start, byte_count, (data) =>
|
||||
this.read_buffer(start, byte_count, (data) =>
|
||||
{
|
||||
//setTimeout(() => {
|
||||
dbg_log("cd read: data arrived", LOG_DISK);
|
||||
|
@ -1236,7 +1243,7 @@ IDEInterface.prototype.atapi_read_dma = function(cmd)
|
|||
this.status = 0x50 | 0x80;
|
||||
this.report_read_start();
|
||||
|
||||
this.buffer.get(start, byte_count, (data) =>
|
||||
this.read_buffer(start, byte_count, (data) =>
|
||||
{
|
||||
dbg_log("atapi_read_dma: Data arrived");
|
||||
this.report_read_end(byte_count);
|
||||
|
@ -1281,8 +1288,7 @@ IDEInterface.prototype.do_atapi_dma = function()
|
|||
}
|
||||
|
||||
dbg_log("dma read dest=" + h(addr) + " count=" + h(count) + " datalen=" + h(this.data_length), LOG_DISK);
|
||||
this.cpu.write_blob(data.subarray(offset,
|
||||
Math.min(offset + count, this.data_length)), addr);
|
||||
this.cpu.write_blob(data.subarray(offset, Math.min(offset + count, this.data_length)), addr);
|
||||
|
||||
offset += count;
|
||||
prdt_start += 8;
|
||||
|
@ -1487,7 +1493,11 @@ IDEInterface.prototype.write_end = function()
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(this.current_command === 0x30 || this.current_command === 0x34);
|
||||
dbg_assert(this.current_command === 0x30 ||
|
||||
this.current_command === 0x34 ||
|
||||
this.current_command === 0xC5,
|
||||
"Unexpected command: " + h(this.current_command));
|
||||
|
||||
// XXX: Should advance here, but do_write does all the advancing
|
||||
//this.ata_advance(this.current_command, 1);
|
||||
this.status = 0x58;
|
||||
|
@ -1561,7 +1571,7 @@ IDEInterface.prototype.ata_read_sectors = function(cmd)
|
|||
this.status = 0x80 | 0x40;
|
||||
this.report_read_start();
|
||||
|
||||
this.buffer.get(start, byte_count, (data) =>
|
||||
this.read_buffer(start, byte_count, (data) =>
|
||||
{
|
||||
//setTimeout(() => {
|
||||
dbg_log("ata_read: Data arrived", LOG_DISK);
|
||||
|
@ -1621,7 +1631,7 @@ IDEInterface.prototype.do_ata_read_sectors_dma = function()
|
|||
|
||||
var orig_prdt_start = this.device.prdt_addr;
|
||||
|
||||
this.buffer.get(start, byte_count, (data) =>
|
||||
this.read_buffer(start, byte_count, (data) =>
|
||||
{
|
||||
//setTimeout(function() {
|
||||
dbg_log("do_ata_read_sectors_dma: Data arrived", LOG_DISK);
|
||||
|
@ -1734,12 +1744,12 @@ IDEInterface.prototype.do_ata_write_sectors_dma = function()
|
|||
var start = lba * this.sector_size;
|
||||
|
||||
var prdt_start = this.device.prdt_addr;
|
||||
var prdt_count = 0;
|
||||
var prdt_write_count = 0;
|
||||
var offset = 0;
|
||||
|
||||
dbg_log("prdt addr: " + h(prdt_start, 8), LOG_DISK);
|
||||
|
||||
const buffer = new Uint8Array(byte_count);
|
||||
|
||||
do {
|
||||
var prd_addr = this.cpu.read32s(prdt_start);
|
||||
var prd_count = this.cpu.read16(prdt_start + 4);
|
||||
|
@ -1756,39 +1766,29 @@ IDEInterface.prototype.do_ata_write_sectors_dma = function()
|
|||
var slice = this.cpu.mem8.subarray(prd_addr, prd_addr + prd_count);
|
||||
dbg_assert(slice.length === prd_count);
|
||||
|
||||
buffer.set(slice, offset);
|
||||
|
||||
//if(DEBUG)
|
||||
//{
|
||||
// dbg_log(hex_dump(slice), LOG_DISK);
|
||||
//}
|
||||
|
||||
this.buffer.set(start + offset, slice, function()
|
||||
{
|
||||
prdt_write_count++;
|
||||
});
|
||||
|
||||
offset += prd_count;
|
||||
prdt_start += 8;
|
||||
prdt_count++;
|
||||
}
|
||||
while(!end);
|
||||
|
||||
if(prdt_write_count === prdt_count)
|
||||
dbg_assert(offset === buffer.length);
|
||||
|
||||
this.buffer.set(start, buffer, () =>
|
||||
{
|
||||
//setTimeout(function() {
|
||||
dbg_log("dma write completed", LOG_DISK);
|
||||
this.ata_advance(this.current_command, count);
|
||||
this.status = 0x50;
|
||||
this.push_irq();
|
||||
this.device.dma_status &= ~1;
|
||||
this.current_command = -1;
|
||||
//}, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
// fails when writes don't happen synchronously, which isn't currently
|
||||
// the case, but might be in the future
|
||||
dbg_assert(false, "dma write not completed", LOG_DISK);
|
||||
}
|
||||
});
|
||||
|
||||
this.report_write(byte_count);
|
||||
};
|
||||
|
@ -2010,6 +2010,35 @@ IDEInterface.prototype.report_write = function(byte_count)
|
|||
this.bus.send("ide-write-end", [this.nr, byte_count, sector_count]);
|
||||
};
|
||||
|
||||
IDEInterface.prototype.read_buffer = function(start, length, callback)
|
||||
{
|
||||
const id = this.last_io_id++;
|
||||
this.in_progress_io_ids.add(id);
|
||||
|
||||
this.buffer.get(start, length, data =>
|
||||
{
|
||||
if(this.cancelled_io_ids.delete(id))
|
||||
{
|
||||
dbg_assert(!this.in_progress_io_ids.has(id));
|
||||
return;
|
||||
}
|
||||
|
||||
const removed = this.in_progress_io_ids.delete(id);
|
||||
dbg_assert(removed);
|
||||
|
||||
callback(data);
|
||||
});
|
||||
};
|
||||
|
||||
IDEInterface.prototype.cancel_io_operations = function()
|
||||
{
|
||||
for(const id of this.in_progress_io_ids)
|
||||
{
|
||||
this.cancelled_io_ids.add(id);
|
||||
}
|
||||
this.in_progress_io_ids.clear();
|
||||
};
|
||||
|
||||
IDEInterface.prototype.get_state = function()
|
||||
{
|
||||
var state = [];
|
||||
|
@ -2041,6 +2070,7 @@ IDEInterface.prototype.get_state = function()
|
|||
state[25] = this.current_command;
|
||||
state[26] = this.data_end;
|
||||
state[27] = this.current_atapi_command;
|
||||
state[28] = this.buffer;
|
||||
return state;
|
||||
};
|
||||
|
||||
|
@ -2075,4 +2105,6 @@ IDEInterface.prototype.set_state = function(state)
|
|||
|
||||
this.data16 = new Uint16Array(this.data.buffer);
|
||||
this.data32 = new Int32Array(this.data.buffer);
|
||||
|
||||
this.buffer && this.buffer.set_state(state[28]);
|
||||
};
|
||||
|
|
5046
src/instructions.js
5046
src/instructions.js
File diff suppressed because it is too large
Load diff
32
src/io.js
32
src/io.js
|
@ -20,7 +20,7 @@ function IO(cpu)
|
|||
this.ports[i] = this.create_empty_entry();
|
||||
}
|
||||
|
||||
var memory_size = cpu.memory_size;
|
||||
var memory_size = cpu.memory_size[0];
|
||||
|
||||
for(var i = 0; (i << MMAP_BLOCK_BITS) < memory_size; i++)
|
||||
{
|
||||
|
@ -246,34 +246,6 @@ IO.prototype.register_write_consecutive = function(port_addr, device, w8_1, w8_2
|
|||
}
|
||||
};
|
||||
|
||||
IO.prototype.in_mmap_range = function(start, count)
|
||||
{
|
||||
start >>>= 0;
|
||||
count >>>= 0;
|
||||
|
||||
var end = start + count;
|
||||
|
||||
if(end >= this.cpu.memory_size)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//dbg_log("in_mmap_range start=" + start + " count=" + count);
|
||||
start &= ~(MMAP_BLOCK_SIZE - 1);
|
||||
|
||||
while(start < end)
|
||||
{
|
||||
if(this.cpu.in_mapped_range(start))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
start += MMAP_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
IO.prototype.mmap_read32_shim = function(addr)
|
||||
{
|
||||
var aligned_addr = addr >>> MMAP_BLOCK_BITS;
|
||||
|
@ -487,5 +459,3 @@ IO.prototype.get_port_description = function(addr)
|
|||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -76,12 +76,24 @@ function IOAPIC(cpu)
|
|||
cpu.io.mmap_register(IOAPIC_ADDRESS, MMAP_BLOCK_SIZE,
|
||||
(addr) =>
|
||||
{
|
||||
dbg_assert(false, "unsupported read8 from ioapic: " + h(addr));
|
||||
addr = addr - IOAPIC_ADDRESS | 0;
|
||||
|
||||
if(addr >= IOWIN && addr < IOWIN + 4)
|
||||
{
|
||||
const byte = addr - IOWIN;
|
||||
dbg_log("ioapic read8 byte " + byte + " " + h(this.ioregsel), LOG_APIC);
|
||||
return this.read(this.ioregsel) >> (8 * byte) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register read: " + h(addr >>> 0), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
(addr, value) =>
|
||||
{
|
||||
dbg_assert(false, "unsupported write8 from ioapic: " + h(addr));
|
||||
dbg_assert(false, "unsupported write8 from ioapic: " + h(addr >>> 0));
|
||||
},
|
||||
(addr) =>
|
||||
{
|
||||
|
@ -97,7 +109,7 @@ function IOAPIC(cpu)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register read: " + h(addr), LOG_APIC);
|
||||
dbg_log("Unexpected IOAPIC register read: " + h(addr >>> 0), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
@ -116,7 +128,7 @@ function IOAPIC(cpu)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register write: " + h(addr) + " <- " + h(value >>> 0, 8), LOG_APIC);
|
||||
dbg_log("Unexpected IOAPIC register write: " + h(addr >>> 0) + " <- " + h(value >>> 0, 8), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
|
235
src/kernel.js
Normal file
235
src/kernel.js
Normal file
|
@ -0,0 +1,235 @@
|
|||
"use strict";
|
||||
|
||||
// https://www.kernel.org/doc/Documentation/x86/boot.txt
|
||||
|
||||
const LINUX_BOOT_HDR_SETUP_SECTS = 0x1F1;
|
||||
const LINUX_BOOT_HDR_SYSSIZE = 0x1F4;
|
||||
const LINUX_BOOT_HDR_VIDMODE = 0x1FA;
|
||||
const LINUX_BOOT_HDR_BOOT_FLAG = 0x1FE;
|
||||
const LINUX_BOOT_HDR_HEADER = 0x202;
|
||||
const LINUX_BOOT_HDR_VERSION = 0x206;
|
||||
const LINUX_BOOT_HDR_TYPE_OF_LOADER = 0x210;
|
||||
const LINUX_BOOT_HDR_LOADFLAGS = 0x211;
|
||||
const LINUX_BOOT_HDR_CODE32_START = 0x214;
|
||||
const LINUX_BOOT_HDR_RAMDISK_IMAGE = 0x218;
|
||||
const LINUX_BOOT_HDR_RAMDISK_SIZE = 0x21C;
|
||||
const LINUX_BOOT_HDR_HEAP_END_PTR = 0x224;
|
||||
const LINUX_BOOT_HDR_CMD_LINE_PTR = 0x228;
|
||||
const LINUX_BOOT_HDR_INITRD_ADDR_MAX = 0x22C;
|
||||
const LINUX_BOOT_HDR_KERNEL_ALIGNMENT = 0x230;
|
||||
const LINUX_BOOT_HDR_RELOCATABLE_KERNEL = 0x234;
|
||||
const LINUX_BOOT_HDR_MIN_ALIGNMENT = 0x235;
|
||||
const LINUX_BOOT_HDR_XLOADFLAGS = 0x236;
|
||||
const LINUX_BOOT_HDR_CMDLINE_SIZE = 0x238;
|
||||
const LINUX_BOOT_HDR_PAYLOAD_OFFSET = 0x248;
|
||||
const LINUX_BOOT_HDR_PAYLOAD_LENGTH = 0x24C;
|
||||
const LINUX_BOOT_HDR_PREF_ADDRESS = 0x258;
|
||||
const LINUX_BOOT_HDR_INIT_SIZE = 0x260;
|
||||
|
||||
const LINUX_BOOT_HDR_CHECKSUM1 = 0xAA55;
|
||||
const LINUX_BOOT_HDR_CHECKSUM2 = 0x53726448;
|
||||
|
||||
const LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED = 0xFF;
|
||||
|
||||
const LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH = 1 << 0;
|
||||
const LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG = 1 << 5;
|
||||
const LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS = 1 << 6;
|
||||
const LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS = 1 << 7;
|
||||
|
||||
|
||||
function load_kernel(mem8, bzimage, initrd, cmdline)
|
||||
{
|
||||
dbg_log("Trying to load kernel of size " + bzimage.byteLength);
|
||||
|
||||
const KERNEL_HIGH_ADDRESS = 0x100000;
|
||||
|
||||
// Put the initrd at the 64 MB boundary. This means the minimum memory size
|
||||
// is 64 MB plus the size of the initrd.
|
||||
// Note: If set too low, kernel may fail to load the initrd with "invalid magic at start of compressed archive"
|
||||
const INITRD_ADDRESS = 64 << 20;
|
||||
|
||||
const quiet = false;
|
||||
|
||||
const bzimage8 = new Uint8Array(bzimage);
|
||||
const bzimage16 = new Uint16Array(bzimage);
|
||||
const bzimage32 = new Uint32Array(bzimage);
|
||||
|
||||
const setup_sects = bzimage8[LINUX_BOOT_HDR_SETUP_SECTS] || 4;
|
||||
const syssize = bzimage32[LINUX_BOOT_HDR_SYSSIZE >> 2] << 4;
|
||||
|
||||
const vidmode = bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1];
|
||||
|
||||
const checksum1 = bzimage16[LINUX_BOOT_HDR_BOOT_FLAG >> 1];
|
||||
if(checksum1 !== LINUX_BOOT_HDR_CHECKSUM1)
|
||||
{
|
||||
dbg_log("Bad checksum1: " + h(checksum1));
|
||||
return;
|
||||
}
|
||||
|
||||
// Not aligned, so split into two 16-bit reads
|
||||
const checksum2 =
|
||||
bzimage16[LINUX_BOOT_HDR_HEADER >> 1] |
|
||||
bzimage16[LINUX_BOOT_HDR_HEADER + 2 >> 1] << 16;
|
||||
if(checksum2 !== LINUX_BOOT_HDR_CHECKSUM2)
|
||||
{
|
||||
dbg_log("Bad checksum2: " + h(checksum2));
|
||||
return;
|
||||
}
|
||||
|
||||
const protocol = bzimage16[LINUX_BOOT_HDR_VERSION >> 1];
|
||||
dbg_assert(protocol >= 0x202); // older not supported by us
|
||||
|
||||
const flags = bzimage8[LINUX_BOOT_HDR_LOADFLAGS];
|
||||
dbg_assert(flags & LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH); // low kernels not supported by us
|
||||
|
||||
// we don't relocate the kernel, so we don't care much about most of these
|
||||
|
||||
const flags2 = bzimage16[LINUX_BOOT_HDR_XLOADFLAGS >> 1];
|
||||
const initrd_addr_max = bzimage32[LINUX_BOOT_HDR_INITRD_ADDR_MAX >> 2];
|
||||
const kernel_alignment = bzimage32[LINUX_BOOT_HDR_KERNEL_ALIGNMENT >> 2];
|
||||
const relocatable_kernel = bzimage8[LINUX_BOOT_HDR_RELOCATABLE_KERNEL];
|
||||
const min_alignment = bzimage8[LINUX_BOOT_HDR_MIN_ALIGNMENT];
|
||||
const cmdline_size = bzimage32[LINUX_BOOT_HDR_CMDLINE_SIZE >> 2];
|
||||
const payload_offset = bzimage32[LINUX_BOOT_HDR_PAYLOAD_OFFSET >> 2];
|
||||
const payload_length = bzimage32[LINUX_BOOT_HDR_PAYLOAD_LENGTH >> 2];
|
||||
const pref_address = bzimage32[LINUX_BOOT_HDR_PREF_ADDRESS >> 2];
|
||||
const pref_address_high = bzimage32[LINUX_BOOT_HDR_PREF_ADDRESS + 4 >> 2];
|
||||
const init_size = bzimage32[LINUX_BOOT_HDR_INIT_SIZE >> 2];
|
||||
|
||||
dbg_log("kernel boot protocol version: " + h(protocol));
|
||||
dbg_log("flags=" + h(flags) + " xflags=" + h(flags2));
|
||||
dbg_log("code32_start=" + h(bzimage32[LINUX_BOOT_HDR_CODE32_START >> 2]));
|
||||
dbg_log("initrd_addr_max=" + h(initrd_addr_max));
|
||||
dbg_log("kernel_alignment=" + h(kernel_alignment));
|
||||
dbg_log("relocatable=" + relocatable_kernel);
|
||||
dbg_log("min_alignment=" + h(min_alignment));
|
||||
dbg_log("cmdline max=" + h(cmdline_size));
|
||||
dbg_log("payload offset=" + h(payload_offset) + " size=" + h(payload_length));
|
||||
dbg_log("pref_address=" + h(pref_address_high) + ":" + h(pref_address));
|
||||
dbg_log("init_size=" + h(init_size));
|
||||
|
||||
const real_mode_segment = 0x8000;
|
||||
const base_ptr = real_mode_segment << 4;
|
||||
|
||||
const heap_end = 0xE000;
|
||||
const heap_end_ptr = heap_end - 0x200;
|
||||
|
||||
// fill in the kernel boot header with infos the kernel needs to know
|
||||
|
||||
bzimage8[LINUX_BOOT_HDR_TYPE_OF_LOADER] = LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED;
|
||||
|
||||
const new_flags =
|
||||
(quiet ? flags | LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG : flags & ~LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG)
|
||||
& ~LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS
|
||||
| LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS;
|
||||
bzimage8[LINUX_BOOT_HDR_LOADFLAGS] = new_flags;
|
||||
|
||||
bzimage16[LINUX_BOOT_HDR_HEAP_END_PTR >> 1] = heap_end_ptr;
|
||||
|
||||
// should parse the vga=... paramter from cmdline here, but we don't really care
|
||||
bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1] = 0xFFFF; // normal
|
||||
|
||||
dbg_log("heap_end_ptr=" + h(heap_end_ptr));
|
||||
|
||||
cmdline += "\x00";
|
||||
dbg_assert(cmdline.length < cmdline_size);
|
||||
|
||||
const cmd_line_ptr = base_ptr + heap_end;
|
||||
dbg_log("cmd_line_ptr=" + h(cmd_line_ptr));
|
||||
|
||||
bzimage32[LINUX_BOOT_HDR_CMD_LINE_PTR >> 2] = cmd_line_ptr;
|
||||
for(let i = 0; i < cmdline.length; i++)
|
||||
{
|
||||
mem8[cmd_line_ptr + i] = cmdline.charCodeAt(i);
|
||||
}
|
||||
|
||||
const prot_mode_kernel_start = (setup_sects + 1) * 512;
|
||||
dbg_log("prot_mode_kernel_start=" + h(prot_mode_kernel_start));
|
||||
|
||||
const real_mode_kernel = new Uint8Array(bzimage, 0, prot_mode_kernel_start);
|
||||
const protected_mode_kernel = new Uint8Array(bzimage, prot_mode_kernel_start);
|
||||
|
||||
let ramdisk_address = 0;
|
||||
let ramdisk_size = 0;
|
||||
|
||||
if(initrd)
|
||||
{
|
||||
ramdisk_address = INITRD_ADDRESS;
|
||||
ramdisk_size = initrd.byteLength;
|
||||
|
||||
dbg_assert(KERNEL_HIGH_ADDRESS + protected_mode_kernel.length < ramdisk_address);
|
||||
|
||||
mem8.set(new Uint8Array(initrd), ramdisk_address);
|
||||
}
|
||||
|
||||
bzimage32[LINUX_BOOT_HDR_RAMDISK_IMAGE >> 2] = ramdisk_address;
|
||||
bzimage32[LINUX_BOOT_HDR_RAMDISK_SIZE >> 2] = ramdisk_size;
|
||||
|
||||
dbg_assert(base_ptr + real_mode_kernel.length < 0xA0000);
|
||||
|
||||
mem8.set(real_mode_kernel, base_ptr);
|
||||
mem8.set(protected_mode_kernel, KERNEL_HIGH_ADDRESS);
|
||||
|
||||
return {
|
||||
option_rom:
|
||||
{
|
||||
name: "genroms/kernel.bin",
|
||||
data: make_linux_boot_rom(real_mode_segment, heap_end),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function make_linux_boot_rom(real_mode_segment, heap_end)
|
||||
{
|
||||
// This rom will be executed by seabios after its initialisation
|
||||
// It sets up segment registers, the stack and calls the kernel real mode entry point
|
||||
|
||||
const SIZE = 0x200;
|
||||
|
||||
const data8 = new Uint8Array(0x100);
|
||||
const data16 = new Uint16Array(data8.buffer);
|
||||
|
||||
data16[0] = 0xAA55;
|
||||
data8[2] = SIZE / 0x200;
|
||||
|
||||
let i = 3;
|
||||
|
||||
data8[i++] = 0xFA; // cli
|
||||
data8[i++] = 0xB8; // mov ax, real_mode_segment
|
||||
data8[i++] = real_mode_segment >> 0;
|
||||
data8[i++] = real_mode_segment >> 8;
|
||||
data8[i++] = 0x8E; // mov es, ax
|
||||
data8[i++] = 0xC0;
|
||||
data8[i++] = 0x8E; // mov ds, ax
|
||||
data8[i++] = 0xD8;
|
||||
data8[i++] = 0x8E; // mov fs, ax
|
||||
data8[i++] = 0xE0;
|
||||
data8[i++] = 0x8E; // mov gs, ax
|
||||
data8[i++] = 0xE8;
|
||||
data8[i++] = 0x8E; // mov ss, ax
|
||||
data8[i++] = 0xD0;
|
||||
data8[i++] = 0xBC; // mov sp, heap_end
|
||||
data8[i++] = heap_end >> 0;
|
||||
data8[i++] = heap_end >> 8;
|
||||
data8[i++] = 0xEA; // jmp (real_mode_segment+0x20):0x0
|
||||
data8[i++] = 0x00;
|
||||
data8[i++] = 0x00;
|
||||
data8[i++] = real_mode_segment + 0x20 >> 0;
|
||||
data8[i++] = real_mode_segment + 0x20 >> 8;
|
||||
|
||||
dbg_assert(i < SIZE);
|
||||
|
||||
const checksum_index = i;
|
||||
data8[checksum_index] = 0;
|
||||
|
||||
let checksum = 0;
|
||||
|
||||
for(let i = 0; i < data8.length; i++)
|
||||
{
|
||||
checksum += data8[i];
|
||||
}
|
||||
|
||||
data8[checksum_index] = -checksum;
|
||||
|
||||
return data8;
|
||||
}
|
148
src/lib.js
148
src/lib.js
|
@ -1,31 +1,60 @@
|
|||
"use strict";
|
||||
|
||||
var goog = goog || {};
|
||||
goog.exportSymbol = function() {};
|
||||
goog.exportProperty = function() {};
|
||||
|
||||
var v86util = v86util || {};
|
||||
|
||||
// pad string with spaces on the right
|
||||
v86util.pads = function(str, len)
|
||||
{
|
||||
str = str ? str + "" : "";
|
||||
|
||||
while(str.length < len)
|
||||
{
|
||||
str = str + " ";
|
||||
}
|
||||
|
||||
return str;
|
||||
str = (str || str === 0) ? str + "" : "";
|
||||
return str.padEnd(len, " ");
|
||||
};
|
||||
|
||||
// pad string with zeros on the left
|
||||
v86util.pad0 = function(str, len)
|
||||
{
|
||||
str = str ? str + "" : "";
|
||||
str = (str || str === 0) ? str + "" : "";
|
||||
return str.padStart(len, "0");
|
||||
};
|
||||
|
||||
while(str.length < len)
|
||||
// generates array given size with zeros
|
||||
v86util.zeros = function(size)
|
||||
{
|
||||
str = "0" + str;
|
||||
}
|
||||
return Array(size).fill(0);
|
||||
};
|
||||
|
||||
return str;
|
||||
// generates [0, 1, 2, ..., size-1]
|
||||
v86util.range = function(size)
|
||||
{
|
||||
return Array.from(Array(size).keys());
|
||||
};
|
||||
|
||||
v86util.view = function(constructor, memory, offset, length)
|
||||
{
|
||||
return new Proxy({},
|
||||
{
|
||||
get: function(target, property, receiver)
|
||||
{
|
||||
const b = new constructor(memory.buffer, offset, length);
|
||||
const x = b[property];
|
||||
if(typeof x === "function")
|
||||
{
|
||||
return x.bind(b);
|
||||
}
|
||||
dbg_assert(/^\d+$/.test(property) || property === "buffer" || property === "length" ||
|
||||
property === "BYTES_PER_ELEMENT" || property === "byteOffset");
|
||||
return x;
|
||||
},
|
||||
set: function(target, property, value, receiver)
|
||||
{
|
||||
dbg_assert(/^\d+$/.test(property));
|
||||
new constructor(memory.buffer, offset, length)[property] = value;
|
||||
return true;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -53,29 +82,26 @@ if(typeof window !== "undefined" && window.crypto && window.crypto.getRandomValu
|
|||
{
|
||||
let rand_data = new Int32Array(1);
|
||||
|
||||
v86util.has_rand_int = function()
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
v86util.get_rand_int = function()
|
||||
{
|
||||
window.crypto.getRandomValues(rand_data);
|
||||
return rand_data[0];
|
||||
};
|
||||
}
|
||||
else
|
||||
else if(typeof require !== "undefined")
|
||||
{
|
||||
v86util.has_rand_int = function()
|
||||
{
|
||||
return false;
|
||||
};
|
||||
/** @type {{ randomBytes: Function }} */
|
||||
const crypto = require("crypto");
|
||||
|
||||
v86util.get_rand_int = function()
|
||||
{
|
||||
console.assert(false);
|
||||
return crypto.randomBytes(4)["readInt32LE"](0);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(false, "Unsupported platform: No cryptographic random values");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -84,6 +110,8 @@ else
|
|||
*/
|
||||
function SyncBuffer(buffer)
|
||||
{
|
||||
dbg_assert(buffer instanceof ArrayBuffer);
|
||||
|
||||
this.buffer = buffer;
|
||||
this.byteLength = buffer.byteLength;
|
||||
this.onload = undefined;
|
||||
|
@ -127,7 +155,19 @@ SyncBuffer.prototype.get_buffer = function(fn)
|
|||
fn(this.buffer);
|
||||
};
|
||||
|
||||
SyncBuffer.prototype.get_state = function()
|
||||
{
|
||||
const state = [];
|
||||
state[0] = this.byteLength;
|
||||
state[1] = new Uint8Array(this.buffer);
|
||||
return state;
|
||||
};
|
||||
|
||||
SyncBuffer.prototype.set_state = function(state)
|
||||
{
|
||||
this.byteLength = state[0];
|
||||
this.buffer = state[1].slice().buffer;
|
||||
};
|
||||
|
||||
(function()
|
||||
{
|
||||
|
@ -192,6 +232,7 @@ SyncBuffer.prototype.get_buffer = function(fn)
|
|||
*/
|
||||
v86util.int_log2 = function(x)
|
||||
{
|
||||
x >>>= 0;
|
||||
dbg_assert(x > 0);
|
||||
|
||||
// http://jsperf.com/integer-log2/6
|
||||
|
@ -225,22 +266,6 @@ SyncBuffer.prototype.get_buffer = function(fn)
|
|||
})();
|
||||
|
||||
|
||||
v86util.mul_low = v86util.imul_low =
|
||||
typeof Math.imul === "function" &&
|
||||
Math.imul(0x01234567, 0x89abcdef) === -0x36b1b9d7 ? Math.imul : function(a, b) {
|
||||
b |= 0;
|
||||
return (a & 0x003fffff) * b + ((a & 0xffc00000) * b | 0) | 0;
|
||||
};
|
||||
|
||||
|
||||
v86util.imul_high = function(a, b) {
|
||||
return Math.floor(a * b / 0x100000000) | 0;
|
||||
};
|
||||
|
||||
v86util.mul_high = function(a, b) {
|
||||
return Math.floor((a >>> 0) * (b >>> 0) / 0x100000000) | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
|
@ -478,3 +503,46 @@ function download(file_or_blob, name)
|
|||
|
||||
window.URL.revokeObjectURL(a.href);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple 1d bitmap
|
||||
* @constructor
|
||||
*/
|
||||
v86util.Bitmap = function(length_or_buffer)
|
||||
{
|
||||
if(typeof length_or_buffer === "number")
|
||||
{
|
||||
this.view = new Uint8Array(length_or_buffer + 7 >> 3);
|
||||
}
|
||||
else if(length_or_buffer instanceof ArrayBuffer)
|
||||
{
|
||||
this.view = new Uint8Array(length_or_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
v86util.Bitmap.prototype.set = function(index, value)
|
||||
{
|
||||
const bit_index = index & 7;
|
||||
const byte_index = index >> 3;
|
||||
const bit_mask = 1 << bit_index;
|
||||
|
||||
this.view[byte_index] =
|
||||
value ? this.view[byte_index] | bit_mask : this.view[byte_index] & ~bit_mask;
|
||||
};
|
||||
|
||||
v86util.Bitmap.prototype.get = function(index)
|
||||
{
|
||||
const bit_index = index & 7;
|
||||
const byte_index = index >> 3;
|
||||
|
||||
return this.view[byte_index] >> bit_index & 1;
|
||||
};
|
||||
|
||||
v86util.Bitmap.prototype.get_buffer = function()
|
||||
{
|
||||
return this.view.buffer;
|
||||
};
|
||||
|
|
|
@ -94,7 +94,7 @@ function dbg_trace(level)
|
|||
{
|
||||
if(!DEBUG) return;
|
||||
|
||||
dbg_log(Error().stack.replace(/(?:(?:t|t16|t32)\.\(anonymous function\)\.)+/g, "t.(anonymous function)."), level);
|
||||
dbg_log(Error().stack, level);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,7 +110,7 @@ function dbg_assert(cond, msg, level)
|
|||
{
|
||||
dbg_assert_failed(msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function dbg_assert_failed(msg)
|
||||
|
|
31
src/main.js
31
src/main.js
|
@ -1,7 +1,10 @@
|
|||
"use strict";
|
||||
|
||||
/** @constructor */
|
||||
function v86(bus)
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Object=} wasm
|
||||
*/
|
||||
function v86(bus, wasm)
|
||||
{
|
||||
/** @type {boolean} */
|
||||
this.running = false;
|
||||
|
@ -10,7 +13,7 @@ function v86(bus)
|
|||
this.stopped = false;
|
||||
|
||||
/** @type {CPU} */
|
||||
this.cpu = new CPU(bus);
|
||||
this.cpu = new CPU(bus, wasm);
|
||||
|
||||
this.bus = bus;
|
||||
bus.register("cpu-init", this.init, this);
|
||||
|
@ -23,6 +26,8 @@ function v86(bus)
|
|||
|
||||
v86.prototype.run = function()
|
||||
{
|
||||
this.stopped = false;
|
||||
|
||||
if(!this.running)
|
||||
{
|
||||
this.bus.send("emulator-started");
|
||||
|
@ -192,20 +197,22 @@ v86.prototype.restore_state = function(state)
|
|||
|
||||
|
||||
if(typeof performance === "object" && performance.now)
|
||||
{
|
||||
v86.microtick = performance.now.bind(performance);
|
||||
}
|
||||
else if(typeof require === "function")
|
||||
{
|
||||
const { performance } = require("perf_hooks");
|
||||
v86.microtick = performance.now.bind(performance);
|
||||
}
|
||||
else if(typeof process === "object" && process.hrtime)
|
||||
{
|
||||
v86.microtick = function()
|
||||
{
|
||||
return performance.now();
|
||||
var t = process.hrtime();
|
||||
return t[0] * 1000 + t[1] / 1e6;
|
||||
};
|
||||
}
|
||||
//else if(typeof process === "object" && process.hrtime)
|
||||
//{
|
||||
// v86.microtick = function()
|
||||
// {
|
||||
// var t = process.hrtime();
|
||||
// return t[0] * 1000 + t[1] / 1e6;
|
||||
// };
|
||||
//}
|
||||
else
|
||||
{
|
||||
v86.microtick = Date.now;
|
||||
|
|
267
src/memory.js
267
src/memory.js
|
@ -1,44 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
/** @const */
|
||||
var A20_MASK = ~(1 << 20);
|
||||
/** @const */
|
||||
var A20_MASK16 = ~(1 << 20 - 1);
|
||||
/** @const */
|
||||
var A20_MASK32 = ~(1 << 20 - 2);
|
||||
|
||||
/** @const */
|
||||
var USE_A20 = false;
|
||||
|
||||
|
||||
// called by all memory writes
|
||||
CPU.prototype.debug_write = function(addr, size, value)
|
||||
{
|
||||
if(!DEBUG)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dbg_assert(typeof value === "number" && !isNaN(value));
|
||||
dbg_assert(value >= -0x80000000 && addr < 0x80000000);
|
||||
|
||||
this.debug_read(addr, size, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean=} is_write
|
||||
*/
|
||||
CPU.prototype.debug_read = function(addr, size, is_write)
|
||||
{
|
||||
if(!DEBUG)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dbg_assert(typeof addr === "number");
|
||||
dbg_assert(!isNaN(addr));
|
||||
};
|
||||
|
||||
|
||||
CPU.prototype.mmap_read8 = function(addr)
|
||||
{
|
||||
|
@ -70,208 +31,37 @@ CPU.prototype.mmap_read32 = function(addr)
|
|||
var aligned_addr = addr >>> MMAP_BLOCK_BITS;
|
||||
|
||||
return this.memory_map_read32[aligned_addr](addr);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.mmap_write32 = function(addr, value)
|
||||
{
|
||||
var aligned_addr = addr >>> MMAP_BLOCK_BITS;
|
||||
|
||||
this.memory_map_write32[aligned_addr](addr, value);
|
||||
}
|
||||
|
||||
CPU.prototype.in_mapped_range = function(addr)
|
||||
{
|
||||
return (addr | 0) >= 0xA0000 && (addr | 0) < 0xC0000 || (addr >>> 0) >= (this.memory_size >>> 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPU.prototype.read8 = function(addr)
|
||||
CPU.prototype.mmap_write64 = function(addr, value0, value1)
|
||||
{
|
||||
this.debug_read(addr, 1);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK;
|
||||
var aligned_addr = addr >>> MMAP_BLOCK_BITS;
|
||||
// This should hold since writes across pages are split up
|
||||
dbg_assert(aligned_addr === (addr + 7) >>> MMAP_BLOCK_BITS);
|
||||
|
||||
if(this.in_mapped_range(addr))
|
||||
{
|
||||
return this.mmap_read8(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.mem8[addr];
|
||||
}
|
||||
var write_func32 = this.memory_map_write32[aligned_addr];
|
||||
write_func32(addr, value0);
|
||||
write_func32(addr + 4, value1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPU.prototype.read16 = function(addr)
|
||||
CPU.prototype.mmap_write128 = function(addr, value0, value1, value2, value3)
|
||||
{
|
||||
this.debug_read(addr, 2);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK;
|
||||
var aligned_addr = addr >>> MMAP_BLOCK_BITS;
|
||||
// This should hold since writes across pages are split up
|
||||
dbg_assert(aligned_addr === (addr + 12) >>> MMAP_BLOCK_BITS);
|
||||
|
||||
if(this.in_mapped_range(addr))
|
||||
{
|
||||
return this.mmap_read16(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.mem8[addr] | this.mem8[addr + 1 | 0] << 8;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPU.prototype.read_aligned16 = function(addr)
|
||||
{
|
||||
dbg_assert(addr >= 0 && addr < 0x80000000);
|
||||
this.debug_read(addr << 1, 2);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK16;
|
||||
|
||||
if(this.in_mapped_range(addr << 1))
|
||||
{
|
||||
return this.mmap_read16(addr << 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.mem16[addr];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPU.prototype.read32s = function(addr)
|
||||
{
|
||||
this.debug_read(addr, 4);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK;
|
||||
|
||||
if(this.in_mapped_range(addr))
|
||||
{
|
||||
return this.mmap_read32(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.mem8[addr] | this.mem8[addr + 1 | 0] << 8 |
|
||||
this.mem8[addr + 2 | 0] << 16 | this.mem8[addr + 3 | 0] << 24;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPU.prototype.read_aligned32 = function(addr)
|
||||
{
|
||||
dbg_assert(addr >= 0 && addr < 0x40000000);
|
||||
this.debug_read(addr << 2, 4);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK32;
|
||||
|
||||
if(this.in_mapped_range(addr << 2))
|
||||
{
|
||||
return this.mmap_read32(addr << 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.mem32s[addr];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
* @param {number} value
|
||||
*/
|
||||
CPU.prototype.write8 = function(addr, value)
|
||||
{
|
||||
this.debug_write(addr, 1, value);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK;
|
||||
|
||||
if(this.in_mapped_range(addr))
|
||||
{
|
||||
this.mmap_write8(addr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mem8[addr] = value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
* @param {number} value
|
||||
*/
|
||||
CPU.prototype.write16 = function(addr, value)
|
||||
{
|
||||
this.debug_write(addr, 2, value);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK;
|
||||
|
||||
if(this.in_mapped_range(addr))
|
||||
{
|
||||
this.mmap_write16(addr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mem8[addr] = value;
|
||||
this.mem8[addr + 1 | 0] = value >> 8;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
* @param {number} value
|
||||
*/
|
||||
CPU.prototype.write_aligned16 = function(addr, value)
|
||||
{
|
||||
dbg_assert(addr >= 0 && addr < 0x80000000);
|
||||
this.debug_write(addr << 1, 2, value);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK16;
|
||||
|
||||
if(this.in_mapped_range(addr << 1))
|
||||
{
|
||||
this.mmap_write16(addr << 1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mem16[addr] = value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} addr
|
||||
* @param {number} value
|
||||
*/
|
||||
CPU.prototype.write32 = function(addr, value)
|
||||
{
|
||||
this.debug_write(addr, 4, value);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK;
|
||||
|
||||
if(this.in_mapped_range(addr))
|
||||
{
|
||||
this.mmap_write32(addr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mem8[addr] = value;
|
||||
this.mem8[addr + 1 | 0] = value >> 8;
|
||||
this.mem8[addr + 2 | 0] = value >> 16;
|
||||
this.mem8[addr + 3 | 0] = value >> 24;
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.write_aligned32 = function(addr, value)
|
||||
{
|
||||
dbg_assert(addr >= 0 && addr < 0x40000000);
|
||||
this.debug_write(addr << 2, 4, value);
|
||||
if(USE_A20 && !this.a20_enabled) addr &= A20_MASK32;
|
||||
|
||||
if(this.in_mapped_range(addr << 2))
|
||||
{
|
||||
this.mmap_write32(addr << 2, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mem32s[addr] = value;
|
||||
}
|
||||
var write_func32 = this.memory_map_write32[aligned_addr];
|
||||
write_func32(addr, value0);
|
||||
write_func32(addr + 4, value1);
|
||||
write_func32(addr + 8, value2);
|
||||
write_func32(addr + 12, value3);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -280,19 +70,24 @@ CPU.prototype.write_aligned32 = function(addr, value)
|
|||
*/
|
||||
CPU.prototype.write_blob = function(blob, offset)
|
||||
{
|
||||
this.debug_write(offset, blob.length, 0)
|
||||
dbg_assert(blob && blob.length >= 0);
|
||||
|
||||
if(blob.length)
|
||||
{
|
||||
dbg_assert(!this.in_mapped_range(offset));
|
||||
dbg_assert(!this.in_mapped_range(offset + blob.length - 1));
|
||||
|
||||
this.jit_dirty_cache(offset, offset + blob.length);
|
||||
this.mem8.set(blob, offset);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Array.<number>|Int32Array} blob
|
||||
* @param {number} offset
|
||||
*/
|
||||
CPU.prototype.write_blob32 = function(blob, offset)
|
||||
CPU.prototype.read_blob = function(offset, length)
|
||||
{
|
||||
dbg_assert(blob && blob.length);
|
||||
this.debug_write(offset, blob.length << 2, 0);
|
||||
this.mem32s.set(blob, offset);
|
||||
if(length)
|
||||
{
|
||||
dbg_assert(!this.in_mapped_range(offset));
|
||||
dbg_assert(!this.in_mapped_range(offset + length - 1));
|
||||
}
|
||||
return this.mem8.subarray(offset, offset + length);
|
||||
};
|
||||
|
|
|
@ -1,558 +0,0 @@
|
|||
/*
|
||||
* Some miscellaneous instructions:
|
||||
*
|
||||
* jmpcc16, jmpcc32, jmp16
|
||||
* loop, loope, loopne, jcxz
|
||||
* test_cc
|
||||
*
|
||||
* mov, push, pop
|
||||
* pusha, popa
|
||||
* xchg, lss
|
||||
* lea
|
||||
* enter
|
||||
* bswap
|
||||
* fxsave, fxrstor
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
CPU.prototype.jmpcc8 = function(condition)
|
||||
{
|
||||
var imm8 = this.read_op8s();
|
||||
if(condition)
|
||||
{
|
||||
this.instruction_pointer = this.instruction_pointer + imm8 | 0;
|
||||
this.branch_taken();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.branch_not_taken();
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.jmp_rel16 = function(rel16)
|
||||
{
|
||||
var current_cs = this.get_seg(reg_cs);
|
||||
|
||||
// limit ip to 16 bit
|
||||
// ugly
|
||||
this.instruction_pointer -= current_cs;
|
||||
this.instruction_pointer = (this.instruction_pointer + rel16) & 0xFFFF;
|
||||
this.instruction_pointer = this.instruction_pointer + current_cs | 0;
|
||||
};
|
||||
|
||||
CPU.prototype.jmpcc16 = function(condition)
|
||||
{
|
||||
var imm16 = this.read_op16();
|
||||
if(condition)
|
||||
{
|
||||
this.jmp_rel16(imm16);
|
||||
this.branch_taken();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.branch_not_taken();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CPU.prototype.jmpcc32 = function(condition)
|
||||
{
|
||||
var imm32s = this.read_op32s();
|
||||
if(condition)
|
||||
{
|
||||
// don't change to `this.instruction_pointer += this.read_op32s()`,
|
||||
// since read_op32s modifies instruction_pointer
|
||||
|
||||
this.instruction_pointer = this.instruction_pointer + imm32s | 0;
|
||||
this.branch_taken();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.branch_not_taken();
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.cmovcc16 = function(condition)
|
||||
{
|
||||
var data = this.read_e16();
|
||||
if(condition)
|
||||
{
|
||||
this.write_g16(data);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.cmovcc32 = function(condition)
|
||||
{
|
||||
var data = this.read_e32s();
|
||||
if(condition)
|
||||
{
|
||||
this.write_g32(data);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.setcc = function(condition)
|
||||
{
|
||||
this.set_e8(condition ? 1 : 0)
|
||||
};
|
||||
|
||||
CPU.prototype.loopne = function(imm8s)
|
||||
{
|
||||
if(this.decr_ecx_asize() && !this.getzf())
|
||||
{
|
||||
this.instruction_pointer = this.instruction_pointer + imm8s | 0;
|
||||
this.branch_taken();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.branch_not_taken();
|
||||
}
|
||||
}
|
||||
|
||||
CPU.prototype.loope = function(imm8s)
|
||||
{
|
||||
if(this.decr_ecx_asize() && this.getzf())
|
||||
{
|
||||
this.instruction_pointer = this.instruction_pointer + imm8s | 0;
|
||||
this.branch_taken();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.branch_not_taken();
|
||||
}
|
||||
}
|
||||
|
||||
CPU.prototype.loop = function(imm8s)
|
||||
{
|
||||
if(this.decr_ecx_asize())
|
||||
{
|
||||
this.instruction_pointer = this.instruction_pointer + imm8s | 0;
|
||||
this.branch_taken();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.branch_not_taken();
|
||||
}
|
||||
}
|
||||
|
||||
CPU.prototype.jcxz = function(imm8s)
|
||||
{
|
||||
if(this.get_reg_asize(reg_ecx) === 0)
|
||||
{
|
||||
this.instruction_pointer = this.instruction_pointer + imm8s | 0;
|
||||
this.branch_taken();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.branch_not_taken();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
* @const
|
||||
*/
|
||||
CPU.prototype.getcf = function()
|
||||
{
|
||||
if(this.flags_changed & 1)
|
||||
{
|
||||
return (this.last_op1 ^ (this.last_op1 ^ this.last_op2) & (this.last_op2 ^ this.last_add_result)) >>> this.last_op_size & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.flags & 1;
|
||||
}
|
||||
};
|
||||
|
||||
/** @return {number} */
|
||||
CPU.prototype.getpf = function()
|
||||
{
|
||||
if(this.flags_changed & flag_parity)
|
||||
{
|
||||
// inverted lookup table
|
||||
return 0x9669 << 2 >> ((this.last_result ^ this.last_result >> 4) & 0xF) & flag_parity;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.flags & flag_parity;
|
||||
}
|
||||
};
|
||||
|
||||
/** @return {number} */
|
||||
CPU.prototype.getaf = function()
|
||||
{
|
||||
if(this.flags_changed & flag_adjust)
|
||||
{
|
||||
return (this.last_op1 ^ this.last_op2 ^ this.last_add_result) & flag_adjust;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.flags & flag_adjust;
|
||||
}
|
||||
};
|
||||
|
||||
/** @return {number} */
|
||||
CPU.prototype.getzf = function()
|
||||
{
|
||||
if(this.flags_changed & flag_zero)
|
||||
{
|
||||
return (~this.last_result & this.last_result - 1) >>> this.last_op_size & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.flags & flag_zero;
|
||||
}
|
||||
};
|
||||
|
||||
/** @return {number} */
|
||||
CPU.prototype.getsf = function()
|
||||
{
|
||||
if(this.flags_changed & flag_sign)
|
||||
{
|
||||
return this.last_result >>> this.last_op_size & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.flags & flag_sign;
|
||||
}
|
||||
};
|
||||
|
||||
/** @return {number} */
|
||||
CPU.prototype.getof = function()
|
||||
{
|
||||
if(this.flags_changed & flag_overflow)
|
||||
{
|
||||
return ((this.last_op1 ^ this.last_add_result) & (this.last_op2 ^ this.last_add_result)) >>> this.last_op_size & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.flags & flag_overflow;
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.test_o = CPU.prototype.getof;
|
||||
CPU.prototype.test_b = CPU.prototype.getcf;
|
||||
CPU.prototype.test_z = CPU.prototype.getzf;
|
||||
CPU.prototype.test_s = CPU.prototype.getsf;
|
||||
CPU.prototype.test_p = CPU.prototype.getpf;
|
||||
|
||||
CPU.prototype.test_be = function()
|
||||
{
|
||||
// Idea:
|
||||
// return this.last_op1 <= this.last_op2;
|
||||
return this.getcf() || this.getzf();
|
||||
}
|
||||
|
||||
CPU.prototype.test_l = function()
|
||||
{
|
||||
// Idea:
|
||||
// return this.last_add_result < this.last_op2;
|
||||
return !this.getsf() !== !this.getof();
|
||||
}
|
||||
|
||||
CPU.prototype.test_le = function()
|
||||
{
|
||||
// Idea:
|
||||
// return this.last_add_result <= this.last_op2;
|
||||
return this.getzf() || !this.getsf() !== !this.getof();
|
||||
}
|
||||
|
||||
|
||||
|
||||
CPU.prototype.push16 = function(imm16)
|
||||
{
|
||||
var sp = this.get_stack_pointer(-2);
|
||||
|
||||
this.safe_write16(sp, imm16);
|
||||
this.adjust_stack_reg(-2);
|
||||
}
|
||||
|
||||
CPU.prototype.push32 = function(imm32)
|
||||
{
|
||||
var sp = this.get_stack_pointer(-4);
|
||||
|
||||
this.safe_write32(sp, imm32);
|
||||
this.adjust_stack_reg(-4);
|
||||
}
|
||||
|
||||
CPU.prototype.pop16 = function()
|
||||
{
|
||||
var sp = this.get_seg(reg_ss) + this.get_stack_reg() | 0,
|
||||
result = this.safe_read16(sp);
|
||||
|
||||
this.adjust_stack_reg(2);
|
||||
return result;
|
||||
}
|
||||
|
||||
CPU.prototype.pop32s = function()
|
||||
{
|
||||
var sp = this.get_seg(reg_ss) + this.get_stack_reg() | 0,
|
||||
result = this.safe_read32s(sp);
|
||||
|
||||
this.adjust_stack_reg(4);
|
||||
return result;
|
||||
}
|
||||
|
||||
CPU.prototype.pusha16 = function()
|
||||
{
|
||||
var temp = this.reg16[reg_sp];
|
||||
|
||||
// make sure we don't get a pagefault after having
|
||||
// pushed several registers already
|
||||
this.writable_or_pagefault(this.get_stack_pointer(-16), 16);
|
||||
|
||||
this.push16(this.reg16[reg_ax]);
|
||||
this.push16(this.reg16[reg_cx]);
|
||||
this.push16(this.reg16[reg_dx]);
|
||||
this.push16(this.reg16[reg_bx]);
|
||||
this.push16(temp);
|
||||
this.push16(this.reg16[reg_bp]);
|
||||
this.push16(this.reg16[reg_si]);
|
||||
this.push16(this.reg16[reg_di]);
|
||||
}
|
||||
|
||||
CPU.prototype.pusha32 = function()
|
||||
{
|
||||
var temp = this.reg32s[reg_esp];
|
||||
|
||||
this.writable_or_pagefault(this.get_stack_pointer(-32), 32);
|
||||
|
||||
this.push32(this.reg32s[reg_eax]);
|
||||
this.push32(this.reg32s[reg_ecx]);
|
||||
this.push32(this.reg32s[reg_edx]);
|
||||
this.push32(this.reg32s[reg_ebx]);
|
||||
this.push32(temp);
|
||||
this.push32(this.reg32s[reg_ebp]);
|
||||
this.push32(this.reg32s[reg_esi]);
|
||||
this.push32(this.reg32s[reg_edi]);
|
||||
}
|
||||
|
||||
CPU.prototype.popa16 = function()
|
||||
{
|
||||
this.translate_address_read(this.get_stack_pointer(0));
|
||||
this.translate_address_read(this.get_stack_pointer(15));
|
||||
|
||||
this.reg16[reg_di] = this.pop16();
|
||||
this.reg16[reg_si] = this.pop16();
|
||||
this.reg16[reg_bp] = this.pop16();
|
||||
this.adjust_stack_reg(2);
|
||||
this.reg16[reg_bx] = this.pop16();
|
||||
this.reg16[reg_dx] = this.pop16();
|
||||
this.reg16[reg_cx] = this.pop16();
|
||||
this.reg16[reg_ax] = this.pop16();
|
||||
}
|
||||
|
||||
CPU.prototype.popa32 = function()
|
||||
{
|
||||
this.translate_address_read(this.get_stack_pointer(0));
|
||||
this.translate_address_read(this.get_stack_pointer(31));
|
||||
|
||||
this.reg32s[reg_edi] = this.pop32s();
|
||||
this.reg32s[reg_esi] = this.pop32s();
|
||||
this.reg32s[reg_ebp] = this.pop32s();
|
||||
this.adjust_stack_reg(4);
|
||||
this.reg32s[reg_ebx] = this.pop32s();
|
||||
this.reg32s[reg_edx] = this.pop32s();
|
||||
this.reg32s[reg_ecx] = this.pop32s();
|
||||
this.reg32s[reg_eax] = this.pop32s();
|
||||
}
|
||||
|
||||
CPU.prototype.xchg8 = function(memory_data, modrm_byte)
|
||||
{
|
||||
var mod = modrm_byte >> 1 & 0xC | modrm_byte >> 5 & 1,
|
||||
tmp = this.reg8[mod];
|
||||
|
||||
this.reg8[mod] = memory_data;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
CPU.prototype.xchg16 = function(memory_data, modrm_byte)
|
||||
{
|
||||
var mod = modrm_byte >> 2 & 14,
|
||||
tmp = this.reg16[mod];
|
||||
|
||||
this.reg16[mod] = memory_data;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
CPU.prototype.xchg16r = function(operand)
|
||||
{
|
||||
var temp = this.reg16[reg_ax];
|
||||
this.reg16[reg_ax] = this.reg16[operand];
|
||||
this.reg16[operand] = temp;
|
||||
}
|
||||
|
||||
CPU.prototype.xchg32 = function(memory_data, modrm_byte)
|
||||
{
|
||||
var mod = modrm_byte >> 3 & 7,
|
||||
tmp = this.reg32s[mod];
|
||||
|
||||
this.reg32s[mod] = memory_data;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
CPU.prototype.xchg32r = function(operand)
|
||||
{
|
||||
var temp = this.reg32s[reg_eax];
|
||||
this.reg32s[reg_eax] = this.reg32s[operand];
|
||||
this.reg32s[operand] = temp;
|
||||
}
|
||||
|
||||
CPU.prototype.lss16 = function(seg)
|
||||
{
|
||||
if(this.modrm_byte >= 0xC0)
|
||||
{
|
||||
// 0xc4c4 #ud (EMULATOR_BOP) is used by reactos and windows to exit vm86 mode
|
||||
this.trigger_ud();
|
||||
}
|
||||
|
||||
var addr = this.modrm_resolve(this.modrm_byte);
|
||||
|
||||
var new_reg = this.safe_read16(addr),
|
||||
new_seg = this.safe_read16(addr + 2 | 0);
|
||||
|
||||
this.switch_seg(seg, new_seg);
|
||||
|
||||
this.reg16[this.modrm_byte >> 2 & 14] = new_reg;
|
||||
}
|
||||
|
||||
CPU.prototype.lss32 = function(seg)
|
||||
{
|
||||
if(this.modrm_byte >= 0xC0)
|
||||
{
|
||||
this.trigger_ud();
|
||||
}
|
||||
|
||||
var addr = this.modrm_resolve(this.modrm_byte);
|
||||
|
||||
var new_reg = this.safe_read32s(addr),
|
||||
new_seg = this.safe_read16(addr + 4 | 0);
|
||||
|
||||
this.switch_seg(seg, new_seg);
|
||||
|
||||
this.reg32s[this.modrm_byte >> 3 & 7] = new_reg;
|
||||
}
|
||||
|
||||
CPU.prototype.enter16 = function(size, nesting_level)
|
||||
{
|
||||
nesting_level &= 31;
|
||||
|
||||
if(nesting_level) dbg_log("enter16 stack=" + (this.stack_size_32 ? 32 : 16) + " size=" + size + " nest=" + nesting_level, LOG_CPU);
|
||||
this.push16(this.reg16[reg_bp]);
|
||||
var frame_temp = this.reg16[reg_sp];
|
||||
|
||||
if(nesting_level > 0)
|
||||
{
|
||||
var tmp_ebp = this.reg16[reg_ebp];
|
||||
for(var i = 1; i < nesting_level; i++)
|
||||
{
|
||||
tmp_ebp -= 2;
|
||||
this.push16(this.safe_read16(this.get_seg(reg_ss) + tmp_ebp | 0));
|
||||
}
|
||||
this.push16(frame_temp);
|
||||
}
|
||||
this.reg16[reg_bp] = frame_temp;
|
||||
this.adjust_stack_reg(-size);
|
||||
};
|
||||
|
||||
CPU.prototype.enter32 = function(size, nesting_level)
|
||||
{
|
||||
nesting_level &= 31;
|
||||
|
||||
if(nesting_level) dbg_log("enter32 stack=" + (this.stack_size_32 ? 32 : 16) + " size=" + size + " nest=" + nesting_level, LOG_CPU);
|
||||
this.push32(this.reg32s[reg_ebp]);
|
||||
var frame_temp = this.reg32s[reg_esp];
|
||||
|
||||
if(nesting_level > 0)
|
||||
{
|
||||
var tmp_ebp = this.reg32s[reg_ebp];
|
||||
for(var i = 1; i < nesting_level; i++)
|
||||
{
|
||||
tmp_ebp -= 4;
|
||||
this.push32(this.safe_read32s(this.get_seg(reg_ss) + tmp_ebp | 0));
|
||||
}
|
||||
this.push32(frame_temp);
|
||||
}
|
||||
this.reg32s[reg_ebp] = frame_temp;
|
||||
this.adjust_stack_reg(-size);
|
||||
};
|
||||
|
||||
CPU.prototype.bswap = function(reg)
|
||||
{
|
||||
var temp = this.reg32s[reg];
|
||||
|
||||
this.reg32s[reg] = temp >>> 24 | temp << 24 | (temp >> 8 & 0xFF00) | (temp << 8 & 0xFF0000);
|
||||
}
|
||||
|
||||
CPU.prototype.fxsave = function(addr)
|
||||
{
|
||||
this.writable_or_pagefault(addr, 512);
|
||||
|
||||
this.safe_write16(addr + 0 | 0, this.fpu.control_word);
|
||||
this.safe_write16(addr + 2 | 0, this.fpu.load_status_word());
|
||||
this.safe_write8( addr + 4 | 0, ~this.fpu.stack_empty & 0xFF);
|
||||
this.safe_write16(addr + 6 | 0, this.fpu.fpu_opcode);
|
||||
this.safe_write32(addr + 8 | 0, this.fpu.fpu_ip);
|
||||
this.safe_write16(addr + 12 | 0, this.fpu.fpu_ip_selector);
|
||||
this.safe_write32(addr + 16 | 0, this.fpu.fpu_dp);
|
||||
this.safe_write16(addr + 20 | 0, this.fpu.fpu_dp_selector);
|
||||
|
||||
this.safe_write32(addr + 24 | 0, this.mxcsr);
|
||||
this.safe_write32(addr + 28 | 0, MXCSR_MASK);
|
||||
|
||||
for(let i = 0; i < 8; i++)
|
||||
{
|
||||
this.fpu.store_m80(addr + 32 + (i << 4) | 0, this.fpu.st[this.fpu.stack_ptr + i & 7]);
|
||||
}
|
||||
|
||||
// If the OSFXSR bit in control register CR4 is not set, the FXSAVE
|
||||
// instruction may not save these registers. This behavior is
|
||||
// implementation dependent.
|
||||
for(let i = 0; i < 8; i++)
|
||||
{
|
||||
this.safe_write32(addr + 160 + (i << 4) + 0 | 0, this.reg_xmm32s[i << 2 | 0]);
|
||||
this.safe_write32(addr + 160 + (i << 4) + 4 | 0, this.reg_xmm32s[i << 2 | 1]);
|
||||
this.safe_write32(addr + 160 + (i << 4) + 8 | 0, this.reg_xmm32s[i << 2 | 2]);
|
||||
this.safe_write32(addr + 160 + (i << 4) + 12 | 0, this.reg_xmm32s[i << 2 | 3]);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.fxrstor = function(addr)
|
||||
{
|
||||
this.translate_address_read(addr | 0);
|
||||
this.translate_address_read(addr + 511 | 0);
|
||||
|
||||
var new_mxcsr = this.safe_read32s(addr + 24 | 0);
|
||||
|
||||
if(new_mxcsr & ~MXCSR_MASK)
|
||||
{
|
||||
dbg_log("Invalid mxcsr bits: " + h((new_mxcsr & ~MXCSR_MASK) >>> 0, 8));
|
||||
this.trigger_gp(0);
|
||||
}
|
||||
|
||||
this.fpu.control_word = this.safe_read16(addr + 0 | 0);
|
||||
this.fpu.set_status_word(this.safe_read16(addr + 2 | 0));
|
||||
this.fpu.stack_empty = ~this.safe_read8(addr + 4 | 0) & 0xFF;
|
||||
this.fpu.fpu_opcode = this.safe_read16(addr + 6 | 0);
|
||||
this.fpu.fpu_ip = this.safe_read32s(addr + 8 | 0);
|
||||
this.fpu.fpu_ip = this.safe_read16(addr + 12 | 0);
|
||||
this.fpu.fpu_dp = this.safe_read32s(addr + 16 | 0);
|
||||
this.fpu.fpu_dp_selector = this.safe_read16(addr + 20 | 0);
|
||||
|
||||
this.mxcsr = new_mxcsr;
|
||||
|
||||
for(let i = 0; i < 8; i++)
|
||||
{
|
||||
this.fpu.st[this.fpu.stack_ptr + i & 7] = this.fpu.load_m80(addr + 32 + (i << 4) | 0);
|
||||
}
|
||||
|
||||
for(let i = 0; i < 8; i++)
|
||||
{
|
||||
this.reg_xmm32s[i << 2 | 0] = this.safe_read32s(addr + 160 + (i << 4) + 0 | 0);
|
||||
this.reg_xmm32s[i << 2 | 1] = this.safe_read32s(addr + 160 + (i << 4) + 4 | 0);
|
||||
this.reg_xmm32s[i << 2 | 2] = this.safe_read32s(addr + 160 + (i << 4) + 8 | 0);
|
||||
this.reg_xmm32s[i << 2 | 3] = this.safe_read32s(addr + 160 + (i << 4) + 12 | 0);
|
||||
}
|
||||
};
|
1253
src/modrm.js
1253
src/modrm.js
File diff suppressed because it is too large
Load diff
484
src/ne2k.js
484
src/ne2k.js
|
@ -1,51 +1,54 @@
|
|||
"use strict";
|
||||
|
||||
// http://www.ethernut.de/pdf/8019asds.pdf
|
||||
|
||||
/** @const */ var E8390_CMD = 0x00 /* The command register (for all pages) */
|
||||
const NE2K_LOG_VERBOSE = false;
|
||||
|
||||
/** @const */ var E8390_CMD = 0x00; /* The command register (for all pages) */
|
||||
|
||||
/* Page 0 register offsets. */
|
||||
/** @const */ var EN0_CLDALO = 0x01 /* Low byte of current local dma addr RD */
|
||||
/** @const */ var EN0_STARTPG = 0x01 /* Starting page of ring bfr WR */
|
||||
/** @const */ var EN0_CLDAHI = 0x02 /* High byte of current local dma addr RD */
|
||||
/** @const */ var EN0_STOPPG = 0x02 /* Ending page +1 of ring bfr WR */
|
||||
/** @const */ var EN0_BOUNDARY = 0x03 /* Boundary page of ring bfr RD WR */
|
||||
/** @const */ var EN0_TSR = 0x04 /* Transmit status reg RD */
|
||||
/** @const */ var EN0_TPSR = 0x04 /* Transmit starting page WR */
|
||||
/** @const */ var EN0_NCR = 0x05 /* Number of collision reg RD */
|
||||
/** @const */ var EN0_TCNTLO = 0x05 /* Low byte of tx byte count WR */
|
||||
/** @const */ var EN0_FIFO = 0x06 /* FIFO RD */
|
||||
/** @const */ var EN0_TCNTHI = 0x06 /* High byte of tx byte count WR */
|
||||
/** @const */ var EN0_ISR = 0x07 /* Interrupt status reg RD WR */
|
||||
/** @const */ var EN0_CRDALO = 0x08 /* low byte of current remote dma address RD */
|
||||
/** @const */ var EN0_RSARLO = 0x08 /* Remote start address reg 0 */
|
||||
/** @const */ var EN0_CRDAHI = 0x09 /* high byte, current remote dma address RD */
|
||||
/** @const */ var EN0_RSARHI = 0x09 /* Remote start address reg 1 */
|
||||
/** @const */ var EN0_RCNTLO = 0x0a /* Remote byte count reg WR */
|
||||
/** @const */ var EN0_RCNTHI = 0x0b /* Remote byte count reg WR */
|
||||
/** @const */ var EN0_RSR = 0x0c /* rx status reg RD */
|
||||
/** @const */ var EN0_RXCR = 0x0c /* RX configuration reg WR */
|
||||
/** @const */ var EN0_TXCR = 0x0d /* TX configuration reg WR */
|
||||
/** @const */ var EN0_COUNTER0 = 0x0d /* Rcv alignment error counter RD */
|
||||
/** @const */ var EN0_DCFG = 0x0e /* Data configuration reg WR */
|
||||
/** @const */ var EN0_COUNTER1 = 0x0e /* Rcv CRC error counter RD */
|
||||
/** @const */ var EN0_IMR = 0x0f /* Interrupt mask reg WR */
|
||||
/** @const */ var EN0_COUNTER2 = 0x0f /* Rcv missed frame error counter RD */
|
||||
/** @const */ var EN0_CLDALO = 0x01; /* Low byte of current local dma addr RD */
|
||||
/** @const */ var EN0_STARTPG = 0x01; /* Starting page of ring bfr WR */
|
||||
/** @const */ var EN0_CLDAHI = 0x02; /* High byte of current local dma addr RD */
|
||||
/** @const */ var EN0_STOPPG = 0x02; /* Ending page +1 of ring bfr WR */
|
||||
/** @const */ var EN0_BOUNDARY = 0x03; /* Boundary page of ring bfr RD WR */
|
||||
/** @const */ var EN0_TSR = 0x04; /* Transmit status reg RD */
|
||||
/** @const */ var EN0_TPSR = 0x04; /* Transmit starting page WR */
|
||||
/** @const */ var EN0_NCR = 0x05; /* Number of collision reg RD */
|
||||
/** @const */ var EN0_TCNTLO = 0x05; /* Low byte of tx byte count WR */
|
||||
/** @const */ var EN0_FIFO = 0x06; /* FIFO RD */
|
||||
/** @const */ var EN0_TCNTHI = 0x06; /* High byte of tx byte count WR */
|
||||
/** @const */ var EN0_ISR = 0x07; /* Interrupt status reg RD WR */
|
||||
/** @const */ var EN0_CRDALO = 0x08; /* low byte of current remote dma address RD */
|
||||
/** @const */ var EN0_RSARLO = 0x08; /* Remote start address reg 0 */
|
||||
/** @const */ var EN0_CRDAHI = 0x09; /* high byte, current remote dma address RD */
|
||||
/** @const */ var EN0_RSARHI = 0x09; /* Remote start address reg 1 */
|
||||
/** @const */ var EN0_RCNTLO = 0x0a; /* Remote byte count reg WR */
|
||||
/** @const */ var EN0_RCNTHI = 0x0b; /* Remote byte count reg WR */
|
||||
/** @const */ var EN0_RSR = 0x0c; /* rx status reg RD */
|
||||
/** @const */ var EN0_RXCR = 0x0c; /* RX configuration reg WR */
|
||||
/** @const */ var EN0_TXCR = 0x0d; /* TX configuration reg WR */
|
||||
/** @const */ var EN0_COUNTER0 = 0x0d; /* Rcv alignment error counter RD */
|
||||
/** @const */ var EN0_DCFG = 0x0e; /* Data configuration reg WR */
|
||||
/** @const */ var EN0_COUNTER1 = 0x0e; /* Rcv CRC error counter RD */
|
||||
/** @const */ var EN0_IMR = 0x0f; /* Interrupt mask reg WR */
|
||||
/** @const */ var EN0_COUNTER2 = 0x0f; /* Rcv missed frame error counter RD */
|
||||
|
||||
/** @const */ var NE_DATAPORT = 0x10 /* NatSemi-defined port window offset. */
|
||||
/** @const */ var NE_RESET = 0x1f /* Issue a read to reset, a write to clear. */
|
||||
/** @const */ var NE_DATAPORT = 0x10; /* NatSemi-defined port window offset. */
|
||||
/** @const */ var NE_RESET = 0x1f; /* Issue a read to reset, a write to clear. */
|
||||
|
||||
/* Bits in EN0_ISR - Interrupt status register */
|
||||
/** @const */ var ENISR_RX = 0x01 /* Receiver, no error */
|
||||
/** @const */ var ENISR_TX = 0x02 /* Transmitter, no error */
|
||||
/** @const */ var ENISR_RX_ERR = 0x04 /* Receiver, with error */
|
||||
/** @const */ var ENISR_TX_ERR = 0x08 /* Transmitter, with error */
|
||||
/** @const */ var ENISR_OVER = 0x10 /* Receiver overwrote the ring */
|
||||
/** @const */ var ENISR_COUNTERS = 0x20 /* Counters need emptying */
|
||||
/** @const */ var ENISR_RDC = 0x40 /* remote dma complete */
|
||||
/** @const */ var ENISR_RESET = 0x80 /* Reset completed */
|
||||
/** @const */ var ENISR_ALL = 0x3f /* Interrupts we will enable */
|
||||
/** @const */ var ENISR_RX = 0x01; /* Receiver, no error */
|
||||
/** @const */ var ENISR_TX = 0x02; /* Transmitter, no error */
|
||||
/** @const */ var ENISR_RX_ERR = 0x04; /* Receiver, with error */
|
||||
/** @const */ var ENISR_TX_ERR = 0x08; /* Transmitter, with error */
|
||||
/** @const */ var ENISR_OVER = 0x10; /* Receiver overwrote the ring */
|
||||
/** @const */ var ENISR_COUNTERS = 0x20; /* Counters need emptying */
|
||||
/** @const */ var ENISR_RDC = 0x40; /* remote dma complete */
|
||||
/** @const */ var ENISR_RESET = 0x80; /* Reset completed */
|
||||
/** @const */ var ENISR_ALL = 0x3f; /* Interrupts we will enable */
|
||||
|
||||
/** @const */ var ENRSR_RXOK = 0x01 /* Received a good packet */
|
||||
/** @const */ var ENRSR_RXOK = 0x01; /* Received a good packet */
|
||||
|
||||
/** @const */ var START_PAGE = 0x40;
|
||||
/** @const */ var START_RX_PAGE = 0x40 + 12;
|
||||
|
@ -56,8 +59,9 @@
|
|||
* @constructor
|
||||
* @param {CPU} cpu
|
||||
* @param {BusConnector} bus
|
||||
* @param {Boolean} preserve_mac_from_state_image
|
||||
*/
|
||||
function Ne2k(cpu, bus)
|
||||
function Ne2k(cpu, bus, preserve_mac_from_state_image)
|
||||
{
|
||||
/** @const @type {CPU} */
|
||||
this.cpu = cpu;
|
||||
|
@ -65,6 +69,8 @@ function Ne2k(cpu, bus)
|
|||
/** @const @type {PCI} */
|
||||
this.pci = cpu.devices.pci;
|
||||
|
||||
this.preserve_mac_from_state_image = preserve_mac_from_state_image;
|
||||
|
||||
/** @const @type {BusConnector} */
|
||||
this.bus = bus;
|
||||
this.bus.register("net0-receive", function(data)
|
||||
|
@ -113,16 +119,16 @@ function Ne2k(cpu, bus)
|
|||
this.tsr = 1;
|
||||
|
||||
// mac address
|
||||
var mac = [
|
||||
this.mac = new Uint8Array([
|
||||
0x00, 0x22, 0x15,
|
||||
Math.random() * 255 | 0,
|
||||
Math.random() * 255 | 0,
|
||||
Math.random() * 255 | 0,
|
||||
];
|
||||
]);
|
||||
|
||||
for(var i = 0; i < 6; i++)
|
||||
{
|
||||
this.memory[i << 1] = this.memory[i << 1 | 1] = mac[i];
|
||||
this.memory[i << 1] = this.memory[i << 1 | 1] = this.mac[i];
|
||||
}
|
||||
|
||||
// the PROM signature of 0x57, 0x57 is also doubled
|
||||
|
@ -130,12 +136,12 @@ function Ne2k(cpu, bus)
|
|||
this.memory[14 << 1] = this.memory[14 << 1 | 1] = 0x57;
|
||||
this.memory[15 << 1] = this.memory[15 << 1 | 1] = 0x57;
|
||||
|
||||
dbg_log("Mac: " + h(mac[0], 2) + ":" +
|
||||
h(mac[1], 2) + ":" +
|
||||
h(mac[2], 2) + ":" +
|
||||
h(mac[3], 2) + ":" +
|
||||
h(mac[4], 2) + ":" +
|
||||
h(mac[5], 2), LOG_NET);
|
||||
dbg_log("Mac: " + h(this.mac[0], 2) + ":" +
|
||||
h(this.mac[1], 2) + ":" +
|
||||
h(this.mac[2], 2) + ":" +
|
||||
h(this.mac[3], 2) + ":" +
|
||||
h(this.mac[4], 2) + ":" +
|
||||
h(this.mac[5], 2), LOG_NET);
|
||||
|
||||
this.rsar = 0;
|
||||
|
||||
|
@ -209,7 +215,8 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg1/1f", LOG_NET);
|
||||
dbg_log("Read pg" + pg + "/1f", LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
@ -224,7 +231,32 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/1f: " + h(data_byte), LOG_NET);
|
||||
dbg_log("Write pg" + pg + "/1f: " + h(data_byte), LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | EN0_STARTPG, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
return this.pstart;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Read pg1/01 (mac[0])", LOG_NET);
|
||||
return this.mac[0];
|
||||
}
|
||||
else if(pg === 2)
|
||||
{
|
||||
return this.pstart;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg" + pg + "/01");
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -236,9 +268,44 @@ function Ne2k(cpu, bus)
|
|||
dbg_log("start page: " + h(data_byte, 2), LOG_NET);
|
||||
this.pstart = data_byte;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("mac[0] = " + h(data_byte), LOG_NET);
|
||||
this.mac[0] = data_byte;
|
||||
}
|
||||
else if(pg === 3)
|
||||
{
|
||||
dbg_log("Unimplemented: Write pg3/01 (9346CR): " + h(data_byte), LOG_NET);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("pg1/1: " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Write pg" + pg + "/01: " + h(data_byte), LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
io.register_read(this.port | EN0_STOPPG, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
return this.pstop;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Read pg1/02 (mac[1])", LOG_NET);
|
||||
return this.mac[1];
|
||||
}
|
||||
else if(pg === 2)
|
||||
{
|
||||
return this.pstop;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg" + pg + "/02", LOG_NET);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -248,11 +315,22 @@ function Ne2k(cpu, bus)
|
|||
if(pg === 0)
|
||||
{
|
||||
dbg_log("stop page: " + h(data_byte, 2), LOG_NET);
|
||||
if(data_byte > (this.memory.length >> 8))
|
||||
{
|
||||
data_byte = this.memory.length >> 8;
|
||||
dbg_log("XXX: Adjusting stop page to " + h(data_byte), LOG_NET);
|
||||
}
|
||||
this.pstop = data_byte;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("mac[1] = " + h(data_byte), LOG_NET);
|
||||
this.mac[1] = data_byte;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("pg1/2: " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Write pg" + pg + "/02: " + h(data_byte), LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -264,11 +342,15 @@ function Ne2k(cpu, bus)
|
|||
dbg_log("Read isr: " + h(this.isr, 2), LOG_NET);
|
||||
return this.isr;
|
||||
}
|
||||
else
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Read curpg: " + h(this.curpg, 2), LOG_NET);
|
||||
return this.curpg;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_write(this.port | EN0_ISR, this, function(data_byte)
|
||||
|
@ -278,13 +360,17 @@ function Ne2k(cpu, bus)
|
|||
{
|
||||
// acknowledge interrupts where bit is set
|
||||
dbg_log("Write isr: " + h(data_byte, 2), LOG_NET);
|
||||
this.isr &= ~data_byte
|
||||
this.isr &= ~data_byte;
|
||||
this.update_irq();
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Write curpg: " + h(data_byte, 2), LOG_NET);
|
||||
this.curpg = data_byte;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write curpg: " + h(data_byte, 2), LOG_NET);
|
||||
this.curpg = data_byte
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -298,7 +384,7 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x0d " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/0d " + h(data_byte, 2), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -312,7 +398,22 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x0e " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/0e " + h(data_byte, 2), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | EN0_RCNTLO, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_log("Read pg0/0a", LOG_NET);
|
||||
return 0x50;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(false, "TODO");
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -326,7 +427,22 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x0a " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/0a " + h(data_byte, 2), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | EN0_RCNTHI, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_log("Read pg0/0b", LOG_NET);
|
||||
return 0x43;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(false, "TODO");
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -340,7 +456,22 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x0b " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/0b " + h(data_byte, 2), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | EN0_RSARLO, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_log("Read remote start address low", LOG_NET);
|
||||
return this.rsar & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unimplemented: Read pg" + pg + "/08", LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -354,7 +485,22 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x08 " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/08 " + h(data_byte, 2), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | EN0_RSARHI, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_log("Read remote start address high", LOG_NET);
|
||||
return this.rsar >> 8 & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unimplemented: Read pg" + pg + "/09", LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -363,12 +509,12 @@ function Ne2k(cpu, bus)
|
|||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_log("Write start address count high: " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Write remote start address low: " + h(data_byte, 2), LOG_NET);
|
||||
this.rsar = this.rsar & 0xFF | data_byte << 8 & 0xFF00;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x09 " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/09 " + h(data_byte, 2), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -383,7 +529,7 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x0f " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/0f " + h(data_byte, 2), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -395,9 +541,20 @@ function Ne2k(cpu, bus)
|
|||
dbg_log("Read boundary: " + h(this.boundary, 2), LOG_NET);
|
||||
return this.boundary;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Read pg1/03 (mac[2])", LOG_NET);
|
||||
return this.mac[2];
|
||||
}
|
||||
else if(pg === 3)
|
||||
{
|
||||
dbg_log("Unimplemented: Read pg3/03 (CONFIG0)", LOG_NET);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg1/0x03", LOG_NET);
|
||||
dbg_log("Read pg" + pg + "/03", LOG_NET);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
@ -410,9 +567,15 @@ function Ne2k(cpu, bus)
|
|||
dbg_log("Write boundary: " + h(data_byte, 2), LOG_NET);
|
||||
this.boundary = data_byte;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("mac[2] = " + h(data_byte), LOG_NET);
|
||||
this.mac[2] = data_byte;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x03 " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Write pg" + pg + "/03: " + h(data_byte), LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -423,9 +586,15 @@ function Ne2k(cpu, bus)
|
|||
{
|
||||
return this.tsr;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Read pg1/04 (mac[3])", LOG_NET);
|
||||
return this.mac[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg1/0x04", LOG_NET);
|
||||
dbg_log("Read pg" + pg + "/04", LOG_NET);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
@ -438,9 +607,41 @@ function Ne2k(cpu, bus)
|
|||
dbg_log("Write tpsr: " + h(data_byte, 2), LOG_NET);
|
||||
this.tpsr = data_byte;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("mac[3] = " + h(data_byte), LOG_NET);
|
||||
this.mac[3] = data_byte;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x04 " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Write pg" + pg + "/04: " + h(data_byte), LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | EN0_TCNTLO, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_log("Unimplemented: Read pg0/05 (NCR: Number of Collisions Register)", LOG_NET);
|
||||
return 0;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Read pg1/05 (mac[4])", LOG_NET);
|
||||
return this.mac[4];
|
||||
}
|
||||
else if(pg === 3)
|
||||
{
|
||||
dbg_log("Unimplemented: Read pg3/05 (CONFIG2)", LOG_NET);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg" + pg + "/05", LOG_NET);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -452,9 +653,45 @@ function Ne2k(cpu, bus)
|
|||
dbg_log("Write tcnt low: " + h(data_byte, 2), LOG_NET);
|
||||
this.tcnt = this.tcnt & ~0xFF | data_byte;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("mac[4] = " + h(data_byte), LOG_NET);
|
||||
this.mac[4] = data_byte;
|
||||
}
|
||||
else if(pg === 3)
|
||||
{
|
||||
dbg_log("Unimplemented: Write pg3/05 (CONFIG2): " + h(data_byte), LOG_NET);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x05 " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Write pg" + pg + "/05: " + h(data_byte), LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | EN0_TCNTHI, this, function()
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_assert(false, "TODO");
|
||||
return 0;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("Read pg1/06 (mac[5])", LOG_NET);
|
||||
return this.mac[5];
|
||||
}
|
||||
else if(pg === 3)
|
||||
{
|
||||
dbg_log("Unimplemented: Read pg3/06 (CONFIG3)", LOG_NET);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg" + pg + "/06", LOG_NET);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -466,9 +703,19 @@ function Ne2k(cpu, bus)
|
|||
dbg_log("Write tcnt high: " + h(data_byte, 2), LOG_NET);
|
||||
this.tcnt = this.tcnt & 0xFF | data_byte << 8;
|
||||
}
|
||||
else if(pg === 1)
|
||||
{
|
||||
dbg_log("mac[5] = " + h(data_byte), LOG_NET);
|
||||
this.mac[5] = data_byte;
|
||||
}
|
||||
else if(pg === 3)
|
||||
{
|
||||
dbg_log("Unimplemented: Write pg3/06 (CONFIG3): " + h(data_byte), LOG_NET);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Write pg1/0x06 " + h(data_byte, 2), LOG_NET);
|
||||
dbg_log("Write pg" + pg + "/06: " + h(data_byte), LOG_NET);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -481,15 +728,24 @@ function Ne2k(cpu, bus)
|
|||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read pg1/0x0c", LOG_NET);
|
||||
dbg_log("Unimplemented: Read pg" + pg + "/0c", LOG_NET);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
io.register_write(this.port | EN0_RXCR, this, function(data_byte)
|
||||
{
|
||||
var pg = this.get_page();
|
||||
if(pg === 0)
|
||||
{
|
||||
dbg_log("RX configuration reg write: " + h(data_byte, 2), LOG_NET);
|
||||
this.rxcr = data_byte;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unimplemented: Write pg" + pg + "/0c: " + h(data_byte), LOG_NET);
|
||||
}
|
||||
});
|
||||
|
||||
io.register_read(this.port | NE_DATAPORT | 0, this,
|
||||
|
@ -522,6 +778,12 @@ Ne2k.prototype.get_state = function()
|
|||
state[8] = this.pstart;
|
||||
state[9] = this.curpg;
|
||||
state[10] = this.boundary;
|
||||
state[11] = this.pstop;
|
||||
state[12] = this.rxcr;
|
||||
state[13] = this.txcr;
|
||||
state[14] = this.tsr;
|
||||
state[15] = this.mac;
|
||||
state[16] = this.memory;
|
||||
|
||||
return state;
|
||||
};
|
||||
|
@ -539,6 +801,16 @@ Ne2k.prototype.set_state = function(state)
|
|||
this.pstart = state[8];
|
||||
this.curpg = state[9];
|
||||
this.boundary = state[10];
|
||||
this.pstop = state[11];
|
||||
this.rxcr = state[12];
|
||||
this.txcr = state[13];
|
||||
this.tsr = state[14];
|
||||
|
||||
if(this.preserve_mac_from_state_image)
|
||||
{
|
||||
this.mac = state[15];
|
||||
this.memory = state[16];
|
||||
}
|
||||
};
|
||||
|
||||
Ne2k.prototype.do_interrupt = function(ir_mask)
|
||||
|
@ -561,19 +833,21 @@ Ne2k.prototype.update_irq = function()
|
|||
};
|
||||
|
||||
Ne2k.prototype.data_port_write = function(data_byte)
|
||||
{
|
||||
if(NE2K_LOG_VERBOSE)
|
||||
{
|
||||
dbg_log("Write data port: data=" + h(data_byte & 0xFF, 2) +
|
||||
" rsar=" + h(this.rsar, 4) +
|
||||
" rcnt=" + h(this.rcnt, 4), LOG_NET);
|
||||
|
||||
if(this.rsar > 0x10 && this.rsar < (START_PAGE << 8))
|
||||
{
|
||||
// unmapped
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.rsar <= 0x10 || this.rsar >= (START_PAGE << 8) && this.rsar < (STOP_PAGE << 8))
|
||||
{
|
||||
this.memory[this.rsar] = data_byte;
|
||||
}
|
||||
|
||||
this.rsar++;
|
||||
this.rcnt--;
|
||||
this.memory[this.rsar++] = data_byte;
|
||||
|
||||
if(this.rsar >= (this.pstop << 8))
|
||||
{
|
||||
|
@ -606,11 +880,21 @@ Ne2k.prototype.data_port_write32 = function(data)
|
|||
|
||||
Ne2k.prototype.data_port_read = function()
|
||||
{
|
||||
var data = this.memory[this.rsar++];
|
||||
let data = 0;
|
||||
|
||||
if(this.rsar < (STOP_PAGE << 8))
|
||||
{
|
||||
data = this.memory[this.rsar];
|
||||
}
|
||||
|
||||
if(NE2K_LOG_VERBOSE)
|
||||
{
|
||||
dbg_log("Read data port: data=" + h(data, 2) +
|
||||
" rsar=" + h(this.rsar - 1, 4) +
|
||||
" rsar=" + h(this.rsar, 4) +
|
||||
" rcnt=" + h(this.rcnt, 4), LOG_NET);
|
||||
}
|
||||
|
||||
this.rsar++;
|
||||
this.rcnt--;
|
||||
|
||||
if(this.rsar >= (this.pstop << 8))
|
||||
|
@ -677,9 +961,9 @@ Ne2k.prototype.receive = function(data)
|
|||
// XXX
|
||||
return;
|
||||
}
|
||||
else if(data[0] === this.memory[0] && data[1] === this.memory[2] &&
|
||||
data[2] === this.memory[4] && data[3] === this.memory[6] &&
|
||||
data[4] === this.memory[8] && data[5] === this.memory[10])
|
||||
else if(data[0] === this.mac[0] && data[1] === this.mac[1] &&
|
||||
data[2] === this.mac[2] && data[3] === this.mac[3] &&
|
||||
data[4] === this.mac[4] && data[5] === this.mac[5])
|
||||
{
|
||||
}
|
||||
else
|
||||
|
@ -696,14 +980,33 @@ Ne2k.prototype.receive = function(data)
|
|||
|
||||
var end = offset + total_length;
|
||||
|
||||
if(end > this.memory.length)
|
||||
const needed = 1 + (total_length >> 8);
|
||||
|
||||
// boundary == curpg interpreted as ringbuffer empty
|
||||
const available = this.boundary > this.curpg ?
|
||||
this.boundary - this.curpg :
|
||||
this.pstop - this.curpg + this.boundary - this.pstart;
|
||||
|
||||
if(available < needed &&
|
||||
this.boundary !== 0 // XXX: ReactOS sets this to 0 initially and never updates it unless it receives a packet
|
||||
)
|
||||
{
|
||||
// shouldn't happen because at this size it can't cross a page
|
||||
dbg_log("Buffer full, dropping packet pstart=" + h(this.pstart) + " pstop=" + h(this.pstop) +
|
||||
" curpg=" + h(this.curpg) + " needed=" + h(needed) + " boundary=" + h(this.boundary) + " available=" + h(available), LOG_NET);
|
||||
return;
|
||||
}
|
||||
|
||||
if(end > (this.pstop << 8))
|
||||
{
|
||||
// Shouldn't happen because at this size it can't cross a page,
|
||||
// so we can skip filling with zeroes
|
||||
dbg_assert(data.length >= 60);
|
||||
|
||||
var cut = this.memory.length - data_start;
|
||||
var cut = (this.pstop << 8) - data_start;
|
||||
dbg_assert(cut >= 0);
|
||||
|
||||
this.memory.set(data.subarray(0, cut), data_start);
|
||||
this.memory.set(data.subarray(cut), START_RX_PAGE);
|
||||
this.memory.set(data.subarray(cut), this.pstart << 8);
|
||||
dbg_log("rcv cut=" + h(cut), LOG_NET);
|
||||
}
|
||||
else
|
||||
|
@ -712,10 +1015,7 @@ Ne2k.prototype.receive = function(data)
|
|||
|
||||
if(data.length < 60)
|
||||
{
|
||||
for(var i = data.length; i < 60; i++)
|
||||
{
|
||||
this.memory[data_start + i] = 0;
|
||||
}
|
||||
this.memory.fill(0, data_start + data.length, data_start + 60);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -739,5 +1039,5 @@ Ne2k.prototype.receive = function(data)
|
|||
|
||||
Ne2k.prototype.get_page = function()
|
||||
{
|
||||
return this.cr & 0xC0;
|
||||
return this.cr >> 6 & 3;
|
||||
};
|
||||
|
|
53
src/pci.js
53
src/pci.js
|
@ -117,6 +117,13 @@ function PCI(cpu)
|
|||
},
|
||||
function(out_byte)
|
||||
{
|
||||
if((this.pci_addr[1] & 0x06) === 0x02 && (out_byte & 0x06) === 0x06)
|
||||
{
|
||||
dbg_log("CPU reboot via PCI");
|
||||
cpu.reboot_internal();
|
||||
return;
|
||||
}
|
||||
|
||||
this.pci_addr[1] = out_byte;
|
||||
},
|
||||
function(out_byte)
|
||||
|
@ -145,6 +152,12 @@ function PCI(cpu)
|
|||
// pci_bars: [],
|
||||
//};
|
||||
|
||||
// This needs to be set in order for seabios to not execute code outside of
|
||||
// mapped memory. While we map the BIOS into high memory, we don't allow
|
||||
// executing code there, which enables optimisations in read_imm8.
|
||||
// See [make_bios_writable_intel] in src/fw/shadow.c in seabios for details
|
||||
const PAM0 = 0x10;
|
||||
|
||||
var host_bridge = {
|
||||
pci_id: 0,
|
||||
pci_space: [
|
||||
|
@ -153,6 +166,8 @@ function PCI(cpu)
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PAM0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
],
|
||||
pci_bars: [],
|
||||
name: "82441FX PMC",
|
||||
|
@ -316,7 +331,7 @@ PCI.prototype.pci_write8 = function(address, written)
|
|||
}
|
||||
|
||||
dbg_assert(!(addr >= 0x10 && addr < 0x2C || addr >= 0x30 && addr < 0x34),
|
||||
"PCI: Expected 32-bit write");
|
||||
"PCI: Expected 32-bit write, got 8-bit (addr: " + h(addr) + ")");
|
||||
|
||||
dbg_log("PCI write8 dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
|
||||
" value=" + h(written, 2), LOG_PCI);
|
||||
|
@ -339,8 +354,15 @@ PCI.prototype.pci_write16 = function(address, written)
|
|||
return;
|
||||
}
|
||||
|
||||
dbg_assert(!(addr >= 0x10 && addr < 0x2C || addr >= 0x30 && addr < 0x34),
|
||||
"PCI: Expected 32-bit write");
|
||||
if(addr >= 0x10 && addr < 0x2C)
|
||||
{
|
||||
// Bochs bios
|
||||
dbg_log("Warning: PCI: Expected 32-bit write, got 16-bit (addr: " + h(addr) + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
dbg_assert(!(addr >= 0x30 && addr < 0x34),
|
||||
"PCI: Expected 32-bit write, got 16-bit (addr: " + h(addr) + ")");
|
||||
|
||||
dbg_log("PCI writ16 dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
|
||||
" value=" + h(written, 4), LOG_PCI);
|
||||
|
@ -446,6 +468,11 @@ PCI.prototype.pci_write32 = function(address, written)
|
|||
space[addr >> 2] = 0;
|
||||
}
|
||||
}
|
||||
else if(addr === 0x04)
|
||||
{
|
||||
dbg_log("PCI write dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
|
||||
" value=" + h(written >>> 0, 8), LOG_PCI);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("PCI write dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
|
||||
|
@ -529,7 +556,7 @@ PCI.prototype.set_io_bars = function(bar, from, to)
|
|||
old_entry.write16 === this.io.empty_port_write &&
|
||||
old_entry.write32 === this.io.empty_port_write)
|
||||
{
|
||||
dbg_log("Move IO bar: Source not mapped, port=" + h(from + i, 4), LOG_PCI);
|
||||
dbg_log("Warning: Bad IO bar: Source not mapped, port=" + h(from + i, 4), LOG_PCI);
|
||||
}
|
||||
|
||||
var entry = bar.entries[i];
|
||||
|
@ -538,13 +565,17 @@ PCI.prototype.set_io_bars = function(bar, from, to)
|
|||
|
||||
ports[to + i] = entry;
|
||||
|
||||
// these can fail if the os maps an io port in multiple bars (indicating a bug)
|
||||
dbg_assert(empty_entry.read8 === this.io.empty_port_read8, "Bad IO bar: Target already mapped");
|
||||
dbg_assert(empty_entry.read16 === this.io.empty_port_read16, "Bad IO bar: Target already mapped");
|
||||
dbg_assert(empty_entry.read32 === this.io.empty_port_read32, "Bad IO bar: Target already mapped");
|
||||
dbg_assert(empty_entry.write8 === this.io.empty_port_write, "Bad IO bar: Target already mapped");
|
||||
dbg_assert(empty_entry.write16 === this.io.empty_port_write, "Bad IO bar: Target already mapped");
|
||||
dbg_assert(empty_entry.write32 === this.io.empty_port_write, "Bad IO bar: Target already mapped");
|
||||
if(empty_entry.read8 === this.io.empty_port_read8 ||
|
||||
empty_entry.read16 === this.io.empty_port_read16 ||
|
||||
empty_entry.read32 === this.io.empty_port_read32 ||
|
||||
empty_entry.write8 === this.io.empty_port_write ||
|
||||
empty_entry.write16 === this.io.empty_port_write ||
|
||||
empty_entry.write32 === this.io.empty_port_write)
|
||||
{
|
||||
// These can fail if the os maps an io port in multiple bars (indicating a bug)
|
||||
// XXX: Fails during restore_state
|
||||
dbg_log("Warning: Bad IO bar: Target already mapped, port=" + h(to + i, 4), LOG_PCI);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
31
src/pic.js
31
src/pic.js
|
@ -129,7 +129,7 @@ function PIC(cpu, master)
|
|||
{
|
||||
PIC_LOG_VERBOSE && dbg_log("master> spurious requested=" + this.requested_irq, LOG_PIC);
|
||||
this.requested_irq = -1;
|
||||
this.cpu.pic_call_irq(this.irq_map | 7);
|
||||
//this.cpu.pic_call_irq(this.irq_map | 7);
|
||||
return;
|
||||
}
|
||||
dbg_assert(this.irr); // spurious
|
||||
|
@ -292,18 +292,25 @@ function PIC(cpu, master)
|
|||
return;
|
||||
}
|
||||
|
||||
var irq_mask = 1 << irq_number;
|
||||
if((this.irq_value & irq_mask) === 0)
|
||||
{
|
||||
if(PIC_LOG_VERBOSE)
|
||||
{
|
||||
dbg_log("master> set irq " + irq_number, LOG_PIC);
|
||||
}
|
||||
|
||||
var irq_mask = 1 << irq_number;
|
||||
if((this.irq_value & irq_mask) === 0)
|
||||
{
|
||||
this.irr |= irq_mask;
|
||||
this.irq_value |= irq_mask;
|
||||
this.check_irqs();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(PIC_LOG_VERBOSE)
|
||||
{
|
||||
dbg_log("master> set irq " + irq_number + ": already set!", LOG_PIC);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.clear_irq = function(irq_number)
|
||||
|
@ -334,18 +341,26 @@ function PIC(cpu, master)
|
|||
this.set_irq = function(irq_number)
|
||||
{
|
||||
dbg_assert(irq_number >= 0 && irq_number < 8);
|
||||
|
||||
var irq_mask = 1 << irq_number;
|
||||
if((this.irq_value & irq_mask) === 0)
|
||||
{
|
||||
if(PIC_LOG_VERBOSE)
|
||||
{
|
||||
dbg_log("slave > set irq " + irq_number, LOG_PIC);
|
||||
}
|
||||
|
||||
var irq_mask = 1 << irq_number;
|
||||
if((this.irq_value & irq_mask) === 0)
|
||||
{
|
||||
this.irr |= irq_mask;
|
||||
this.irq_value |= irq_mask;
|
||||
this.check_irqs();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(PIC_LOG_VERBOSE)
|
||||
{
|
||||
dbg_log("slave > set irq " + irq_number + ": already set!", LOG_PIC);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.clear_irq = function(irq_number)
|
||||
|
@ -398,7 +413,7 @@ PIC.prototype.set_state = function(state)
|
|||
this.isr = state[2];
|
||||
this.irr = state[3];
|
||||
this.is_master = state[4];
|
||||
this.slave = state[5];
|
||||
this.slave && this.slave.set_state(state[5]);
|
||||
this.expect_icw4 = state[6];
|
||||
this.state = state[7];
|
||||
this.read_isr = state[8];
|
||||
|
|
14
src/pit.js
14
src/pit.js
|
@ -113,6 +113,11 @@ PIT.prototype.timer = function(now, no_irq)
|
|||
|
||||
dbg_log("pit interrupt. new value: " + this.counter_start_value[0], LOG_PIT);
|
||||
|
||||
// This isn't strictly correct, but it's necessary since browsers
|
||||
// may sleep longer than necessary to trigger the else branch below
|
||||
// and clear the irq
|
||||
this.cpu.device_lower_irq(0);
|
||||
|
||||
this.cpu.device_raise_irq(0);
|
||||
var mode = this.counter_mode[0];
|
||||
|
||||
|
@ -277,7 +282,7 @@ PIT.prototype.port43_write = function(reg_byte)
|
|||
this.counter_latch[i] = 2;
|
||||
var value = this.get_counter_value(i, v86.microtick());
|
||||
dbg_log("latch: " + value, LOG_PIT);
|
||||
this.counter_latch_value[i] = value ? value - 1 : 0
|
||||
this.counter_latch_value[i] = value ? value - 1 : 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -329,3 +334,10 @@ PIT.prototype.port43_write = function(reg_byte)
|
|||
|
||||
this.bus.send("pcspeaker-update", [this.counter_mode[2], this.counter_reload[2]]);
|
||||
};
|
||||
|
||||
PIT.prototype.dump = function()
|
||||
{
|
||||
const reload = this.counter_reload[0];
|
||||
const time = (reload || 0x10000) / OSCILLATOR_FREQ;
|
||||
dbg_log("counter0 ticks every " + time + "ms (reload=" + reload + ")");
|
||||
};
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
/** @const */
|
||||
let PS2_LOG_VERBOSE = false;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {CPU} cpu
|
||||
|
@ -322,7 +325,10 @@ PS2.prototype.send_mouse_packet = function(dx, dy)
|
|||
this.mouse_buffer.push(delta_x);
|
||||
this.mouse_buffer.push(delta_y);
|
||||
|
||||
if(PS2_LOG_VERBOSE)
|
||||
{
|
||||
dbg_log("adding mouse packets: " + [info_byte, dx, dy], LOG_PS2);
|
||||
}
|
||||
|
||||
this.raise_irq();
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue