Compare commits
6 Commits
cbe5733fcd
...
profiler
| Author | SHA1 | Date | |
|---|---|---|---|
|
956d42d008
|
|||
|
87d8345017
|
|||
|
d39fe580fc
|
|||
|
ba8c99a123
|
|||
|
cc8ae742f0
|
|||
|
812443d6ee
|
24
README.md
24
README.md
@@ -74,21 +74,20 @@ guile tests.scm | xmllint --format -
|
|||||||
|
|
||||||
| Name | Address | Size / B | Access |
|
| Name | Address | Size / B | Access |
|
||||||
|--------|---------|----------|--------------|
|
|--------|---------|----------|--------------|
|
||||||
| TXBUF | 000h | 32 | write |
|
| TXBUF | 00h | 32 | write |
|
||||||
| RXBUF | 080h | 32 | read |
|
| RXBUF | 20h | 32 | read |
|
||||||
| TXHEAD | 100h | 4 | atomic read |
|
| TXHEAD | 40h | 4 | atomic read |
|
||||||
| TXTAIL | 104h | 4 | atomic write |
|
| TXTAIL | 44h | 4 | atomic write |
|
||||||
| RXHEAD | 108h | 4 | atomic write |
|
| RXHEAD | 48h | 4 | atomic write |
|
||||||
| RXTAIL | 10Ch | 4 | atomic read |
|
| RXTAIL | 4Ch | 4 | atomic read |
|
||||||
|
|
||||||
For both sending (`TX`) and receiving (`RX`), there are three
|
For both sending (`TX`) and receiving (`RX`), there are three
|
||||||
registers: `xBUF`, `xHEAD` and `xTAIL`:
|
registers: `xBUF`, `xHEAD` and `xTAIL`:
|
||||||
|
|
||||||
- `xBUF` registers are 128-byte FIFO ring buffers used for data
|
- `xBUF` registers are 32-byte FIFO ring buffers used for data
|
||||||
- The `xHEAD` and `xTAIL` registers specify the start and end of data
|
- The `xHEAD` and `xTAIL` registers specify the start and end of data
|
||||||
in the ring buffer, `xHEAD` being the offset of the first byte of
|
in the ring buffer, `xHEAD` being the offset of the first byte of
|
||||||
data, and `xTAIL` being the offset of the first byte *after* the
|
data, and `xTAIL` being the offset of the first byte *after* the data.
|
||||||
data.
|
|
||||||
|
|
||||||
In order to be distinguishable from the empty state, the ring buffers
|
In order to be distinguishable from the empty state, the ring buffers
|
||||||
must never be completely full -- there must always be *at least one*
|
must never be completely full -- there must always be *at least one*
|
||||||
@@ -98,10 +97,7 @@ unoccupied byte between the tail and the head.
|
|||||||
|
|
||||||
| Name | Address | Size / B | Access |
|
| Name | Address | Size / B | Access |
|
||||||
|----------|---------|----------|--------------|
|
|----------|---------|----------|--------------|
|
||||||
| SYSREADY | 110h | 4 | atomic write |
|
| SYSREADY | 50h | 1 | atomic write |
|
||||||
| SYSINTER | 114h | 4 | atomic read |
|
|
||||||
|
|
||||||
The `SYSREADY` register is used to indicate when the system has booted
|
The `SYSREADY` register is used to indicate when the system has booted
|
||||||
up and is ready for user input. `SYSINTER` is set (and notified on)
|
up and is ready for user input.
|
||||||
once the emulator has enabled user input and the system is
|
|
||||||
interactive.
|
|
||||||
|
|||||||
158
asm.js
158
asm.js
@@ -14,29 +14,20 @@ class Tokenizer {
|
|||||||
this.buffer = [];
|
this.buffer = [];
|
||||||
this.comment = false;
|
this.comment = false;
|
||||||
this.string = false;
|
this.string = false;
|
||||||
this.cursor = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
find(pred) {
|
|
||||||
for (let i = this.cursor; i < this.buffer.length; ++i) {
|
|
||||||
if (pred(this.buffer[i]))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
skip() {
|
skip() {
|
||||||
const idx = this.find((cp) => !this.skips.has(cp));
|
const idx = this.buffer.findIndex((cp) => !this.skips.has(cp));
|
||||||
this.cursor = idx == null ? this.buffer.length : idx;
|
this.buffer = idx == -1 ? [] : this.buffer.slice(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
next_string() {
|
next_string() {
|
||||||
const idx = this.find((cp) => cp == this.string_quote);
|
const idx = this.buffer.findIndex((cp) => cp == this.string_quote);
|
||||||
if (idx == null) {
|
if (idx == -1) {
|
||||||
this.string = true;
|
this.string = true;
|
||||||
} else {
|
} else {
|
||||||
const string = this.buffer.slice(this.cursor, idx).join("");
|
const string = this.buffer.slice(0, idx).join("");
|
||||||
this.cursor = idx + 1;
|
this.buffer = this.buffer.slice(idx + 1);
|
||||||
this.string = false;
|
this.string = false;
|
||||||
return { string: string };
|
return { string: string };
|
||||||
}
|
}
|
||||||
@@ -47,20 +38,18 @@ class Tokenizer {
|
|||||||
return this.next_string();
|
return this.next_string();
|
||||||
|
|
||||||
this.skip();
|
this.skip();
|
||||||
if (this.buffer[this.cursor] == LINE_END) {
|
if (this.buffer[0] == LINE_END)
|
||||||
++this.cursor;
|
return this.buffer.shift();
|
||||||
return LINE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.buffer[this.cursor] == this.string_quote) {
|
if (this.buffer[0] == this.string_quote) {
|
||||||
++this.cursor;
|
this.buffer.shift();
|
||||||
return this.next_string();
|
return this.next_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
const idx = this.find((cp) => this.delims.has(cp));
|
const idx = this.buffer.findIndex((cp) => this.delims.has(cp));
|
||||||
if (idx != null) {
|
if (idx != -1) {
|
||||||
const token = this.buffer.slice(this.cursor, idx).join("");
|
const token = this.buffer.slice(0, idx).join("");
|
||||||
this.cursor = idx;
|
this.buffer = this.buffer.slice(idx);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,49 +144,52 @@ const types = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const opcodes = {
|
const opcodes = {
|
||||||
"block": 0x02,
|
"block": 0x02,
|
||||||
"loop": 0x03,
|
"loop": 0x03,
|
||||||
"if": 0x04,
|
"if": 0x04,
|
||||||
"else": 0x05,
|
"else": 0x05,
|
||||||
"end": 0x0b,
|
"end": 0x0b,
|
||||||
"br": 0x0c,
|
"br": 0x0c,
|
||||||
"br_if": 0x0d,
|
"br_if": 0x0d,
|
||||||
"call": 0x10,
|
"call": 0x10,
|
||||||
"call_indirect": 0x11,
|
"call_indirect": 0x11,
|
||||||
"drop": 0x0a,
|
"drop": 0x0a,
|
||||||
"local.get": 0x20,
|
"local.get": 0x20,
|
||||||
"local.set": 0x21,
|
"local.set": 0x21,
|
||||||
"local.tee": 0x22,
|
"local.tee": 0x22,
|
||||||
"global.get": 0x23,
|
"global.get": 0x23,
|
||||||
"global.set": 0x24,
|
"global.set": 0x24,
|
||||||
"i32.load": 0x28,
|
"i32.load": 0x28,
|
||||||
"i32.load8_u": 0x2d,
|
"i32.load8_u": 0x2d,
|
||||||
"i32.store": 0x36,
|
"i32.store": 0x36,
|
||||||
"i32.store8": 0x3a,
|
"i32.store8": 0x3a,
|
||||||
"i32.const": 0x41,
|
"i32.const": 0x41,
|
||||||
"i64.const": 0x42,
|
"i64.const": 0x42,
|
||||||
"i32.eqz": 0x45,
|
"i32.eqz": 0x45,
|
||||||
"i32.eq": 0x46,
|
"i32.eq": 0x46,
|
||||||
"i32.ne": 0x47,
|
"i32.ne": 0x47,
|
||||||
"i32.lt_s": 0x48,
|
"i32.lt_s": 0x48,
|
||||||
"i32.lt_u": 0x49,
|
"i32.lt_u": 0x49,
|
||||||
"i32.gt_s": 0x4a,
|
"i32.gt_s": 0x4a,
|
||||||
"i32.gt_u": 0x4b,
|
"i32.gt_u": 0x4b,
|
||||||
"i32.le_s": 0x4c,
|
"i32.le_s": 0x4c,
|
||||||
"i32.le_u": 0x4d,
|
"i32.le_u": 0x4d,
|
||||||
"i32.ge_s": 0x4e,
|
"i32.ge_s": 0x4e,
|
||||||
"i32.ge_u": 0x4f,
|
"i32.ge_u": 0x4f,
|
||||||
"i32.add": 0x6a,
|
"i32.add": 0x6a,
|
||||||
"i32.sub": 0x6b,
|
"i32.sub": 0x6b,
|
||||||
"i32.mul": 0x6c,
|
"i32.mul": 0x6c,
|
||||||
"i32.div_s": 0x6d,
|
"i32.div_s": 0x6d,
|
||||||
"i32.rem_s": 0x6f,
|
"i32.rem_s": 0x6f,
|
||||||
"i32.and": 0x71,
|
"i32.and": 0x71,
|
||||||
"i32.or": 0x72,
|
"i32.or": 0x72,
|
||||||
"i32.xor": 0x73,
|
"i32.xor": 0x73,
|
||||||
"i32.shl": 0x74,
|
"i32.shl": 0x74,
|
||||||
"i32.shr_s": 0x75,
|
"i32.shr_s": 0x75,
|
||||||
"i32.shr_u": 0x76,
|
"i32.shr_u": 0x76,
|
||||||
|
"i64.or": 0x84,
|
||||||
|
"i64.shl": 0x86,
|
||||||
|
"i64.extend_i32_u": 0xad,
|
||||||
|
|
||||||
// Threads instructions
|
// Threads instructions
|
||||||
"memory.atomic.notify": [ 0xfe, 0x00 ],
|
"memory.atomic.notify": [ 0xfe, 0x00 ],
|
||||||
@@ -205,6 +197,7 @@ const opcodes = {
|
|||||||
"i32.atomic.load": [ 0xfe, 0x10 ],
|
"i32.atomic.load": [ 0xfe, 0x10 ],
|
||||||
"i32.atomic.load8_u": [ 0xfe, 0x12 ],
|
"i32.atomic.load8_u": [ 0xfe, 0x12 ],
|
||||||
"i32.atomic.store": [ 0xfe, 0x17 ],
|
"i32.atomic.store": [ 0xfe, 0x17 ],
|
||||||
|
"i64.atomic.store": [ 0xfe, 0x18 ],
|
||||||
"i32.atomic.store8": [ 0xfe, 0x19 ],
|
"i32.atomic.store8": [ 0xfe, 0x19 ],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -897,8 +890,9 @@ const Section = Object.freeze({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const Kind = Object.freeze({
|
const Kind = Object.freeze({
|
||||||
FUNC: 0x00,
|
FUNC: 0x00,
|
||||||
MEM: 0x02,
|
MEM: 0x02,
|
||||||
|
GLOBAL: 0x03,
|
||||||
});
|
});
|
||||||
|
|
||||||
export class Assembler {
|
export class Assembler {
|
||||||
@@ -953,8 +947,28 @@ export class Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
action_export(action) {
|
action_export(action) {
|
||||||
const index = Object.keys(this.funcs).indexOf(action.name);
|
const func_index = Object.keys(this.funcs).indexOf(action.name);
|
||||||
this.exports[action.name] = { kind: Kind.FUNC, index };
|
if (func_index != -1) {
|
||||||
|
this.exports[action.name] = {
|
||||||
|
kind: Kind.FUNC,
|
||||||
|
index: func_index,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const global_index = Object.keys(this.globals).indexOf(action.name);
|
||||||
|
if (global_index != -1) {
|
||||||
|
this.exports[action.name] = {
|
||||||
|
kind: Kind.GLOBAL,
|
||||||
|
index: global_index,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
`ERROR: Unable to resolve export ${action.name} `
|
||||||
|
+ "(only functions and globals currently supported)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
action_func(action) {
|
action_func(action) {
|
||||||
|
|||||||
73
emu.js
73
emu.js
@@ -1,17 +1,17 @@
|
|||||||
const TXBUF = 0x000;
|
const TXBUF = 0x00;
|
||||||
const RXBUF = 0x080;
|
const RXBUF = 0x20;
|
||||||
const TXHEAD = 0x100;
|
const TXHEAD = 0x40;
|
||||||
const TXTAIL = 0x104;
|
const TXTAIL = 0x44;
|
||||||
const RXHEAD = 0x108;
|
const RXHEAD = 0x48;
|
||||||
const RXTAIL = 0x10c;
|
const RXTAIL = 0x4c;
|
||||||
|
const SYSREADY = 0x50;
|
||||||
|
|
||||||
const SYSREADY = 0x110;
|
const TXBUF_SIZE = 32;
|
||||||
const SYSINTER = 0x114;
|
const RXBUF_SIZE = 32;
|
||||||
|
const PERIPHS_SIZE = 81;
|
||||||
|
|
||||||
const PERIPHS_SIZE = 0x200;
|
const POLL_INTERVAL_MS = 20;
|
||||||
|
const DOT_INTERVAL_MS = 120;
|
||||||
const POLL_INTERVAL_MS = 5;
|
|
||||||
const DOT_INTERVAL_MS = 25;
|
|
||||||
|
|
||||||
const COLS = 80;
|
const COLS = 80;
|
||||||
const TAB_WIDTH = 8;
|
const TAB_WIDTH = 8;
|
||||||
@@ -51,18 +51,28 @@ class Emulator {
|
|||||||
document.addEventListener('keydown', (e) => this.handle_keydown(e));
|
document.addEventListener('keydown', (e) => this.handle_keydown(e));
|
||||||
window.addEventListener('resize', () => this.handle_resize());
|
window.addEventListener('resize', () => this.handle_resize());
|
||||||
|
|
||||||
this.forth = new Worker('boot.js', { type: 'module' });
|
this.prof = new Worker("prof.js");
|
||||||
|
this.prof.onmessage = (e) => {
|
||||||
|
const blob = new Blob(
|
||||||
|
[JSON.stringify(e.data)],
|
||||||
|
{ type: "application/json" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = "wipforth-profile.json";
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
this.print("Assembling kernel ");
|
this.print("Assembling kernel ");
|
||||||
this.dots = setInterval(() => this.print("."), DOT_INTERVAL_MS);
|
const dots = setInterval(() => this.print("."), DOT_INTERVAL_MS);
|
||||||
this.forth.postMessage({ type: "load", mem: this.mem });
|
this.worker = new Worker('boot.js', { type: 'module' });
|
||||||
this.forth.onmessage = (e) => {
|
this.worker.postMessage({ type: "load", mem: this.mem });
|
||||||
clearInterval(this.dots);
|
this.worker.onmessage = (e) => {
|
||||||
|
clearInterval(dots);
|
||||||
this.print(" done\n");
|
this.print(" done\n");
|
||||||
|
this.worker.postMessage({ type: "boot" });
|
||||||
this.print("Loading prelude ");
|
this.prof.postMessage({ type: "start", mem: this.mem });
|
||||||
this.forth.postMessage({ type: "boot" });
|
|
||||||
this.dots = setInterval(() => this.print("."), DOT_INTERVAL_MS);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch('prelude.f')
|
fetch('prelude.f')
|
||||||
@@ -89,21 +99,16 @@ class Emulator {
|
|||||||
if (!this.input_enable) {
|
if (!this.input_enable) {
|
||||||
const sysready = Atomics.load(this.mem_u8, SYSREADY);
|
const sysready = Atomics.load(this.mem_u8, SYSREADY);
|
||||||
if (sysready != 0) {
|
if (sysready != 0) {
|
||||||
clearInterval(this.dots);
|
this.prof.postMessage({ type: "stop" });
|
||||||
this.print(" done\n");
|
|
||||||
|
|
||||||
Atomics.store(this.mem_u8, SYSINTER, 1);
|
|
||||||
Atomics.notify(this.mem_i32, SYSINTER / 4);
|
|
||||||
|
|
||||||
this.input_enable = true;
|
this.input_enable = true;
|
||||||
this.blink = true;
|
|
||||||
this.flush_output();
|
this.flush_output();
|
||||||
|
document.getElementById('cursor').classList.add('blinking');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fifo_next(idx) {
|
fifo_next(idx) {
|
||||||
return (idx + 1) & 0x7f;
|
return (idx + 1) & 0x1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_tx_data(head, tail) {
|
handle_tx_data(head, tail) {
|
||||||
@@ -191,14 +196,12 @@ class Emulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.blink = false;
|
|
||||||
this.flush_output();
|
this.flush_output();
|
||||||
|
|
||||||
document.getElementById('cursor').classList.remove('blinking');
|
document.getElementById('cursor').classList.remove('blinking');
|
||||||
if (this.idle_timer)
|
if (this.idle_timer)
|
||||||
clearTimeout(this.idle_timer);
|
clearTimeout(this.idle_timer);
|
||||||
this.idle_timer = setTimeout(() => {
|
this.idle_timer = setTimeout(() => {
|
||||||
this.blink = true;
|
|
||||||
document.getElementById('cursor').classList.add('blinking');
|
document.getElementById('cursor').classList.add('blinking');
|
||||||
}, CURSOR_IDLE_TIME_MS);
|
}, CURSOR_IDLE_TIME_MS);
|
||||||
}
|
}
|
||||||
@@ -285,12 +288,10 @@ class Emulator {
|
|||||||
return row.map((c, x) => {
|
return row.map((c, x) => {
|
||||||
const ec = this.html_escape(c);
|
const ec = this.html_escape(c);
|
||||||
if (this.input_enable
|
if (this.input_enable
|
||||||
&& x == this.cursor.x && y == this.cursor.y) {
|
&& x == this.cursor.x && y == this.cursor.y)
|
||||||
const cl = this.blink ? 'class="blinking"' : '';
|
return '<span id="cursor">' + ec + '</span>';
|
||||||
return `<span id="cursor" ${cl}>` + ec + '</span>';
|
else
|
||||||
} else {
|
|
||||||
return ec;
|
return ec;
|
||||||
}
|
|
||||||
}).join('').trimEnd();
|
}).join('').trimEnd();
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
this.output.innerHTML = html;
|
this.output.innerHTML = html;
|
||||||
|
|||||||
58
prelude.f
58
prelude.f
@@ -1,5 +1,12 @@
|
|||||||
|
76 EMIT 111 EMIT 97 EMIT 100 EMIT 105 EMIT 110 EMIT 103 EMIT 32 EMIT
|
||||||
|
112 EMIT 114 EMIT 101 EMIT 108 EMIT 117 EMIT 100 EMIT 101 EMIT 32 EMIT
|
||||||
|
|
||||||
: \ KEY 10 = 0BRANCH [ -20 , ] ; IMMEDIATE \ Now we have line comments :)
|
: \ KEY 10 = 0BRANCH [ -20 , ] ; IMMEDIATE \ Now we have line comments :)
|
||||||
|
|
||||||
|
\ We'll periodically sprinkle these in so that it's clear to the user
|
||||||
|
\ that things are happening.
|
||||||
|
46 EMIT
|
||||||
|
|
||||||
\ Conditionals
|
\ Conditionals
|
||||||
|
|
||||||
: IF
|
: IF
|
||||||
@@ -20,6 +27,8 @@
|
|||||||
SWAP !
|
SWAP !
|
||||||
; IMMEDIATE
|
; IMMEDIATE
|
||||||
|
|
||||||
|
46 EMIT
|
||||||
|
|
||||||
\ Loops
|
\ Loops
|
||||||
|
|
||||||
: BEGIN HERE @ ; IMMEDIATE
|
: BEGIN HERE @ ; IMMEDIATE
|
||||||
@@ -34,6 +43,8 @@
|
|||||||
HERE @ - ,
|
HERE @ - ,
|
||||||
; IMMEDIATE
|
; IMMEDIATE
|
||||||
|
|
||||||
|
46 EMIT
|
||||||
|
|
||||||
\ Recursive calls
|
\ Recursive calls
|
||||||
|
|
||||||
: RECURSE LATEST @ >CFA , ; IMMEDIATE
|
: RECURSE LATEST @ >CFA , ; IMMEDIATE
|
||||||
@@ -50,6 +61,8 @@
|
|||||||
|
|
||||||
( ( Take that, C ) )
|
( ( Take that, C ) )
|
||||||
|
|
||||||
|
46 EMIT
|
||||||
|
|
||||||
\ Printing utilities
|
\ Printing utilities
|
||||||
|
|
||||||
: CR 10 EMIT ;
|
: CR 10 EMIT ;
|
||||||
@@ -67,6 +80,8 @@
|
|||||||
+ EMIT
|
+ EMIT
|
||||||
;
|
;
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
: .
|
: .
|
||||||
\ Handle negatives
|
\ Handle negatives
|
||||||
DUP 0< IF CHAR - EMIT NEGATE THEN
|
DUP 0< IF CHAR - EMIT NEGATE THEN
|
||||||
@@ -95,6 +110,8 @@
|
|||||||
2DROP
|
2DROP
|
||||||
;
|
;
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
: TYPE ( addr len -- )
|
: TYPE ( addr len -- )
|
||||||
BEGIN
|
BEGIN
|
||||||
DUP 0= IF 2DROP EXIT THEN
|
DUP 0= IF 2DROP EXIT THEN
|
||||||
@@ -103,6 +120,8 @@
|
|||||||
AGAIN
|
AGAIN
|
||||||
;
|
;
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
: C, HERE @ C! 1 HERE +! ;
|
: C, HERE @ C! 1 HERE +! ;
|
||||||
|
|
||||||
: ."
|
: ."
|
||||||
@@ -132,6 +151,8 @@
|
|||||||
THEN
|
THEN
|
||||||
; IMMEDIATE
|
; IMMEDIATE
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
\ Misc utilities
|
\ Misc utilities
|
||||||
|
|
||||||
: NIP SWAP DROP ;
|
: NIP SWAP DROP ;
|
||||||
@@ -150,6 +171,8 @@
|
|||||||
|
|
||||||
: [COMPILE] ' , ; IMMEDIATE
|
: [COMPILE] ' , ; IMMEDIATE
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
\ Constants, variables and values
|
\ Constants, variables and values
|
||||||
|
|
||||||
: CONSTANT
|
: CONSTANT
|
||||||
@@ -181,21 +204,24 @@
|
|||||||
THEN
|
THEN
|
||||||
; IMMEDIATE
|
; IMMEDIATE
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
\ Peripheral register addresses
|
\ Peripheral register addresses
|
||||||
|
|
||||||
HEX
|
HEX
|
||||||
|
|
||||||
000 CONSTANT TXBUF
|
00 CONSTANT TXBUF
|
||||||
080 CONSTANT RXBUF
|
20 CONSTANT RXBUF
|
||||||
100 CONSTANT TXHEAD
|
40 CONSTANT TXHEAD
|
||||||
104 CONSTANT TXTAIL
|
44 CONSTANT TXTAIL
|
||||||
108 CONSTANT RXHEAD
|
48 CONSTANT RXHEAD
|
||||||
10C CONSTANT RXTAIL
|
4C CONSTANT RXTAIL
|
||||||
110 CONSTANT SYSREADY
|
50 CONSTANT SYSREADY
|
||||||
114 CONSTANT SYSINTER
|
|
||||||
|
|
||||||
DECIMAL
|
DECIMAL
|
||||||
|
|
||||||
|
46 EMIT
|
||||||
|
|
||||||
\ A better word-not-found handler
|
\ A better word-not-found handler
|
||||||
|
|
||||||
: ANY-RX? RXHEAD AC@ RXTAIL AC@ <> ;
|
: ANY-RX? RXHEAD AC@ RXTAIL AC@ <> ;
|
||||||
@@ -218,11 +244,13 @@ DECIMAL
|
|||||||
|
|
||||||
' WNF-HANDLER TO WNFHOOK
|
' WNF-HANDLER TO WNFHOOK
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
\ Version number
|
\ Version number
|
||||||
|
|
||||||
0 CONSTANT VERSION-MAJOR
|
0 CONSTANT VERSION-MAJOR
|
||||||
2 CONSTANT VERSION-MINOR
|
2 CONSTANT VERSION-MINOR
|
||||||
2 CONSTANT VERSION-PATCH
|
1 CONSTANT VERSION-PATCH
|
||||||
|
|
||||||
: PRINT-VERSION
|
: PRINT-VERSION
|
||||||
CHAR v EMIT VERSION-MAJOR .
|
CHAR v EMIT VERSION-MAJOR .
|
||||||
@@ -230,6 +258,8 @@ DECIMAL
|
|||||||
CHAR . EMIT VERSION-PATCH .
|
CHAR . EMIT VERSION-PATCH .
|
||||||
;
|
;
|
||||||
|
|
||||||
|
CHAR . EMIT
|
||||||
|
|
||||||
\ Welcome banner
|
\ Welcome banner
|
||||||
|
|
||||||
: BANNER
|
: BANNER
|
||||||
@@ -239,7 +269,7 @@ DECIMAL
|
|||||||
." |__,__/_/ .__(_)___/_//_/" CR
|
." |__,__/_/ .__(_)___/_//_/" CR
|
||||||
." /_/ " CR
|
." /_/ " CR
|
||||||
CR
|
CR
|
||||||
." Welcome to Wipforth " PRINT-VERSION ." !" CR
|
." Wipforth " PRINT-VERSION CR
|
||||||
." Copyright (c) Camden Dixie O'Brien" CR
|
." Copyright (c) Camden Dixie O'Brien" CR
|
||||||
CR
|
CR
|
||||||
." Wipforth is freely available to use, modify and distribute for personal use" CR
|
." Wipforth is freely available to use, modify and distribute for personal use" CR
|
||||||
@@ -247,8 +277,8 @@ DECIMAL
|
|||||||
CR
|
CR
|
||||||
;
|
;
|
||||||
|
|
||||||
\ Set SYSREADY high and wait until interactive
|
." done" CR
|
||||||
1 SYSREADY AC!
|
|
||||||
SYSINTER WAIT DROP
|
|
||||||
|
|
||||||
BANNER
|
BANNER
|
||||||
|
|
||||||
|
\ Set SYSREADY high to enable user input
|
||||||
|
1 SYSREADY AC!
|
||||||
|
|||||||
48
prof.js
Normal file
48
prof.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const INTERVAL_MS = 1;
|
||||||
|
const RS_TOP_ADDR = 0x10000;
|
||||||
|
const PROF_DATA_ADDR = 0x58;
|
||||||
|
const PROF_DATA_IDX = PROF_DATA_ADDR / 8;
|
||||||
|
|
||||||
|
let mem_8;
|
||||||
|
let mem_64;
|
||||||
|
let sampler;
|
||||||
|
|
||||||
|
const samples = [];
|
||||||
|
|
||||||
|
function sample() {
|
||||||
|
const data = Atomics.load(mem_64, PROF_DATA_IDX);
|
||||||
|
const ip = Number(data & 0xffffffffn);
|
||||||
|
const rsp = Number(data >> 32n);
|
||||||
|
samples.push({ ip, rs_bytes: mem_8.slice(rsp, RS_TOP_ADDR) });
|
||||||
|
}
|
||||||
|
|
||||||
|
function i32(bytes) {
|
||||||
|
return bytes[0]
|
||||||
|
| (bytes[1] << 8)
|
||||||
|
| (bytes[2] << 16)
|
||||||
|
| (bytes[3] << 24);
|
||||||
|
}
|
||||||
|
function postproc({ ip, rs_bytes }) {
|
||||||
|
const rs = [];
|
||||||
|
for (let i = 0; i < rs_bytes.length; i += 4)
|
||||||
|
rs.push(i32(rs_bytes.slice(i, i + 4)));
|
||||||
|
rs.reverse();
|
||||||
|
return { ip, rs };
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onmessage = (e) => {
|
||||||
|
switch (e.data.type) {
|
||||||
|
case "start":
|
||||||
|
console.log("Starting profiler");
|
||||||
|
mem_8 = new Uint8Array(e.data.mem.buffer);
|
||||||
|
mem_64 = new BigUint64Array(e.data.mem.buffer);
|
||||||
|
ip = e.data.ip;
|
||||||
|
rsp = e.data.rsp;
|
||||||
|
sampler = setInterval(sample, INTERVAL_MS);
|
||||||
|
break;
|
||||||
|
case "stop":
|
||||||
|
clearInterval(sample);
|
||||||
|
console.log("Stopped profiler");
|
||||||
|
self.postMessage(samples.map(postproc));
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
(define client (client-setup))
|
(define client (client-setup))
|
||||||
|
|
||||||
(navigate client "http://localhost:8080")
|
(navigate client "http://localhost:8080")
|
||||||
(sleep 1)
|
(sleep 5)
|
||||||
|
|
||||||
(define-test kernel-assembles-successfully
|
(define-test kernel-assembles-successfully
|
||||||
(let* ((display (get-display client))
|
(let* ((display (get-display client))
|
||||||
|
|||||||
44
wipforth.ws
44
wipforth.ws
@@ -2,12 +2,15 @@
|
|||||||
.import main "emu" "mem"
|
.import main "emu" "mem"
|
||||||
|
|
||||||
;; Peripheral registers
|
;; Peripheral registers
|
||||||
.def TXBUF 000h
|
.def TXBUF 00h
|
||||||
.def RXBUF 080h
|
.def RXBUF 20h
|
||||||
.def TXHEAD 100h
|
.def TXHEAD 40h
|
||||||
.def TXTAIL 104h
|
.def TXTAIL 44h
|
||||||
.def RXHEAD 108h
|
.def RXHEAD 48h
|
||||||
.def RXTAIL 10Ch
|
.def RXTAIL 4Ch
|
||||||
|
|
||||||
|
;; Mirror of registers for profiler to sample
|
||||||
|
.def PROF_DATA 58h
|
||||||
|
|
||||||
.def DICT_START 0200h
|
.def DICT_START 0200h
|
||||||
|
|
||||||
@@ -635,15 +638,6 @@
|
|||||||
end
|
end
|
||||||
call next
|
call next
|
||||||
|
|
||||||
.func wait
|
|
||||||
.elem codewords wait WAIT_CODEWORD
|
|
||||||
call pop
|
|
||||||
i32.const 0
|
|
||||||
i64.const -1
|
|
||||||
memory.atomic.wait32 2 0
|
|
||||||
call push
|
|
||||||
call next
|
|
||||||
|
|
||||||
;; Core utility words
|
;; Core utility words
|
||||||
|
|
||||||
.func exit
|
.func exit
|
||||||
@@ -761,7 +755,7 @@
|
|||||||
local.get head
|
local.get head
|
||||||
i32.const 1
|
i32.const 1
|
||||||
i32.add
|
i32.add
|
||||||
i32.const 7Fh
|
i32.const 1Fh
|
||||||
i32.and
|
i32.and
|
||||||
i32.atomic.store8 0 0
|
i32.atomic.store8 0 0
|
||||||
|
|
||||||
@@ -777,7 +771,7 @@
|
|||||||
local.tee tail
|
local.tee tail
|
||||||
i32.const 1
|
i32.const 1
|
||||||
i32.add
|
i32.add
|
||||||
i32.const 7Fh
|
i32.const 1Fh
|
||||||
i32.and
|
i32.and
|
||||||
local.tee n
|
local.tee n
|
||||||
i32.const TXHEAD
|
i32.const TXHEAD
|
||||||
@@ -1235,15 +1229,6 @@ COPY:
|
|||||||
.word COPY_CODEWORD
|
.word COPY_CODEWORD
|
||||||
.def PREV _COPY
|
.def PREV _COPY
|
||||||
|
|
||||||
_WAIT:
|
|
||||||
.word PREV
|
|
||||||
.byte 4
|
|
||||||
.utf8 "WAIT"
|
|
||||||
.align
|
|
||||||
WAIT:
|
|
||||||
.word WAIT_CODEWORD
|
|
||||||
.def PREV _WAIT
|
|
||||||
|
|
||||||
_EXIT:
|
_EXIT:
|
||||||
.word PREV
|
.word PREV
|
||||||
.byte 4
|
.byte 4
|
||||||
@@ -2091,6 +2076,13 @@ KERNEL_DEFS_END:
|
|||||||
.func trampoline
|
.func trampoline
|
||||||
loop iter
|
loop iter
|
||||||
global.get fn call_indirect codeword codewords
|
global.get fn call_indirect codeword codewords
|
||||||
|
|
||||||
|
i32.const PROF_DATA
|
||||||
|
global.get ip i64.extend_i32_u
|
||||||
|
global.get rsp i64.extend_i32_u
|
||||||
|
i64.const 32 i64.shl i64.or
|
||||||
|
i64.atomic.store 3 0
|
||||||
|
|
||||||
global.get run br_if iter
|
global.get run br_if iter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
16
words.js
Normal file
16
words.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Assembler } from "./asm.js";
|
||||||
|
|
||||||
|
const asm = new Assembler();
|
||||||
|
for await (const chunk of Deno.stdin.readable) {
|
||||||
|
asm.push(chunk);
|
||||||
|
}
|
||||||
|
asm.wasm();
|
||||||
|
|
||||||
|
const defs = Object.entries(asm.defs);
|
||||||
|
while (defs[0][0] != '_DUP')
|
||||||
|
defs.shift();
|
||||||
|
while (defs.at(-1)[0] != 'WNF_HANDLER')
|
||||||
|
defs.pop();
|
||||||
|
const words = Object.fromEntries(defs.filter(([k,v]) => !k.startsWith("_")));
|
||||||
|
|
||||||
|
console.log(JSON.stringify(words));
|
||||||
Reference in New Issue
Block a user