https://github.com/liatemplates/logicemu
Integrate the graphical logicemu-project into LiaScript
https://github.com/liatemplates/logicemu
emulator liascript logic template
Last synced: about 1 month ago
JSON representation
Integrate the graphical logicemu-project into LiaScript
- Host: GitHub
- URL: https://github.com/liatemplates/logicemu
- Owner: LiaTemplates
- Created: 2019-01-15T08:36:20.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2019-11-24T22:42:37.000Z (over 6 years ago)
- Last Synced: 2025-03-17T06:15:08.476Z (12 months ago)
- Topics: emulator, liascript, logic, template
- Homepage: https://liascript.github.io/course/?https://raw.githubusercontent.com/liaTemplates/LogicEmu/master/README.md
- Size: 172 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# LogicEmu - Template
--{{0}}--
This document makes the logic emulator [logicemu](https://lodev.org/logicemu/)
available in [LiaScript](https://LiaScript.github.io). You can either run
simulations directly or associate them with an code-block, to make them editable
and executable.
__Try it on LiaScript:__
https://liascript.github.io/course/?https://raw.githubusercontent.com/liaTemplates/logicemu/master/README.md
__See the project on Github:__
https://github.com/liaTemplates/logicemu
--{{1}}--
There are three ways to use this template. The easiest way is to use the
`import` statement and the url of the raw text-file of the master branch or any
other branch or version. But you can also copy the required functionionality
directly into the header of your Markdown document, see therefor the [last
slide](#4 "Implementation"). And of course, you could also clone this project
and change it, as you wish.
{{1}}
1. Load the macros via
`import: https://raw.githubusercontent.com/liaTemplates/logicemu/master/README.md`
2. Copy the definitions into your Project
3. Clone this repository on GitHub
## `@LogicEmu.run`
{{0}}
Use the following block-macro, if you only want to execute a certain simulation.
That means, use the common Markdown code-block notation, add a syntax
highlighting and the macro `@LogicEmu.run` to the head of this block. That's it
the simulation will be automatically executed in LiaScript.
``` text @LogicEmu.run
s******>a****>l
^
s******
s**>a**>o**>l
> ^
s**>e**>a
>
s******>e**>l
```
## `@LogicEmu.eval`
{{0}}
If you want to allow editing, then simply attach the macro `@LogicEmu.eval` to
the end of your code-block.
```
"128" "64" "32" "16" "8" "4" "2" "1"
l l l l l l l l
^ ^ ^ ^ ^ ^ ^ ^
"carry"l
let code = window.encodeBoard(`@input`);
let iframe = document.getElementById("logic_emu@0");
iframe.contentWindow.location.reload(true);
iframe.contentWindow.location.replace("https://liatemplates.github.io/LogicEmu/docs/index.html#code="+code);
"LIA: stop";
@end
@LogicEmu.run: @LogicEmu._run_(@uid,```@0```)
@LogicEmu._run_
let code = window.encodeBoard(`@1`);
let iframe = document.getElementById("logic_emu@0");
iframe.contentWindow.location.reload(true);
iframe.contentWindow.location.replace("https://liatemplates.github.io/LogicEmu/docs/index.html#code="+code);
@end
@onload
function LZ77Coder() {
this.lz77MatchLen = function(text, i0, i1) {
var l = 0;
while(i1 + l < text.length && text[i1 + l] == text[i0 + l] && l < 255) {
l++;
}
return l;
};
this.encodeString = function(text) {
return arrayToString(this.encode(stringToArray(text)));
};
this.decodeString = function(text) {
return arrayToString(this.decode(stringToArray(text)));
};
// Designed mainly for 7-bit ASCII text. Although the text array may contain values
// above 127 (e.g. unicode codepoints), only values 0-127 are encoded efficiently.
this.encode = function(text) {
var result = [];
var map = {};
var encodeVarint = function(i, arr) {
if(i < 128) {
arr.push(i);
} else if(i < 16384) {
arr.push(128 | (i & 127));
arr.push(i >> 7);
} else {
arr.push(128 | (i & 127));
arr.push(128 | ((i >> 7) & 127));
arr.push((i >> 14) & 127);
}
};
for(var i = 0; i < text.length; i++) {
var len = 0;
var dist = 0;
var sub = arrayToStringPart(text, i, 4);
var s = map[sub];
if(s) {
for(var j = s.length - 1; j >= 0; j--) {
var i2 = s[j];
var d = i - i2;
if(d > 2097151) break;
var l = this.lz77MatchLen(text, i2, i);
if(l > len) {
len = l;
dist = d;
if(l > 255) break; // good enough, stop search
}
}
}
if(len > 2097151) len = 2097151;
if(!(len > 5 || (len > 4 && dist < 16383) || (len > 3 && dist < 127))) {
len = 1;
}
for(var j = 0; j < len; j++) {
var sub = arrayToStringPart(text, i + j, 4);
if(!map[sub]) map[sub] = [];
if(map[sub].length > 1000) map[sub] = []; // prune
map[sub].push(i + j);
}
i += len - 1;
if(len >= 3) {
if(len < 130) {
result.push(128 + len - 3);
} else {
var len2 = len - 128;
result.push(255);
encodeVarint(len2, result);
}
encodeVarint(dist, result);
} else {
var c = text[i];
if(c < 128) {
result.push(c);
} else {
// Above-ascii character, encoded as unicode codepoint (not UTF-16).
// Normally such character does not appear in circuits, but it could in comments.
result.push(255);
encodeVarint(c - 128, result);
result.push(0);
}
}
}
return result;
};
this.decode = function(encoded) {
var result = [];
var temp;
for(var i = 0; i < encoded.length;) {
var c = encoded[i++];
if(c > 127) {
var len = c + 3 - 128;
if(c == 255) {
len = encoded[i++];
if(len > 127) len += (encoded[i++] << 7) - 128;
if(len > 16383) len += (encoded[i++] << 14) - 16384;
len += 128;
}
dist = encoded[i++];
if(dist > 127) dist += (encoded[i++] << 7) - 128;
if(dist > 16383) dist += (encoded[i++] << 14) - 16384;
if(dist == 0) {
result.push(len);
} else {
for(var j = 0; j < len; j++) {
result.push(result[result.length - dist]);
}
}
} else {
result.push(c);
}
}
return result;
};
}
function arrayToString(a) {
var s = '';
for(var i = 0; i < a.length; i++) {
//s += String.fromCharCode(a[i]);
var c = a[i];
if (c < 0x10000) {
s += String.fromCharCode(c);
} else if (c <= 0x10FFFF) {
s += String.fromCharCode((c >> 10) + 0xD7C0);
s += String.fromCharCode((c & 0x3FF) + 0xDC00);
} else {
s += ' ';
}
}
return s;
}
function stringToArray(s) {
var a = [];
for(var i = 0; i < s.length; i++) {
//a.push(s.charCodeAt(i));
var c = s.charCodeAt(i);
if (c >= 0xD800 && c <= 0xDBFF && i + 1 < s.length) {
var c2 = s.charCodeAt(i + 1);
if (c2 >= 0xDC00 && c2 <= 0xDFFF) {
c = (c << 10) + c2 - 0x35FDC00;
i++;
}
}
a.push(c);
}
return a;
}
// ignores the utf-32 unlike arrayToString but that's ok for now
function arrayToStringPart(a, pos, len) {
var s = '';
for(var i = pos; i < pos + len; i++) {
s += String.fromCharCode(a[i]);
}
return s;
}
function RangeCoder() {
this.base = 256;
this.high = 1 << 24;
this.low = 1 << 16;
this.num = 256;
this.values = [];
this.inc = 8;
this.reset = function() {
this.values = [];
for(var i = 0; i <= this.num; i++) {
this.values.push(i);
}
};
this.floordiv = function(a, b) {
return Math.floor(a / b);
};
// Javascript numbers are doubles with 53 bits of integer precision so can
// represent unsigned 32-bit ints, but logic operators like & and >> behave as
// if on 32-bit signed integers (31-bit unsigned). Mask32 makes the result
// positive again. Use e.g. after multiply to simulate unsigned 32-bit overflow.
this.mask32 = function(a) {
return ((a >> 1) & 0x7fffffff) * 2 + (a & 1);
};
this.update = function(symbol) {
// too large denominator
if(this.getTotal() + this.inc >= this.low) {
var last = this.values[0];
for(var i = 0; i < this.num; i++) {
var d = this.values[i + 1] - last;
d = (d > 1) ? this.floordiv(d, 2) : d;
last = this.values[i + 1];
this.values[i + 1] = this.values[i] + d;
}
}
for(var i = symbol + 1; i < this.values.length; i++) {
this.values[i] += this.inc;
}
};
this.getProbability = function(symbol) {
return [this.values[symbol], this.values[symbol + 1]];
};
this.getSymbol = function(scaled_value) {
var symbol = this.binSearch(this.values, scaled_value);
var p = this.getProbability(symbol);
p.push(symbol);
return p;
};
this.getTotal = function() {
return this.values[this.values.length - 1];
};
// returns last index in values that contains entry that is <= value
this.binSearch = function(values, value) {
var high = values.length - 1, low = 0, result = 0;
if(value > values[high]) return high;
while(low <= high) {
var mid = this.floordiv(low + high, 2);
if(values[mid] >= value) {
result = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
if(result > 0 && values[result] > value) result--;
return result;
};
this.encodeString = function(text) {
return arrayToString(this.encode(stringToArray(text)));
};
this.decodeString = function(text) {
return arrayToString(this.decode(stringToArray(text)));
};
this.encode = function(data) {
this.reset();
var result = [1];
var low = 0;
var range = 0xffffffff;
result.push(data.length & 255);
result.push((data.length >> 8) & 255);
result.push((data.length >> 16) & 255);
result.push((data.length >> 24) & 255);
for(var i = 0; i < data.length; i++) {
var c = data[i];
var p = this.getProbability(c);
var total = this.getTotal();
var start = p[0];
var size = p[1] - p[0];
this.update(c);
range = this.floordiv(range, total);
low = this.mask32(start * range + low);
range = this.mask32(range * size);
for(;;) {
if(low == 0 && range == 0) {
return null; // something went wrong, avoid hanging
}
if(this.mask32(low ^ (low + range)) >= this.high) {
if(range >= this.low) break;
range = this.mask32((-low) & (this.low - 1));
}
result.push((this.floordiv(low, this.high)) & (this.base - 1));
range = this.mask32(range * this.base);
low = this.mask32(low * this.base);
}
}
for(var i = this.high; i > 0; i = this.floordiv(i, this.base)) {
result.push(this.floordiv(low, this.high) & (this.base - 1));
low = this.mask32(low * this.base);
}
if(result.length > data.length) {
result = [0];
for(var i = 0; i < data.length; i++) result[i + 1] = data[i];
}
return result;
};
this.decode = function(data) {
if(data.length < 1) return null;
var result = [];
if(data[0] == 0) {
for(var i = 1; i < data.length; i++) result[i - 1] = data[i];
return result;
}
if(data[0] != 1) return null;
if(data.length < 5) return null;
this.reset();
var code = 0;
var low = 0;
var range = 0xffffffff;
var pos = 1;
var symbolsize = data[pos++];
symbolsize |= (data[pos++] << 8);
symbolsize |= (data[pos++] << 16);
symbolsize |= (data[pos++] << 24);
symbolsize = this.mask32(symbolsize);
for(var i = this.high; i > 0; i = this.floordiv(i, this.base)) {
var d = pos >= data.length ? 0 : data[pos++];
code = this.mask32(code * this.base + d);
}
for(var i = 0; i < symbolsize; i++) {
var total = this.getTotal();
var scaled_value = this.floordiv(code - low, (this.floordiv(range, total)));
var p = this.getSymbol(scaled_value);
var c = p[2];
result.push(c);
var start = p[0];
var size = p[1] - p[0];
this.update(c);
range = this.floordiv(range, total);
low = this.mask32(start * range + low);
range = this.mask32(range * size);
for(;;) {
if(low == 0 && range == 0) {
return null; // something went wrong, avoid hanging
}
if(this.mask32(low ^ (low + range)) >= this.high) {
if(range >= this.low) break;
range = this.mask32((-low) & (this.low - 1));
}
var d = pos >= data.length ? 0 : data[pos++];
code = this.mask32(code * this.base + d);
range = this.mask32(range * this.base);
low = this.mask32(low * this.base);
}
}
return result;
};
}
window.encodeBoard = function(text) {
var lz77 = (new LZ77Coder()).encodeString(text);
var range = (new RangeCoder()).encodeString(lz77);
return '0' + toBase64(range); // '0' = format version
}
function toBase64(text) {
var result = btoa(text);
result = result.split('=')[0];
result = result.replace(new RegExp('\\+', 'g'), '-');
result = result.replace(new RegExp('/', 'g'), '_');
return result;
}
@end
`````
--{{1}}--
If you want to minimize loading effort in your LiaScript project, you can also
copy this code and paste it into your main comment header, see the code in the
raw file of this document.
{{1}} https://raw.githubusercontent.com/liaTemplates/logicemu/master/README.md