Compare commits

..

16 Commits

Author SHA1 Message Date
0c0b94fa47 Remove some esp32 platform things (that came from where?) 2026-01-18 15:51:10 -08:00
a0e693c57a Add SPIFFS.exists 2026-01-18 15:51:10 -08:00
4a207ea9dd Add Serial/Serial2 begin3 and writeChar 2026-01-18 15:51:10 -08:00
6ecdd56375 Update documentation 2026-01-18 15:51:10 -08:00
1bed37b417 Convert Arduino bool values to forth flags 2026-01-18 15:51:10 -08:00
7224a44726 Add more MDNS functions 2026-01-18 15:51:10 -08:00
4752b5e83d Adding ICE40 FPGA synthesizer 2025-11-14 22:00:32 -08:00
134cc8215b Fix ESP32 layering. 2025-09-06 01:30:10 -07:00
c13c7a9fe2 Fixed oled bug, made build more flexible + fix build errors. 2025-09-06 01:24:36 -07:00
19593ee4ad Fix 2025-07-04 16:41:17 -07:00
9291f47c2a Fix 2025-07-04 16:35:24 -07:00
6dd856c26a Update version. 2025-07-04 16:25:37 -07:00
16f23dfee4 Drop windows releases. 2025-07-04 16:18:03 -07:00
3471b6cf41 Fix printing bug. 2025-05-24 10:45:37 -07:00
f2ce65585d Adding missed file, add password prompt and instructions. 2025-05-10 13:03:00 -07:00
3a5daad8af Adding jotblk sample.
A web editor that embeds uEforth.
2025-05-10 12:41:18 -07:00
28 changed files with 2109 additions and 73 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
out out
build.ninja build.ninja
attic/jotblk/env

View File

@ -0,0 +1,19 @@
# This file specifies files that are *not* uploaded to Google Cloud
# using gcloud. It follows the same syntax as .gitignore, with the addition of
# "#!include" directives (which insert the entries of the given .gitignore-style
# file at that point).
#
# For more information, run:
# $ gcloud topic gcloudignore
#
.gcloudignore
# If you would like to upload your .git directory, .gitignore file or files
# from your .gitignore file, remove the corresponding line
# below:
.git
.gitignore
# Python pycache:
__pycache__/
# Ignored by the build system
/setup.cfg

12
attic/jotblk/Makefile Executable file
View File

@ -0,0 +1,12 @@
all:
./run.sh
deploy:
gcloud app deploy -q --project jotblk *.yaml
setup:
python3 -m venv env
env/bin/pip install -r requirements.txt
datastore:
echo "Run: . dstore.sh"

22
attic/jotblk/README.md Normal file
View File

@ -0,0 +1,22 @@
# jotblk
A web based block based editor to run on AppEngine.
To run you need the AppEngine SDK.
To setup run:
```
make setup
```
In parallel run (for the datastore):
```
. dstore.sh
```
And separately (to run a webserver on http://localhost:8080):
```
./run.sh
```
Before use, type password. Default is: xyzzy

17
attic/jotblk/app.yaml Normal file
View File

@ -0,0 +1,17 @@
runtime: python39
default_expiration: "10m"
handlers:
- url: /
static_files: static/index.html
upload: static/index.html
secure: always
- url: /static
static_dir: static
secure: always
- url: /io
script: main.app
secure: always

2
attic/jotblk/dstore.sh Normal file
View File

@ -0,0 +1,2 @@
# Run this to start an datastore emulator.
gcloud emulators firestore start --database-mode=datastore-mode --project=jotblk --host-port=127.0.0.1:8099

81
attic/jotblk/main.py Normal file
View File

@ -0,0 +1,81 @@
# Copyright 2025 Bradley D. Nelson
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import base64
from flask import Flask, request, send_from_directory
from google.cloud import datastore
client = datastore.Client()
root_passwd_key = client.key('passwd', 'root')
root_passwd_entity = client.get(root_passwd_key)
if root_passwd_entity is None:
root_passwd = 'xyzzy'
else:
root_passwd = root_passwd_entity['secret']
def SaveBlock(index, data):
assert index >= 0
assert len(data) == 1024, len(data)
entity = datastore.Entity(key=client.key('block', index+1))
entity.update({'data': data})
client.put(entity)
return ''
def LoadBlocks(start, end):
assert end >= end
assert start >= 0
assert end - start <= 128
query = client.query(kind='block')
first_key = client.key('block', start+1)
last_key = client.key('block', end+1)
query.key_filter(first_key, '>=')
query.key_filter(last_key, '<')
entities = query.fetch()
blks = {}
for i in entities:
blks[i.key.path[0]['id']-1] = i['data']
result = []
for i in range(start, end+1):
if i in blks:
result.append(blks[i])
else:
result.append(b' ' * 1024)
return b''.join(result)
app = Flask(__name__)
@app.route('/<path:filename>')
def canned(filename):
return send_from_directory('static', filename)
@app.route('/')
def root():
return canned('index.html')
@app.route('/io', methods=['POST'])
def io():
if root_passwd != request.form['passwd']:
return 'deny', 403
if request.form['command'] == 'read':
start = int(request.form['start'])
end = int(request.form['end'])
return LoadBlocks(start, end)
elif request.form['command'] == 'write':
index = int(request.form['index'])
data = request.files['data'].read()
return SaveBlock(index, data)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)

View File

@ -0,0 +1,2 @@
Flask==3.0.0
google-cloud-datastore==2.15.1

8
attic/jotblk/run.sh Executable file
View File

@ -0,0 +1,8 @@
#! /bin/bash
export DATASTORE_DATASET=jotblk
export DATASTORE_EMULATOR_HOST=127.0.0.1:8099
export DATASTORE_EMULATOR_HOST_PATH=127.0.0.1:8099/datastore
export DATASTORE_HOST=http://127.0.0.1:8099
export DATASTORE_PROJECT_ID=jotblk
env/bin/python3 main.py

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<body>
<script src="jotblk.js"></script>

View File

@ -0,0 +1,570 @@
// Copyright 2025 Bradley D. Nelson
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
'use strict';
const MAX_BLOCKS = 16 * 1024;
const SYNC_CHUNK = 64;
const DIRTY = 1;
const LOADED = 2;
var blocks = new Uint8Array(1024 * MAX_BLOCKS);
var flags = new Uint8Array(MAX_BLOCKS);
var clipboard = [];
var screen_history = [];
var keymap = {};
var scr = 0;
var pos = 0;
var marker = 0;
var passwd = '';
var ForthKeyDown = null;
var ForthKeyPress = null;
var forth_added = false;
var ueforth = null;
document.body.style.overflow = 'hidden';
document.body.style.margin = '0';
document.body.style.border = '0';
document.body.style.padding = '0';
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
function SpaceIt() {
if (!(flags[scr] & LOADED)) {
return;
}
if (blocks[scr * 1024] == 0) {
for (var i = 0; i < 1024; ++i) {
blocks[i + scr * 1024] = 32;
}
flags[scr] |= DIRTY;
}
}
function IsEmpty(n) {
for (var i = 0; i < 1024; i++) {
var ch = blocks[i + n * 1024];
if (ch != 32 && ch != 0) {
return false;
}
}
return true;
}
function FindEnd() {
var i = pos;
while (i < 1023 &&
(blocks[scr * 1024 + i] != 32 ||
blocks[scr * 1024 + i + 1] != 32)) {
i++;
}
return i;
}
function Insert() {
var end = FindEnd();
for (var i = end; i >= pos; i--) {
blocks[scr * 1024 + i + 1] = blocks[scr * 1024 + i];
}
blocks[scr * 1024 + pos] = 32;
flags[scr] |= DIRTY;
}
function Delete() {
var end = FindEnd();
for (var i = pos; i < end; i++) {
blocks[scr * 1024 + i] = blocks[scr * 1024 + i + 1];
}
flags[scr] |= DIRTY;
}
function FindSpan() {
var i = Math.floor(pos / 64) * 64;
var min = 63;
var max = 0;
for (var j = 0; j < 64; j++) {
if (blocks[scr * 1024 + i + j] != 32) {
min = Math.min(min, j);
max = Math.max(max, j);
}
}
if (min > max) {
return [i, i + 63];
}
return [i + min, i + max];
}
function Home() {
var span = FindSpan();
var start = Math.floor(pos / 64) * 64;
if (pos == span[0]) {
pos = start;
} else {
pos = span[0];
}
}
function End() {
var span = FindSpan();
var end = Math.floor(pos / 64) * 64 + 63;
var span1 = Math.min(span[1] + 1, end);
if (pos == span1) {
pos = end;
} else {
pos = span1;
}
}
function Up() {
if (pos >= 64) {
pos -= 64;
}
}
function Down() {
if (pos + 64 < 1024) {
pos += 64;
}
}
function Left() {
pos = Math.max(0, pos - 1);
}
function Right() {
pos = Math.min(1023, pos + 1);
}
function Copy() {
var row = Math.floor(pos / 64) * 64;
clipboard.push(blocks.slice(scr * 1024 + row, scr * 1024 + row + 64));
Up();
}
function Cut() {
var row = Math.floor(pos / 64) * 64;
clipboard.push(blocks.slice(scr * 1024 + row, scr * 1024 + row + 64));
for (var j = 0; j < 64; j++) {
blocks[scr * 1024 + row + j] = 32;
}
flags[scr] |= DIRTY;
Up();
}
function Paste() {
if (clipboard.length == 0) {
return;
}
var row = Math.floor(pos / 64) * 64;
var data = clipboard.pop();
for (var j = 0; j < 64; j++) {
blocks[scr * 1024 + row + j] = data[j];
}
flags[scr] |= DIRTY;
Down();
}
function Update() {
SpaceIt();
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = '16px consolas, Monaco, monospace';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.save();
ctx.scale(canvas.width / 64 , canvas.height / 17);
ctx.fillStyle = '#750';
if (window.onkeydown === Login) {
ctx.fillRect(10, 0, 1, 1);
} else {
for (var j = 0; j < 16; ++j) {
for (var i = 0; i < 64; ++i) {
if (pos === i + j * 64) {
ctx.fillRect(i, j, 1, 1);
}
}
}
}
ctx.save();
var m = ctx.measureText('W');
var w = m.width;
var h = m.fontBoundingBoxAscent + m.fontBoundingBoxDescent;
ctx.scale(1 / w, 1 / h);
ctx.fillStyle = '#fb0';
if (window.onkeydown === Login) {
ctx.fillText('password:', 0.5 * w * 9, 0.5 * h);
} else {
for (var j = 0; j < 16; ++j) {
for (var i = 0; i < 64; ++i) {
var ch = String.fromCharCode(blocks[i + j * 64 + scr * 1024]);
ctx.fillText(ch, (i + 0.5) * w, (j + 0.5) * h);
}
}
}
ctx.fillStyle = '#750';
ctx.textAlign = 'right';
var info = '';
if (flags[scr] & DIRTY) {
info += 'D ';
}
if (!(flags[scr] & LOADED)) {
info += 'L ';
}
info += scr;
ctx.fillText(info, 63.5 * w, 16.5 * h);
ctx.restore();
ctx.restore();
}
function LoadChunk(n) {
if ((flags[n * SYNC_CHUNK] & LOADED)) {
return Promise.resolve();
} else {
return LoadBlocks(n * SYNC_CHUNK, (n + 1) * SYNC_CHUNK).then(function() {
Update();
});
}
}
function MaybeLoad() {
var s = Math.floor(scr / SYNC_CHUNK);
return LoadChunk(s);
}
function LineString(blk, row) {
var result = '';
for (var col = 0; col < 64; col++) {
result += String.fromCharCode(blocks[blk * 1024 + row * 64 + col]);
}
return result;
}
function BlockString(blk, linebreaks) {
var result = '';
for (var row = 0; row < 16; row++) {
result += LineString(blk, row);
if (linebreaks) {
result += '\n';
}
}
return result;
}
function Eval(n) {
eval(BlockString(n, true));
}
function Print() {
var start = Math.min(marker, scr);
var end = Math.max(marker, scr);
var content = '';
content += '<!DOCTYPE html>\n';
for (var i = start; i <= end; i++) {
content += '<pre style="border: 1px solid; display: inline-block;">\n';
content += BlockString(i, true).replaceAll('<', '&lt;');
content += '<hr/>' + i;
content += '</pre><br/>\n';
}
var blob = new Blob([content], { type: 'text/html' });
var url = URL.createObjectURL(blob);
window.open(url, '_blank');
}
function Backspace() {
if (pos > 0) {
--pos;
Delete();
}
}
function Goto(n) {
scr = n;
MaybeLoad();
}
function Gosub(n) {
screen_history.push(scr);
Goto(n);
}
function Adjust(n) {
Goto(Math.max(0, Math.min(MAX_BLOCKS - 1, scr + n)));
}
function Enter() {
pos = Math.floor((pos + 64) / 64) * 64;
if (pos > 1023) {
pos -= 64;
}
}
function ShiftUp() {
pos = (pos % 64);
}
function ShiftDown() {
pos = (pos % 64) + 15 * 64;
}
function GetLink() {
var s = BlockString(scr, false);
var at = s.indexOf('@', pos);
var paren = s.indexOf(' )', pos);
if (at >= 0 && (at < paren || paren < 0)) {
return s.slice(at).split(' ')[0];
}
if (paren >= 0) {
var p2 = s.lastIndexOf('( ', paren);
if (p2 >= 0) {
return s.slice(p2, paren + 2);
}
}
return '';
}
function Find(s) {
for (var i = 0; i < MAX_BLOCKS; i++) {
var j = (scr + 1 + i) % MAX_BLOCKS;
if (BlockString(j, false).indexOf(s) >= 0) {
return j;
}
}
return null;
}
function FollowLink() {
var link = GetLink();
if (link.startsWith('@')) {
var n = parseInt(link.slice(1));
Gosub(n);
} else if (link.startsWith('( ')) {
var n = Find(link.slice(2, -2));
if (n !== null) {
Gosub(n);
}
}
}
function Type(ch) {
Insert();
blocks[pos + scr * 1024] = ch;
pos = Math.min(1023, pos + 1);
}
function Key(e) {
if (e.ctrlKey && keymap['^' + e.key]) {
keymap['^' + e.key](e);
} else if (e.shiftKey && keymap['+' + e.key]) {
keymap['+' + e.key](e);
} else if (keymap[e.key]) {
keymap[e.key](e);
} else if (e.key.length == 1 && !e.ctrlKey) {
Type(e.key.charCodeAt(0));
} else {
return true;
}
Update();
e.preventDefault();
return false;
}
function Login(e) {
if (e.key == 'Backspace') {
passwd = passwd.slice(0, -1);
} else if (e.key == 'Enter') {
window.onkeydown = Key;
MaybeLoad().then(function() {
Eval(63);
});
} else if (e.key.length == 1) {
passwd += e.key;
}
Update();
e.preventDefault();
return false;
}
function Resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
Update();
}
function SaveBlock(i) {
var fd = new FormData();
fd.append('command', 'write');
fd.append('passwd', passwd);
fd.append('index', i);
fd.append('data', new Blob([blocks.slice(i * 1024, (i + 1) * 1024)],
{type: "application/octet-stream"}));
return fetch('/io', {'method': 'POST', body: fd}).then(function() {
flags[i] &= ~DIRTY;
Update();
});
}
function Sync() {
for (var i = 0; i < MAX_BLOCKS; ++i) {
if ((flags[i] & LOADED) && (flags[i] & DIRTY)) {
SaveBlock(i);
}
}
}
function FindEmpty() {
screen_history.push(scr);
while (!IsEmpty(scr)) {
Adjust(1);
}
}
function StampLog() {
var now = new Date();
var month = ('0' + (1 + now.getMonth())).slice(-2);
var day = ('0' + now.getDate()).slice(-2);
var dt = now.getFullYear() + '-' + month + '-' + day;
for (var i = 0; i < dt.length; i++) {
blocks[54 + i + scr * 1024] = dt.charCodeAt(i);
}
var mark = 'LOG: ';
for (var i = 0; i < mark.length; i++) {
blocks[i + scr * 1024] = mark.charCodeAt(i);
}
pos = mark.length;
flags[scr] |= DIRTY;
}
function Back() {
if (screen_history.length) {
Goto(screen_history.pop());
}
}
function LoadBlocks(start, end) {
var fd = new FormData();
fd.append('command', 'read');
fd.append('passwd', passwd);
fd.append('start', start);
fd.append('end', end);
return fetch('/io', {'method': 'POST', body: fd}).then(function(response) {
if (!response.ok) {
throw 'bad fetch';
}
return response.arrayBuffer().then(function(data) {
var u8 = new Uint8Array(data);
if (u8.length != (end + 1 - start) * 1024) {
throw 'bad load';
}
for (var i = start; i < end; i++) {
if (flags[i] & LOADED) {
return;
}
var dst = i * 1024;
var src = (i - start) * 1024;
for (var j = 0; j < 1024; j++) {
blocks[j + dst] = u8[j + src];
}
flags[i] |= LOADED;
}
});
});
}
function ForthKeyFilter(e) {
if (e.key == 'f' && e.ctrlKey) {
ToggleForth();
e.preventDefault();
return false;
}
return ForthKeyDown(e);
}
function ToggleForth() {
if (!forth_added) {
forth_added = true;
var fscript = document.createElement('script');
fscript.src = 'myforth.fs';
fscript.type = 'text/forth';
document.body.appendChild(fscript);
var script = document.createElement('script');
script.src = 'https://eforth.appspot.com/ueforth.js';
document.body.appendChild(script);
function Loader() {
if (ueforth !== null) {
ueforth.Start();
canvas.style.display = 'none';
setTimeout(function() {
ForthKeyDown = window.onkeydown;
ForthKeyPress = window.onkeypress;
window.onkeydown = ForthKeyFilter;
}, 500);
} else {
setTimeout(Loader, 10);
}
}
Loader();
return;
}
if (window.onkeydown === Key) {
window.onkeydown = ForthKeyFilter;
window.onkeypress = ForthKeyPress;
canvas.style.display = 'none';
ueforth.screen.style.display = '';
} else {
window.onkeydown = Key;
window.onkeypress = null;
ueforth.screen.style.display = 'none';
canvas.style.display = '';
Resize();
}
}
function Init() {
keymap['Delete'] = Delete;
keymap['Backspace'] = Backspace;
keymap['PageUp'] = function() { Adjust(-1); };
keymap['PageDown'] = function() { Adjust(1); };
keymap['+PageUp'] = function() { Adjust(-16); };
keymap['+PageDown'] = function() { Adjust(16); };
keymap['Home'] = Home;
keymap['End'] = End;
keymap['Enter'] = Enter;
keymap['^Enter'] = FollowLink;
keymap['ArrowUp'] = Up;
keymap['ArrowDown'] = Down;
keymap['ArrowLeft'] = Left;
keymap['ArrowRight'] = Right;
keymap['+ArrowUp'] = ShiftUp;
keymap['+ArrowDown'] = ShiftDown;
keymap['+ArrowLeft'] = Home;
keymap['+ArrowRight'] = End;
keymap['^c'] = Copy;
keymap['^x'] = Cut;
keymap['^v'] = Paste;
keymap['^m'] = function() { marker = scr; };
keymap['^p'] = Print;
keymap['^g'] = function() { Eval(scr); };
keymap['^o'] = FindEmpty;
keymap['^l'] = StampLog;
keymap['^b'] = Back;
keymap['^f'] = ToggleForth;
keymap['^h'] = function() { Gosub(0); };
window.addEventListener('resize', Resize);
window.onkeydown = Login;
Resize();
}
Init();
setInterval(Sync, 3000);

View File

@ -0,0 +1,43 @@
\ Copyright 2021 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
web definitions
JSWORD: block-read { n a -- }
for (var i = 0; i < 1024; i++) {
u8[a + i] = blocks[n * 1024 + i];
}
~
also internals
create block-buffer 1024 allot
also forth definitions
variable scr
: block ( n -- a ) block-buffer block-read block-buffer ;
: buffer ( n -- a ) block ;
only forth definitions
( Loading )
: load ( n -- ) block 1024 evaluate ;
: thru ( a b -- ) over - 1+ for aft dup >r load r> 1+ then next drop ;
( Listing )
: list ( n -- ) scr ! ." Block " scr @ . cr scr @ block
15 for dup 63 type [char] | emit space 15 r@ - . cr 64 + next drop ;
only forth definitions

View File

@ -42,7 +42,6 @@ CFLAGS_MINIMIZE = [
'-s', '-s',
'-DUEFORTH_MINIMAL', '-DUEFORTH_MINIMAL',
'-fno-exceptions', '-fno-exceptions',
'-ffreestanding',
'-fno-stack-protector', '-fno-stack-protector',
'-fomit-frame-pointer', '-fomit-frame-pointer',
'-fno-ident', '-fno-ident',
@ -136,7 +135,7 @@ def DetectWindowsTools(args):
sys.stderr.write('Windows %LOCALAPPDATA% not available, Windows support disabled.\n') sys.stderr.write('Windows %LOCALAPPDATA% not available, Windows support disabled.\n')
return return
LOCALAPPDATA = LOCALAPPDATAR.replace('C:/', '/mnt/c/') LOCALAPPDATA = LOCALAPPDATAR.replace('C:/', '/mnt/c/')
ARDUINO_CLI = LOCALAPPDATA + '/Programs/arduino-ide/resources/app/lib/backend/resources/arduino-cli.exe' ARDUINO_CLI = LSQ(LOCALAPPDATA + '/Programs/*rduino*/resources/app/lib/backend/resources/arduino-cli.exe')
WINTMP = LOCALAPPDATA + '/Temp' WINTMP = LOCALAPPDATA + '/Temp'
WINTMPR = LOCALAPPDATAR + '/Temp' WINTMPR = LOCALAPPDATAR + '/Temp'
PROGFILES = '/mnt/c/Program Files' PROGFILES = '/mnt/c/Program Files'

View File

@ -76,7 +76,7 @@ ESP32_ZIP_FILES += [
# Zip it. # Zip it.
ESP32_FILES += [ ESP32_FILES += [
Zip('$dst/esp32/ESP32forth.zip', ESP32_ZIP_FILES, base='$dst/esp32/ESP32forth'), Zip('$dst/esp32/ESP32forth.zip', ESP32_ZIP_FILES, base='$dst/esp32'),
] ]
# Simulator. # Simulator.

View File

@ -35,7 +35,7 @@ static Adafruit_SSD1306 *oled_display = 0;
YV(oled, OledNew, oled_display = new Adafruit_SSD1306(n2, n1, &Wire, n0); DROPn(3)) \ YV(oled, OledNew, oled_display = new Adafruit_SSD1306(n2, n1, &Wire, n0); DROPn(3)) \
YV(oled, OledDelete, delete oled_display) \ YV(oled, OledDelete, delete oled_display) \
YV(oled, OledBegin, n0 = oled_display->begin(n1, n0); NIP) \ YV(oled, OledBegin, n0 = oled_display->begin(n1, n0); NIP) \
YV(oled, OledHOME, oled_display->setCursor(0,0); DROP) \ YV(oled, OledHOME, oled_display->setCursor(0, 0)) \
YV(oled, OledCLS, oled_display->clearDisplay()) \ YV(oled, OledCLS, oled_display->clearDisplay()) \
YV(oled, OledTextc, oled_display->setTextColor(n0); DROP) \ YV(oled, OledTextc, oled_display->setTextColor(n0); DROP) \
YV(oled, OledPrintln, oled_display->println(c0); DROP) \ YV(oled, OledPrintln, oled_display->println(c0); DROP) \
@ -45,14 +45,14 @@ static Adafruit_SSD1306 *oled_display = 0;
YV(oled, OledPrint, oled_display->write(c0); DROP) \ YV(oled, OledPrint, oled_display->write(c0); DROP) \
YV(oled, OledInvert, oled_display->invertDisplay(n0); DROP) \ YV(oled, OledInvert, oled_display->invertDisplay(n0); DROP) \
YV(oled, OledTextsize, oled_display->setTextSize(n0); DROP) \ YV(oled, OledTextsize, oled_display->setTextSize(n0); DROP) \
YV(oled, OledSetCursor, oled_display->setCursor(n1,n0); DROPn(2)) \ YV(oled, OledSetCursor, oled_display->setCursor(n1, n0); DROPn(2)) \
YV(oled, OledPixel, oled_display->drawPixel(n2, n1, n0); DROPn(2)) \ YV(oled, OledPixel, oled_display->drawPixel(n2, n1, n0); DROPn(3)) \
YV(oled, OledDrawL, oled_display->drawLine(n4, n3, n2, n1, n0); DROPn(4)) \ YV(oled, OledDrawL, oled_display->drawLine(n4, n3, n2, n1, n0); DROPn(5)) \
YV(oled, OledCirc, oled_display->drawCircle(n3,n2, n1, n0); DROPn(3)) \ YV(oled, OledCirc, oled_display->drawCircle(n3, n2, n1, n0); DROPn(4)) \
YV(oled, OledCircF, oled_display->fillCircle(n3, n2, n1, n0); DROPn(3)) \ YV(oled, OledCircF, oled_display->fillCircle(n3, n2, n1, n0); DROPn(4)) \
YV(oled, OledRect, oled_display->drawRect(n4, n3, n2, n1, n0); DROPn(4)) \ YV(oled, OledRect, oled_display->drawRect(n4, n3, n2, n1, n0); DROPn(5)) \
YV(oled, OledRectF, oled_display->fillRect(n4, n3, n2, n1, n0); DROPn(3)) \ YV(oled, OledRectF, oled_display->fillRect(n4, n3, n2, n1, n0); DROPn(5)) \
YV(oled, OledRectR, oled_display->drawRoundRect(n5, n4, n3, n2, n1, n0); DROPn(5)) \ YV(oled, OledRectR, oled_display->drawRoundRect(n5, n4, n3, n2, n1, n0); DROPn(6)) \
YV(oled, OledRectRF, oled_display->fillRoundRect(n5, n4, n3, n2, n1, n0 ); DROPn(5)) YV(oled, OledRectRF, oled_display->fillRoundRect(n5, n4, n3, n2, n1, n0); DROPn(6))
#include "gen/esp32_oled.h" #include "gen/esp32_oled.h"

View File

@ -0,0 +1,44 @@
\ Copyright 2025 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
vocabulary flyclasses flyclasses definitions
0 value classes
1 value methods
0 value dispatch
0 value implementing
: flyclass create classes , 1 +to classes does> @ ;
: method& ( m cls -- a ) classes mod methods * + cells dispatch + ;
: accrued ( -- a ) 0 implementing method& ;
: method create methods , 1 +to methods does> @ over method& @ execute ;
: implementation ( cls -- ) to implementing ;
: >min ( a -- n ) cell+ @ ; : >max ( a -- a ) @ ;
: >below ( a -- a ) 2 cells + @ ; : >above ( a -- a) 3 cells + @ ;
: field ( min max -- "name" )
create 2dup , , accrued @ , swap - 1+ accrued @ * dup , accrued !
does> >r r@ >above mod r@ >below / r> >min + ;
: doput ( n o -- o "name" ) >r dup r@ >below mod swap r@ >above / r@ >above * +
swap r@ >max min r@ >min - r> >below * + ;
: put ( n o -- o "name" ) ' >body postpone literal postpone doput ; immediate
: extension ( cls -- ) 0 swap method& accrued methods cells cmove ;
: initiate here to dispatch
classes 1- for classes , methods 1- 1- for ['] abort , next next ;
: do:: ( o cls m -- ) swap method& @ execute ;
: :: ( o cls "name" -- ) ' >body @ postpone literal postpone do:: ; immediate
: m: ' >body @ :noname ;
: ;m postpone ; swap implementing method& ! ; immediate
forth definitions

16
pico-ice/ice40/ice40.fs Normal file
View File

@ -0,0 +1,16 @@
\ Copyright 2025 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
needs ice40_synthesis.fs
needs ice40_storage.fs

View File

@ -0,0 +1,72 @@
\ Copyright 2025 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
needs ice40_layout.fs
ice40 synthesis definitions
1 value tx
1 value ty
0 value tb
: PLACE ( x y -- ) to ty to tx 0 to tb ;
: PLACE-OUTPUT ( -- o ) tx ty tb Output .create ;
: USED? ( -- f ) place-output .isLogic? 0= if -1 exit then
place-output .getLogic 0<> ;
: ADVANCE
tb 7 < if 1 +to tb exit then
ty cells-height 1- < if 1 +to ty 0 to tb exit then
tx cells-width 1- < assert
1 +to tx 0 to ty 0 to tb
;
: allot-lut ( -- o )
begin used? while
advance
repeat
place-output
;
: ROUTE! { src dst -- }
src dst route 0= if
." ERROR UNABLE TO ROUTE!!!" cr
." SOURCE: " src .print cr
." DESTINATION: " dst .print cr
123 throw
then
src dst route.
;
: LUT4 { i0 i1 i2 i3 tbl -- o }
allot-lut { o }
tbl o .setLogic
i0 0 o .getInput route!
i1 1 o .getInput route!
i2 2 o .getInput route!
i3 3 o .getInput route!
o
;
: FFL ( -- o )
allot-lut { o }
$aaaa o .setLogic
-1 o .dffEnableBit .setBit
o
;
: FF! ( v ff -- ) 0 swap .getInput route! ;
forth definitions

View File

@ -0,0 +1,65 @@
\ Copyright 2025 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
vocabulary ice40 ice40 definitions
( For the 5k model in the pico-ice )
26 constant cells-width
32 constant cells-height
6 constant bram-column1
19 constant bram-column2
692 constant cram-bank-width
336 constant cram-height-lower
176 constant cram-height-upper
cram-height-lower cram-height-upper + constant cram-height
cram-bank-width 2* cram-height * 8 / constant cram-size
160 constant bram-width-lower
80 constant bram-width-upper
256 constant bram-bank-height
128 constant bram-chunk-size
( For the SG48 package in the pico-ice )
48 constant pinmax pinmax 1+ 2* cells constant pinsize
create pinmap pinsize allot pinmap pinsize 0 fill
0 value pinpos : x 1 +to pinpos ; : p ( p# -- ) pinpos swap cells pinmap + ! x ;
: pin#s ( p# -- x y b ) cells pinmap + @ 24 /mod >r 1+ r> 2 /mod 1 xor 31 * swap ;
\ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 COLUMN
\ | | | | | | | | | | | | | | | | | | | | | | | |
x x x 39 p 40 p 41 p x 42 p 43 p x x x 37 p x x 32 p 28 p 26 p 23 p x x x x x \ 0 TOP
x x x x x x x 38 p 36 p x x 35 p 34 p x x 31 p x 27 p 25 p x x x x x \ 1
\ | | | | | | | | | | | | | | | | | | | | | | | | PIN#
x x x x 46 p 47 p 48 p 2 p 4 p x x x x x 9 p 10 p 11 p 12 p 13 p x x x 14 p 15 p \ 0
x x x x x 44 p 45 p x 3 p x x x 6 p x x x x 21 p 20 p x 19 p 18 p 17 p 16 p \ 1 BOT
( Whole bitmap for config ram )
cram-size allocate throw constant cram
cram cram-size 0 fill
( Clear, read, and write as a bitmap )
: clear cram cram-size 0 fill ;
: cr& ( x y -- a*8 ) cram-height-lower /mod if
cram-height-upper 1- swap - cram-height-lower +
then cram-bank-width * >r
cram-bank-width 2 - /mod if
cram-bank-width 1- swap - 2 - cram-bank-width cram-height * +
then r> + ;
: bit! ( b pos v -- v ) >r 1 swap 7 swap - lshift dup invert r> and >r swap 0<> and r> or ;
: cram! ( b x y -- ) cr& 8 /mod cram + dup >r c@ bit! r> c! ;
: cram@ ( x y -- b ) cr& 8 /mod cram + c@ swap 7 swap - rshift 1 and 0<> ; ( UNTESTED )
forth definitions

View File

@ -0,0 +1,795 @@
\ Copyright 2025 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
needs ice40_config.fs
needs flyclasses.fs
ice40 definitions
vocabulary synthesis synthesis definitions
also flyclasses
54 constant logic-width
42 constant ram-width
logic-width ram-width - constant logicram-diff
flyclass CramBit
flyclass CramCell
flyclass Output
flyclass Input
flyclass Input0
flyclass Input1
flyclass Input2
flyclass Input3
flyclass LocalG
flyclass LocalG0
flyclass LocalG1
flyclass LocalG2
flyclass LocalG3
flyclass IOLocalG
flyclass IOLocalG0
flyclass IOLocalG1
flyclass SpanWire
flyclass Sp4HR
flyclass Sp4VB
flyclass Sp12HR
flyclass Sp12VB
flyclass IOPin
flyclass IOInput
flyclass IOOutput
flyclass IOOutput0
flyclass IOOutput1
flyclass IOEnable
flyclass IOFabOutput
flyclass LUTFFGlobal
flyclass FFSetReset
flyclass FFClock
flyclass FFEnable
flyclass GlobalNetwork
flyclass NotConnected
method .create ( <various> o -- o )
method .optionCount ( o -- n )
method .optionWire ( i o -- wire )
method .getOption ( o -- n )
method .setOption ( n o -- )
method .print ( o -- )
method .getXY ( o -- x y )
method .getBit ( o -- b ) ( overloaded for wires and CramBits )
method .setBit ( b o -- )
method .inside ( x y o -- o' )
method .isLogic? ( o -- f )
method .isRam? ( o -- f )
method .isIO? ( o -- f )
method .isBottom? ( o -- f )
method .isInside? ( o -- f )
method .listBits ( x o -- )
method .enableBit ( o -- bit )
method .inputEnableBit ( o -- bit )
method .setNoResetBit ( o -- bit )
method .asyncResetBit ( o -- bit )
method .carryEnableBit ( o -- bit )
method .dffEnableBit ( o -- bit )
method .setPath ( n o -- )
method .getPath ( o -- n )
method .getInput ( n o -- wire )
method .setLogic ( n o -- )
method .getLogic ( o -- n )
method .routes ( xt target o -- ) ( xt gets: bit wire )
method .walk ( xt o -- ) ( xt gets: bit wire )
method .makeOutput ( o -- )
method .makeInput ( o -- )
method .setPinType ( n o -- )
method .getPinType ( o -- n )
method .getParity ( o -- n )
method .getRow ( o -- n )
initiate
CramBit implementation
0 cram-bank-width 2* 1- field x
0 cram-height 1- field y
m: .create ( x y o -- o ) put y put x ;m
m: .print { o -- } ." CramBit(" o x . ." , " o y . ." ) " ;m
m: .setBit { b o -- } b o x o y cram! ;m
m: .getBit { o -- b } o x o y cram@ ;m
: Sp4RVB { x y i -- o } x 1+ y i Sp4VB .create ;
: span4_horz { i o -- wire } o .getXY i Sp4HR .create ;
: span4_vert { i o -- wire }
o .isBottom? if
o .getXY 1+ i Sp4VB .create
else
i 35 > if NotConnected .create exit then
o .getXY i Sp4VB .create
then ;
: span12_vert { i o -- wire }
o .isBottom? if
o .getXY 1+ i Sp12VB .create
else
i 21 > if NotConnected .create exit then
o .getXY i Sp12VB .create
then ;
CramCell implementation
-13 cells-width 13 + 1- field cx
-13 cells-height 13 + 1- field cy
m: .create ( x y o -- o ) put cy put cx ;m
m: .print { o -- } ." CramCell(" o cx . ." , " o cy . ." ) " ;m
m: .getXY { o -- cx cy } o cx o cy ;m
m: .inside { x y o -- o' } o cx 54 * x +
o cx 6 > if logicram-diff - then
o cx 19 > if logicram-diff - then
o cy 16 * y + CramBit .create ;m
m: .isInside? { o -- f } o cx 0 >= o cx cells-width < and
o cy 0 >= o cy cells-height < and and ;m
m: .isIO? { o -- f } o cx 0 = o cx cells-width 1- = or
o cy 0 = or o cy cells-height 1- = or ;m
m: .isBottom? { o -- g } o cy 0= ;m
m: .isRam? { o -- f } o cx bram-column1 = o cx bram-column2 = or o .isIO? 0= and ;m
m: .isLogic? { o -- f } o .isRam? 0= o .isIO? 0= and ;m
: route12 { x y i m n target xt o }
target i o span12_vert = if
x y o .inside o .getXY m n IOInput .create xt execute
then ;
: route4v { x y i m n target xt o }
target i o span4_vert = if
x y o .inside o .getXY m n IOInput .create xt execute
then ;
: route4h { x y i m n target xt o }
target i o span4_horz = if
x y o .inside o .getXY m n IOInput .create xt execute
then ;
m: .routes { xt target o -- }
o .isIO? if
5 1 0 0 0 target xt o route12
5 3 8 0 0 target xt o route12
5 5 16 0 0 target xt o route12
23 0 16 0 0 target xt o route4v
23 1 0 0 0 target xt o route4v
23 2 40 0 0 target xt o route4v
23 3 4 0 0 target xt o route4h
25 0 24 0 0 target xt o route4v
25 1 8 0 0 target xt o route4v
25 2 0 0 0 target xt o route4h
25 3 8 0 0 target xt o route4h
26 1 32 0 0 target xt o route4v
26 2 12 0 0 target xt o route4h
4 6 2 0 1 target xt o route12
5 6 10 0 1 target xt o route12
4 7 18 0 1 target xt o route12
23 4 18 0 1 target xt o route4v
23 5 2 0 1 target xt o route4v
23 6 42 0 1 target xt o route4v
23 7 5 0 1 target xt o route4h
25 4 26 0 1 target xt o route4v
25 5 10 0 1 target xt o route4v
25 6 1 0 1 target xt o route4h
25 7 9 0 1 target xt o route4h
26 5 34 0 1 target xt o route4v
26 6 13 0 1 target xt o route4h
4 9 4 1 0 target xt o route12
5 9 12 1 0 target xt o route12
4 8 20 1 0 target xt o route12
23 8 20 1 0 target xt o route4v
23 9 4 1 0 target xt o route4v
23 10 44 1 0 target xt o route4v
23 11 6 1 0 target xt o route4h
25 8 28 1 0 target xt o route4v
25 9 12 1 0 target xt o route4v
25 10 2 1 0 target xt o route4h
25 11 10 1 0 target xt o route4h
26 9 36 1 0 target xt o route4v
26 10 14 1 0 target xt o route4h
5 10 6 1 1 target xt o route12
5 12 14 1 1 target xt o route12
5 14 22 1 1 target xt o route12
23 12 22 1 1 target xt o route4v
23 13 6 1 1 target xt o route4v
23 14 46 1 1 target xt o route4v
23 15 7 1 1 target xt o route4h
25 12 30 1 1 target xt o route4v
25 13 14 1 1 target xt o route4v
25 14 3 1 1 target xt o route4h
25 15 11 1 1 target xt o route4h
26 13 38 1 1 target xt o route4v
26 13 15 1 1 target xt o route4h
exit
then
o .isInside? 0= if exit then
8 0 do
target o .getXY i 2* Sp4HR .create = if
46 i 2* 1+ o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 16 + Sp4HR .create = if
46 i 2* o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 32 + Sp4HR .create = if
47 i 2* 1+ o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 1+ Sp4RVB = if
52 i 2* 1+ o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 17 + Sp4RVB = if
53 i 2* o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 33 + Sp4RVB = if
53 i 2* 1+ o .inside o .getXY i Output .create xt execute
then
loop
4 0 do
target o .getXY i 2* 8 + Sp12HR .create = if
47 i 2* o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* Sp12HR .create = if
47 i 2* 8 + o .inside o .getXY i 4 + Output .create xt execute
then
target o .getXY i 2* Sp4VB .create = if
48 i 2* o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 16 + Sp4VB .create = if
48 i 2* 1+ o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 16 + Sp12HR .create = if
48 i 2* 8 + o .inside o .getXY i 4 + Output .create xt execute
then
target o .getXY i 2* 8 + Sp4VB .create = if
48 i 2* 1+ 8 + o .inside o .getXY i 4 + Output .create xt execute
then
target o .getXY i 2* Sp12VB .create = if
51 i 2* o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 32 + Sp4VB .create = if
51 i 2* 1+ o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 40 + Sp4VB .create = if
51 i 2* 8 + o .inside o .getXY i 4 + Output .create xt execute
then
target o .getXY i 2* 24 + Sp4VB .create = if
51 i 2* 1+ 8 + o .inside o .getXY i 4 + Output .create xt execute
then
target o .getXY i 2* 16 + Sp12VB .create = if
52 i 2* o .inside o .getXY i Output .create xt execute
then
target o .getXY i 2* 8 + Sp4VB .create = if
52 i 2* 8 + o .inside o .getXY i 4 + Output .create xt execute
then
loop
;m
: getBits { n i w -- n i } n w .getBit 1 and i lshift or i 1+ ;
: setBits { n w -- n } n 1 and w .setBit n 2/ ;
Output implementation CramCell extension
0 7 field bit
m: .create { cx cy b o -- o } cx cy o CramCell :: .create b swap put bit ;m
m: .print { o -- } ." Output(" o .getXY swap . . ." , " o bit . ." ) " ;m
m: .getBit ( o -- b ) bit ;m
m: .optionCount { o -- n } 0 ;m
m: .optionWire ( i o -- wire ) abort ;m
m: .setOption ( n o -- ) abort ;m
m: .getOption { o -- n } 0 ;m
create input_table Input0 , Input1 , Input2 , Input3 ,
m: .getInput { n o -- wire } o .getXY o .getBit n cells input_table + @ .create ;m
create logic_table $04 c, $14 c, $15 c, $05 c, $06 c, $16 c, $17 c, $07 c,
$03 c, $13 c, $12 c, $02 c, $01 c, $11 c, $10 c, $00 c,
m: .setLogic { n o -- } n ['] setBits o .listBits drop ;m
m: .getLogic { o -- n } 0 0 ['] getBits o .listBits drop ;m
m: .listBits { x o -- } 16 0 do
i logic_table + c@ 16 /mod >r 36 + r> o .getBit 2* + o .inside x execute
loop ;m
m: .setNoResetBit { o -- bit } 44 o .getBit 2* 1+ o .inside ;m
m: .asyncResetBit { o -- bit } 45 o .getBit 2* 1+ o .inside ;m
m: .carryEnableBit { o -- bit } 44 o .getBit 2* o .inside ;m
m: .dffEnableBit { o -- bit } 45 o .getBit 2* o .inside ;m
Input implementation Output extension
m: .getPath { o -- n } 0 0 ['] getBits o .listBits drop ;m
m: .setPath { n o -- } n ['] setBits o .listBits drop ;m
m: .optionCount { o -- n } 17 ;m
m: .getOption { o -- n } o .enableBit .getBit if
o .getPath 1+
else
0
then ;m
m: .setOption { n o -- } n if
-1 o .enableBit .setBit
n 1- o .setPath
else
0 o .enableBit .setBit
0 o .setPath ( for good measure )
then ;m
: inOptWire { i o lo hi -- wire }
i 0= if NotConnected .create exit then
i 1- 2 rshift 3 and { rt }
o .getXY
o .getBit 1 and if hi else lo then rt rshift 1 and i 1- 2* or
LocalG .create
;
Input0 implementation Input extension
m: .print { o -- } ." Input0(" o .getXY swap . . ." , " o .getBit . ." ) " ;m
m: .enableBit { o -- wire } 29 o .getBit 2* 1+ o .inside ;m
m: .listBits { x o -- } 26 o .getBit 2* 1+ o .inside x execute
26 o .getBit 2* o .inside x execute
27 o .getBit 2* 1+ o .inside x execute
28 o .getBit 2* 1+ o .inside x execute ;m
m: .optionWire ( i o -- wire ) $a $5 inOptWire ;m
Input1 implementation Input extension
m: .print { o -- } ." Input1(" o .getXY swap . . ." , " o .getBit . ." ) " ;m
m: .enableBit { o -- wire } 29 o .getBit 2* o .inside ;m
m: .listBits { x o -- } 30 o .getBit 2* 1+ o .inside x execute
30 o .getBit 2* o .inside x execute
27 o .getBit 2* o .inside x execute
28 o .getBit 2* o .inside x execute ;m
m: .optionWire ( i o -- wire ) $5 $a inOptWire ;m
Input2 implementation Input extension
m: .print { o -- } ." Input2(" o .getXY swap . . ." , " o .getBit . ." ) " ;m
m: .enableBit { o -- wire } 32 o .getBit 2* 1+ o .inside ;m
m: .listBits { x o -- } 35 o .getBit 2* 1+ o .inside x execute
35 o .getBit 2* o .inside x execute
34 o .getBit 2* 1+ o .inside x execute
33 o .getBit 2* 1+ o .inside x execute ;m
m: .optionWire ( i o -- wire ) $a $5 inOptWire ;m
Input3 implementation Input extension
m: .print { o -- } ." Input3(" o .getXY swap . . ." , " o .getBit . ." ) " ;m
m: .enableBit { o -- wire } 32 o .getBit 2* o .inside ;m
m: .listBits { x o -- } 31 o .getBit 2* 1+ o .inside x execute
31 o .getBit 2* o .inside x execute
34 o .getBit 2* o .inside x execute
33 o .getBit 2* o .inside x execute ;m
m: .optionWire ( i o -- wire ) $5 $a inOptWire ;m
LocalG implementation CramCell extension
0 31 field identifier
create localg_table LocalG0 , LocalG1 , LocalG2 , LocalG3 ,
m: .create { cx cy id o -- o }
id 4 mod cells localg_table + @ to o
cx cy o CramCell :: .create id swap put identifier ;m
m: .print { o -- } ." LocalG(" o .getXY swap . . ." , g" o identifier 8 /mod . . ." ) " ;m
m: .getPath { o -- n } 0 0 ['] getBits o .listBits drop ;m
m: .setPath { n o -- } n ['] setBits o .listBits drop ;m
m: .optionCount { o -- n } 15 ;m
m: .getBit { o -- n } o identifier 4 / 8 mod ;m
m: .getOption { o -- n } o .enableBit .getBit if
o .getPath 1-
else
0
then ;m
m: .setOption { n o -- } n if
-1 o .enableBit .setBit
n 1+ o .setPath
else
0 o .enableBit .setBit
0 o .setPath ( for good measure )
then ;m
: sp4skew { id off -- n } id 8 mod id 16 >= if 24 + then off + ;
: sp12skew { id off -- n cls } id 8 mod off + id 16 >= if Sp12VB else Sp12HR then ;
m: .optionWire { i o -- wire }
i 0 >= assert i o .optionCount < assert
i 0= if NotConnected .create exit then
i case
( TODO: shift by 2 and add r_v_b )
6 of o .getXY o identifier 0 sp12skew .create exit endof
7 of o .getXY o identifier 8 sp12skew .create exit endof
8 of o .getXY o identifier 16 sp12skew .create exit endof
9 of o .getXY o identifier 16 sp4skew Sp4VB .create exit endof
10 of o .getXY o identifier 0 sp4skew Sp4HR .create exit endof
11 of o .getXY o identifier 0 sp4skew Sp4VB .create exit endof
12 of o .getXY o identifier 8 sp4skew Sp4VB .create exit endof
13 of o .getXY o identifier 8 sp4skew Sp4HR .create exit endof
14 of o .getXY o identifier 16 sp4skew Sp4HR .create exit endof
endcase
o identifier 16 < if
i case
1 of 0 -1 endof
2 of 0 1 endof
3 of 0 0 endof
4 of 1 -1 endof
5 of -1 0 endof
endcase
else
i case
1 of 1 1 endof
2 of -1 1 endof
3 of 0 0 endof
4 of -1 -1 endof
5 of 1 0 endof
endcase
then
{ x y }
o .getXY y + to y x + to x
x 1 < if NotConnected .create exit then
x cells-width 1- >= if NotConnected .create exit then
y cells-height >= if NotConnected .create exit then
y 0< if NotConnected .create exit then
x y o identifier 8 mod Output .create
;m
LocalG0 implementation LocalG extension
m: .enableBit { o -- wire } 17 o .getBit 2* 1+ o .inside ;m
m: .listBits { x o -- } 14 o .getBit 2* 1+ o .inside x execute
15 o .getBit 2* 1+ o .inside x execute
14 o .getBit 2* o .inside x execute
16 o .getBit 2* 1+ o .inside x execute ;m
LocalG1 implementation LocalG extension
m: .enableBit { o -- wire } 17 o .getBit 2* o .inside ;m
m: .listBits { x o -- } 18 o .getBit 2* 1+ o .inside x execute
15 o .getBit 2* o .inside x execute
18 o .getBit 2* o .inside x execute
16 o .getBit 2* o .inside x execute ;m
LocalG2 implementation LocalG extension
m: .enableBit { o -- wire } 22 o .getBit 2* 1+ o .inside ;m
m: .listBits { x o -- } 25 o .getBit 2* 1+ o .inside x execute
24 o .getBit 2* 1+ o .inside x execute
25 o .getBit 2* o .inside x execute
23 o .getBit 2* 1+ o .inside x execute ;m
LocalG3 implementation LocalG extension
m: .enableBit { o -- wire } 22 o .getBit 2* o .inside ;m
m: .listBits { x o -- } 21 o .getBit 2* 1+ o .inside x execute
24 o .getBit 2* o .inside x execute
21 o .getBit 2* o .inside x execute
23 o .getBit 2* o .inside x execute ;m
create iopermy 0 , 1 , 3 , 2 , 4 , 5 , 7 , 6 , 8 , 9 , 11 , 10 , 12 , 13 , 15 , 14 ,
: >iopermy ( n -- n ) cells iopermy + @ ;
IOLocalG implementation CramCell extension
0 15 field identifier
create localg_table IOLocalG0 , IOLocalG1 ,
m: .create { cx cy id o -- o }
id 2 mod cells localg_table + @ to o
cx cy o CramCell :: .create id swap put identifier ;m
m: .print { o -- } ." IOLocalG(" o .getXY swap . . ." , g" o identifier 8 /mod . . ." ) " ;m
m: .inside { x y o -- wire } x y >iopermy o .isBottom? if 15 swap - then o CramCell :: .inside ;m
m: .getPath { o -- n } 0 0 ['] getBits o .listBits drop ;m
m: .setPath { n o -- } n ['] setBits o .listBits drop ;m
m: .optionCount { o -- n } 15 ;m
m: .getBit { o -- n } o identifier 8 mod ;m
m: .getRow { o -- n } o identifier 2/ ;m
m: .getOption { o -- n } o .enableBit .getBit if
o .getPath 1-
else
0
then ;m
m: .setOption { n o -- } n if
-1 o .enableBit .setBit
n 1+ o .setPath
else
0 o .enableBit .setBit
0 o .setPath ( for good measure )
then ;m
: iosp4v { offset o -- wire } o .getBit offset + o span4_vert ;
: iosp12v { offset o -- wire } o .getBit offset + o span12_vert ;
: iosp4h { offset o -- wire } o .getBit offset + o span4_horz ;
m: .optionWire { i o -- wire }
i 0 >= assert i o .optionCount < assert
i 0= if NotConnected .create exit then
1 +to i ( There's no 1 )
i case
5 of 0 o iosp4h exit endof
6 of 8 o iosp4h exit endof
7 of 0 o iosp12v exit endof
8 of 8 o iosp12v exit endof
9 of 16 o iosp12v exit endof
10 of 0 o iosp4v exit endof
11 of 8 o iosp4v exit endof
12 of 16 o iosp4v exit endof
13 of 24 o iosp4v exit endof
14 of 32 o iosp4v exit endof
15 of 40 o iosp4v exit endof
endcase
i 3 - 1 o .getXY nip if negate then { x y }
o .getXY y + to y x + to x
( Could use .isLogic ? )
x 1 < if NotConnected .create exit then
x cells-width 1- >= if NotConnected .create exit then
y cells-height 1- >= if NotConnected .create exit then
y 1 < if NotConnected .create exit then
x y o .getBit Output .create
;m
IOLocalG0 implementation IOLocalG extension
m: .enableBit { o -- wire } 19 o .getRow 2* 1+ o .inside ;m
m: .listBits { x o -- } 16 o .getRow 2* 1+ o .inside x execute
16 o .getRow 2* o .inside x execute
17 o .getRow 2* 1+ o .inside x execute
18 o .getRow 2* 1+ o .inside x execute ;m
IOLocalG1 implementation IOLocalG extension
m: .enableBit { o -- wire } 19 o .getRow 2* o .inside ;m
m: .listBits { x o -- } 20 o .getRow 2* 1+ o .inside x execute
20 o .getRow 2* o .inside x execute
17 o .getRow 2* o .inside x execute
18 o .getRow 2* o .inside x execute ;m
: optcount { n bit wire -- n } n 1+ ;
: optget { goal n bit wire -- goal n } bit .getBit if n else goal then n 1+ ;
: optset { goal n bit wire -- goal n } goal n = bit .setBit goal n 1+ ;
: optwire { answer goal n bit wire -- answer goal n } n goal = if wire else answer then goal n 1+ ;
: cross { idx n -- off ii } idx n /mod swap over 1 and xor ;
SpanWire implementation CramCell extension
m: .optionCount { o -- n } 1 ['] optcount o .walk ;m
m: .getOption { o -- n } 0 1 ['] optget o .walk drop ;m
m: .setOption { n o -- } n 1 ['] optset o .walk 2drop ;m
m: .optionWire { i o -- wire } NotConnected .create i 1 ['] optwire o .walk 2drop ;m
Sp4HR implementation SpanWire extension
0 11 field index
m: .create { cx cy idx o -- o } cx idx 12 cross { ii } - cy o SpanWire :: .create
ii swap put index ;m
m: .print { o -- } ." Sp4HR(" o .getXY swap . . ." , " o index . ." ) " ;m
m: .walk { xt o -- } 6 -5 do xt o o .getXY swap i + swap CramCell .create .routes loop ;m
Sp4VB implementation SpanWire extension
0 11 field index
m: .create { cx cy idx o -- o } cx cy idx 12 cross { ii } + o SpanWire :: .create
ii swap put index ;m
m: .print { o -- } ." Sp4VB(" o .getXY swap . . ." , " o index . ." ) " ;m
m: .walk { xt o -- } 6 -5 do xt o o .getXY i + CramCell .create .routes loop ;m
Sp12HR implementation SpanWire extension
0 1 field index
m: .create { cx cy idx o -- o } cx idx 2 cross { ii } - cy o SpanWire :: .create
ii swap put index ;m
m: .print { o -- } ." Sp12HR(" o .getXY swap . . ." , " o index . ." ) " ;m
m: .walk { xt o -- } 13 -13 do xt o o .getXY swap i + swap CramCell .create .routes loop ;m
Sp12VB implementation SpanWire extension
0 1 field index
m: .create { cx cy idx o -- o } cx cy idx 2 cross { ii } + o SpanWire :: .create
ii swap put index ;m
m: .print { o -- } ." Sp12VB(" o .getXY swap . . ." , " o index . ." ) " ;m
m: .walk { xt o -- } 13 -13 do xt o o .getXY i + CramCell .create .routes loop ;m
IOPin implementation CramCell extension
0 1 field pinIndex
m: .create { x y pin o -- o } x y o CramCell :: .create pin swap put pinIndex ;m
m: .print { o -- } ." IOPin(" o .getXY swap . . o pinIndex . ." ) " ;m
m: .inside { x y o -- wire } o .getXY nip 0= if 15 y - to y then x y o CramCell :: .inside ;m
: nopullup { o -- }
o pinIndex 0= if
-1 37 7 o .inside .setBit
else
-1 37 12 o .inside .setBit
then
;
m: .makeInput { o -- }
1 o .setPinType
-1 o .enableBit .setBit
-1 o .inputEnableBit .setBit
o nopullup
(
o .getXY o pinIndex 0 IOInput .create
)
o .getXY o pinIndex 2* Output .create
;m
m: .makeOutput { o -- output }
25 o .setPinType
-1 o .enableBit .setBit
0 o .inputEnableBit .setBit
o nopullup
o .getXY o pinIndex IOOutput0 .create
;m
m: .getPinType { o -- n } 0 0 ['] getBits o .listBits drop ;m
m: .setPinType { n o -- } n ['] setBits o .listBits drop ;m
m: .listBits { x o -- ... }
5 2 o pinIndex 11 * + o .inside x execute
4 2 o pinIndex 11 * + o .inside x execute
5 0 o pinIndex 11 * + o .inside x execute
4 0 o pinIndex 11 * + o .inside x execute
4 4 o pinIndex 11 * + o .inside x execute
5 4 o pinIndex 11 * + o .inside x execute
;m
m: .enableBit { o -- bit } o pinIndex if 27 1 else 26 7 then o .inside ;m
m: .inputEnableBit { o -- bit } 27 9 o pinIndex 1 xor 2* - o .inside ;m
: ioOptWire { i o parity -- wire }
o .getXY i 2* parity xor i 4 >= if 1 xor then IOLocalG .create
;
IOInput implementation SpanWire extension
0 1 field pinIndex
0 1 field dinNum
m: .create { x y pin din o -- o } x y o CramCell :: .create pin swap put pinIndex din swap put dinNum ;m
m: .print { o -- } ." IOInput(" o .getXY swap . . o pinIndex . o dinNum . ." ) " ;m
m: .optionCount { o -- n } 0 ;m
m: .getOption { o -- n } 0 ;m
IOOutput implementation CramCell extension
0 1 field pinIndex
m: .create { x y pin o -- o } x y o CramCell :: .create pin swap put pinIndex ;m
m: .print { o -- } ." IOOutput(" o .getXY swap . . o pinIndex . ." ) " ;m
m: .inside { x y o -- wire } x y o .isBottom? if 15 swap - then o CramCell :: .inside ;m
m: .getPath { o -- n } 0 0 ['] getBits o .listBits drop ;m
m: .setPath { n o -- } n ['] setBits o .listBits drop ;m
m: .optionCount { o -- n } 9 ;m
m: .getOption { o -- n } o .enableBit .getBit if
o .getPath 1+
else
0
then ;m
m: .setOption { n o -- } n if
-1 o .enableBit .setBit
n 1- o .setPath
else
0 o .enableBit .setBit
0 o .setPath ( for good measure )
then ;m
m: .optionWire { i o -- wire }
i 0= if NotConnected .create exit then
i 1- o o pinIndex o .getParity xor ioOptWire ;m
IOOutput0 implementation IOOutput extension
m: .print { o -- } ." IOOutput0(" o .getXY swap . . o pinIndex . ." ) " ;m
m: .enableBit { o -- wire } 35 5 o pinIndex 5 * + o .inside ;m
m: .listBits { x o -- ... }
34 5 o pinIndex 5 * + o .inside x execute
35 4 o pinIndex 7 * + o .inside x execute
34 4 o pinIndex 7 * + o .inside x execute
;m
m: .getParity { o -- n } 0 ;m
IOOutput1 implementation IOOutput extension
m: .print { o -- } ." IOOutput1(" o .getXY swap . . o pinIndex . ." ) " ;m
m: .enableBit { o -- wire } 33 9 o .inside ;m
m: .listBits { x o -- ... }
32 9 o pinIndex 5 * + o .inside x execute
32 8 o pinIndex 7 * + o .inside x execute
33 8 o pinIndex 7 * + o .inside x execute
;m
m: .getParity { o -- n } 1 ;m
IOEnable implementation IOOutput extension
m: .print { o -- } ." IOEnable(" o .getXY swap . . o pinIndex . ." ) " ;m
m: .enableBit { o -- wire } 33 5 o .inside ;m
m: .listBits { x o -- ... }
32 5 o pinIndex 5 * + o .inside x execute
32 4 o pinIndex 7 * + o .inside x execute
33 4 o pinIndex 7 * + o .inside x execute
;m
m: .getParity { o -- n } 1 ;m
IOFabOutput implementation IOOutput extension
m: .create { x y o -- o } x y 0 o IOOutput :: .create ;m
m: .print { o -- } ." IOFabOutput(" o .getXY swap . . ." ) " ;m
m: .enableBit { o -- wire } 37 4 o .inside ;m
m: .listBits { x o -- ... }
37 5 o .inside x execute
36 4 o .inside x execute
36 5 o .inside x execute
;m
m: .getParity { o -- n } 0 ;m
LUTFFGlobal implementation CramCell extension
m: .print { o -- } ." LUTFFGlobal(" o .getXY swap . . ." ) " ;m
m: .getPath { o -- n } 0 0 ['] getBits o .listBits drop ;m
m: .setPath { n o -- } n ['] setBits o .listBits drop ;m
m: .optionCount { o -- n } 9 ;m
m: .getOption { o -- n } o .enableBit .getBit if
o .getPath 1+
else
0
then ;m
m: .setOption { n o -- } n if
-1 o .enableBit .setBit
n 1- o .setPath
else
0 o .enableBit .setBit
0 o .setPath ( for good measure )
then ;m
m: .optionWire { i o -- wire }
o .getXY i case
( TODO: glb_netwk )
5 of 0 o .getRow + LocalG .create exit endof
6 of 9 o .getRow + LocalG .create exit endof
7 of 16 o .getRow + LocalG .create exit endof
8 of 25 o .getRow + LocalG .create exit endof
endcase 2drop NotConnected .create ;m
FFSetReset implementation LUTFFGlobal extension
m: .print { o -- } ." FFSetReset(" o .getXY swap . . ." ) " ;m
m: .enableBit { o -- wire } 1 14 o .inside ;m
m: .listBits { x o -- ... }
0 15 o .inside x execute
0 14 o .inside x execute
1 15 o .inside x execute
;m
m: .getRow ( -- n ) 4 ;m
m: .optionWire { i o -- wire }
i 0 > i 5 < and if i 1- 2* GlobalNetwork .create exit then
i o LUTFFGlobal :: .optionWire ;m
FFEnable implementation LUTFFGlobal extension
m: .print { o -- } ." FFEnable(" o .getXY swap . . ." ) " ;m
m: .enableBit { o -- wire } 1 4 o .inside ;m
m: .listBits { x o -- ... }
0 5 o .inside x execute
0 4 o .inside x execute
1 5 o .inside x execute
;m
m: .getRow ( -- n ) 2 ;m
m: .optionWire { i o -- wire }
i 0 > i 5 < and if i 1- 2* 1+ GlobalNetwork .create exit then
i o LUTFFGlobal :: .optionWire ;m
FFClock implementation LUTFFGlobal extension
m: .print { o -- } ." FFClock(" o .getXY swap . . ." ) " ;m
m: .enableBit { o -- wire } 2 2 o .inside ;m
m: .optionCount { o -- n } 13 ;m
m: .listBits { x o -- ... }
0 3 o .inside x execute
1 2 o .inside x execute
0 2 o .inside x execute
2 1 o .inside x execute
;m
m: .optionWire { i o -- wire }
i 0 > i 9 < and if i 1- GlobalNetwork .create exit then
i 4 - o LUTFFGlobal :: .optionWire ;m
GlobalNetwork implementation
0 7 field index
m: .create { i o -- o } i o put index ;m
m: .print { o -- } ." GlobalNetwork(" o index . ." ) " ;m
m: .getOption { o -- n } 0 ;m
m: .optionCount { o -- n } 0 ;m
NotConnected implementation
m: .create ( o -- o ) ;m
m: .print { o -- } ." NotConnected" ;m
m: .getOption { o -- n } 0 ;m
m: .optionCount { o -- n } 0 ;m
: pin ( n -- o ) pin#s IOPin .create ;
: route { src dst -- f }
\ ." SRC: " src .print ." DST: " dst .print cr
src dst = if -1 exit then
dst .getOption { p }
p if src p dst .optionWire recurse exit then
dst .optionCount { n }
n 0 ?do
i dst .setOption
src i dst .optionWire recurse if -1 unloop exit then
0 dst .setOption
loop 0
;
: route. { src dst }
." ROUTE: "
begin src dst <> while
dst .print ." <- "
dst .getOption dst .optionWire to dst
repeat
src .print cr
;
previous
forth definitions

View File

@ -0,0 +1,203 @@
\ Copyright 2025 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
needs ice40_config.fs
ice40 definitions
vocabulary storage storage definitions
: ew! ( n a -- ) over 8 rshift over c! 1+ c! ;
: w, ( n -- ) here ew! 2 allot ;
create header1
$ff c, $00 c, $00 c, $ff c, ( Init blop, maybe not needed? )
$7e c, $aa c, $99 c, $7e c, ( Preamble )
$51 c, $00 c, ( Frequency 0=low, 1=medium, 2=high )
$01 c, $05 c, ( Reset CRC crc = 0xffff )
here header1 - constant header1#
create header2
$92 c, $0020 w, ( Set flags bit0=nosleep bit5=warmboot )
$62 c, cram-bank-width 1- w, ( Set bank width )
$82 c, $0000 w, ( Set bank offset to 0 )
here header2 - constant header2#
0 value bank-id
0 value bank-height
create bank-header
$72 c, here to bank-height 0 w, ( Set bank height, 5k only, upper vs lower )
$11 c, here to bank-id 0 c, ( Set bank )
$01 c, $01 c, ( Prefix )
here bank-header - constant bank-header#
create bank-footer
$00 c, $00 c, ( Suffix )
here bank-footer - constant bank-footer#
create header3
$72 c, bram-chunk-size w, ( Setting bank height? )
here header3 - constant header3#
0 value bram-bank
create bram-header1
$11 c, here to bram-bank 0 c, ( Setting bank )
here bram-header1 - constant bram-header1#
0 value bram-offset
0 value bram-bank-width
create bram-header2
$82 c, here to bram-offset 0 w, ( Setting bank offset )
$62 c, here to bram-bank-width 0 w, ( Setting bank width )
here bram-header2 - constant bram-header2#
0 value footer-crc
create footer
$22 c, here to footer-crc $0000 w, ( CRC )
$01 c, $06 c, ( Wakeup )
$00 c, ( Padding )
here footer - constant footer#
variable crc16
: +crc16 ( ch -- )
7 for
dup i rshift 1 and ( peel off one bit )
crc16 uw@ 15 rshift xor if $1021 else 0 then
crc16 uw@ 2* xor crc16 w!
next
drop
;
: *crc16 ( a n -- ) for aft dup c@ +crc16 1+ then next drop ;
: crc-write ( a n output -- ) >r 2dup *crc16 r> execute ;
: cram-write { output -- }
( write header )
header1 header1# output execute
$ffff crc16 w!
header2 header2# output crc-write
( Bank 0 )
0 bank-id c!
cram-height-lower bank-height ew!
bank-header bank-header# output crc-write
cram cram-bank-width cram-height-lower * 8 / output crc-write
bank-footer bank-footer# output crc-write
( Bank 1 )
1 bank-id c!
cram-height-upper bank-height ew!
bank-header bank-header# output crc-write
cram cram-bank-width cram-height-lower * 8 / +
cram-bank-width cram-height-upper * 8 / output crc-write
bank-footer bank-footer# output crc-write
( Bank 2 )
2 bank-id c!
cram-height-lower bank-height ew!
bank-header bank-header# output crc-write
cram cram-bank-width cram-height-lower cram-height-upper + * 8 / +
cram-bank-width cram-height-lower * 8 / output crc-write
bank-footer bank-footer# output crc-write
( Bank 3 )
3 bank-id c!
cram-height-upper bank-height ew!
bank-header bank-header# output crc-write
cram cram-bank-width cram-height-lower 2* cram-height-upper + * 8 / +
cram-bank-width cram-height-upper * 8 / output crc-write
bank-footer bank-footer# output crc-write
( BRAM Header )
header3 header3# output crc-write
( BRAM Bank 0 )
0 bram-bank c!
bram-header1 bram-header1# output crc-write
bram-chunk-size 0 * bram-offset ew!
bram-width-lower 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
bram-chunk-size 1 * bram-offset ew!
bram-width-lower 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
( BRAM Bank 1 )
1 bram-bank c!
bram-header1 bram-header1# output crc-write
bram-chunk-size 0 * bram-offset ew!
bram-width-upper 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
bram-chunk-size 1 * bram-offset ew!
bram-width-upper 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
( BRAM Bank 2 )
2 bram-bank c!
bram-header1 bram-header1# output crc-write
bram-chunk-size 0 * bram-offset ew!
bram-width-lower 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
bram-chunk-size 1 * bram-offset ew!
bram-width-lower 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
( BRAM Bank 3 )
3 bram-bank c!
bram-header1 bram-header1# output crc-write
bram-chunk-size 0 * bram-offset ew!
bram-width-upper 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
bram-chunk-size 1 * bram-offset ew!
bram-width-upper 1- bram-bank-width ew!
bram-header2 bram-header2# output crc-write
( write footer )
$22 +crc16 ( Add first byte of footer )
crc16 uw@ footer-crc ew! ( Update CRC )
footer footer# output crc-write
;
0 value save-fh
: save-write ( a n -- ) save-fh write-file throw ;
ice40 definitions also storage
: save ( a n -- )
w/o bin create-file throw to save-fh
['] save-write cram-write
save-fh close-file throw
;
DEFINED? ice [IF]
also ice
DEFINED? ice_cram_open [IF]
: deploy
ice_cram_open
['] ice_cram_write cram-write
ice_cram_close
;
[THEN]
previous
[THEN]
previous
forth definitions

View File

@ -0,0 +1,104 @@
\ Copyright 2025 Bradley D. Nelson
\
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\
\ http://www.apache.org/licenses/LICENSE-2.0
\
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.
needs ice40_layout.fs
needs ice40_allocation.fs
ice40 synthesis definitions
39 constant LED_G
40 constant LED_B
41 constant LED_R
: XOR1 ( a b -- o ) NotConnected NotConnected $6666 LUT4 ;
: OR1 ( a b -- o ) NotConnected NotConnected $eeee LUT4 ;
: AND1 ( a b -- o ) NotConnected NotConnected $8888 LUT4 ;
: INVERT1 ( a b -- o ) NotConnected NotConnected NotConnected $5555 LUT4 ;
: BUFFER1 ( a b -- o ) NotConnected NotConnected NotConnected $aaaa LUT4 ;
: HA ( x y -- so co ) 2dup AND1 >r XOR1 r> ;
: FA ( x y z -- so co ) HA >r HA r> OR1 ;
: BUS ( v bus -- bus ) here >r , , r> ;
: UNBUS ( bus -- v bus ) dup cell+ @ swap @ ;
: nBUS ( v* n -- bus ) 0 swap 0 ?do BUS loop ;
: nUNBUS ( bus -- v* ) begin dup while UNBUS repeat drop ;
: NEARBY { wire -- wire } wire .getXY dup 2 < if 1+ else 1- then
2dup CramCell .create .isRam? if >r 1+ r> then
PLACE wire ;
: IN1PIN ( n -- bus ) pin .makeInput NEARBY BUFFER1 ;
: OUT1PIN! ( wire n -- ) pin .makeOutput NEARBY >r BUFFER1 r> route! ;
: INPINS ( p1..pn n -- wire ) 0 swap 0 ?do swap IN1PIN swap BUS loop ;
: OUTPINS! ( bus p1..pn n -- )
nBUS begin dup while
UNBUS >r swap UNBUS >r swap OUT1PIN! r> r>
repeat 2drop ;
: REVERSE ( a -- a' )
0 swap begin dup while
UNBUS >r swap BUS r>
repeat drop
;
: INVERT ( a -- a' )
dup 0= if exit then
UNBUS >r INVERT1 r> recurse BUS
;
: BUFFER ( a -- a' )
dup 0= if exit then
UNBUS >r BUFFER1 r> recurse BUS
;
: AND ( a b -- c )
dup 0= if nip exit then
UNBUS >r >r UNBUS r> swap >r AND1
r> r> recurse BUS
;
: OR ( a b -- c )
dup 0= if nip exit then
UNBUS >r >r UNBUS r> swap >r OR1
r> r> recurse BUS
;
: XOR ( a b -- c )
dup 0= if nip exit then
UNBUS >r >r UNBUS r> swap >r XOR1
r> r> recurse BUS
;
: +c ( a b ci -- c )
>r dup 0= if 2drop r> 0 BUS exit then r>
-rot
UNBUS >r >r UNBUS r> swap >r FA
r> r> rot recurse BUS
;
: + ( a b -- c ) NotConnected +c ;
: REGISTER ( n -- bus )
0 swap 0 ?do FFL swap BUS loop REVERSE ;
: REG! ( v a -- )
dup 0= if 2drop exit then
UNBUS >r >r UNBUS r> swap >r FF!
r> r> recurse
;
forth definitions

View File

@ -29,9 +29,9 @@
#include "common/calls.h" #include "common/calls.h"
#ifndef UEFORTH_SIM #ifndef UEFORTH_SIM
# define HEAP_SIZE (100 * 1024) # define HEAP_SIZE (150 * 1024)
#else #else
# define HEAP_SIZE (200 * 1024) # define HEAP_SIZE (300 * 1024)
#endif #endif
#define STACK_CELLS (4 * 1024) #define STACK_CELLS (4 * 1024)

View File

@ -18,5 +18,6 @@ needs ../common/phase2.fs
needs ../common/tasks.fs needs ../common/tasks.fs
needs ../common/streams.fs needs ../common/streams.fs
needs platform.fs needs platform.fs
needs ice40/ice40.fs
needs autoboot.fs needs autoboot.fs
needs ../common/fini.fs needs ../common/fini.fs

View File

@ -101,30 +101,6 @@ if PICO_ICE_ENABLED:
])), ])),
] ]
if WINDOWS_ENABLED:
Alias('publish-win32', ' '.join([
Publish('publish-win32-rev',
'$dst/windows/uEf32.exe',
'uEf32-{{VERSION}}-{{REVSHORT}}.exe'),
Publish('publish-win32-ver',
'$dst/windows/uEf32.exe',
'uEf32-{{VERSION}}.exe'),
]))
PUBLISH_PARTS += [
Alias('publish-win64', ' '.join([
Publish('publish-win64-rev',
'$dst/windows/uEf64.exe',
'uEf64-{{VERSION}}-{{REVSHORT}}.exe'),
Publish('publish-win64-ver',
'$dst/windows/uEf64.exe',
'uEf64-{{VERSION}}.exe'),
])),
]
Alias('publish-win', ' '.join([
'publish-win32',
'publish-win64',
]))
Alias('publish', ' '.join([ Alias('publish', ' '.join([
Command('publish-index', ' '.join([ Command('publish-index', ' '.join([
'$src/tools/webindex.py', '$src/tools/webindex.py',

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
runtime: go121 runtime: go123
default_expiration: "1m" default_expiration: "1m"

View File

@ -25,46 +25,27 @@ limitations under the License.
#include "menu.html" #include "menu.html"
<div class="wrapper"> <div class="wrapper">
<h2>Download</h2> <h2>Building</h2>
<h3>STABLE RELEASE</h3>
<p> <p>
<a href="https://eforth.storage.googleapis.com/releases/uEf64-{{STABLE_VERSION}}.exe">uEf64-{{STABLE_VERSION}}.exe</a> Unfortunately, Window Defender's heuristics falsely identify uEforth.exe builds as malware.
- Window 64-bit EXE µEforth<br/> And recently Google Cloud also started identifying them as abuse.
<i>Version: {{STABLE_VERSION}} (Stable)</i> As a result, I've removed hosting of them.
</p> </p>
<h3>LONG TERM STABLE RELEASE</h3>
<p> <p>
<a href="https://eforth.storage.googleapis.com/releases/uEf64-{{OLD_STABLE_VERSION}}.exe">uEf64-{{OLD_STABLE_VERSION}}.exe</a> Instead build as follows (using the Windows Subsystem for Linux):
- Window 64-bit EXE µEforth<br/> <pre>
<i>Version: {{OLD_STABLE_VERSION}} (Long-term Stable)</i> sudo apt install ninja-build
</p> git clone https://github.com/flagxor/ueforth
cd ueforth
<h3>Beta Release</h3> ./configure.py
<p> ninja
<a href="https://eforth.storage.googleapis.com/releases/uEf64-{{VERSION}}.exe">uEf64-{{VERSION}}.exe</a> </pre>
- Window 64-bit EXE µEforth<br/>
<i>Version: {{VERSION}} (Beta)</i>
</p> </p>
<hr/> <hr/>
<p>
<b>NOTE: 32-bit builds are no longer published. They can still be built from source.
</p>
<hr/>
<p>
<a href="https://eforth.storage.googleapis.com/releases/archive.html" target="_blank">Release Archive</a>
- Prior Releases
</p>
<p>
<a href="https://github.com/flagxor/ueforth" target="_blank">http://github.com/flagxor/ueforth</a>
- Complete Unprocessed Source Code
</p>
<h2>µEforth</h2> <h2>µEforth</h2>
<h3>µEforth Words</h3> <h3>µEforth Words</h3>

View File

@ -65,7 +65,7 @@ int main(int argc, char *argv[]) {
#undef Z #undef Z
} else if (argc == 2 && strcmp(argv[1], "dict") == 0) { } else if (argc == 2 && strcmp(argv[1], "dict") == 0) {
#define V(name) \ #define V(name) \
printf(" Create(\"" #name "-builtins\", %d);\n", BUILTIN_FORK, OP_DOCREATE); \ printf(" Create(\"" #name "-builtins\", %d);\n", BUILTIN_FORK); \
printf(" COMMA(%d);\n", VOC_ ## name); printf(" COMMA(%d);\n", VOC_ ## name);
VOCABULARY_LIST VOCABULARY_LIST
#undef V #undef V
@ -84,7 +84,7 @@ int main(int argc, char *argv[]) {
#define G_SYS 256 #define G_SYS 256
printf(" const g_sys = %d;\n", G_SYS); printf(" const g_sys = %d;\n", G_SYS);
#define EMITSYS(name) \ #define EMITSYS(name) \
printf(" const g_sys_%s = %d;\n", #name, 256 + 4 * (((cell_t *) &g_sys->name) - (cell_t*) g_sys)); printf(" const g_sys_%s = %d;\n", #name, 256 + 4 * (int) (((cell_t *) &g_sys->name) - (cell_t*) g_sys));
EMITSYS(heap); EMITSYS(heap);
EMITSYS(current); EMITSYS(current);
EMITSYS(context); EMITSYS(context);