sci2-ts 0.0.0-stage → 0.2.1
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/CHANGELOG.md +72 -0
- package/LICENSE +21 -0
- package/README.md +113 -2
- package/apps/viewer/editor.html +43 -0
- package/apps/viewer/explore.html +27 -0
- package/apps/viewer/index.html +26 -0
- package/apps/viewer/package.json +27 -0
- package/apps/viewer/play.html +26 -0
- package/apps/viewer/plugin.d.ts +18 -0
- package/apps/viewer/plugin.js +75 -0
- package/apps/viewer/plugin.ts +84 -0
- package/apps/viewer/src/adlib.d.ts +20 -0
- package/apps/viewer/src/adlib.js +55 -0
- package/apps/viewer/src/adlib.ts +62 -0
- package/apps/viewer/src/app.d.ts +5 -0
- package/apps/viewer/src/app.js +5 -0
- package/apps/viewer/src/app.ts +5 -0
- package/apps/viewer/src/asset-index.d.ts +22 -0
- package/apps/viewer/src/asset-index.js +74 -0
- package/apps/viewer/src/asset-index.ts +90 -0
- package/apps/viewer/src/audio.d.ts +28 -0
- package/apps/viewer/src/audio.js +102 -0
- package/apps/viewer/src/audio.ts +110 -0
- package/apps/viewer/src/editor-handles.d.ts +63 -0
- package/apps/viewer/src/editor-handles.js +332 -0
- package/apps/viewer/src/editor-handles.ts +368 -0
- package/apps/viewer/src/editor-languages.d.ts +23 -0
- package/apps/viewer/src/editor-languages.js +157 -0
- package/apps/viewer/src/editor-languages.ts +135 -0
- package/apps/viewer/src/editor.css +213 -0
- package/apps/viewer/src/editor.d.ts +1 -0
- package/apps/viewer/src/editor.js +393 -0
- package/apps/viewer/src/editor.ts +412 -0
- package/apps/viewer/src/explore/classes.d.ts +7 -0
- package/apps/viewer/src/explore/classes.js +63 -0
- package/apps/viewer/src/explore/classes.ts +61 -0
- package/apps/viewer/src/explore/context.d.ts +33 -0
- package/apps/viewer/src/explore/context.js +96 -0
- package/apps/viewer/src/explore/context.ts +124 -0
- package/apps/viewer/src/explore/explore.css +93 -0
- package/apps/viewer/src/explore/explore.d.ts +1 -0
- package/apps/viewer/src/explore/explore.js +102 -0
- package/apps/viewer/src/explore/explore.ts +104 -0
- package/apps/viewer/src/explore/inspect.d.ts +5 -0
- package/apps/viewer/src/explore/inspect.js +208 -0
- package/apps/viewer/src/explore/inspect.ts +279 -0
- package/apps/viewer/src/explore/live.d.ts +22 -0
- package/apps/viewer/src/explore/live.js +318 -0
- package/apps/viewer/src/explore/live.ts +365 -0
- package/apps/viewer/src/explore/map.d.ts +11 -0
- package/apps/viewer/src/explore/map.js +182 -0
- package/apps/viewer/src/explore/map.ts +193 -0
- package/apps/viewer/src/files.d.ts +21 -0
- package/apps/viewer/src/files.js +69 -0
- package/apps/viewer/src/files.ts +72 -0
- package/apps/viewer/src/game-profile.d.ts +19 -0
- package/apps/viewer/src/game-profile.js +4 -0
- package/apps/viewer/src/game-profile.ts +26 -0
- package/apps/viewer/src/main.d.ts +1 -0
- package/apps/viewer/src/main.js +473 -0
- package/apps/viewer/src/main.ts +527 -0
- package/apps/viewer/src/music.d.ts +14 -0
- package/apps/viewer/src/music.js +32 -0
- package/apps/viewer/src/music.ts +34 -0
- package/apps/viewer/src/opl-worklet.d.ts +1 -0
- package/apps/viewer/src/opl-worklet.js +47 -0
- package/apps/viewer/src/opl-worklet.ts +55 -0
- package/apps/viewer/src/overlay.d.ts +31 -0
- package/apps/viewer/src/overlay.js +103 -0
- package/apps/viewer/src/overlay.ts +123 -0
- package/apps/viewer/src/play.css +14 -0
- package/apps/viewer/src/play.d.ts +1 -0
- package/apps/viewer/src/play.js +179 -0
- package/apps/viewer/src/play.ts +177 -0
- package/apps/viewer/src/saves.d.ts +10 -0
- package/apps/viewer/src/saves.js +50 -0
- package/apps/viewer/src/saves.ts +51 -0
- package/apps/viewer/src/session.d.ts +91 -0
- package/apps/viewer/src/session.js +301 -0
- package/apps/viewer/src/session.ts +321 -0
- package/apps/viewer/src/sound-preview.d.ts +7 -0
- package/apps/viewer/src/sound-preview.js +214 -0
- package/apps/viewer/src/sound-preview.ts +228 -0
- package/apps/viewer/src/style.css +64 -0
- package/apps/viewer/vite.config.ts +25 -0
- package/bin/sci-ts.mjs +9 -0
- package/lib/994.sc +49 -0
- package/lib/995.sc +53 -0
- package/lib/996.sc +299 -0
- package/lib/997.sc +198 -0
- package/lib/998.sc +338 -0
- package/lib/999.sc +189 -0
- package/lib/resources.d.ts +2 -0
- package/lib/resources.js +84 -0
- package/lib/resources.ts +88 -0
- package/lib/system.sh +62 -0
- package/package.json +75 -4
- package/packages/content/room.schema.json +267 -0
- package/packages/content/src/index.d.ts +3 -0
- package/packages/content/src/index.js +3 -0
- package/packages/content/src/index.ts +3 -0
- package/packages/content/src/room.d.ts +111 -0
- package/packages/content/src/room.js +886 -0
- package/packages/content/src/room.ts +1011 -0
- package/packages/content/src/target.d.ts +80 -0
- package/packages/content/src/target.js +1 -0
- package/packages/content/src/target.ts +60 -0
- package/packages/content/src/yarn.d.ts +90 -0
- package/packages/content/src/yarn.js +214 -0
- package/packages/content/src/yarn.ts +261 -0
- package/packages/sci/src/analysis/game.d.ts +90 -0
- package/packages/sci/src/analysis/game.js +221 -0
- package/packages/sci/src/analysis/game.ts +305 -0
- package/packages/sci/src/analysis/runtime.d.ts +58 -0
- package/packages/sci/src/analysis/runtime.js +133 -0
- package/packages/sci/src/analysis/runtime.ts +163 -0
- package/packages/sci/src/audio/adlib.d.ts +73 -0
- package/packages/sci/src/audio/adlib.js +280 -0
- package/packages/sci/src/audio/adlib.ts +329 -0
- package/packages/sci/src/audio/index.d.ts +43 -0
- package/packages/sci/src/audio/index.js +99 -0
- package/packages/sci/src/audio/index.ts +122 -0
- package/packages/sci/src/audio/midi.d.ts +92 -0
- package/packages/sci/src/audio/midi.js +242 -0
- package/packages/sci/src/audio/midi.ts +262 -0
- package/packages/sci/src/audio/opl2.d.ts +74 -0
- package/packages/sci/src/audio/opl2.js +283 -0
- package/packages/sci/src/audio/opl2.ts +283 -0
- package/packages/sci/src/audio/sol.d.ts +32 -0
- package/packages/sci/src/audio/sol.js +81 -0
- package/packages/sci/src/audio/sol.ts +100 -0
- package/packages/sci/src/audio/write.d.ts +35 -0
- package/packages/sci/src/audio/write.js +91 -0
- package/packages/sci/src/audio/write.ts +108 -0
- package/packages/sci/src/gfx/cel.d.ts +36 -0
- package/packages/sci/src/gfx/cel.js +82 -0
- package/packages/sci/src/gfx/cel.ts +110 -0
- package/packages/sci/src/gfx/palette.d.ts +24 -0
- package/packages/sci/src/gfx/palette.js +49 -0
- package/packages/sci/src/gfx/palette.ts +56 -0
- package/packages/sci/src/gfx/pic-file.d.ts +46 -0
- package/packages/sci/src/gfx/pic-file.js +114 -0
- package/packages/sci/src/gfx/pic-file.ts +155 -0
- package/packages/sci/src/gfx/pic.d.ts +27 -0
- package/packages/sci/src/gfx/pic.js +34 -0
- package/packages/sci/src/gfx/pic.ts +53 -0
- package/packages/sci/src/gfx/quantize.d.ts +42 -0
- package/packages/sci/src/gfx/quantize.js +199 -0
- package/packages/sci/src/gfx/quantize.ts +218 -0
- package/packages/sci/src/gfx/view-file.d.ts +62 -0
- package/packages/sci/src/gfx/view-file.js +182 -0
- package/packages/sci/src/gfx/view-file.ts +233 -0
- package/packages/sci/src/gfx/view.d.ts +28 -0
- package/packages/sci/src/gfx/view.js +45 -0
- package/packages/sci/src/gfx/view.ts +62 -0
- package/packages/sci/src/index.d.ts +38 -0
- package/packages/sci/src/index.js +38 -0
- package/packages/sci/src/index.ts +38 -0
- package/packages/sci/src/motion/pathfinding.d.ts +40 -0
- package/packages/sci/src/motion/pathfinding.js +140 -0
- package/packages/sci/src/motion/pathfinding.ts +147 -0
- package/packages/sci/src/resource/archive.d.ts +16 -0
- package/packages/sci/src/resource/archive.js +51 -0
- package/packages/sci/src/resource/archive.ts +56 -0
- package/packages/sci/src/resource/lzs.d.ts +12 -0
- package/packages/sci/src/resource/lzs.js +64 -0
- package/packages/sci/src/resource/lzs.ts +66 -0
- package/packages/sci/src/resource/manager.d.ts +45 -0
- package/packages/sci/src/resource/manager.js +158 -0
- package/packages/sci/src/resource/manager.ts +184 -0
- package/packages/sci/src/resource/map.d.ts +18 -0
- package/packages/sci/src/resource/map.js +34 -0
- package/packages/sci/src/resource/map.ts +44 -0
- package/packages/sci/src/resource/types.d.ts +37 -0
- package/packages/sci/src/resource/types.js +45 -0
- package/packages/sci/src/resource/types.ts +57 -0
- package/packages/sci/src/script/assembly.d.ts +67 -0
- package/packages/sci/src/script/assembly.js +748 -0
- package/packages/sci/src/script/assembly.ts +714 -0
- package/packages/sci/src/script/compiler.d.ts +75 -0
- package/packages/sci/src/script/compiler.js +993 -0
- package/packages/sci/src/script/compiler.ts +1040 -0
- package/packages/sci/src/script/disasm.d.ts +81 -0
- package/packages/sci/src/script/disasm.js +318 -0
- package/packages/sci/src/script/disasm.ts +323 -0
- package/packages/sci/src/script/kernel-names.d.ts +6 -0
- package/packages/sci/src/script/kernel-names.js +165 -0
- package/packages/sci/src/script/kernel-names.ts +165 -0
- package/packages/sci/src/script/opcodes.d.ts +22 -0
- package/packages/sci/src/script/opcodes.js +45 -0
- package/packages/sci/src/script/opcodes.ts +72 -0
- package/packages/sci/src/script/script.d.ts +56 -0
- package/packages/sci/src/script/script.js +63 -0
- package/packages/sci/src/script/script.ts +111 -0
- package/packages/sci/src/script/sexpr.d.ts +45 -0
- package/packages/sci/src/script/sexpr.js +151 -0
- package/packages/sci/src/script/sexpr.ts +139 -0
- package/packages/sci/src/script/vocab.d.ts +15 -0
- package/packages/sci/src/script/vocab.js +44 -0
- package/packages/sci/src/script/vocab.ts +48 -0
- package/packages/sci/src/script/world.d.ts +25 -0
- package/packages/sci/src/script/world.js +65 -0
- package/packages/sci/src/script/world.ts +70 -0
- package/packages/sci/src/text/font.d.ts +37 -0
- package/packages/sci/src/text/font.js +103 -0
- package/packages/sci/src/text/font.ts +125 -0
- package/packages/sci/src/text/message.d.ts +69 -0
- package/packages/sci/src/text/message.js +171 -0
- package/packages/sci/src/text/message.ts +205 -0
- package/packages/sci/src/vm/kernels/arrays.d.ts +19 -0
- package/packages/sci/src/vm/kernels/arrays.js +182 -0
- package/packages/sci/src/vm/kernels/arrays.ts +164 -0
- package/packages/sci/src/vm/kernels/audio.d.ts +70 -0
- package/packages/sci/src/vm/kernels/audio.js +347 -0
- package/packages/sci/src/vm/kernels/audio.ts +349 -0
- package/packages/sci/src/vm/kernels/core.d.ts +5 -0
- package/packages/sci/src/vm/kernels/core.js +87 -0
- package/packages/sci/src/vm/kernels/core.ts +91 -0
- package/packages/sci/src/vm/kernels/graphics.d.ts +141 -0
- package/packages/sci/src/vm/kernels/graphics.js +467 -0
- package/packages/sci/src/vm/kernels/graphics.ts +502 -0
- package/packages/sci/src/vm/kernels/index.d.ts +8 -0
- package/packages/sci/src/vm/kernels/index.js +29 -0
- package/packages/sci/src/vm/kernels/index.ts +32 -0
- package/packages/sci/src/vm/kernels/lists.d.ts +6 -0
- package/packages/sci/src/vm/kernels/lists.js +127 -0
- package/packages/sci/src/vm/kernels/lists.ts +111 -0
- package/packages/sci/src/vm/kernels/motion.d.ts +7 -0
- package/packages/sci/src/vm/kernels/motion.js +204 -0
- package/packages/sci/src/vm/kernels/motion.ts +194 -0
- package/packages/sci/src/vm/kernels/palette.d.ts +82 -0
- package/packages/sci/src/vm/kernels/palette.js +292 -0
- package/packages/sci/src/vm/kernels/palette.ts +303 -0
- package/packages/sci/src/vm/kernels/saves.d.ts +45 -0
- package/packages/sci/src/vm/kernels/saves.js +123 -0
- package/packages/sci/src/vm/kernels/saves.ts +149 -0
- package/packages/sci/src/vm/kernels/system.d.ts +62 -0
- package/packages/sci/src/vm/kernels/system.js +110 -0
- package/packages/sci/src/vm/kernels/system.ts +128 -0
- package/packages/sci/src/vm/kernels/text.d.ts +13 -0
- package/packages/sci/src/vm/kernels/text.js +338 -0
- package/packages/sci/src/vm/kernels/text.ts +328 -0
- package/packages/sci/src/vm/memory.d.ts +138 -0
- package/packages/sci/src/vm/memory.js +168 -0
- package/packages/sci/src/vm/memory.ts +266 -0
- package/packages/sci/src/vm/replace.d.ts +11 -0
- package/packages/sci/src/vm/replace.js +14 -0
- package/packages/sci/src/vm/replace.ts +16 -0
- package/packages/sci/src/vm/savegame.d.ts +89 -0
- package/packages/sci/src/vm/savegame.js +161 -0
- package/packages/sci/src/vm/savegame.ts +188 -0
- package/packages/sci/src/vm/value.d.ts +17 -0
- package/packages/sci/src/vm/value.js +12 -0
- package/packages/sci/src/vm/value.ts +25 -0
- package/packages/sci/src/vm/vm.d.ts +155 -0
- package/packages/sci/src/vm/vm.js +1053 -0
- package/packages/sci/src/vm/vm.ts +914 -0
- package/tools/adlib-render.d.ts +1 -0
- package/tools/adlib-render.js +49 -0
- package/tools/adlib-render.ts +42 -0
- package/tools/analyze.d.ts +1 -0
- package/tools/analyze.js +25 -0
- package/tools/analyze.ts +25 -0
- package/tools/art/build.d.ts +34 -0
- package/tools/art/build.js +175 -0
- package/tools/art/build.ts +153 -0
- package/tools/art.d.ts +1 -0
- package/tools/art.js +31 -0
- package/tools/art.ts +29 -0
- package/tools/asm.d.ts +1 -0
- package/tools/asm.js +61 -0
- package/tools/asm.ts +55 -0
- package/tools/cli.d.ts +1 -0
- package/tools/cli.js +99 -0
- package/tools/cli.ts +100 -0
- package/tools/editor-server.d.ts +29 -0
- package/tools/editor-server.js +162 -0
- package/tools/editor-server.ts +176 -0
- package/tools/explore.d.ts +1 -0
- package/tools/explore.js +305 -0
- package/tools/explore.ts +250 -0
- package/tools/game/audio.d.ts +19 -0
- package/tools/game/audio.js +143 -0
- package/tools/game/audio.ts +130 -0
- package/tools/game/build.d.ts +46 -0
- package/tools/game/build.js +259 -0
- package/tools/game/build.ts +283 -0
- package/tools/game/defaults.d.ts +26 -0
- package/tools/game/defaults.js +82 -0
- package/tools/game/defaults.ts +83 -0
- package/tools/game/font.d.ts +3 -0
- package/tools/game/font.js +124 -0
- package/tools/game/font.ts +126 -0
- package/tools/game/kit.d.ts +7 -0
- package/tools/game/kit.js +7 -0
- package/tools/game/kit.ts +10 -0
- package/tools/game/new.d.ts +8 -0
- package/tools/game/new.js +39 -0
- package/tools/game/new.ts +38 -0
- package/tools/game/target.d.ts +7 -0
- package/tools/game/target.js +41 -0
- package/tools/game/target.ts +42 -0
- package/tools/game/template/resources.ts.tmpl +43 -0
- package/tools/game/template/rooms/1.room.yaml +9 -0
- package/tools/game/template/rooms/1.yarn +17 -0
- package/tools/game/template/scripts/0.sc +24 -0
- package/tools/game.d.ts +1 -0
- package/tools/game.js +77 -0
- package/tools/game.ts +70 -0
- package/tools/mod-build.d.ts +28 -0
- package/tools/mod-build.js +173 -0
- package/tools/mod-build.ts +179 -0
- package/tools/mod-target.d.ts +16 -0
- package/tools/mod-target.js +26 -0
- package/tools/mod-target.ts +32 -0
- package/tools/mod.d.ts +1 -0
- package/tools/mod.js +47 -0
- package/tools/mod.ts +45 -0
- package/tools/msg.d.ts +1 -0
- package/tools/msg.js +40 -0
- package/tools/msg.ts +36 -0
- package/tools/node-files.d.ts +3 -0
- package/tools/node-files.js +34 -0
- package/tools/node-files.ts +33 -0
- package/tools/patches.d.ts +3 -0
- package/tools/patches.js +13 -0
- package/tools/patches.ts +13 -0
- package/tools/pic-build.d.ts +9 -0
- package/tools/pic-build.js +26 -0
- package/tools/pic-build.ts +27 -0
- package/tools/pic.d.ts +1 -0
- package/tools/pic.js +64 -0
- package/tools/pic.ts +61 -0
- package/tools/play.d.ts +1 -0
- package/tools/play.js +54 -0
- package/tools/play.ts +54 -0
- package/tools/png.d.ts +16 -0
- package/tools/png.js +124 -0
- package/tools/png.ts +123 -0
- package/tools/res.d.ts +1 -0
- package/tools/res.js +48 -0
- package/tools/res.ts +50 -0
- package/tools/scenario.d.ts +1 -0
- package/tools/scenario.js +74 -0
- package/tools/scenario.ts +80 -0
- package/tools/site/build.d.ts +8 -0
- package/tools/site/build.js +110 -0
- package/tools/site/build.ts +116 -0
- package/tools/site/markdown.d.ts +11 -0
- package/tools/site/markdown.js +88 -0
- package/tools/site/markdown.ts +84 -0
- package/tools/view-build.d.ts +34 -0
- package/tools/view-build.js +47 -0
- package/tools/view-build.ts +71 -0
- package/tools/view.d.ts +1 -0
- package/tools/view.js +54 -0
- package/tools/view.ts +52 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const op = (name, ...operands) => ({ name, operands });
|
|
2
|
+
const base = [
|
|
3
|
+
// 0x00–0x16: arithmetic and comparison. Binary ops combine the stack top with acc.
|
|
4
|
+
op("bnot"), op("add"), op("sub"), op("mul"), op("div"), op("mod"), op("shr"), op("shl"),
|
|
5
|
+
op("xor"), op("and"), op("or"), op("neg"), op("not"), op("eq?"), op("ne?"), op("gt?"),
|
|
6
|
+
op("ge?"), op("lt?"), op("le?"), op("ugt?"), op("uge?"), op("ult?"), op("ule?"),
|
|
7
|
+
// 0x17–0x1e: branches and the stack
|
|
8
|
+
op("bt", "R"), op("bnt", "R"), op("jmp", "R"), op("ldi", "S"), op("push"), op("pushi", "S"), op("toss"), op("dup"),
|
|
9
|
+
// 0x1f–0x25: calls. The last operand is the argument frame size in bytes.
|
|
10
|
+
op("link", "W"),
|
|
11
|
+
op("call", "R", "U"), // local procedure
|
|
12
|
+
op("callk", "W", "U"), // kernel (interpreter built-in)
|
|
13
|
+
op("callb", "W", "U"), // export of script 0
|
|
14
|
+
op("calle", "W", "W", "U"), // export of another script
|
|
15
|
+
op("ret"),
|
|
16
|
+
op("send", "W"), // send messages to the object in acc
|
|
17
|
+
// 0x26–0x2f: objects
|
|
18
|
+
op("dummy26"), op("dummy27"),
|
|
19
|
+
op("class", "W"), // acc = class object
|
|
20
|
+
op("dummy29"),
|
|
21
|
+
op("self", "W"), op("super", "W", "U"), op("&rest", "W"),
|
|
22
|
+
op("lea", "W", "W"), op("selfID"), op("dummy2f"),
|
|
23
|
+
// 0x30–0x38: properties of the current object (operand = property offset in bytes)
|
|
24
|
+
op("pprev"), op("pToa", "W"), op("aTop", "W"), op("pTos", "W"), op("sTop", "W"),
|
|
25
|
+
op("ipToa", "W"), op("dpToa", "W"), op("ipTos", "W"), op("dpTos", "W"),
|
|
26
|
+
// 0x39–0x3f
|
|
27
|
+
op("lofsa", "W"), op("lofss", "W"), // address of a heap object/string
|
|
28
|
+
op("push0"), op("push1"), op("push2"), op("pushSelf"), op("dummy3f"),
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* 0x40–0x7f: variable access, generated from a pattern:
|
|
32
|
+
* bits 0-1: g(lobal) l(ocal) t(emp) p(aram)
|
|
33
|
+
* bit 2: a = acc, s = stack
|
|
34
|
+
* bit 3: i = indexed by acc
|
|
35
|
+
* bits 4-5: l(oad), s(tore), + (increment), - (decrement)
|
|
36
|
+
*/
|
|
37
|
+
const varOps = Array.from({ length: 64 }, (_, i) => {
|
|
38
|
+
const kind = "gltp"[i & 3];
|
|
39
|
+
const target = i & 4 ? "s" : "a";
|
|
40
|
+
const indexed = i & 8 ? "i" : "";
|
|
41
|
+
const action = ["l", "s", "+", "-"][i >> 4];
|
|
42
|
+
return op(`${action}${target}${kind}${indexed}`, "W");
|
|
43
|
+
});
|
|
44
|
+
export const opcodes = [...base, ...varOps];
|
|
45
|
+
export const varKind = (opcode) => opcode >= 0x40 ? ["global", "local", "temp", "param"][opcode & 3] : undefined;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCI bytecode. Each instruction's first byte is `opcode << 1 | size`: when the low bit is
|
|
3
|
+
* set, "sized" operands are 1 byte instead of 2.
|
|
4
|
+
*
|
|
5
|
+
* Operand kinds:
|
|
6
|
+
* W sized unsigned (word or byte) S sized signed
|
|
7
|
+
* R sized signed, relative to the next instruction (jump/call target)
|
|
8
|
+
* B always 1 byte U always 2 bytes
|
|
9
|
+
*
|
|
10
|
+
* SCI2 quirk, found by measuring: the argument-frame operand of call/callk/callb/calle/super
|
|
11
|
+
* is always 16-bit, even in the byte form; send and self frames follow the size bit.
|
|
12
|
+
* (SCI0–1.1 used a fixed byte.) Decoding it as a byte leaves a stray 0x00, which shows up
|
|
13
|
+
* as thousands of bogus `bnot` instructions.
|
|
14
|
+
*/
|
|
15
|
+
export type OperandKind = "W" | "S" | "R" | "B" | "U";
|
|
16
|
+
|
|
17
|
+
export interface OpcodeInfo {
|
|
18
|
+
name: string;
|
|
19
|
+
operands: OperandKind[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const op = (name: string, ...operands: OperandKind[]): OpcodeInfo => ({ name, operands });
|
|
23
|
+
|
|
24
|
+
const base: OpcodeInfo[] = [
|
|
25
|
+
// 0x00–0x16: arithmetic and comparison. Binary ops combine the stack top with acc.
|
|
26
|
+
op("bnot"), op("add"), op("sub"), op("mul"), op("div"), op("mod"), op("shr"), op("shl"),
|
|
27
|
+
op("xor"), op("and"), op("or"), op("neg"), op("not"), op("eq?"), op("ne?"), op("gt?"),
|
|
28
|
+
op("ge?"), op("lt?"), op("le?"), op("ugt?"), op("uge?"), op("ult?"), op("ule?"),
|
|
29
|
+
// 0x17–0x1e: branches and the stack
|
|
30
|
+
op("bt", "R"), op("bnt", "R"), op("jmp", "R"), op("ldi", "S"), op("push"), op("pushi", "S"), op("toss"), op("dup"),
|
|
31
|
+
// 0x1f–0x25: calls. The last operand is the argument frame size in bytes.
|
|
32
|
+
op("link", "W"),
|
|
33
|
+
op("call", "R", "U"), // local procedure
|
|
34
|
+
op("callk", "W", "U"), // kernel (interpreter built-in)
|
|
35
|
+
op("callb", "W", "U"), // export of script 0
|
|
36
|
+
op("calle", "W", "W", "U"), // export of another script
|
|
37
|
+
op("ret"),
|
|
38
|
+
op("send", "W"), // send messages to the object in acc
|
|
39
|
+
// 0x26–0x2f: objects
|
|
40
|
+
op("dummy26"), op("dummy27"),
|
|
41
|
+
op("class", "W"), // acc = class object
|
|
42
|
+
op("dummy29"),
|
|
43
|
+
op("self", "W"), op("super", "W", "U"), op("&rest", "W"),
|
|
44
|
+
op("lea", "W", "W"), op("selfID"), op("dummy2f"),
|
|
45
|
+
// 0x30–0x38: properties of the current object (operand = property offset in bytes)
|
|
46
|
+
op("pprev"), op("pToa", "W"), op("aTop", "W"), op("pTos", "W"), op("sTop", "W"),
|
|
47
|
+
op("ipToa", "W"), op("dpToa", "W"), op("ipTos", "W"), op("dpTos", "W"),
|
|
48
|
+
// 0x39–0x3f
|
|
49
|
+
op("lofsa", "W"), op("lofss", "W"), // address of a heap object/string
|
|
50
|
+
op("push0"), op("push1"), op("push2"), op("pushSelf"), op("dummy3f"),
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 0x40–0x7f: variable access, generated from a pattern:
|
|
55
|
+
* bits 0-1: g(lobal) l(ocal) t(emp) p(aram)
|
|
56
|
+
* bit 2: a = acc, s = stack
|
|
57
|
+
* bit 3: i = indexed by acc
|
|
58
|
+
* bits 4-5: l(oad), s(tore), + (increment), - (decrement)
|
|
59
|
+
*/
|
|
60
|
+
const varOps = Array.from({ length: 64 }, (_, i) => {
|
|
61
|
+
const kind = "gltp"[i & 3]!;
|
|
62
|
+
const target = i & 4 ? "s" : "a";
|
|
63
|
+
const indexed = i & 8 ? "i" : "";
|
|
64
|
+
const action = ["l", "s", "+", "-"][i >> 4]!;
|
|
65
|
+
return op(`${action}${target}${kind}${indexed}`, "W");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export const opcodes: readonly OpcodeInfo[] = [...base, ...varOps];
|
|
69
|
+
|
|
70
|
+
export type VarKind = "global" | "local" | "temp" | "param";
|
|
71
|
+
export const varKind = (opcode: number): VarKind | undefined =>
|
|
72
|
+
opcode >= 0x40 ? (["global", "local", "temp", "param"] as const)[opcode & 3] : undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Object property slots common to every object (SCI1.1/SCI2 layout, in words). */
|
|
2
|
+
export declare const ObjectSlot: {
|
|
3
|
+
readonly Magic: 0;
|
|
4
|
+
readonly Size: 1;
|
|
5
|
+
readonly PropDict: 2;
|
|
6
|
+
readonly MethDict: 3;
|
|
7
|
+
readonly ClassScript: 4;
|
|
8
|
+
readonly Species: 5;
|
|
9
|
+
readonly Super: 6;
|
|
10
|
+
readonly Info: 7;
|
|
11
|
+
readonly Name: 8;
|
|
12
|
+
};
|
|
13
|
+
export interface Method {
|
|
14
|
+
selector: number;
|
|
15
|
+
/** Code offset in the script resource. */
|
|
16
|
+
offset: number;
|
|
17
|
+
}
|
|
18
|
+
export interface ScriptObject {
|
|
19
|
+
/** Offset of the object in the heap resource. */
|
|
20
|
+
offset: number;
|
|
21
|
+
name: string;
|
|
22
|
+
isClass: boolean;
|
|
23
|
+
species: number;
|
|
24
|
+
superclass: number;
|
|
25
|
+
/** Raw property values, including the header slots above. */
|
|
26
|
+
properties: number[];
|
|
27
|
+
/** Selector id per property; only present on classes (instances use their class's). */
|
|
28
|
+
propSelectors?: number[];
|
|
29
|
+
methods: Method[];
|
|
30
|
+
}
|
|
31
|
+
export interface Export {
|
|
32
|
+
index: number;
|
|
33
|
+
offset: number;
|
|
34
|
+
/** Objects live in the heap; procedures are code in the script. Offset 0 = unused slot. */
|
|
35
|
+
kind: "object" | "code" | "empty";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A loaded script: bytecode and dictionaries in the script resource, data (locals,
|
|
39
|
+
* objects, strings) in the heap resource of the same number. SCI3 later merged the two.
|
|
40
|
+
*/
|
|
41
|
+
export interface Script {
|
|
42
|
+
number: number;
|
|
43
|
+
code: Uint8Array;
|
|
44
|
+
heap: Uint8Array;
|
|
45
|
+
exports: Export[];
|
|
46
|
+
locals: number[];
|
|
47
|
+
objects: ScriptObject[];
|
|
48
|
+
}
|
|
49
|
+
/** Reads a NUL-terminated Latin-1 string. */
|
|
50
|
+
export declare function readString(data: Uint8Array, offset: number): string;
|
|
51
|
+
/**
|
|
52
|
+
* Script resource: +0 u16 relocation table offset | +6 u16 export count | +8 u16 exports
|
|
53
|
+
* Heap resource: +0 u16 relocation table offset | +2 u16 local count | locals |
|
|
54
|
+
* objects (each starts with 0x1234) until a 0 word | strings
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseScript(number: number, code: Uint8Array, heap: Uint8Array): Script;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Object property slots common to every object (SCI1.1/SCI2 layout, in words). */
|
|
2
|
+
export const ObjectSlot = {
|
|
3
|
+
Magic: 0, // 0x1234
|
|
4
|
+
Size: 1, // property count
|
|
5
|
+
PropDict: 2, // script offset: selector id per property (classes only)
|
|
6
|
+
MethDict: 3, // script offset: u16 count, then (selector, code offset) pairs
|
|
7
|
+
ClassScript: 4,
|
|
8
|
+
Species: 5, // class number; 0xFFFF on instances
|
|
9
|
+
Super: 6, // superclass number
|
|
10
|
+
Info: 7, // flags; 0x8000 = class
|
|
11
|
+
Name: 8, // heap offset of the name string
|
|
12
|
+
};
|
|
13
|
+
const OBJECT_MAGIC = 0x1234;
|
|
14
|
+
const INFO_CLASS = 0x8000;
|
|
15
|
+
/** Reads a NUL-terminated Latin-1 string. */
|
|
16
|
+
export function readString(data, offset) {
|
|
17
|
+
let end = offset;
|
|
18
|
+
while (end < data.length && data[end] !== 0)
|
|
19
|
+
end++;
|
|
20
|
+
return new TextDecoder("latin1").decode(data.subarray(offset, end));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Script resource: +0 u16 relocation table offset | +6 u16 export count | +8 u16 exports
|
|
24
|
+
* Heap resource: +0 u16 relocation table offset | +2 u16 local count | locals |
|
|
25
|
+
* objects (each starts with 0x1234) until a 0 word | strings
|
|
26
|
+
*/
|
|
27
|
+
export function parseScript(number, code, heap) {
|
|
28
|
+
const cv = new DataView(code.buffer, code.byteOffset, code.byteLength);
|
|
29
|
+
const hv = new DataView(heap.buffer, heap.byteOffset, heap.byteLength);
|
|
30
|
+
const heapWord = (o) => hv.getUint16(o, true);
|
|
31
|
+
const codeWord = (o) => cv.getUint16(o, true);
|
|
32
|
+
const localCount = heapWord(2);
|
|
33
|
+
const locals = Array.from({ length: localCount }, (_, i) => heapWord(4 + i * 2));
|
|
34
|
+
const objects = [];
|
|
35
|
+
for (let pos = 4 + localCount * 2; pos + 2 <= heap.length && heapWord(pos) === OBJECT_MAGIC;) {
|
|
36
|
+
const size = heapWord(pos + 2);
|
|
37
|
+
const properties = Array.from({ length: size }, (_, i) => heapWord(pos + i * 2));
|
|
38
|
+
const isClass = (properties[ObjectSlot.Info] & INFO_CLASS) !== 0;
|
|
39
|
+
const methDict = properties[ObjectSlot.MethDict];
|
|
40
|
+
const methods = Array.from({ length: codeWord(methDict) }, (_, i) => ({
|
|
41
|
+
selector: codeWord(methDict + 2 + i * 4),
|
|
42
|
+
offset: codeWord(methDict + 4 + i * 4),
|
|
43
|
+
}));
|
|
44
|
+
const propDict = properties[ObjectSlot.PropDict];
|
|
45
|
+
objects.push({
|
|
46
|
+
offset: pos,
|
|
47
|
+
name: readString(heap, properties[ObjectSlot.Name]),
|
|
48
|
+
isClass,
|
|
49
|
+
species: properties[ObjectSlot.Species],
|
|
50
|
+
superclass: properties[ObjectSlot.Super],
|
|
51
|
+
properties,
|
|
52
|
+
propSelectors: isClass ? Array.from({ length: size }, (_, i) => codeWord(propDict + i * 2)) : undefined,
|
|
53
|
+
methods,
|
|
54
|
+
});
|
|
55
|
+
pos += size * 2;
|
|
56
|
+
}
|
|
57
|
+
const objectOffsets = new Set(objects.map((o) => o.offset));
|
|
58
|
+
const exports = Array.from({ length: codeWord(6) }, (_, index) => {
|
|
59
|
+
const offset = codeWord(8 + index * 2);
|
|
60
|
+
return { index, offset, kind: offset === 0 ? "empty" : objectOffsets.has(offset) ? "object" : "code" };
|
|
61
|
+
});
|
|
62
|
+
return { number, code, heap, exports, locals, objects };
|
|
63
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/** Object property slots common to every object (SCI1.1/SCI2 layout, in words). */
|
|
2
|
+
export const ObjectSlot = {
|
|
3
|
+
Magic: 0, // 0x1234
|
|
4
|
+
Size: 1, // property count
|
|
5
|
+
PropDict: 2, // script offset: selector id per property (classes only)
|
|
6
|
+
MethDict: 3, // script offset: u16 count, then (selector, code offset) pairs
|
|
7
|
+
ClassScript: 4,
|
|
8
|
+
Species: 5, // class number; 0xFFFF on instances
|
|
9
|
+
Super: 6, // superclass number
|
|
10
|
+
Info: 7, // flags; 0x8000 = class
|
|
11
|
+
Name: 8, // heap offset of the name string
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
const OBJECT_MAGIC = 0x1234;
|
|
15
|
+
const INFO_CLASS = 0x8000;
|
|
16
|
+
|
|
17
|
+
export interface Method {
|
|
18
|
+
selector: number;
|
|
19
|
+
/** Code offset in the script resource. */
|
|
20
|
+
offset: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ScriptObject {
|
|
24
|
+
/** Offset of the object in the heap resource. */
|
|
25
|
+
offset: number;
|
|
26
|
+
name: string;
|
|
27
|
+
isClass: boolean;
|
|
28
|
+
species: number;
|
|
29
|
+
superclass: number;
|
|
30
|
+
/** Raw property values, including the header slots above. */
|
|
31
|
+
properties: number[];
|
|
32
|
+
/** Selector id per property; only present on classes (instances use their class's). */
|
|
33
|
+
propSelectors?: number[];
|
|
34
|
+
methods: Method[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Export {
|
|
38
|
+
index: number;
|
|
39
|
+
offset: number;
|
|
40
|
+
/** Objects live in the heap; procedures are code in the script. Offset 0 = unused slot. */
|
|
41
|
+
kind: "object" | "code" | "empty";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A loaded script: bytecode and dictionaries in the script resource, data (locals,
|
|
46
|
+
* objects, strings) in the heap resource of the same number. SCI3 later merged the two.
|
|
47
|
+
*/
|
|
48
|
+
export interface Script {
|
|
49
|
+
number: number;
|
|
50
|
+
code: Uint8Array;
|
|
51
|
+
heap: Uint8Array;
|
|
52
|
+
exports: Export[];
|
|
53
|
+
locals: number[];
|
|
54
|
+
objects: ScriptObject[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Reads a NUL-terminated Latin-1 string. */
|
|
58
|
+
export function readString(data: Uint8Array, offset: number): string {
|
|
59
|
+
let end = offset;
|
|
60
|
+
while (end < data.length && data[end] !== 0) end++;
|
|
61
|
+
return new TextDecoder("latin1").decode(data.subarray(offset, end));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Script resource: +0 u16 relocation table offset | +6 u16 export count | +8 u16 exports
|
|
66
|
+
* Heap resource: +0 u16 relocation table offset | +2 u16 local count | locals |
|
|
67
|
+
* objects (each starts with 0x1234) until a 0 word | strings
|
|
68
|
+
*/
|
|
69
|
+
export function parseScript(number: number, code: Uint8Array, heap: Uint8Array): Script {
|
|
70
|
+
const cv = new DataView(code.buffer, code.byteOffset, code.byteLength);
|
|
71
|
+
const hv = new DataView(heap.buffer, heap.byteOffset, heap.byteLength);
|
|
72
|
+
const heapWord = (o: number) => hv.getUint16(o, true);
|
|
73
|
+
const codeWord = (o: number) => cv.getUint16(o, true);
|
|
74
|
+
|
|
75
|
+
const localCount = heapWord(2);
|
|
76
|
+
const locals = Array.from({ length: localCount }, (_, i) => heapWord(4 + i * 2));
|
|
77
|
+
|
|
78
|
+
const objects: ScriptObject[] = [];
|
|
79
|
+
for (let pos = 4 + localCount * 2; pos + 2 <= heap.length && heapWord(pos) === OBJECT_MAGIC; ) {
|
|
80
|
+
const size = heapWord(pos + 2);
|
|
81
|
+
const properties = Array.from({ length: size }, (_, i) => heapWord(pos + i * 2));
|
|
82
|
+
const isClass = (properties[ObjectSlot.Info]! & INFO_CLASS) !== 0;
|
|
83
|
+
|
|
84
|
+
const methDict = properties[ObjectSlot.MethDict]!;
|
|
85
|
+
const methods = Array.from({ length: codeWord(methDict) }, (_, i) => ({
|
|
86
|
+
selector: codeWord(methDict + 2 + i * 4),
|
|
87
|
+
offset: codeWord(methDict + 4 + i * 4),
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
const propDict = properties[ObjectSlot.PropDict]!;
|
|
91
|
+
objects.push({
|
|
92
|
+
offset: pos,
|
|
93
|
+
name: readString(heap, properties[ObjectSlot.Name]!),
|
|
94
|
+
isClass,
|
|
95
|
+
species: properties[ObjectSlot.Species]!,
|
|
96
|
+
superclass: properties[ObjectSlot.Super]!,
|
|
97
|
+
properties,
|
|
98
|
+
propSelectors: isClass ? Array.from({ length: size }, (_, i) => codeWord(propDict + i * 2)) : undefined,
|
|
99
|
+
methods,
|
|
100
|
+
});
|
|
101
|
+
pos += size * 2;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const objectOffsets = new Set(objects.map((o) => o.offset));
|
|
105
|
+
const exports = Array.from({ length: codeWord(6) }, (_, index): Export => {
|
|
106
|
+
const offset = codeWord(8 + index * 2);
|
|
107
|
+
return { index, offset, kind: offset === 0 ? "empty" : objectOffsets.has(offset) ? "object" : "code" };
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return { number, code, heap, exports, locals, objects };
|
|
111
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reader for SCI script source: parenthesised lists, with a few kinds of atom.
|
|
3
|
+
*
|
|
4
|
+
* (send obj x: 1) a list [buf 3] an array element (or declaration)
|
|
5
|
+
* foo x: y? &rest symbols 42 -7 $1F %101 `a numbers
|
|
6
|
+
* "text" {text} strings #init a selector
|
|
7
|
+
* @buf @[buf i] a variable's address, an element's ; ... a comment, to the end of the line
|
|
8
|
+
*/
|
|
9
|
+
export type Node = {
|
|
10
|
+
kind: "list";
|
|
11
|
+
items: Node[];
|
|
12
|
+
line: number;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "index";
|
|
15
|
+
items: Node[];
|
|
16
|
+
line: number;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "sym";
|
|
19
|
+
name: string;
|
|
20
|
+
line: number;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "num";
|
|
23
|
+
value: number;
|
|
24
|
+
line: number;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "str";
|
|
27
|
+
value: string;
|
|
28
|
+
line: number;
|
|
29
|
+
} | {
|
|
30
|
+
kind: "sel";
|
|
31
|
+
name: string;
|
|
32
|
+
line: number;
|
|
33
|
+
} | {
|
|
34
|
+
kind: "addr";
|
|
35
|
+
name: string;
|
|
36
|
+
index?: Node;
|
|
37
|
+
line: number;
|
|
38
|
+
};
|
|
39
|
+
export declare class ReadError extends Error {
|
|
40
|
+
readonly line: number;
|
|
41
|
+
constructor(message: string, line: number);
|
|
42
|
+
}
|
|
43
|
+
export declare function read(text: string): Node[];
|
|
44
|
+
/** A node as source text again, for error messages. */
|
|
45
|
+
export declare function show(n: Node): string;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
export class ReadError extends Error {
|
|
2
|
+
line;
|
|
3
|
+
constructor(message, line) {
|
|
4
|
+
super(`line ${line}: ${message}`);
|
|
5
|
+
this.line = line;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
const DELIMITERS = new Set([..."()[]{}\";"]);
|
|
9
|
+
const isSpace = (c) => c === " " || c === "\t" || c === "\r" || c === "\n";
|
|
10
|
+
export function read(text) {
|
|
11
|
+
let p = 0, line = 1;
|
|
12
|
+
const top = [];
|
|
13
|
+
const stack = [];
|
|
14
|
+
const add = (n) => (stack.length ? stack[stack.length - 1].node.items : top).push(n);
|
|
15
|
+
while (p < text.length) {
|
|
16
|
+
const c = text[p];
|
|
17
|
+
if (c === "\n") {
|
|
18
|
+
line++;
|
|
19
|
+
p++;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (isSpace(c)) {
|
|
23
|
+
p++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (c === ";") {
|
|
27
|
+
while (p < text.length && text[p] !== "\n")
|
|
28
|
+
p++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (c === "(" || c === "[") {
|
|
32
|
+
const node = { kind: c === "(" ? "list" : "index", items: [], line };
|
|
33
|
+
add(node);
|
|
34
|
+
stack.push({ node, close: c === "(" ? ")" : "]" });
|
|
35
|
+
p++;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (c === ")" || c === "]") {
|
|
39
|
+
const open = stack.pop();
|
|
40
|
+
if (!open)
|
|
41
|
+
throw new ReadError(`unexpected ${c}`, line);
|
|
42
|
+
if (open.close !== c)
|
|
43
|
+
throw new ReadError(`expected ${open.close} to close the ${open.close === ")" ? "(" : "["} on line ${open.node.line}, got ${c}`, line);
|
|
44
|
+
p++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (c === '"' || c === "{") {
|
|
48
|
+
const end = c === '"' ? '"' : "}";
|
|
49
|
+
const start = line;
|
|
50
|
+
let value = "";
|
|
51
|
+
p++;
|
|
52
|
+
for (;;) {
|
|
53
|
+
if (p >= text.length)
|
|
54
|
+
throw new ReadError("string never ends", start);
|
|
55
|
+
const ch = text[p];
|
|
56
|
+
if (ch === end) {
|
|
57
|
+
p++;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
if (ch === "\n")
|
|
61
|
+
line++;
|
|
62
|
+
if (ch === "\\") {
|
|
63
|
+
const n = text[p + 1] ?? "";
|
|
64
|
+
if (n === "n")
|
|
65
|
+
value += "\n";
|
|
66
|
+
else if (n === "t")
|
|
67
|
+
value += "\t";
|
|
68
|
+
else if (/[0-9a-fA-F]/.test(n) && /[0-9a-fA-F]/.test(text[p + 2] ?? "")) {
|
|
69
|
+
value += String.fromCharCode(parseInt(text.slice(p + 1, p + 3), 16));
|
|
70
|
+
p++;
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
value += n;
|
|
74
|
+
p += 2;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
value += ch;
|
|
78
|
+
p++;
|
|
79
|
+
}
|
|
80
|
+
add({ kind: "str", value, line: start });
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (c === "`") {
|
|
84
|
+
// A character: `a is 97; `^a is control-A; `#4 is a key with a scan code (not supported).
|
|
85
|
+
const ch = text[p + 1];
|
|
86
|
+
if (ch === undefined)
|
|
87
|
+
throw new ReadError("` needs a character", line);
|
|
88
|
+
if (ch === "^" && text[p + 2] && !isSpace(text[p + 2])) {
|
|
89
|
+
add({ kind: "num", value: text[p + 2].toUpperCase().charCodeAt(0) - 64, line });
|
|
90
|
+
p += 3;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
add({ kind: "num", value: ch.charCodeAt(0), line });
|
|
94
|
+
p += 2;
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
let e = p;
|
|
99
|
+
while (e < text.length && !isSpace(text[e]) && !DELIMITERS.has(text[e]))
|
|
100
|
+
e++;
|
|
101
|
+
const token = text.slice(p, e);
|
|
102
|
+
p = e;
|
|
103
|
+
add(atom(token, line));
|
|
104
|
+
}
|
|
105
|
+
if (stack.length)
|
|
106
|
+
throw new ReadError(`( never closed`, stack[stack.length - 1].node.line);
|
|
107
|
+
return addresses(top);
|
|
108
|
+
}
|
|
109
|
+
/** `@` then `[buf i]` is one thing: the address of an element. */
|
|
110
|
+
function addresses(nodes) {
|
|
111
|
+
const out = [];
|
|
112
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
113
|
+
const n = nodes[i];
|
|
114
|
+
const next = nodes[i + 1];
|
|
115
|
+
if (n.kind === "sym" && n.name === "@" && next?.kind === "index" && next.items.length === 2 && next.items[0].kind === "sym") {
|
|
116
|
+
out.push({ kind: "addr", name: next.items[0].name, index: addresses([next.items[1]])[0], line: n.line });
|
|
117
|
+
i++;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (n.kind === "list" || n.kind === "index")
|
|
121
|
+
out.push({ ...n, items: addresses(n.items) });
|
|
122
|
+
else
|
|
123
|
+
out.push(n);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
function atom(token, line) {
|
|
128
|
+
if (/^-?\d+$/.test(token))
|
|
129
|
+
return { kind: "num", value: Number(token), line };
|
|
130
|
+
if (/^-?\$[0-9a-fA-F]+$/.test(token))
|
|
131
|
+
return { kind: "num", value: (token.startsWith("-") ? -1 : 1) * parseInt(token.replace(/^-?\$/, ""), 16), line };
|
|
132
|
+
if (/^%[01]+$/.test(token))
|
|
133
|
+
return { kind: "num", value: parseInt(token.slice(1), 2), line };
|
|
134
|
+
if (token.startsWith("#") && token.length > 1)
|
|
135
|
+
return { kind: "sel", name: token.slice(1), line };
|
|
136
|
+
if (token.startsWith("@") && token.length > 1)
|
|
137
|
+
return { kind: "addr", name: token.slice(1), line };
|
|
138
|
+
return { kind: "sym", name: token, line };
|
|
139
|
+
}
|
|
140
|
+
/** A node as source text again, for error messages. */
|
|
141
|
+
export function show(n) {
|
|
142
|
+
switch (n.kind) {
|
|
143
|
+
case "list": return `(${n.items.map(show).join(" ")})`;
|
|
144
|
+
case "index": return `[${n.items.map(show).join(" ")}]`;
|
|
145
|
+
case "sym": return n.name;
|
|
146
|
+
case "num": return String(n.value);
|
|
147
|
+
case "str": return JSON.stringify(n.value);
|
|
148
|
+
case "sel": return `#${n.name}`;
|
|
149
|
+
case "addr": return n.index ? `@[${n.name} ${show(n.index)}]` : `@${n.name}`;
|
|
150
|
+
}
|
|
151
|
+
}
|