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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/rk86.js +744 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rk86",
3
- "version": "2.0.13",
3
+ "version": "2.0.16",
4
4
  "description": "Эмулятор Радио-86РК (Intel 8080) для терминала",
5
5
  "bin": {
6
6
  "rk86": "rk86.js"
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.12",
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 content = await fetchFile(programFile);
3746
- const { ok, json } = parse(content);
3747
- if (ok) {
3748
- rk86_snapshot_restore(json, machine);
3749
- entryPoint = parseInt(json.cpu.pc);
3750
- loadInfo = `\u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D: ${programFile} (PC=${hex16(entryPoint)})`;
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 file = parse_rk86_binary(programFile, content);
3753
- machine.memory.load_file(file);
3754
- entryPoint = file.entry;
3755
- loadInfo = `\u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D: ${programFile} (${hex16(file.start)}-${hex16(file.end)}, G${hex16(file.entry)})`;
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
- console.log();
3767
- 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));
3768
- process.exit(0);
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(() => machine.cpu.jump(entryPoint), 500);
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");