# wasm 如何互動
https://github.com/x213212/wasm_riscv_emu
五年前wasm files系統很不友善只有編譯過,硬改後也沒什結果,這次再結合online ide一下打算做一個類似debug的工具,今天就先把檔案系統給搞定.
https://openhome.cc/Gossip/WebAssembly/C2Wasm.html
https://www.itread01.com/hkcxxxe.html
```c=
#include<emscripten/emscripten.h>
int EMSCRIPTEN_KEEPALIVE sum(){
printf("sum = %i\n", 100);
return 1;
}
```
![](https://i.imgur.com/zIn44eI.png)
編譯時
```
emcc main.c ./src/bus.c ./src/cpu.c ./src/csr.c ./src/dram.c -o main -I ./include -s WASM=1 -o hello2.html --preload-file ./addi.bin -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']"
```
-s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" 應該也是不用下,除非要對到其他fucntion
```javascript=
var Module = typeof Module != 'undefined' ? Module : {};
var back;
```
```javascript=
back.then(prog => {
console.log(prog.instance.exports.sum());
});
back.then(prog => {
console.log(prog.instance.exports.sum());
});
```
這樣就可以進行呼叫c 語言
![](https://i.imgur.com/zWY6NOi.png)
因為模組導出也是module
![](https://i.imgur.com/Y3lp38w.png)
```htmlmixed=
function sampleClick() {
back.then(prog => {
console.log(prog.instance.exports.sum());
});
back.then(prog => {
console.log(prog.instance.exports.sum());
});
back.then(prog => {
console.log(prog.instance.exports.sum2(1,5));
});
}
</script>
<script async type="text/javascript" src="hello2.js"></script>
<button type="button" id="btn" onclick="sampleClick()">Click Here!</button>
```
![](https://i.imgur.com/oVzdIFv.png)
在js裡面找到一樣把back
```javascript=
var result = WebAssembly.instantiateStreaming(response, info);
return result.then(
receiveInstantiationResult,
```
改為
```javascript=
back = WebAssembly.instantiateStreaming(response, info);
return back.then(
receiveInstantiationResult,
```
並在最上層加入
```
var back;
```
讓上層fucntion可以呼叫
https://www.cnblogs.com/xuanhun/p/9963082.html
https://medium.com/@scalevectors/webassembly-c-pointers-strings-part-2-8e173e50912b
從c語言讀值,這部分是ok但我們更多想知道的是把js的value 傳至 c code
找到一個範例
https://github.com/dudeofx/webassembly-IDBFS-barebones/blob/main/index.html
不過這wasm 版本基本上經過大改
這樣傳值可能會出錯(或者我沒找到更好方法
基本上idbfs已經廢除
```bash=
emcc main.c ./src/bus.c ./src/cpu.c ./src/csr.c ./src/dram.c -o main -I ./include -s WASM=1 -o hello2.html --preload-file ./addi.bin -lidbfs.js -s "EXTRA_EXPORTED_RUNTIME_METHODS=['UTF8ToString', 'getValue','stringToUTF8','lengthBytesUTF8']"
```
後面的是我修改過後的code
```
-lidbfs.js -s "EXTRA_EXPORTED_RUNTIME_METHODS=['UTF8ToString', 'getValue','stringToUTF8','lengthBytesUTF8']"
```
那麼這個example
```c=
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <emscripten.h>
//-----------------------------------------------------------------------
EMSCRIPTEN_KEEPALIVE
char *LoadData() {
int fd;
int size;
char *buff;
fd = open("/data/textfile.txt", O_RDONLY);
if (fd == -1) return strerror(errno);
size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
buff = (char *) malloc(size+1);
read(fd, buff, size);
buff[size] = '\0';
close(fd);
return buff;
}
//-----------------------------------------------------------------------
EMSCRIPTEN_KEEPALIVE
void SaveData(char *data) {
int fd;
int size;
if (data == NULL) return;
fd = open("/data/textfile.txt", O_CREAT | O_WRONLY, 0666);
if (fd == -1) {
printf("ERROR: could not open the file for writing!\n, %s\n", strerror(errno));
return;
}
size = strlen(data);
printf("saving %i bytes... %s\n", size, data);
write(fd, data, size);
ftruncate(fd, size);
close(fd);
EM_ASM ( FS.syncfs(false, function (err) {} ); );
}
//-----------------------------------------------------------------------
int main() {
EM_ASM(
FS.mkdir('/data');
FS.mount(IDBFS, {}, '/data');
FS.syncfs(true, function (err) {
// provide the DOM side a way to execute code after directory is mounted
if (typeof Module.OnDataMounted !== 'undefined') {
Module.OnDataMounted();
}
} );
);
emscripten_exit_with_live_runtime();
}
//-----------------------------------------------------------------------
```
沒意外可以編譯成功,接下來我們假設怎麼從
js的輸入框讀value
和讀出buffer的值呢
```js
<script type='text/javascript'>
function InboxHandler() {
var str= document.getElementById('inbox').value;
var strLen = Module.lengthBytesUTF8(str);
var strPtr = Module._malloc(strLen + 1);
Module.stringToUTF8(str, strPtr, strLen + 1);
console.log(strLen);
back.then(prog => {
// prog.instance.exports.queryString(ppcStr);
var pcStr = Module.getValue(strPtr, "i32");
var jsStr = Module.UTF8ToString(pcStr);
console.log("_queryString: " + jsStr);
// var jsStr = Module.UTF8ToString(pcStr);
// console.log("_queryString: " + ppcStr);
prog.instance.exports.SaveData(strPtr);
});
// /
}
function OutboxHandler() {
back.then(prog => {
var jsStr = Module.UTF8ToString( document.getElementById('outbox').value =prog.instance.exports.LoadData());
console.log("_queryString2: " + jsStr);
document.getElementById('outbox').value =jsStr;
// console.log(prog.instance.exports.LoadData());
});
}
// Module.OnDataMounted = OutboxHandler;
</script>
<input id="inbox" type="text" /><br>
<input id="outbox" type="text" readonly /><br>
```
![](https://i.imgur.com/tVVnTS0.png)
這樣就可以完成非同步的讀入/讀出file的資訊
在更複雜一點前端是binary輸入我們要轉成Uint8Array怎辦
```javascript=
<input id="file-input" type="file" />
<script>
// var worker = new Worker('worker.js');
var inputElement = document.getElementById("file-input");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
var fileList = this.files[0];
console.log(fileList)
let buffer = fileList;
let u = new Uint8Array(buffer);
// console.log(this.files[0].size);
// console.log(this.files[0].arrayBuffer);
const reader = new FileReader();
const fileByteArray = [];
reader.readAsArrayBuffer(this.files[0]);
reader.onloadend = (evt) => {
if (evt.target.readyState === FileReader.DONE) {
const arrayBuffer = evt.target.result,
array = new Uint8Array(arrayBuffer);
for (const a of array) {
fileByteArray.push(a);
}
console.log(fileByteArray)
}
}
}
</script>
```
![](https://i.imgur.com/YlwHRc0.png)
實測後轉成blob比較方便
https://yahone-chow.medium.com/file-blob-arraybuffer-576a8e99de0d
再從blob 轉成hex
```javascript=
function buf2hex(buffer) { // buffer is an ArrayBuffer
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
function handleFiles() {
var fileList = this.files[0];
console.log(fileList)
let buffer = fileList;
let u = new Uint8Array(buffer);
// console.log(this.files[0].size);
// console.log(this.files[0].arrayBuffer);
const reader = new FileReader();
const fileByteArray = [];
var reader2 = new FileReader();
reader2.onload = function (event) {
var content = reader2.result;//内容就在这里
alert(content);
console.log(content);
var blob = new Blob([content], {
type: ''
});
// console.info(blob);
// console.info(blob.slice(1, 3, 'text/plain'));
};
reader.readAsArrayBuffer(this.files[0]);
reader.onloadend = (evt) => {
if (evt.target.readyState === FileReader.DONE) {
const arrayBuffer = evt.target.result,
array = new Uint8Array(arrayBuffer);
for (const a of array) {
fileByteArray.push(a);
}
console.log(fileByteArray)
console.log(array)
var after = Uint8ArrayToString(array)
var blob = new Blob([array])
console.info(blob);
var href = URL.createObjectURL(blob);
// 從 Blob 取出資料
// var link = document.createElement("a");
// document.body.appendChild(link);
// link.href = href;
// link.download = "fileName";
// link.click();
// reader2.readAsText(blob);
console.log(fileByteArray.length)
console.log(sumUp(fileByteArray));
console.log(buf2hex(fileByteArray))
document.getElementById('inbox').value=buf2hex(fileByteArray)
}
}
}
```
![](https://i.imgur.com/9NZ2NOp.png)
我們hex在to string
到這邊可以轉成string後,我們傳達字元陣列給c
這邊本來要用uint8array進行傳值
不過試到後面還是轉hex再轉string 進行存取比較方便
```c=
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include<emscripten/emscripten.h>
#include <emscripten.h>
#include "includes/cpu.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>
#include "test.h"
EMSCRIPTEN_KEEPALIVE
void tesqwe(char *input2);
// ANSI colors
#define ANSI_RED "\x1b[31m"
#define ANSI_GREEN "\x1b[32m"
#define ANSI_YELLOW "\x1b[33m"
#define ANSI_BLUE "\x1b[34m"
#define ANSI_MAGENTA "\x1b[35m"
#define ANSI_CYAN "\x1b[36m"
#define ANSI_RESET "\x1b[0m"
void read_file(CPU* cpu, char *filename)
{
FILE *file;
uint8_t *buffer;
unsigned long fileLen;
//Open file
file = fopen(filename, "rb");
if (!file)
{
fprintf(stderr, "Unable to open file %s", filename);
}
//Get file length
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);
//Allocate memory
buffer=(uint8_t *)malloc(fileLen+1);
if (!buffer)
{
fprintf(stderr, "Memory error!");
fclose(file);
}
//Read file contents into buffer
fread(buffer, fileLen, 1, file);
fclose(file);
// Print file contents in hex
for (int i=0; i<fileLen; i+=2) {
if (i%16==0) printf("\n%.8x: ", i);
printf("%02x%02x ", *(buffer+i), *(buffer+i+1));
}
/*printf("\n");*/
// copy the bin executable to dram
memcpy(cpu->bus.dram.mem, buffer, fileLen*sizeof(uint8_t));
free(buffer);
}
int EMSCRIPTEN_KEEPALIVE sum(){
printf("sum = %i\n", 100);
return 1;
}
int EMSCRIPTEN_KEEPALIVE sum2(int a,int b){
printf("sum2 = %i\n",a+b);
return 0;
}
//-----------------------------------------------------------------------
EMSCRIPTEN_KEEPALIVE
char *LoadData() {
int fd;
int size;
char *buff;
fd = open("/data/textfile.txt", O_RDONLY);
if (fd == -1) return strerror(errno);
size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
buff = (uint8_t *) malloc(size+1);
read(fd, buff, size);
buff[size] = '\0';
for (int i=0; i<size; i+=2) {
if (i%16==0) printf("\n%.8x: ", i);
printf("%02x%02x ", *(buff+i), *(buff+i+1));
}
close(fd);
printf("read %s bytes...\n",buff);
struct CPU cpu;
cpu_init(&cpu);
// Read input file
read_file(&cpu,"/data/textfile2.txt");
while (1) {
// fetch
uint32_t inst = cpu_fetch(&cpu);
// Increment the program counter
cpu.pc += 4;
// execute
if (!cpu_execute(&cpu, inst))
break;
// dump_registers(&cpu);
if(cpu.pc==0)
break;
}
return buff;
}
//-----------------------------------------------------------------------
EMSCRIPTEN_KEEPALIVE
void SaveData(char *data) {
int fd;
int size;
if (data == NULL) return;
fd = open("/data/textfile.txt", O_CREAT | O_WRONLY, 0666);
if (fd == -1) {
printf("ERROR: could not open the file for writing!\n, %s\n", strerror(errno));
return;
}
size = strlen(data);
printf("saving %i bytes... %s\n", size, data);
write(fd, data, size);
ftruncate(fd, size);
tesqwe(data);
close(fd);
EM_ASM ( FS.syncfs(false, function (err) {} ); );
}
static char g_str[256] = "Hello, world!";
void EMSCRIPTEN_KEEPALIVE queryString(char** ppStr) {
*ppStr = g_str;
}
void printArry(unsigned char arr[], int n)
{
int i;
for (i = 0; i < n; i++)
{
printf("%x ", arr[i]);
}
printf("\n");
}
void float2u8Arry(uint8_t *u8Arry, float *floatdata)
{
uint8_t farray[4];
*(float *)farray = *floatdata;
u8Arry[3] = farray[0];
u8Arry[2] = farray[1];
u8Arry[1] = farray[2];
u8Arry[0] = farray[3];
}
float EMSCRIPTEN_KEEPALIVE sum_up(float vals[], int size) {
float res = 0;
for(int i=0; i<size; i++){
res += vals[i];
printf("%f \n",vals[i]);
uint8_t u8data[4];
float2u8Arry(u8data, &vals[i]);
printArry(u8data, 4);
// printf("saving %d bytes... %s\n", vals[i]);
}
// write_to_file()
return res;
}
//-----------------------------------------------------------------------
int main(int argc, char* argv[]) {
// if (argc != 2) {
// printf("Usage: rvemu <filename>\n");
// exit(1);
// }
EM_ASM(
FS.mkdir('/data');
FS.mount(IDBFS, {}, '/data');
FS.syncfs(true, function (err) {
// provide the DOM side a way to execute code after directory is mounted
if (typeof Module.OnDataMounted !== 'undefined') {
Module.OnDataMounted();
}
} );
);
// Initialize cpu, registers and program counter
struct CPU cpu;
cpu_init(&cpu);
// Read input file
read_file(&cpu,"addi.bin");
while (1) {
// fetch
uint32_t inst = cpu_fetch(&cpu);
// Increment the program counter
cpu.pc += 4;
// execute
if (!cpu_execute(&cpu, inst))
break;
// dump_registers(&cpu);
if(cpu.pc==0)
break;
}
emscripten_exit_with_live_runtime();
return 0;
}
```
主要異動SaveData和LoadData各自存去不同的檔案
/data/textfile.txt
/data/textfile2.txt
SaveData 的部分還有去呼叫tesqwe(data);
這邊順便體驗一下link,c++ hex 轉 bin 檔案很簡單
```bash
emcc -c test.cpp -o test
```
link ,多了./test
```bash
emcc main.c ./test ./src/bus.c ./src/cpu.c ./src/csr.c ./src/dram.c -o main -I ./include -s WASM=1 -o hello2.html --preload-file ./addi.bin -lidbfs.js -s "EXTRA_EXPORTED_RUNTIME_METHODS=['UTF8ToString', 'getValue','stringToUTF8','lengthBytesUTF8']"
```
到這邊c就可以去呼叫c++的fucntion了!
```cpp=
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <ios>
#include "test.h"
std::string wut = "6f008004732f2034930f80006308ff03930f90006304ff03930fb0006300ff03130f000063040f0067000f00732f203463540f006f00400093e19153171f000023223ffc6ff09fff93000000130100009301000013020000930200001303000093030000130400009304000013050000930500001306000093060000130700009307000013080000930800001309000093090000130a0000930a0000130b0000930b0000130c0000930c0000130d0000930d0000130e0000930e0000130f0000930f0000732540f16310050097020000938202017390523073500018970200009382020273905230b70200809382f2ff7390023b9302f0017390023a73504030970200009382420173905230735020307350303093010000970200009382c2ee73905230130510001315f501634c05000f00f00f930110009308d005130500007300000093020000638a020073905210b7b20000938292107390223073500030970200009382420173901234732540f17300203093000000138700009303000093012000631c7726930010001387100093032000930130006312772693003000138770009303a000930140006318772493000000138700809303008093015000631e7722b700008013870000b70300809301600063147722b700008013870080b7030080938303809301700063187720930000001387f07f9303f07f93018000631e771eb70000809380f0ff13870000b70300809383f3ff930190006310771eb70000809380f0ff1387f07fb70300809383e37f9301a0006312771cb70000801387f07fb70300809383f37f9301b0006316771ab70000809380f0ff13870080b7f3ff7f9383f37f9301c00063187718930000001387f0ff9303f0ff9301d000631e77169300f0ff13871000930300009301e000631477169300f0ff1387f0ff9303e0ff9301f000631a7714b70000809380f0ff13871000b703008093010001631e77129300d0009380b000930380019301100163947012130200009300d0001387b000130307001302120093022000e31652fe930380019301200163107310130200009300d0001387a00013000000130307001302120093022000e31452fe9303700193013001631a730c130200009300d000138790001300000013000000130307001302120093022000e31252fe93036001930140016312730a130200009300d0001387b0001302120093022000e31852fe930380019301500163107708130200009300d000130000001387a0001302120093022000e31652fe9303700193016001631c7704130200009300d0001300000013000000138790001302120093022000e31452fe930360019301700163167702930000029303000293018001639e70009300100213802003930300009301900163147000631030020f00f00f638001009391110093e111009308d00513850100730000000f00f00f930110009308d0051305000073000000731000c00000000000000000000000000000000000000000000000000";
void tesqwe(char *input2){
std::ofstream datafile("/data/textfile2.txt", std::ios_base::binary | std::ios_base::out);
char buf[3];
buf[2] = 0;
// std::string s = input2;
std::string str(input2);
// std::string rjo(1,input2);
std::stringstream input(str);
input.flags(std::ios_base::hex);
while (input)
{
input >> buf[0] >> buf[1];
long val = strtol(buf, nullptr, 16);
datafile << static_cast<unsigned char>(val & 0xff);
}
std::cout<< "ok"<<std::endl;
}
```
到這邊我們就可以透過tesqwe 把前端的hex char array轉為string
存入前面掛載的目錄,如果要讀出來呢,那就反向一下
```cpp=
//-----------------------------------------------------------------------
EMSCRIPTEN_KEEPALIVE
char *LoadData() {
int fd;
int size;
char *buff;
fd = open("/data/textfile.txt", O_RDONLY);
if (fd == -1) return strerror(errno);
size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
buff = (uint8_t *) malloc(size+1);
read(fd, buff, size);
buff[size] = '\0';
for (int i=0; i<size; i+=2) {
if (i%16==0) printf("\n%.8x: ", i);
printf("%02x%02x ", *(buff+i), *(buff+i+1));
}
close(fd);
/////////////////////////////////////////////////////////////
struct CPU cpu;
cpu_init(&cpu);
// Read input file
read_file(&cpu,"/data/textfile2.txt");
while (1) {
// fetch
uint32_t inst = cpu_fetch(&cpu);
// Increment the program counter
cpu.pc += 4;
// execute
if (!cpu_execute(&cpu, inst))
break;
// dump_registers(&cpu);
if(cpu.pc==0)
break;
}
return buff;
}
```
在第二個分隔線就可以讓我們的模擬器去讀前端載入的elf檔案,實現動態加載分析檔案的功能
read_file(&cpu,"/data/textfile2.txt");
並呼叫載入elf執行模擬指令。
![](https://i.imgur.com/G3Qrvy1.png)
到這邊如果換成是gb模擬器,應該就完全可以在瀏覽器實現讀檔案寫檔案的功能。
目前看來emcc在編譯過程中把datatype某些部分給優化掉導致指令沒辦法正常顯示
計算結果正確,應該是wasm把某些datatype給轉換調導致printf無法判斷。
```c=
printf("\n%#.8lx -> Inst: %llx ",
cpu->pc-4, inst); // DEBUG*/
printf("\n<OpCode: %#.2x, funct3:%#x, funct7:%#x> ", opcode, funct3, funct7);
for (int i=0; i<8; i++) {
printf(" %4s:%llx ", abi[i], cpu->regs[i]);
printf(" %2s:%llx ", abi[i+8], cpu->regs[i+8]);
printf(" %2s:%llx ", abi[i+16], cpu->regs[i+16]);
printf(" %3s:%llx\n", abi[i+24], cpu->regs[i+24]);
}
```
![](https://i.imgur.com/gXOcBdM.png)
上述c code重寫後就可以正常顯示
# debug
更多時候等待並不是c很慢是
![](https://i.imgur.com/yszgDXw.png)
這邊註解output就不會一直拉到最下導致瀏覽器速度降低
```
// element.scrollTop = element.scrollHeight; // focus on bottom
```