寫程式時的幾個重點:
<型別>.load(8|16|32)_(u|s) index
指令可以從index指定的位置把資料從Memory載入<型別>.store(8|16|32) index
指令可以把資料寫回Memory中index指定的位置Memory可以直接在WebAssembly程式中設定後輸出:
(module
(memory (export "out") 1)
)
這樣透過Javascript API裡面Instance的exports.out,就可以取得他的ArrayBuffer:
...
.then(instance => {
let view = new Uint8Array(instance.exports.out.buffer);
...
})
或是在Javascript端先定義好:
let out = new WebAssembly.Memory({initial: 1});
let exportObjects = {
js: {
out: out
}
};
let view = new Uint8Array(out.buffer);
view[0] = 0x61;
然後在WebAssembly程式端輸入:
(module
(memory (import "js" "out") 1)
)
WebAssembly程式:
(module
(memory (export "out") 1)
(func $space (result i32) i32.const 32)
(func $comma (result i32) i32.const 44)
(func $period (result i32) i32.const 46)
(func $H (result i32) i32.const 72)
(func $W (result i32) i32.const 87)
(func $d (result i32) i32.const 100)
(func $e (result i32) i32.const 101)
(func $l (result i32) i32.const 108)
(func $o (result i32) i32.const 111)
(func $r (result i32) i32.const 114)
(func (export "hello")
i32.const 0
call $H
i32.store8
i32.const 1
call $e
i32.store8
i32.const 2
call $l
i32.store8
i32.const 3
call $l
i32.store8
i32.const 4
call $o
i32.store8
i32.const 5
call $comma
i32.store8
i32.const 6
call $space
i32.store8
i32.const 7
call $W
i32.store8
i32.const 8
call $o
i32.store8
i32.const 9
call $r
i32.store8
i32.const 10
call $l
i32.store8
i32.const 11
call $d
i32.store8
i32.const 12
call $period
i32.store8
)
)
簡單說,就是依序把Hello, World.
的ASCII Code(其實應該是UTF-8,但是在英文這段,資料是一樣的)填入Memory。
然後在Javascript端透過ArrayBuffer取出,透過View來轉成字串:
<html>
<body>
<div id="panel"></div>
<script src="../wasm_util.js"></script>
<script>
let m = new Wasm('test003.wasm');
m.getInstance()
.then(instance => {
let buffer = instance.exports.out.buffer;
instance.exports.hello();
document.getElementById('panel').innerHTML = String.fromCharCode.apply(null, new Uint8Array(buffer));
})
</script>
</body>
</html>
這樣就會在網頁輸出Hello, World.
:
上面這樣的作法,的確可以輸出Hello, World.
,但是一個一個輸出ASCII Code實在很費事。實際上並不需要這麼麻煩,透過Data區段,就可以把資料字串預先填入到Memory來使用。例如:
(module
(memory (export "out") 1)
(data (i32.const 1024) "Hello, World.")
)
這樣就會從Memory的1024這個索引位置開始,填入Hello, World.
字串。
用這個方式改寫一下之前的例子,同時嘗試由Javascript端設定Memory:
(module
(func $log (import "js" "log") (param i32) (param i32))
(memory (import "js" "out") 1)
(data (i32.const 1024) "Hello, World.")
(func (export "hello")
i32.const 1024
i32.const 13
call $log
)
)
程式的作用就是輸出一個hello
函數,當他被呼叫時,就呼叫傳入的log
函數,把資料的起點及長度傳給他,這樣他才知道從哪裡取出結果字串:
<html>
<body>
<script src="../wasm_util.js"></script>
<script>
let out = new WebAssembly.Memory({initial: 1});
let importObjects = {
js: {
out: out,
log: log
}
}
let m = new Wasm('test004.wasm');
m.getModule()
.then(module => {
console.log('exports', WebAssembly.Module.exports(module));
console.log('imports', WebAssembly.Module.imports(module));
return m.getInstance(importObjects)
})
.then(instance => {
instance.exports.hello();
});
function log(offset, length) {
let view = new Uint8Array(out.buffer, offset, length);
console.log(view);
console.log(String.fromCharCode.apply(null, view));
}
</script>
</body>
</html>
程式裡面順便觀察了一下,怎麼使用Module.exports()
以及Module.imports()
來取得輸出入的資訊。頁面同樣會看到Hello, World.
:
下一次的主題還是Memory,只是會同時從Javascript端以及WebAssembly程式端寫入資料,然後取得結果。
真的假的
看到我都想寫
INT 21H
了
一個小小 typo 更正建議
Memroy 可能是「Memory」之誤
...改了XD