rk86 2.0.13 → 2.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/rk86.js +744 -20
package/package.json
CHANGED
package/rk86.js
CHANGED
|
@@ -1588,12 +1588,696 @@ var init_catalog_data = __esm(() => {
|
|
|
1588
1588
|
];
|
|
1589
1589
|
});
|
|
1590
1590
|
|
|
1591
|
+
// node_modules/asm8080/dist/asm8.js
|
|
1592
|
+
class AsmError extends Error {
|
|
1593
|
+
line;
|
|
1594
|
+
column;
|
|
1595
|
+
source;
|
|
1596
|
+
constructor(message, line, source, column = 1) {
|
|
1597
|
+
super(message);
|
|
1598
|
+
this.name = "AsmError";
|
|
1599
|
+
this.line = line;
|
|
1600
|
+
this.source = source;
|
|
1601
|
+
this.column = column;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
function firstNonSpaceCol(s) {
|
|
1605
|
+
const m = s.match(/\S/);
|
|
1606
|
+
return m ? (m.index ?? 0) + 1 : 1;
|
|
1607
|
+
}
|
|
1608
|
+
var REG8 = {
|
|
1609
|
+
B: 0,
|
|
1610
|
+
C: 1,
|
|
1611
|
+
D: 2,
|
|
1612
|
+
E: 3,
|
|
1613
|
+
H: 4,
|
|
1614
|
+
L: 5,
|
|
1615
|
+
M: 6,
|
|
1616
|
+
A: 7
|
|
1617
|
+
};
|
|
1618
|
+
var REG_PAIR = {
|
|
1619
|
+
B: 0,
|
|
1620
|
+
D: 1,
|
|
1621
|
+
H: 2,
|
|
1622
|
+
SP: 3
|
|
1623
|
+
};
|
|
1624
|
+
var REG_PAIR_PUSH = {
|
|
1625
|
+
B: 0,
|
|
1626
|
+
D: 1,
|
|
1627
|
+
H: 2,
|
|
1628
|
+
PSW: 3
|
|
1629
|
+
};
|
|
1630
|
+
var IMPLIED = {
|
|
1631
|
+
NOP: 0,
|
|
1632
|
+
HLT: 118,
|
|
1633
|
+
RET: 201,
|
|
1634
|
+
XCHG: 235,
|
|
1635
|
+
EI: 251,
|
|
1636
|
+
DI: 243,
|
|
1637
|
+
CMA: 47,
|
|
1638
|
+
STC: 55,
|
|
1639
|
+
CMC: 63,
|
|
1640
|
+
DAA: 39,
|
|
1641
|
+
RLC: 7,
|
|
1642
|
+
RRC: 15,
|
|
1643
|
+
RAL: 23,
|
|
1644
|
+
RAR: 31,
|
|
1645
|
+
PCHL: 233,
|
|
1646
|
+
SPHL: 249,
|
|
1647
|
+
XTHL: 227,
|
|
1648
|
+
RNZ: 192,
|
|
1649
|
+
RZ: 200,
|
|
1650
|
+
RNC: 208,
|
|
1651
|
+
RC: 216,
|
|
1652
|
+
RPO: 224,
|
|
1653
|
+
RPE: 232,
|
|
1654
|
+
RP: 240,
|
|
1655
|
+
RM: 248
|
|
1656
|
+
};
|
|
1657
|
+
var ALU_REG = {
|
|
1658
|
+
ADD: 128,
|
|
1659
|
+
ADC: 136,
|
|
1660
|
+
SUB: 144,
|
|
1661
|
+
SBB: 152,
|
|
1662
|
+
ANA: 160,
|
|
1663
|
+
XRA: 168,
|
|
1664
|
+
ORA: 176,
|
|
1665
|
+
CMP: 184
|
|
1666
|
+
};
|
|
1667
|
+
var ALU_IMM = {
|
|
1668
|
+
ADI: 198,
|
|
1669
|
+
ACI: 206,
|
|
1670
|
+
SUI: 214,
|
|
1671
|
+
SBI: 222,
|
|
1672
|
+
ANI: 230,
|
|
1673
|
+
XRI: 238,
|
|
1674
|
+
ORI: 246,
|
|
1675
|
+
CPI: 254
|
|
1676
|
+
};
|
|
1677
|
+
var ADDR16 = {
|
|
1678
|
+
JMP: 195,
|
|
1679
|
+
JNZ: 194,
|
|
1680
|
+
JZ: 202,
|
|
1681
|
+
JNC: 210,
|
|
1682
|
+
JC: 218,
|
|
1683
|
+
JPO: 226,
|
|
1684
|
+
JPE: 234,
|
|
1685
|
+
JP: 242,
|
|
1686
|
+
JM: 250,
|
|
1687
|
+
CALL: 205,
|
|
1688
|
+
CNZ: 196,
|
|
1689
|
+
CZ: 204,
|
|
1690
|
+
CNC: 212,
|
|
1691
|
+
CC: 220,
|
|
1692
|
+
CPO: 228,
|
|
1693
|
+
CPE: 236,
|
|
1694
|
+
CP: 244,
|
|
1695
|
+
CM: 252,
|
|
1696
|
+
LDA: 58,
|
|
1697
|
+
STA: 50,
|
|
1698
|
+
LHLD: 42,
|
|
1699
|
+
SHLD: 34
|
|
1700
|
+
};
|
|
1701
|
+
var ALL_MNEMONICS = new Set([
|
|
1702
|
+
...Object.keys(IMPLIED),
|
|
1703
|
+
...Object.keys(ALU_REG),
|
|
1704
|
+
...Object.keys(ALU_IMM),
|
|
1705
|
+
...Object.keys(ADDR16),
|
|
1706
|
+
"MOV",
|
|
1707
|
+
"MVI",
|
|
1708
|
+
"INR",
|
|
1709
|
+
"DCR",
|
|
1710
|
+
"LXI",
|
|
1711
|
+
"DAD",
|
|
1712
|
+
"INX",
|
|
1713
|
+
"DCX",
|
|
1714
|
+
"PUSH",
|
|
1715
|
+
"POP",
|
|
1716
|
+
"LDAX",
|
|
1717
|
+
"STAX",
|
|
1718
|
+
"IN",
|
|
1719
|
+
"OUT",
|
|
1720
|
+
"RST",
|
|
1721
|
+
"DB",
|
|
1722
|
+
"DW",
|
|
1723
|
+
"DS",
|
|
1724
|
+
"ORG",
|
|
1725
|
+
"SECTION",
|
|
1726
|
+
"END",
|
|
1727
|
+
"EQU"
|
|
1728
|
+
]);
|
|
1729
|
+
var MAX_STATEMENTS_PER_LINE = 10;
|
|
1730
|
+
function splitStatements(line) {
|
|
1731
|
+
const src = stripComment(line);
|
|
1732
|
+
const out = [];
|
|
1733
|
+
let start = 0;
|
|
1734
|
+
let inQ = false;
|
|
1735
|
+
let qc = "";
|
|
1736
|
+
for (let i = 0;i + 2 < src.length; i++) {
|
|
1737
|
+
const c = src[i];
|
|
1738
|
+
if (inQ) {
|
|
1739
|
+
if (c === qc)
|
|
1740
|
+
inQ = false;
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
if (c === '"' || c === "'") {
|
|
1744
|
+
inQ = true;
|
|
1745
|
+
qc = c;
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
if (c !== " " || src[i + 1] !== "/" || src[i + 2] !== " ")
|
|
1749
|
+
continue;
|
|
1750
|
+
let j = i + 3;
|
|
1751
|
+
while (j < src.length && src[j] === " ")
|
|
1752
|
+
j++;
|
|
1753
|
+
let tokStart = j;
|
|
1754
|
+
if (src[j] === ".")
|
|
1755
|
+
j++;
|
|
1756
|
+
let tokEnd = j;
|
|
1757
|
+
while (tokEnd < src.length && /\w/.test(src[tokEnd]))
|
|
1758
|
+
tokEnd++;
|
|
1759
|
+
if (tokEnd === j)
|
|
1760
|
+
continue;
|
|
1761
|
+
let tok = src.slice(tokStart, tokEnd).toUpperCase();
|
|
1762
|
+
if (tok.startsWith("."))
|
|
1763
|
+
tok = tok.slice(1);
|
|
1764
|
+
if (!ALL_MNEMONICS.has(tok))
|
|
1765
|
+
continue;
|
|
1766
|
+
out.push(src.slice(start, i));
|
|
1767
|
+
start = i + 2;
|
|
1768
|
+
i += 2;
|
|
1769
|
+
}
|
|
1770
|
+
out.push(src.slice(start));
|
|
1771
|
+
if (out.length > MAX_STATEMENTS_PER_LINE) {
|
|
1772
|
+
throw new Error(`too many statements on one line (max ${MAX_STATEMENTS_PER_LINE})`);
|
|
1773
|
+
}
|
|
1774
|
+
return out;
|
|
1775
|
+
}
|
|
1776
|
+
function instrSize(m) {
|
|
1777
|
+
if (m in IMPLIED)
|
|
1778
|
+
return 1;
|
|
1779
|
+
if (m in ALU_REG)
|
|
1780
|
+
return 1;
|
|
1781
|
+
if (m === "MOV" || m === "INR" || m === "DCR")
|
|
1782
|
+
return 1;
|
|
1783
|
+
if (m === "PUSH" || m === "POP")
|
|
1784
|
+
return 1;
|
|
1785
|
+
if (m === "DAD" || m === "INX" || m === "DCX")
|
|
1786
|
+
return 1;
|
|
1787
|
+
if (m === "LDAX" || m === "STAX")
|
|
1788
|
+
return 1;
|
|
1789
|
+
if (m === "RST")
|
|
1790
|
+
return 1;
|
|
1791
|
+
if (m === "MVI")
|
|
1792
|
+
return 2;
|
|
1793
|
+
if (m in ALU_IMM)
|
|
1794
|
+
return 2;
|
|
1795
|
+
if (m === "IN" || m === "OUT")
|
|
1796
|
+
return 2;
|
|
1797
|
+
if (m === "LXI")
|
|
1798
|
+
return 3;
|
|
1799
|
+
if (m in ADDR16)
|
|
1800
|
+
return 3;
|
|
1801
|
+
throw new Error(`unknown mnemonic: ${m}`);
|
|
1802
|
+
}
|
|
1803
|
+
function stripComment(line) {
|
|
1804
|
+
let inQ = false;
|
|
1805
|
+
let qc = "";
|
|
1806
|
+
for (let i = 0;i < line.length; i++) {
|
|
1807
|
+
const c = line[i];
|
|
1808
|
+
if (inQ) {
|
|
1809
|
+
if (c === qc)
|
|
1810
|
+
inQ = false;
|
|
1811
|
+
} else if (c === '"' || c === "'") {
|
|
1812
|
+
inQ = true;
|
|
1813
|
+
qc = c;
|
|
1814
|
+
} else if (c === ";")
|
|
1815
|
+
return line.slice(0, i);
|
|
1816
|
+
}
|
|
1817
|
+
return line;
|
|
1818
|
+
}
|
|
1819
|
+
function splitOperands(s) {
|
|
1820
|
+
const r = [];
|
|
1821
|
+
let current = "";
|
|
1822
|
+
let inQ = false;
|
|
1823
|
+
let qc = "";
|
|
1824
|
+
for (const c of s) {
|
|
1825
|
+
if (inQ) {
|
|
1826
|
+
current += c;
|
|
1827
|
+
if (c === qc)
|
|
1828
|
+
inQ = false;
|
|
1829
|
+
} else if (c === '"' || c === "'") {
|
|
1830
|
+
inQ = true;
|
|
1831
|
+
qc = c;
|
|
1832
|
+
current += c;
|
|
1833
|
+
} else if (c === ",") {
|
|
1834
|
+
r.push(current.trim());
|
|
1835
|
+
current = "";
|
|
1836
|
+
} else
|
|
1837
|
+
current += c;
|
|
1838
|
+
}
|
|
1839
|
+
if (current.trim())
|
|
1840
|
+
r.push(current.trim());
|
|
1841
|
+
return r;
|
|
1842
|
+
}
|
|
1843
|
+
var DIRECTIVES = new Set(["ORG", "SECTION", "END", "DB", "DW", "DS", "EQU"]);
|
|
1844
|
+
function stripDirectiveDot(s) {
|
|
1845
|
+
if (s.startsWith(".") && DIRECTIVES.has(s.slice(1).toUpperCase())) {
|
|
1846
|
+
return s.slice(1);
|
|
1847
|
+
}
|
|
1848
|
+
return s;
|
|
1849
|
+
}
|
|
1850
|
+
function parseLine(line) {
|
|
1851
|
+
let s = stripComment(line).trim();
|
|
1852
|
+
if (!s)
|
|
1853
|
+
return { operands: [] };
|
|
1854
|
+
let label;
|
|
1855
|
+
const ci = s.indexOf(":");
|
|
1856
|
+
if (ci > 0 && /^[A-Za-z_]\w*$/.test(s.slice(0, ci).trim())) {
|
|
1857
|
+
label = s.slice(0, ci).trim();
|
|
1858
|
+
s = s.slice(ci + 1).trim();
|
|
1859
|
+
}
|
|
1860
|
+
if (!s)
|
|
1861
|
+
return { label, operands: [] };
|
|
1862
|
+
const si = s.search(/\s/);
|
|
1863
|
+
const first = si < 0 ? s : s.slice(0, si);
|
|
1864
|
+
const rest = si < 0 ? "" : s.slice(si).trim();
|
|
1865
|
+
if (!label && rest) {
|
|
1866
|
+
const parts = rest.split(/\s+/);
|
|
1867
|
+
if (stripDirectiveDot(parts[0]).toUpperCase() === "EQU") {
|
|
1868
|
+
return {
|
|
1869
|
+
label: first,
|
|
1870
|
+
mnemonic: "EQU",
|
|
1871
|
+
operands: [parts.slice(1).join(" ")],
|
|
1872
|
+
isEqu: true
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
return {
|
|
1877
|
+
label,
|
|
1878
|
+
mnemonic: stripDirectiveDot(first),
|
|
1879
|
+
operands: rest ? splitOperands(rest) : []
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
function tokenizeExpr(expr) {
|
|
1883
|
+
const tokens = [];
|
|
1884
|
+
let i = 0;
|
|
1885
|
+
while (i < expr.length) {
|
|
1886
|
+
let c = expr[i];
|
|
1887
|
+
if (/\s/.test(c)) {
|
|
1888
|
+
i++;
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
if (c === "'" && i + 2 < expr.length && expr[i + 2] === "'") {
|
|
1892
|
+
tokens.push({ kind: "num", val: expr.charCodeAt(i + 1) });
|
|
1893
|
+
i += 3;
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1896
|
+
if (/[0-9]/.test(c)) {
|
|
1897
|
+
let j = i;
|
|
1898
|
+
while (j < expr.length && /[0-9A-Fa-f]/.test(expr[j]))
|
|
1899
|
+
j++;
|
|
1900
|
+
if (j < expr.length && /[hH]/.test(expr[j])) {
|
|
1901
|
+
tokens.push({ kind: "num", val: parseInt(expr.slice(i, j), 16) });
|
|
1902
|
+
j++;
|
|
1903
|
+
} else {
|
|
1904
|
+
tokens.push({ kind: "num", val: parseInt(expr.slice(i, j), 10) });
|
|
1905
|
+
}
|
|
1906
|
+
i = j;
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
if (/[A-Za-z_]/.test(c)) {
|
|
1910
|
+
let j = i;
|
|
1911
|
+
while (j < expr.length && /\w/.test(expr[j]))
|
|
1912
|
+
j++;
|
|
1913
|
+
tokens.push({ kind: "id", val: expr.slice(i, j) });
|
|
1914
|
+
i = j;
|
|
1915
|
+
continue;
|
|
1916
|
+
}
|
|
1917
|
+
if (c === "<" && expr[i + 1] === "<") {
|
|
1918
|
+
tokens.push({ kind: "op", val: "<<" });
|
|
1919
|
+
i += 2;
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
if (c === ">" && expr[i + 1] === ">") {
|
|
1923
|
+
tokens.push({ kind: "op", val: ">>" });
|
|
1924
|
+
i += 2;
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
if ("+-*/%&|^~()".includes(c)) {
|
|
1928
|
+
tokens.push({ kind: "op", val: c });
|
|
1929
|
+
i++;
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
throw new Error(`unexpected character in expression: '${c}'`);
|
|
1933
|
+
}
|
|
1934
|
+
return tokens;
|
|
1935
|
+
}
|
|
1936
|
+
function evalExpr(expr, symbols) {
|
|
1937
|
+
const tokens = tokenizeExpr(expr);
|
|
1938
|
+
let pos = 0;
|
|
1939
|
+
function peek() {
|
|
1940
|
+
return tokens[pos];
|
|
1941
|
+
}
|
|
1942
|
+
function next() {
|
|
1943
|
+
return tokens[pos++];
|
|
1944
|
+
}
|
|
1945
|
+
function isOp(val) {
|
|
1946
|
+
const t = peek();
|
|
1947
|
+
return t !== undefined && t.kind === "op" && t.val === val;
|
|
1948
|
+
}
|
|
1949
|
+
function atom() {
|
|
1950
|
+
const t = peek();
|
|
1951
|
+
if (!t)
|
|
1952
|
+
throw new Error("unexpected end of expression");
|
|
1953
|
+
if (t.kind === "num") {
|
|
1954
|
+
next();
|
|
1955
|
+
return t.val;
|
|
1956
|
+
}
|
|
1957
|
+
if (t.kind === "id") {
|
|
1958
|
+
next();
|
|
1959
|
+
const k = t.val.toUpperCase();
|
|
1960
|
+
if (k === "LOW" || k === "HIGH") {
|
|
1961
|
+
if (!isOp("("))
|
|
1962
|
+
throw new Error(`${k} requires parentheses`);
|
|
1963
|
+
next();
|
|
1964
|
+
const v = parseOr();
|
|
1965
|
+
if (!isOp(")"))
|
|
1966
|
+
throw new Error("expected ')'");
|
|
1967
|
+
next();
|
|
1968
|
+
return k === "LOW" ? v & 255 : v >> 8 & 255;
|
|
1969
|
+
}
|
|
1970
|
+
if (symbols.has(k))
|
|
1971
|
+
return symbols.get(k);
|
|
1972
|
+
throw new Error(`unknown symbol: ${t.val}`);
|
|
1973
|
+
}
|
|
1974
|
+
if (t.kind === "op" && t.val === "(") {
|
|
1975
|
+
next();
|
|
1976
|
+
const v = parseOr();
|
|
1977
|
+
if (!isOp(")"))
|
|
1978
|
+
throw new Error("expected ')'");
|
|
1979
|
+
next();
|
|
1980
|
+
return v;
|
|
1981
|
+
}
|
|
1982
|
+
throw new Error(`unexpected token: ${t.val}`);
|
|
1983
|
+
}
|
|
1984
|
+
function unary() {
|
|
1985
|
+
if (isOp("-")) {
|
|
1986
|
+
next();
|
|
1987
|
+
return -unary() & 65535;
|
|
1988
|
+
}
|
|
1989
|
+
if (isOp("+")) {
|
|
1990
|
+
next();
|
|
1991
|
+
return unary();
|
|
1992
|
+
}
|
|
1993
|
+
if (isOp("~")) {
|
|
1994
|
+
next();
|
|
1995
|
+
return ~unary() & 65535;
|
|
1996
|
+
}
|
|
1997
|
+
return atom();
|
|
1998
|
+
}
|
|
1999
|
+
function multiplicative() {
|
|
2000
|
+
let v = unary();
|
|
2001
|
+
while (isOp("*") || isOp("/") || isOp("%")) {
|
|
2002
|
+
const op = next().val;
|
|
2003
|
+
let r = unary();
|
|
2004
|
+
if (op === "*")
|
|
2005
|
+
v = v * r & 65535;
|
|
2006
|
+
else if (op === "/")
|
|
2007
|
+
v = Math.trunc(v / r) & 65535;
|
|
2008
|
+
else
|
|
2009
|
+
v = v % r & 65535;
|
|
2010
|
+
}
|
|
2011
|
+
return v;
|
|
2012
|
+
}
|
|
2013
|
+
function additive() {
|
|
2014
|
+
let v = multiplicative();
|
|
2015
|
+
while (isOp("+") || isOp("-")) {
|
|
2016
|
+
const op = next().val;
|
|
2017
|
+
let r = multiplicative();
|
|
2018
|
+
v = op === "+" ? v + r & 65535 : v - r & 65535;
|
|
2019
|
+
}
|
|
2020
|
+
return v;
|
|
2021
|
+
}
|
|
2022
|
+
function shift() {
|
|
2023
|
+
let v = additive();
|
|
2024
|
+
while (isOp("<<") || isOp(">>")) {
|
|
2025
|
+
const op = next().val;
|
|
2026
|
+
let r = additive();
|
|
2027
|
+
v = op === "<<" ? v << r & 65535 : v >>> r & 65535;
|
|
2028
|
+
}
|
|
2029
|
+
return v;
|
|
2030
|
+
}
|
|
2031
|
+
function parseAnd() {
|
|
2032
|
+
let v = shift();
|
|
2033
|
+
while (isOp("&")) {
|
|
2034
|
+
next();
|
|
2035
|
+
v = v & shift();
|
|
2036
|
+
}
|
|
2037
|
+
return v;
|
|
2038
|
+
}
|
|
2039
|
+
function parseXor() {
|
|
2040
|
+
let v = parseAnd();
|
|
2041
|
+
while (isOp("^")) {
|
|
2042
|
+
next();
|
|
2043
|
+
v = (v ^ parseAnd()) & 65535;
|
|
2044
|
+
}
|
|
2045
|
+
return v;
|
|
2046
|
+
}
|
|
2047
|
+
function parseOr() {
|
|
2048
|
+
let v = parseXor();
|
|
2049
|
+
while (isOp("|")) {
|
|
2050
|
+
next();
|
|
2051
|
+
v = (v | parseXor()) & 65535;
|
|
2052
|
+
}
|
|
2053
|
+
return v;
|
|
2054
|
+
}
|
|
2055
|
+
const result = parseOr();
|
|
2056
|
+
if (pos < tokens.length)
|
|
2057
|
+
throw new Error(`unexpected token: ${tokens[pos].val}`);
|
|
2058
|
+
return result;
|
|
2059
|
+
}
|
|
2060
|
+
function encode(m, ops, symbols) {
|
|
2061
|
+
if (m in IMPLIED)
|
|
2062
|
+
return [IMPLIED[m]];
|
|
2063
|
+
if (m in ALU_REG)
|
|
2064
|
+
return [ALU_REG[m] | REG8[ops[0].toUpperCase()]];
|
|
2065
|
+
if (m in ALU_IMM)
|
|
2066
|
+
return [ALU_IMM[m], evalExpr(ops[0], symbols) & 255];
|
|
2067
|
+
if (m in ADDR16) {
|
|
2068
|
+
const v = evalExpr(ops[0], symbols);
|
|
2069
|
+
return [ADDR16[m], v & 255, v >> 8 & 255];
|
|
2070
|
+
}
|
|
2071
|
+
if (m === "MOV")
|
|
2072
|
+
return [
|
|
2073
|
+
64 | REG8[ops[0].toUpperCase()] << 3 | REG8[ops[1].toUpperCase()]
|
|
2074
|
+
];
|
|
2075
|
+
if (m === "MVI") {
|
|
2076
|
+
const v = evalExpr(ops[1], symbols);
|
|
2077
|
+
return [6 | REG8[ops[0].toUpperCase()] << 3, v & 255];
|
|
2078
|
+
}
|
|
2079
|
+
if (m === "INR")
|
|
2080
|
+
return [4 | REG8[ops[0].toUpperCase()] << 3];
|
|
2081
|
+
if (m === "DCR")
|
|
2082
|
+
return [5 | REG8[ops[0].toUpperCase()] << 3];
|
|
2083
|
+
if (m === "LXI") {
|
|
2084
|
+
const v = evalExpr(ops[1], symbols);
|
|
2085
|
+
return [
|
|
2086
|
+
1 | REG_PAIR[ops[0].toUpperCase()] << 4,
|
|
2087
|
+
v & 255,
|
|
2088
|
+
v >> 8 & 255
|
|
2089
|
+
];
|
|
2090
|
+
}
|
|
2091
|
+
if (m === "DAD")
|
|
2092
|
+
return [9 | REG_PAIR[ops[0].toUpperCase()] << 4];
|
|
2093
|
+
if (m === "INX")
|
|
2094
|
+
return [3 | REG_PAIR[ops[0].toUpperCase()] << 4];
|
|
2095
|
+
if (m === "DCX")
|
|
2096
|
+
return [11 | REG_PAIR[ops[0].toUpperCase()] << 4];
|
|
2097
|
+
if (m === "PUSH")
|
|
2098
|
+
return [197 | REG_PAIR_PUSH[ops[0].toUpperCase()] << 4];
|
|
2099
|
+
if (m === "POP")
|
|
2100
|
+
return [193 | REG_PAIR_PUSH[ops[0].toUpperCase()] << 4];
|
|
2101
|
+
if (m === "LDAX")
|
|
2102
|
+
return [10 | REG_PAIR[ops[0].toUpperCase()] << 4];
|
|
2103
|
+
if (m === "STAX")
|
|
2104
|
+
return [2 | REG_PAIR[ops[0].toUpperCase()] << 4];
|
|
2105
|
+
if (m === "IN")
|
|
2106
|
+
return [219, evalExpr(ops[0], symbols) & 255];
|
|
2107
|
+
if (m === "OUT")
|
|
2108
|
+
return [211, evalExpr(ops[0], symbols) & 255];
|
|
2109
|
+
if (m === "RST") {
|
|
2110
|
+
const n = evalExpr(ops[0], symbols);
|
|
2111
|
+
return [199 | n << 3];
|
|
2112
|
+
}
|
|
2113
|
+
throw new Error(`cannot encode: ${m} ${ops.join(", ")}`);
|
|
2114
|
+
}
|
|
2115
|
+
function dbBytes(operands, symbols) {
|
|
2116
|
+
const out = [];
|
|
2117
|
+
for (const op of operands) {
|
|
2118
|
+
if (op.startsWith('"') && op.endsWith('"') || op.startsWith("'") && op.endsWith("'")) {
|
|
2119
|
+
for (const ch of op.slice(1, -1))
|
|
2120
|
+
out.push(ch.charCodeAt(0));
|
|
2121
|
+
} else {
|
|
2122
|
+
out.push(evalExpr(op, symbols) & 255);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
return out;
|
|
2126
|
+
}
|
|
2127
|
+
function dwBytes(operands, symbols) {
|
|
2128
|
+
const out = [];
|
|
2129
|
+
for (const op of operands) {
|
|
2130
|
+
const v = evalExpr(op, symbols) & 65535;
|
|
2131
|
+
out.push(v & 255, v >> 8 & 255);
|
|
2132
|
+
}
|
|
2133
|
+
return out;
|
|
2134
|
+
}
|
|
2135
|
+
function parseDs(operands) {
|
|
2136
|
+
if (operands.length !== 1)
|
|
2137
|
+
throw new Error("DS takes one operand: count [(fill)]");
|
|
2138
|
+
const m = operands[0].match(/^(.+?)\s+\((.+)\)\s*$/);
|
|
2139
|
+
if (m)
|
|
2140
|
+
return { count: m[1], fill: m[2] };
|
|
2141
|
+
return { count: operands[0], fill: "0" };
|
|
2142
|
+
}
|
|
2143
|
+
function dsBytes(operands, symbols) {
|
|
2144
|
+
const { count, fill } = parseDs(operands);
|
|
2145
|
+
const n = evalExpr(count, symbols);
|
|
2146
|
+
const f = evalExpr(fill, symbols) & 255;
|
|
2147
|
+
return new Array(n).fill(f);
|
|
2148
|
+
}
|
|
2149
|
+
function countDs(operands, symbols) {
|
|
2150
|
+
const { count } = parseDs(operands);
|
|
2151
|
+
return evalExpr(count, symbols);
|
|
2152
|
+
}
|
|
2153
|
+
function countDb(operands) {
|
|
2154
|
+
let n = 0;
|
|
2155
|
+
for (const op of operands) {
|
|
2156
|
+
if (op.startsWith('"') && op.endsWith('"') || op.startsWith("'") && op.endsWith("'"))
|
|
2157
|
+
n += op.length - 2;
|
|
2158
|
+
else
|
|
2159
|
+
n++;
|
|
2160
|
+
}
|
|
2161
|
+
return n;
|
|
2162
|
+
}
|
|
2163
|
+
function asm(source) {
|
|
2164
|
+
const lines = source.split(`
|
|
2165
|
+
`);
|
|
2166
|
+
const symbols = new Map;
|
|
2167
|
+
let pc = 0;
|
|
2168
|
+
let ended = false;
|
|
2169
|
+
for (let idx = 0;idx < lines.length && !ended; idx++) {
|
|
2170
|
+
const line = lines[idx];
|
|
2171
|
+
try {
|
|
2172
|
+
for (const stmt of splitStatements(line)) {
|
|
2173
|
+
const parts = parseLine(stmt);
|
|
2174
|
+
if (parts.label) {
|
|
2175
|
+
if (parts.isEqu) {
|
|
2176
|
+
symbols.set(parts.label.toUpperCase(), evalExpr(parts.operands[0], symbols));
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
symbols.set(parts.label.toUpperCase(), pc);
|
|
2180
|
+
}
|
|
2181
|
+
if (!parts.mnemonic)
|
|
2182
|
+
continue;
|
|
2183
|
+
const m = parts.mnemonic.toUpperCase();
|
|
2184
|
+
if (m === "EQU")
|
|
2185
|
+
continue;
|
|
2186
|
+
if (m === "ORG") {
|
|
2187
|
+
pc = evalExpr(parts.operands[0], symbols);
|
|
2188
|
+
continue;
|
|
2189
|
+
}
|
|
2190
|
+
if (m === "SECTION")
|
|
2191
|
+
continue;
|
|
2192
|
+
if (m === "END") {
|
|
2193
|
+
ended = true;
|
|
2194
|
+
break;
|
|
2195
|
+
}
|
|
2196
|
+
if (m === "DB") {
|
|
2197
|
+
pc += countDb(parts.operands);
|
|
2198
|
+
continue;
|
|
2199
|
+
}
|
|
2200
|
+
if (m === "DW") {
|
|
2201
|
+
pc += parts.operands.length * 2;
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
if (m === "DS") {
|
|
2205
|
+
pc += countDs(parts.operands, symbols);
|
|
2206
|
+
continue;
|
|
2207
|
+
}
|
|
2208
|
+
pc += instrSize(m);
|
|
2209
|
+
}
|
|
2210
|
+
} catch (e) {
|
|
2211
|
+
if (e instanceof AsmError)
|
|
2212
|
+
throw e;
|
|
2213
|
+
throw new AsmError(e.message, idx + 1, line, firstNonSpaceCol(line));
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
const sections = [];
|
|
2217
|
+
let current = null;
|
|
2218
|
+
const sectionNames = new Set;
|
|
2219
|
+
let endedPass2 = false;
|
|
2220
|
+
for (let idx = 0;idx < lines.length && !endedPass2; idx++) {
|
|
2221
|
+
const line = lines[idx];
|
|
2222
|
+
try {
|
|
2223
|
+
for (const stmt of splitStatements(line)) {
|
|
2224
|
+
const parts = parseLine(stmt);
|
|
2225
|
+
if (parts.isEqu || !parts.mnemonic)
|
|
2226
|
+
continue;
|
|
2227
|
+
const m = parts.mnemonic.toUpperCase();
|
|
2228
|
+
if (m === "EQU")
|
|
2229
|
+
continue;
|
|
2230
|
+
if (m === "ORG") {
|
|
2231
|
+
if (current && current.data.length) {
|
|
2232
|
+
current.end = current.start + current.data.length - 1;
|
|
2233
|
+
sections.push(current);
|
|
2234
|
+
}
|
|
2235
|
+
const addr = evalExpr(parts.operands[0], symbols);
|
|
2236
|
+
current = { start: addr, end: addr, data: [] };
|
|
2237
|
+
continue;
|
|
2238
|
+
}
|
|
2239
|
+
if (m === "SECTION") {
|
|
2240
|
+
if (!current)
|
|
2241
|
+
throw new Error("SECTION before ORG");
|
|
2242
|
+
const name = parts.operands[0];
|
|
2243
|
+
if (!name)
|
|
2244
|
+
throw new Error("SECTION requires a name");
|
|
2245
|
+
if (sectionNames.has(name.toUpperCase()))
|
|
2246
|
+
throw new Error(`duplicate section name: ${name}`);
|
|
2247
|
+
sectionNames.add(name.toUpperCase());
|
|
2248
|
+
current.name = name;
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
if (m === "END") {
|
|
2252
|
+
endedPass2 = true;
|
|
2253
|
+
break;
|
|
2254
|
+
}
|
|
2255
|
+
if (!current)
|
|
2256
|
+
throw new Error("code before ORG");
|
|
2257
|
+
const bytes = m === "DB" ? dbBytes(parts.operands, symbols) : m === "DW" ? dwBytes(parts.operands, symbols) : m === "DS" ? dsBytes(parts.operands, symbols) : encode(m, parts.operands, symbols);
|
|
2258
|
+
current.data.push(...bytes);
|
|
2259
|
+
}
|
|
2260
|
+
} catch (e) {
|
|
2261
|
+
if (e instanceof AsmError)
|
|
2262
|
+
throw e;
|
|
2263
|
+
throw new AsmError(e.message, idx + 1, line, firstNonSpaceCol(line));
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
if (current && current.data.length) {
|
|
2267
|
+
current.end = current.start + current.data.length - 1;
|
|
2268
|
+
sections.push(current);
|
|
2269
|
+
}
|
|
2270
|
+
return sections;
|
|
2271
|
+
}
|
|
2272
|
+
if (false) {}
|
|
2273
|
+
|
|
1591
2274
|
// src/lib/terminal/rk86_terminal.ts
|
|
1592
2275
|
import { existsSync } from "fs";
|
|
2276
|
+
import { readFile } from "fs/promises";
|
|
1593
2277
|
// packages/rk86/package.json
|
|
1594
2278
|
var package_default = {
|
|
1595
2279
|
name: "rk86",
|
|
1596
|
-
version: "2.0.
|
|
2280
|
+
version: "2.0.15",
|
|
1597
2281
|
description: "\u042D\u043C\u0443\u043B\u044F\u0442\u043E\u0440 \u0420\u0430\u0434\u0438\u043E-86\u0420\u041A (Intel 8080) \u0434\u043B\u044F \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430",
|
|
1598
2282
|
bin: {
|
|
1599
2283
|
rk86: "rk86.js"
|
|
@@ -1615,9 +2299,6 @@ var package_default = {
|
|
|
1615
2299
|
}
|
|
1616
2300
|
};
|
|
1617
2301
|
|
|
1618
|
-
// src/lib/terminal/rk86_terminal.ts
|
|
1619
|
-
import { readFile } from "fs/promises";
|
|
1620
|
-
|
|
1621
2302
|
// src/lib/core/hex.ts
|
|
1622
2303
|
function hex(v, prefix) {
|
|
1623
2304
|
return v.toString(16).toUpperCase();
|
|
@@ -3033,7 +3714,7 @@ class Runner {
|
|
|
3033
3714
|
}
|
|
3034
3715
|
}
|
|
3035
3716
|
execute(options = {}) {
|
|
3036
|
-
const { terminate_address, on_terminate, exit_on_halt } = options;
|
|
3717
|
+
const { terminate_address, on_terminate, exit_on_halt, armed } = options;
|
|
3037
3718
|
clearTimeout(this.execute_timer);
|
|
3038
3719
|
if (!this.paused) {
|
|
3039
3720
|
let batch_ticks = 0;
|
|
@@ -3061,6 +3742,8 @@ class Runner {
|
|
|
3061
3742
|
this.machine.ui.on_visualizer_hit(this.machine.memory.read_raw(this.machine.cpu.pc));
|
|
3062
3743
|
}
|
|
3063
3744
|
batch_instructions += 1;
|
|
3745
|
+
if (armed?.value === false)
|
|
3746
|
+
continue;
|
|
3064
3747
|
if (terminate_address !== undefined && this.machine.cpu.pc === terminate_address) {
|
|
3065
3748
|
on_terminate?.();
|
|
3066
3749
|
return;
|
|
@@ -3655,6 +4338,7 @@ function printHelp() {
|
|
|
3655
4338
|
-l \u0441\u043F\u0438\u0441\u043E\u043A \u0444\u0430\u0439\u043B\u043E\u0432 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430
|
|
3656
4339
|
-m <\u0444\u0430\u0439\u043B> \u043C\u043E\u043D\u0438\u0442\u043E\u0440 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E: \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0439 mon32.bin)
|
|
3657
4340
|
-p \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0444\u0430\u0439\u043B \u0431\u0435\u0437 \u0437\u0430\u043F\u0443\u0441\u043A\u0430
|
|
4341
|
+
-g <\u0430\u0434\u0440\u0435\u0441> \u0430\u0434\u0440\u0435\u0441 \u0437\u0430\u043F\u0443\u0441\u043A\u0430 (\u043D\u0435\u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C \u0441 -p)
|
|
3658
4342
|
--exit-halt \u0432\u044B\u0445\u043E\u0434 \u043F\u0440\u0438 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0438 HLT
|
|
3659
4343
|
--exit-address [\u0430\u0434\u0440\u0435\u0441] \u0432\u044B\u0445\u043E\u0434 \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0435 \u043D\u0430 \u0430\u0434\u0440\u0435\u0441 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E: 0xFFFE)
|
|
3660
4344
|
|
|
@@ -3666,6 +4350,8 @@ function printHelp() {
|
|
|
3666
4350
|
bunx rk86 --exit-halt prog.bin \u0432\u044B\u0445\u043E\u0434 \u043F\u0440\u0438 HLT
|
|
3667
4351
|
bunx rk86 --exit-address prog.bin \u0432\u044B\u0445\u043E\u0434 \u043F\u0440\u0438 JMP FFFEh
|
|
3668
4352
|
bunx rk86 -l \u0441\u043F\u0438\u0441\u043E\u043A \u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0445 \u0444\u0430\u0439\u043B\u043E\u0432
|
|
4353
|
+
bunx rk86 --exit-halt prog.asm \u0441\u043E\u0431\u0440\u0430\u0442\u044C \u0438 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C .asm \u0444\u0430\u0439\u043B
|
|
4354
|
+
bunx rk86 -g 0x100 prog.bin \u0437\u0430\u043F\u0443\u0441\u043A \u0441 \u0430\u0434\u0440\u0435\u0441\u0430 100h
|
|
3669
4355
|
|
|
3670
4356
|
\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435:
|
|
3671
4357
|
Ctrl+C \u0432\u044B\u0445\u043E\u0434`);
|
|
@@ -3716,6 +4402,11 @@ async function main() {
|
|
|
3716
4402
|
process.exit(0);
|
|
3717
4403
|
}
|
|
3718
4404
|
const loadOnly = flag(args, "-p");
|
|
4405
|
+
const goAddr = arg(args, "-g", undefined, /^0x[0-9a-fA-F]+$/i, (v) => parseInt(v, 16));
|
|
4406
|
+
if (loadOnly && goAddr !== undefined) {
|
|
4407
|
+
console.error("\u043E\u0448\u0438\u0431\u043A\u0430: -p \u0438 -g \u043D\u0435\u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u044B");
|
|
4408
|
+
process.exit(1);
|
|
4409
|
+
}
|
|
3719
4410
|
const exitOnHalt = flag(args, "--exit-halt");
|
|
3720
4411
|
const exitAddrValue = arg(args, "--exit-address", "0xFFFE", /^0x[0-9a-fA-F]+$/i, (v) => parseInt(v, 16));
|
|
3721
4412
|
const exitAddr = exitAddrValue !== undefined;
|
|
@@ -3742,18 +4433,44 @@ async function main() {
|
|
|
3742
4433
|
let entryPoint;
|
|
3743
4434
|
let loadInfo = "";
|
|
3744
4435
|
if (programFile) {
|
|
3745
|
-
const
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
4436
|
+
const ext = file_ext(programFile).toLowerCase();
|
|
4437
|
+
if (ext === "asm") {
|
|
4438
|
+
const source = await readFile(programFile, "utf-8");
|
|
4439
|
+
const sections = asm(source);
|
|
4440
|
+
if (sections.length === 0) {
|
|
4441
|
+
console.error("\u043E\u0448\u0438\u0431\u043A\u0430: \u0430\u0441\u0441\u0435\u043C\u0431\u043B\u0435\u0440 \u043D\u0435 \u0432\u0435\u0440\u043D\u0443\u043B \u0441\u0435\u043A\u0446\u0438\u0439");
|
|
4442
|
+
process.exit(1);
|
|
4443
|
+
}
|
|
4444
|
+
const lines = [];
|
|
4445
|
+
for (const section of sections) {
|
|
4446
|
+
const data = section.data;
|
|
4447
|
+
for (let i = 0;i < data.length; i++) {
|
|
4448
|
+
machine.memory.write(section.start + i, data[i]);
|
|
4449
|
+
}
|
|
4450
|
+
const name = section.name ? ` [${section.name}]` : "";
|
|
4451
|
+
lines.push(`${hex16(section.start)}-${hex16(section.end)}${name} (${data.length} \u0431\u0430\u0439\u0442)`);
|
|
4452
|
+
}
|
|
4453
|
+
entryPoint = goAddr ?? sections[0].start;
|
|
4454
|
+
loadInfo = `\u0441\u043E\u0431\u0440\u0430\u043D: ${programFile}
|
|
4455
|
+
` + lines.join(`
|
|
4456
|
+
`) + `
|
|
4457
|
+
\u0437\u0430\u043F\u0443\u0441\u043A: G${hex16(entryPoint)}`;
|
|
3751
4458
|
} else {
|
|
3752
|
-
const
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
4459
|
+
const content = await fetchFile(programFile);
|
|
4460
|
+
const { ok, json } = parse(content);
|
|
4461
|
+
if (ok) {
|
|
4462
|
+
rk86_snapshot_restore(json, machine);
|
|
4463
|
+
entryPoint = parseInt(json.cpu.pc);
|
|
4464
|
+
loadInfo = `\u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D: ${programFile} (PC=${hex16(entryPoint)})`;
|
|
4465
|
+
} else {
|
|
4466
|
+
const file = parse_rk86_binary(programFile, content);
|
|
4467
|
+
machine.memory.load_file(file);
|
|
4468
|
+
entryPoint = file.entry;
|
|
4469
|
+
loadInfo = `\u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D: ${programFile} (${hex16(file.start)}-${hex16(file.end)}, G${hex16(file.entry)})`;
|
|
4470
|
+
}
|
|
3756
4471
|
}
|
|
4472
|
+
if (goAddr !== undefined)
|
|
4473
|
+
entryPoint = goAddr;
|
|
3757
4474
|
}
|
|
3758
4475
|
process.stdout.write("\x1B[?25l");
|
|
3759
4476
|
process.stdout.write("\x1B[2J");
|
|
@@ -3763,17 +4480,24 @@ async function main() {
|
|
|
3763
4480
|
machine.screen.start(renderer);
|
|
3764
4481
|
const onTerminate = exitOnHalt || exitAddr ? () => {
|
|
3765
4482
|
renderer.update();
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
4483
|
+
setTimeout(() => {
|
|
4484
|
+
console.log();
|
|
4485
|
+
console.log("\u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430 \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0430", hex16(machine.cpu.pc));
|
|
4486
|
+
process.exit(0);
|
|
4487
|
+
}, 1000);
|
|
3769
4488
|
} : undefined;
|
|
4489
|
+
const armed = { value: entryPoint === undefined };
|
|
3770
4490
|
machine.runner.execute({
|
|
3771
4491
|
terminate_address: exitAddr ? exitAddrValue : undefined,
|
|
3772
4492
|
exit_on_halt: exitOnHalt,
|
|
3773
|
-
on_terminate: onTerminate
|
|
4493
|
+
on_terminate: onTerminate,
|
|
4494
|
+
armed
|
|
3774
4495
|
});
|
|
3775
4496
|
if (entryPoint !== undefined && !loadOnly) {
|
|
3776
|
-
setTimeout(() =>
|
|
4497
|
+
setTimeout(() => {
|
|
4498
|
+
machine.cpu.jump(entryPoint);
|
|
4499
|
+
armed.value = true;
|
|
4500
|
+
}, 500);
|
|
3777
4501
|
}
|
|
3778
4502
|
process.on("exit", () => {
|
|
3779
4503
|
process.stdout.write("\x1B[?25h");
|