forked from sellicott/tcc-riscv32
/
test-nuttx.js
146 lines (122 loc) · 4.27 KB
/
test-nuttx.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
const fs = require('fs');
const source = fs.readFileSync("./tcc-wasm.wasm");
const typedArray = new Uint8Array(source);
// Log WebAssembly Messages from Zig to JavaScript Console
// https://github.com/daneelsan/zig-wasm-logger/blob/master/script.js
const text_decoder = new TextDecoder();
let console_log_buffer = "";
// WebAssembly Helper Functions
const wasm = {
// WebAssembly Instance
instance: undefined,
// Init the WebAssembly Instance
init: function (obj) {
this.instance = obj.instance;
},
// Fetch the Zig String from a WebAssembly Pointer
getString: function (ptr, len) {
const memory = this.instance.exports.memory;
return text_decoder.decode(
new Uint8Array(memory.buffer, ptr, len)
);
},
};
// Load the WebAssembly
WebAssembly.instantiate(typedArray, {
env: {
// Write to JavaScript Console from Zig
// https://github.com/daneelsan/zig-wasm-logger/blob/master/script.js
jsConsoleLogWrite: function(ptr, len) {
console_log_buffer += wasm.getString(ptr, len);
},
// Flush JavaScript Console from Zig
// https://github.com/daneelsan/zig-wasm-logger/blob/master/script.js
jsConsoleLogFlush: function() {
console.log(console_log_buffer);
console_log_buffer = "";
},
}
}).then(result => {
// Store references to WebAssembly Functions and Memory exported by Zig
wasm.init(result);
// Allocate a String for passing the Compiler Options to Zig
const options = ["-c", "-r", "hello.c"];
const options_ptr = allocateString(JSON.stringify(options));
// Allocate a String for passing Program Code to Zig
const code_ptr = allocateString(`
int main(int argc, char *argv[])
{
// Make NuttX System Call to write(fd, buf, buflen)
const unsigned int nbr = 61; // SYS_write
const void *parm1 = 1; // File Descriptor (stdout)
const void *parm2 = "Hello, World!!\\n"; // Buffer
const void *parm3 = 15; // Buffer Length
// Execute ECALL for System Call to NuttX Kernel
register long r0 asm("a0") = (long)(nbr);
register long r1 asm("a1") = (long)(parm1);
register long r2 asm("a2") = (long)(parm2);
register long r3 asm("a3") = (long)(parm3);
asm volatile
(
// Load 61 to Register A0 (SYS_write)
"addi a0, zero, 61 \\n"
// Load 1 to Register A1 (File Descriptor)
"addi a1, zero, 1 \\n"
// Load 0xc0101000 to Register A2 (Buffer)
"lui a2, 0xc0 \\n"
"addiw a2, a2, 257 \\n"
"slli a2, a2, 0xc \\n"
// Load 15 to Register A3 (Buffer Length)
"addi a3, zero, 15 \\n"
// ECALL for System Call to NuttX Kernel
"ecall \\n"
// NuttX needs NOP after ECALL
".word 0x0001 \\n"
// Input+Output Registers: None
// Input-Only Registers: A0 to A3
// Clobbers the Memory
:
: "r"(r0), "r"(r1), "r"(r2), "r"(r3)
: "memory"
);
// TODO: TCC says this is invalid
// asm volatile("nop" : "=r"(r0));
// Loop Forever
for(;;) {}
return 0;
}
`);
// Call TCC to compile a program
const ptr = wasm.instance.exports
.compile_program(options_ptr, code_ptr);
console.log(`ptr=${ptr}`);
// Get the `a.out` size from first 4 bytes returned
const memory = wasm.instance.exports.memory;
const data_len = new Uint8Array(memory.buffer, ptr, 4);
const len = data_len[0] | data_len[1] << 8 | data_len[2] << 16 | data_len[3] << 24;
console.log(`main: len=${len}`);
if (len <= 0) { return; }
// Save the `a.out` data from the rest of the bytes returned
const data = new Uint8Array(memory.buffer, ptr + 4, len);
fs.writeFileSync("/tmp/a.out", Buffer.from(data));
console.log("TCC Output saved to /tmp/a.out");
});
// Allocate a String for passing to Zig
// https://blog.battlefy.com/zig-made-it-easy-to-pass-strings-back-and-forth-with-webassembly
const allocateString = (string) => {
// WebAssembly Memory exported by Zig
const memory = wasm.instance.exports.memory;
const buffer = new TextEncoder().encode(string);
// Ask Zig to allocate memory
const pointer = wasm.instance.exports
.allocUint8(buffer.length + 1);
const slice = new Uint8Array(
memory.buffer,
pointer,
buffer.length + 1
);
slice.set(buffer);
// Terminate the string with null
slice[buffer.length] = 0;
return pointer;
};