Compare commits

...

7 Commits

Author SHA1 Message Date
cdo 9fb6faca8e Add favicon 2026-03-02 15:52:59 +00:00
cdo 53171df913 Set output rows based on window size 2026-03-02 15:52:59 +00:00
cdo 67b67c2505 Fix EMIT-DIGIT 2026-03-02 15:52:59 +00:00
cdo c1a3e86222 Fix output width at 80 chars and remove trailing spaces 2026-03-02 15:52:59 +00:00
cdo e8138414c0 Implement basic scrolling in emu.js 2026-03-02 15:52:59 +00:00
cdo 289443192f Add copyright and license message to the banner 2026-03-02 15:52:59 +00:00
cdo 50f2f240fa Shorten SYSREADY comment in prelude 2026-03-01 20:26:28 +00:00
5 changed files with 52 additions and 8 deletions
+41 -4
View File
@@ -13,7 +13,6 @@ const PERIPHS_SIZE = 69; // Nice
const POLL_INTERVAL_MS = 20; const POLL_INTERVAL_MS = 20;
const COLS = 80; const COLS = 80;
const ROWS = 36;
const TAB_WIDTH = 8; const TAB_WIDTH = 8;
const CURSOR_IDLE_TIME_MS = 1000; const CURSOR_IDLE_TIME_MS = 1000;
@@ -36,8 +35,9 @@ class Emulator {
this.rx_queue = []; this.rx_queue = [];
this.timer = setInterval(() => this.poll(), POLL_INTERVAL_MS); this.timer = setInterval(() => this.poll(), POLL_INTERVAL_MS);
this.rows = this.max_rows();
this.grid = Array.from( this.grid = Array.from(
{ length: ROWS }, { length: this.rows },
() => new Array(COLS).fill(' ')); () => new Array(COLS).fill(' '));
this.cursor = { x: 0, y: 0 }; this.cursor = { x: 0, y: 0 };
this.range = { this.range = {
@@ -47,6 +47,7 @@ class Emulator {
this.idle_timer = null; this.idle_timer = null;
this.input_enable = false; this.input_enable = false;
document.addEventListener('keydown', (e) => this.handle_keydown(e)); document.addEventListener('keydown', (e) => this.handle_keydown(e));
window.addEventListener('resize', () => this.handle_resize());
this.worker = new Worker('boot.js'); this.worker = new Worker('boot.js');
this.worker.postMessage(this.mem); this.worker.postMessage(this.mem);
@@ -142,6 +143,8 @@ class Emulator {
this.cursor_move(1, 0); this.cursor_move(1, 0);
} else if (e.key == 'Enter') { } else if (e.key == 'Enter') {
this.cursor.y = this.range.end.y + 1; this.cursor.y = this.range.end.y + 1;
while (this.cursor.y >= this.rows)
this.scroll();
this.cursor.x = 0; this.cursor.x = 0;
this.submit_line(); this.submit_line();
Object.assign(this.range.start, this.cursor); Object.assign(this.range.start, this.cursor);
@@ -226,7 +229,7 @@ class Emulator {
next_cell(cell) { next_cell(cell) {
if (cell.x < COLS - 1) if (cell.x < COLS - 1)
return { x: cell.x + 1, y: cell.y }; return { x: cell.x + 1, y: cell.y };
else if (cell.y < ROWS - 1) else if (cell.y < this.rows - 1)
return { x: 0, y: cell.y + 1 }; return { x: 0, y: cell.y + 1 };
else else
return null; return null;
@@ -239,6 +242,8 @@ class Emulator {
this.grid[cell.y][cell.x] = this.grid[prev.y][prev.x]; this.grid[cell.y][cell.x] = this.grid[prev.y][prev.x];
cell = prev; cell = prev;
} }
if (this.next_cell(this.range.end) == null)
this.scroll();
this.range.end = this.next_cell(this.range.end); this.range.end = this.next_cell(this.range.end);
} }
@@ -262,7 +267,7 @@ class Emulator {
return '<span id="cursor">' + ec + '</span>'; return '<span id="cursor">' + ec + '</span>';
else else
return ec; return ec;
}).join(''); }).join('').trimEnd();
}).join('\n'); }).join('\n');
this.output.innerHTML = html; this.output.innerHTML = html;
} }
@@ -283,6 +288,38 @@ class Emulator {
return c; return c;
} }
} }
scroll() {
this.grid.shift()
this.grid.push(new Array(COLS).fill(' '));
this.cursor.y -= 1;
this.range.start.y -= 1;
this.range.end.y -= 1;
}
max_rows() {
const style = getComputedStyle(this.output);
const line_height = parseFloat(style.lineHeight);
const margin_top = parseFloat(style.marginTop);
const margin_bottom = parseFloat(style.marginBottom);
const viewport_height = window.innerHeight;
const output_height = viewport_height - margin_top - margin_bottom;
return Math.floor(output_height / line_height) - 1;
}
handle_resize() {
this.rows = this.max_rows();
while (this.grid.length < this.rows)
this.grid.push(new Array(COLS).fill(' '));
while (this.grid.length > this.rows) {
this.grid.shift()
this.cursor.y -= 1;
this.range.start.y -= 1;
this.range.end.y -= 1;
}
this.flush_output();
}
} }
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:720f0aca1aeff1b79874bebcf15a0ecf4d88a8c076dd6964aaed66fb38e10842
size 499
+1
View File
@@ -3,6 +3,7 @@
<head> <head>
<title>Wipforth</title> <title>Wipforth</title>
<link rel="stylesheet" type="text/css" href="styles.css"/> <link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="icon" type="image/png" href="favicon.png"/>
</head> </head>
<body> <body>
<script type="text/javascript" src="emu.js"></script> <script type="text/javascript" src="emu.js"></script>
+6 -4
View File
@@ -88,7 +88,7 @@
; IMMEDIATE ; IMMEDIATE
: EMIT-DIGIT : EMIT-DIGIT
DUP 10 < IF CHAR 0 ELSE CHAR A THEN DUP 10 < IF CHAR 0 ELSE 10 - CHAR A THEN
+ EMIT + EMIT
; ;
@@ -258,13 +258,15 @@ CHAR . EMIT
." /_/ " CR ." /_/ " CR
CR CR
." Wipforth " VERSION-PRINT CR ." Wipforth " VERSION-PRINT CR
." Copyright (c) Camden Dixie O'Brien" CR
CR
." Wipforth is available to use, modify and distribute for personal use under" CR
." the Komorebi license, version 2.0.0." CR
CR CR
; ;
." done" CR ." done" CR
BANNER BANNER
\ Set SYSREADY high to indicate prelude has finished and enable user \ Set SYSREADY high to enable user input
\ input on the JS side.
1 SYSREADY AC! 1 SYSREADY AC!
+1
View File
@@ -14,6 +14,7 @@ body {
} }
#output { #output {
width: 80ch;
margin: 1.5em auto; margin: 1.5em auto;
white-space: pre; white-space: pre;
} }