From c81080659ef7c2494d82d64c0a727321d53ede8d Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:11:42 -0700 Subject: [PATCH] refactor(frontend): fix loading code to editor Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- cspell.json | 5 +- ic10emu/src/interpreter/instructions.rs | 12 + ic10emu/src/vm/instructions/codegen/enums.rs | 12 + ic10emu/src/vm/instructions/codegen/traits.rs | 59 ++- www/package.json | 4 +- www/pnpm-lock.yaml | 21 +- www/src/ts/app/save.ts | 6 +- www/src/ts/editor/index.ts | 4 +- www/src/ts/presets/default.ts | 99 +++++ www/src/ts/presets/demo.ts | 3 +- www/src/ts/presets/index.ts | 3 +- www/src/ts/session.ts | 400 ++---------------- www/src/ts/sessionDB.ts | 345 +++++++++++++++ www/src/ts/virtualMachine/index.ts | 17 +- www/src/ts/virtualMachine/state.ts | 11 +- xtask/src/generate/instructions.rs | 1 + 16 files changed, 599 insertions(+), 403 deletions(-) create mode 100644 www/src/ts/presets/default.ts create mode 100644 www/src/ts/sessionDB.ts diff --git a/cspell.json b/cspell.json index db8a446..87dd65a 100644 --- a/cspell.json +++ b/cspell.json @@ -60,6 +60,7 @@ "brne", "brnez", "Circuitboard", + "Clrd", "codegen", "conv", "cstyle", @@ -110,6 +111,7 @@ "reagentmodes", "repr", "retval", + "Rmap", "rocketstation", "rparen", "sapz", @@ -143,6 +145,7 @@ "trunc", "Tsify", "uneval", - "whos" + "whos", + "xtask" ] } diff --git a/ic10emu/src/interpreter/instructions.rs b/ic10emu/src/interpreter/instructions.rs index f36df66..df5cd38 100644 --- a/ic10emu/src/interpreter/instructions.rs +++ b/ic10emu/src/interpreter/instructions.rs @@ -2700,3 +2700,15 @@ impl LabelInstruction for T { Ok(()) } } + +impl RmapInstruction for T { + ///rmap r? d? reagentHash(r?|num) + fn execute_inner( + &mut self, + r: &crate::vm::instructions::operands::InstOperand, + d: &crate::vm::instructions::operands::InstOperand, + reagent_hash: &crate::vm::instructions::operands::InstOperand, + ) -> Result<(), crate::errors::ICError> { + todo!() + } +} diff --git a/ic10emu/src/vm/instructions/codegen/enums.rs b/ic10emu/src/vm/instructions/codegen/enums.rs index b35806d..80d659a 100644 --- a/ic10emu/src/vm/instructions/codegen/enums.rs +++ b/ic10emu/src/vm/instructions/codegen/enums.rs @@ -36,6 +36,7 @@ use wasm_bindgen::prelude::*; #[strum(use_phf, serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] pub enum InstructionOp { + #[strum(props(example = "", desc = "No Operation", operands = "0"))] Nop, #[strum( props( @@ -837,6 +838,14 @@ pub enum InstructionOp { ) )] Rand, + #[strum( + props( + example = "rmap r? d? reagentHash(r?|num)", + desc = "Given a reagent hash, store the corresponding prefab hash that the device expects to fulfill the reagent requirement. For example, on an autolathe, the hash for Iron will store the hash for ItemIronIngot.", + operands = "3" + ) + )] + Rmap, #[strum( props( example = "round r? a(r?|num)", @@ -1459,6 +1468,9 @@ impl InstructionOp { ic.execute_putd(&operands[0usize], &operands[1usize], &operands[2usize]) } Self::Rand => ic.execute_rand(&operands[0usize]), + Self::Rmap => { + ic.execute_rmap(&operands[0usize], &operands[1usize], &operands[2usize]) + } Self::Round => ic.execute_round(&operands[0usize], &operands[1usize]), Self::S => { ic.execute_s(&operands[0usize], &operands[1usize], &operands[2usize]) diff --git a/ic10emu/src/vm/instructions/codegen/traits.rs b/ic10emu/src/vm/instructions/codegen/traits.rs index 53167b0..eff587a 100644 --- a/ic10emu/src/vm/instructions/codegen/traits.rs +++ b/ic10emu/src/vm/instructions/codegen/traits.rs @@ -3291,6 +3291,41 @@ pub trait RandInstruction: IntegratedCircuit { r: &crate::vm::instructions::operands::InstOperand, ) -> Result<(), crate::errors::ICError>; } +pub trait RmapInstruction: IntegratedCircuit { + ///rmap r? d? reagentHash(r?|num) + fn execute_rmap( + &mut self, + r: &crate::vm::instructions::operands::Operand, + d: &crate::vm::instructions::operands::Operand, + reagent_hash: &crate::vm::instructions::operands::Operand, + ) -> Result<(), crate::errors::ICError> { + RmapInstruction::execute_inner( + self, + &crate::vm::instructions::operands::InstOperand::new( + r, + InstructionOp::Rmap, + 0usize, + ), + &crate::vm::instructions::operands::InstOperand::new( + d, + InstructionOp::Rmap, + 1usize, + ), + &crate::vm::instructions::operands::InstOperand::new( + reagent_hash, + InstructionOp::Rmap, + 2usize, + ), + ) + } + ///rmap r? d? reagentHash(r?|num) + fn execute_inner( + &mut self, + r: &crate::vm::instructions::operands::InstOperand, + d: &crate::vm::instructions::operands::InstOperand, + reagent_hash: &crate::vm::instructions::operands::InstOperand, + ) -> Result<(), crate::errors::ICError>; +} pub trait RoundInstruction: IntegratedCircuit { ///round r? a(r?|num) fn execute_round( @@ -4587,7 +4622,7 @@ pub trait YieldInstruction: IntegratedCircuit { ///yield fn execute_inner(&mut self) -> Result<(), crate::errors::ICError>; } -pub trait ICInstructable: AbsInstruction + AcosInstruction + AddInstruction + AliasInstruction + AndInstruction + AsinInstruction + AtanInstruction + Atan2Instruction + BapInstruction + BapalInstruction + BapzInstruction + BapzalInstruction + BdnsInstruction + BdnsalInstruction + BdseInstruction + BdsealInstruction + BeqInstruction + BeqalInstruction + BeqzInstruction + BeqzalInstruction + BgeInstruction + BgealInstruction + BgezInstruction + BgezalInstruction + BgtInstruction + BgtalInstruction + BgtzInstruction + BgtzalInstruction + BleInstruction + BlealInstruction + BlezInstruction + BlezalInstruction + BltInstruction + BltalInstruction + BltzInstruction + BltzalInstruction + BnaInstruction + BnaalInstruction + BnanInstruction + BnazInstruction + BnazalInstruction + BneInstruction + BnealInstruction + BnezInstruction + BnezalInstruction + BrapInstruction + BrapzInstruction + BrdnsInstruction + BrdseInstruction + BreqInstruction + BreqzInstruction + BrgeInstruction + BrgezInstruction + BrgtInstruction + BrgtzInstruction + BrleInstruction + BrlezInstruction + BrltInstruction + BrltzInstruction + BrnaInstruction + BrnanInstruction + BrnazInstruction + BrneInstruction + BrnezInstruction + CeilInstruction + ClrInstruction + ClrdInstruction + CosInstruction + DefineInstruction + DivInstruction + ExpInstruction + FloorInstruction + GetInstruction + GetdInstruction + HcfInstruction + JInstruction + JalInstruction + JrInstruction + LInstruction + LabelInstruction + LbInstruction + LbnInstruction + LbnsInstruction + LbsInstruction + LdInstruction + LogInstruction + LrInstruction + LsInstruction + MaxInstruction + MinInstruction + ModInstruction + MoveInstruction + MulInstruction + NorInstruction + NotInstruction + OrInstruction + PeekInstruction + PokeInstruction + PopInstruction + PushInstruction + PutInstruction + PutdInstruction + RandInstruction + RoundInstruction + SInstruction + SapInstruction + SapzInstruction + SbInstruction + SbnInstruction + SbsInstruction + SdInstruction + SdnsInstruction + SdseInstruction + SelectInstruction + SeqInstruction + SeqzInstruction + SgeInstruction + SgezInstruction + SgtInstruction + SgtzInstruction + SinInstruction + SlaInstruction + SleInstruction + SleepInstruction + SlezInstruction + SllInstruction + SltInstruction + SltzInstruction + SnaInstruction + SnanInstruction + SnanzInstruction + SnazInstruction + SneInstruction + SnezInstruction + SqrtInstruction + SraInstruction + SrlInstruction + SsInstruction + SubInstruction + TanInstruction + TruncInstruction + XorInstruction + YieldInstruction {} +pub trait ICInstructable: AbsInstruction + AcosInstruction + AddInstruction + AliasInstruction + AndInstruction + AsinInstruction + AtanInstruction + Atan2Instruction + BapInstruction + BapalInstruction + BapzInstruction + BapzalInstruction + BdnsInstruction + BdnsalInstruction + BdseInstruction + BdsealInstruction + BeqInstruction + BeqalInstruction + BeqzInstruction + BeqzalInstruction + BgeInstruction + BgealInstruction + BgezInstruction + BgezalInstruction + BgtInstruction + BgtalInstruction + BgtzInstruction + BgtzalInstruction + BleInstruction + BlealInstruction + BlezInstruction + BlezalInstruction + BltInstruction + BltalInstruction + BltzInstruction + BltzalInstruction + BnaInstruction + BnaalInstruction + BnanInstruction + BnazInstruction + BnazalInstruction + BneInstruction + BnealInstruction + BnezInstruction + BnezalInstruction + BrapInstruction + BrapzInstruction + BrdnsInstruction + BrdseInstruction + BreqInstruction + BreqzInstruction + BrgeInstruction + BrgezInstruction + BrgtInstruction + BrgtzInstruction + BrleInstruction + BrlezInstruction + BrltInstruction + BrltzInstruction + BrnaInstruction + BrnanInstruction + BrnazInstruction + BrneInstruction + BrnezInstruction + CeilInstruction + ClrInstruction + ClrdInstruction + CosInstruction + DefineInstruction + DivInstruction + ExpInstruction + FloorInstruction + GetInstruction + GetdInstruction + HcfInstruction + JInstruction + JalInstruction + JrInstruction + LInstruction + LabelInstruction + LbInstruction + LbnInstruction + LbnsInstruction + LbsInstruction + LdInstruction + LogInstruction + LrInstruction + LsInstruction + MaxInstruction + MinInstruction + ModInstruction + MoveInstruction + MulInstruction + NorInstruction + NotInstruction + OrInstruction + PeekInstruction + PokeInstruction + PopInstruction + PushInstruction + PutInstruction + PutdInstruction + RandInstruction + RmapInstruction + RoundInstruction + SInstruction + SapInstruction + SapzInstruction + SbInstruction + SbnInstruction + SbsInstruction + SdInstruction + SdnsInstruction + SdseInstruction + SelectInstruction + SeqInstruction + SeqzInstruction + SgeInstruction + SgezInstruction + SgtInstruction + SgtzInstruction + SinInstruction + SlaInstruction + SleInstruction + SleepInstruction + SlezInstruction + SllInstruction + SltInstruction + SltzInstruction + SnaInstruction + SnanInstruction + SnanzInstruction + SnazInstruction + SneInstruction + SnezInstruction + SqrtInstruction + SraInstruction + SrlInstruction + SsInstruction + SubInstruction + TanInstruction + TruncInstruction + XorInstruction + YieldInstruction {} impl ICInstructable for T where T: AbsInstruction + AcosInstruction + AddInstruction + AliasInstruction @@ -4615,15 +4650,15 @@ where + MinInstruction + ModInstruction + MoveInstruction + MulInstruction + NorInstruction + NotInstruction + OrInstruction + PeekInstruction + PokeInstruction + PopInstruction + PushInstruction + PutInstruction - + PutdInstruction + RandInstruction + RoundInstruction + SInstruction - + SapInstruction + SapzInstruction + SbInstruction + SbnInstruction - + SbsInstruction + SdInstruction + SdnsInstruction + SdseInstruction - + SelectInstruction + SeqInstruction + SeqzInstruction + SgeInstruction - + SgezInstruction + SgtInstruction + SgtzInstruction + SinInstruction - + SlaInstruction + SleInstruction + SleepInstruction + SlezInstruction - + SllInstruction + SltInstruction + SltzInstruction + SnaInstruction - + SnanInstruction + SnanzInstruction + SnazInstruction + SneInstruction - + SnezInstruction + SqrtInstruction + SraInstruction + SrlInstruction - + SsInstruction + SubInstruction + TanInstruction + TruncInstruction - + XorInstruction + YieldInstruction, + + PutdInstruction + RandInstruction + RmapInstruction + RoundInstruction + + SInstruction + SapInstruction + SapzInstruction + SbInstruction + + SbnInstruction + SbsInstruction + SdInstruction + SdnsInstruction + + SdseInstruction + SelectInstruction + SeqInstruction + SeqzInstruction + + SgeInstruction + SgezInstruction + SgtInstruction + SgtzInstruction + + SinInstruction + SlaInstruction + SleInstruction + SleepInstruction + + SlezInstruction + SllInstruction + SltInstruction + SltzInstruction + + SnaInstruction + SnanInstruction + SnanzInstruction + SnazInstruction + + SneInstruction + SnezInstruction + SqrtInstruction + SraInstruction + + SrlInstruction + SsInstruction + SubInstruction + TanInstruction + + TruncInstruction + XorInstruction + YieldInstruction, {} diff --git a/www/package.json b/www/package.json index f3416a1..acca53f 100644 --- a/www/package.json +++ b/www/package.json @@ -52,8 +52,8 @@ "@lit/context": "^1.1.2", "@popperjs/core": "^2.11.8", "@shoelace-style/shoelace": "^2.16.0", - "ace-builds": "^1.35.4", - "ace-linters": "^1.2.3", + "ace-builds": "^1.36.0", + "ace-linters": "^1.3.0", "bootstrap": "^5.3.3", "bson": "^6.8.0", "buffer": "^6.0.3", diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml index bc60e5d..843e8be 100644 --- a/www/pnpm-lock.yaml +++ b/www/pnpm-lock.yaml @@ -24,11 +24,11 @@ importers: specifier: ^2.16.0 version: 2.16.0(@types/react@18.2.79) ace-builds: - specifier: ^1.35.4 - version: 1.35.4 + specifier: ^1.36.0 + version: 1.36.0 ace-linters: - specifier: ^1.2.3 - version: 1.2.3 + specifier: ^1.3.0 + version: 1.3.0 bootstrap: specifier: ^5.3.3 version: 5.3.3(@popperjs/core@2.11.8) @@ -645,11 +645,11 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - ace-builds@1.35.4: - resolution: {integrity: sha512-r0KQclhZ/uk5a4zOqRYQkJuQuu4vFMiA6VTj54Tk4nI1TUR3iEMMppZkWbNoWEgWwv4ciDloObb9Rf4V55Qgjw==} + ace-builds@1.36.0: + resolution: {integrity: sha512-7to4F86V5N13EY4M9LWaGo2Wmr9iWe5CrYpc28F+/OyYCf7yd+xBV5x9v/GB73EBGGoYd89m6JjeIUjkL6Yw+w==} - ace-linters@1.2.3: - resolution: {integrity: sha512-kfr3WG3zeAQWYLX9NwD+2AYVShvkyfVGJQLuh/wX2qIh2f71bYaFL9h7wHWtl8hofkKgDsWqfsX2LjXjoCqv5g==} + ace-linters@1.3.0: + resolution: {integrity: sha512-dX34G8iVapk+nl2cnhY5ZTKfXLo0MbtvwILsr9PVHEW8/NubudJkEE5twkQ69vGOJp2dW2i5jrU417X8CpXSIA==} acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} @@ -3320,9 +3320,9 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - ace-builds@1.35.4: {} + ace-builds@1.36.0: {} - ace-linters@1.2.3: + ace-linters@1.3.0: dependencies: '@xml-tools/ast': 5.0.5 '@xml-tools/constraints': 1.1.1 @@ -3337,6 +3337,7 @@ snapshots: vscode-languageserver-protocol: 3.17.5 vscode-languageserver-textdocument: 1.0.12 vscode-languageserver-types: 3.17.5 + vscode-uri: 3.0.8 vscode-ws-jsonrpc: 2.0.2 transitivePeerDependencies: - encoding diff --git a/www/src/ts/app/save.ts b/www/src/ts/app/save.ts index a4fe9ca..14cc33d 100644 --- a/www/src/ts/app/save.ts +++ b/www/src/ts/app/save.ts @@ -1,7 +1,7 @@ -import { HTMLTemplateResult, html, css, CSSResultGroup } from "lit"; -import { customElement, property, query, state } from "lit/decorators.js"; +import { html, css} from "lit"; +import { customElement, query, state } from "lit/decorators.js"; import { BaseElement, defaultCss } from "components"; -import { SessionDB } from "session"; +import { SessionDB } from "sessionDB"; import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js"; import { repeat } from "lit/directives/repeat.js"; diff --git a/www/src/ts/editor/index.ts b/www/src/ts/editor/index.ts index 41fdafa..340d76f 100644 --- a/www/src/ts/editor/index.ts +++ b/www/src/ts/editor/index.ts @@ -243,9 +243,9 @@ export class IC10Editor extends BaseElement { app.session.onLoad((_e) => { const session = app.session; const updated_ids: number[] = []; - for (const [id, code] of session.programs.value) { + for (const [id, code] of session.programs) { updated_ids.push(id); - that.createOrSetSession(id, code); + that.createOrSetSession(id, code.peek()); } that.activateSession(that.activeSession); for (const [id, _] of that.sessions) { diff --git a/www/src/ts/presets/default.ts b/www/src/ts/presets/default.ts new file mode 100644 index 0000000..33af011 --- /dev/null +++ b/www/src/ts/presets/default.ts @@ -0,0 +1,99 @@ + +import { SessionDB } from "../sessionDB"; + +export const defaultVMState: SessionDB.CurrentDBVmState = { + vm: { + objects: [ + { + obj_info: { + id: 1, + prefab: "StructureCircuitHousing", + socketed_ic: 2, + slots: new Map([ + [0, { id: 2, quantity: 1 }], + ]), + connections: new Map([ + [0, 1], + ]), + // unused, provided to make compiler happy + name: undefined, + prefab_hash: undefined, + compile_errors: undefined, + parent_slot: undefined, + root_parent_human: undefined, + damage: undefined, + device_pins: undefined, + reagents: undefined, + logic_values: undefined, + slot_logic_values: undefined, + entity: undefined, + visible_devices: undefined, + memory: undefined, + source_code: undefined, + circuit: undefined, + }, + template: undefined, + database_template: true, + }, + { + obj_info: { + id: 2, + prefab: "ItemIntegratedCircuit10", + source_code: "", + memory: new Array(512).fill(0), + circuit: { + instruction_pointer: 0, + yield_instruction_count: 0, + state: "Start", + aliases: new Map(), + defines: new Map(), + labels: new Map(), + registers: new Array(18).fill(0), + }, + + // unused, provided to make compiler happy + name: undefined, + prefab_hash: undefined, + compile_errors: undefined, + slots: undefined, + parent_slot: undefined, + root_parent_human: undefined, + damage: undefined, + device_pins: undefined, + connections: undefined, + reagents: undefined, + logic_values: undefined, + slot_logic_values: undefined, + entity: undefined, + socketed_ic: undefined, + visible_devices: undefined, + }, + template: undefined, + database_template: true, + }, + ], + networks: [ + { + id: 1, + devices: [1], + power_only: [], + channels: Array(8).fill(NaN) as [ + number, + number, + number, + number, + number, + number, + number, + number, + ], + }, + ], + program_holders: [2], + circuit_holders: [1], + default_network_key: 1, + wireless_receivers: [], + wireless_transmitters: [], + }, + activeIC: 1, +}; diff --git a/www/src/ts/presets/demo.ts b/www/src/ts/presets/demo.ts index 6b31100..b562eea 100644 --- a/www/src/ts/presets/demo.ts +++ b/www/src/ts/presets/demo.ts @@ -1,5 +1,4 @@ -import { ObjectInfo } from "ic10emu_wasm"; -import { SessionDB } from "../session"; +import { SessionDB } from "../sessionDB"; export const demoCode = `# Highlighting Demo diff --git a/www/src/ts/presets/index.ts b/www/src/ts/presets/index.ts index 21054d2..d28acc9 100644 --- a/www/src/ts/presets/index.ts +++ b/www/src/ts/presets/index.ts @@ -1,3 +1,4 @@ +import { defaultVMState } from "./default"; import { demoVMState } from "./demo"; -export { demoVMState }; +export { defaultVMState, demoVMState }; diff --git a/www/src/ts/session.ts b/www/src/ts/session.ts index b224e0e..a34fc35 100644 --- a/www/src/ts/session.ts +++ b/www/src/ts/session.ts @@ -1,31 +1,20 @@ import type { ICError, - FrozenVM, - RegisterSpec, - DeviceSpec, - LogicType, - LogicSlotType, - LogicField, - Class as SlotType, - FrozenCableNetwork, - FrozenObject, - ObjectInfo, - ICState, ObjectID, } from "ic10emu_wasm"; import { App } from "./app"; -import { openDB, DBSchema, IDBPTransaction, IDBPDatabase } from "idb"; +import { openDB, IDBPTransaction } from "idb"; import { TypedEventTarget, - crc32, - dispatchTypedEvent, fromJson, + structuralEqual, toJson, } from "./utils"; import * as presets from "./presets"; -import { batch, computed, effect, signal, Signal } from "@lit-labs/preact-signals"; +import { computed, signal, Signal } from "@lit-labs/preact-signals"; +import { SessionDB } from "sessionDB"; const { demoVMState } = presets; export interface SessionEventMap { @@ -38,7 +27,7 @@ export interface SessionEventMap { } export class Session extends TypedEventTarget() { - private _programs: Signal>; + private _programs: Map>; private _errors: Signal>; private _activeIC: Signal; private _activeLines: Signal>; @@ -49,7 +38,7 @@ export class Session extends TypedEventTarget() { constructor(app: App) { super(); this.app = app; - this._programs = signal(new Map()); + this._programs = new Map(); this._errors = signal(new Map()); this._save_timeout = undefined; this._activeIC = signal(null); @@ -60,16 +49,23 @@ export class Session extends TypedEventTarget() { window.addEventListener("hashchange", (_event) => { that.loadFromFragment(); }); - - this._programs.subscribe((_) => {this._fireOnLoad()}); } - get programs(): Signal> { + get programs(): Map> { return this._programs; } - set programs(programs: Iterable<[number, string]>) { - this._programs.value = new Map(programs); + set programs(programs: Iterable<[ObjectID, string]>) { + const seenIds: ObjectID[] = [] + for (const [id, code] of programs) { + this.setProgram(id, code); + seenIds.push(id); + } + for (const id of this._programs.keys()) { + if (!seenIds.includes(id)) { + this.setProgram(id, null); + } + } } get activeIC(): Signal { @@ -82,16 +78,14 @@ export class Session extends TypedEventTarget() { } changeID(oldID: ObjectID, newID: ObjectID) { - if (this.programs.peek().has(oldID)) { - const newVal = new Map(this.programs.value); - newVal.set(newID, newVal.get(oldID)); - newVal.delete(oldID); - this.programs.value = newVal; + if (this._programs.has(oldID)) { + this._programs.set(newID, this._programs.get(oldID)); + this._programs.delete(oldID); } this.dispatchCustomEvent("session-id-change", { old: oldID, new: newID }); } - onIDChange(callback: (e: CustomEvent<{ old: ObjectID; new: ObjectID}>) => any) { + onIDChange(callback: (e: CustomEvent<{ old: ObjectID; new: ObjectID }>) => any) { this.addEventListener("session-id-change", callback); } @@ -110,21 +104,36 @@ export class Session extends TypedEventTarget() { setActiveLine(id: ObjectID, line: number) { const last = this._activeLines.peek().get(id); if (last !== line) { - this._activeLines.value = new Map([ ... this._activeLines.value.entries(), [id, line]]); + this._activeLines.value = new Map([... this._activeLines.value.entries(), [id, line]]); this._fireOnActiveLine(id); } } setProgramCode(id: ObjectID, code: string) { - this._programs.value = new Map([ ...this._programs.value.entries(), [id, code]]); + this.setProgram(id, code); if (this.app.vm) { this.app.vm.updateCode(); } this.save(); } + getProgram(id: ObjectID): Signal { + if (!this._programs.has(id)) { + this._programs.set(id, signal(null)); + } + return this._programs.get(id); + } + + private setProgram(id: ObjectID, code: string) { + if (!this._programs.has(id)) { + this._programs.set(id, signal(code)); + } else { + this._programs.get(id).value = code; + } + } + setProgramErrors(id: ObjectID, errors: ICError[]) { - this._errors.value = new Map([ ...this._errors.value.entries(), [id, errors]]); + this._errors.value = new Map([...this._errors.value.entries(), [id, errors]]); this._fireOnErrors([id]); } @@ -223,6 +232,8 @@ export class Session extends TypedEventTarget() { console.log("Bad session data:", data); } } + } else { + this.load(presets.defaultVMState); } } @@ -312,333 +323,6 @@ export class Session extends TypedEventTarget() { } } -export namespace SessionDB { - export namespace V1 { - export interface VMState { - activeIC: number; - vm: FrozenVM; - } - - export interface FrozenVM { - ics: FrozenIC[]; - devices: DeviceTemplate[]; - networks: FrozenNetwork[]; - default_network: number; - } - - export interface FrozenNetwork { - id: number; - devices: number[]; - power_only: number[]; - channels: number[]; - } - export type RegisterSpec = { - readonly RegisterSpec: { - readonly indirection: number; - readonly target: number; - }; - }; - export type DeviceSpec = { - readonly DeviceSpec: { - readonly device: - | "Db" - | { readonly Numbered: number } - | { - readonly Indirect: { - readonly indirection: number; - readonly target: number; - }; - }; - readonly connection: number | undefined; - }; - }; - export type Alias = RegisterSpec | DeviceSpec; - - export type Aliases = Map; - - export type Defines = Map; - - export type Pins = (number | undefined)[]; - export interface SlotOccupantTemplate { - id?: number; - fields: { [key in LogicSlotType]?: LogicField }; - } - export interface ConnectionCableNetwork { - CableNetwork: { - net: number | undefined; - typ: string; - }; - } - export type Connection = ConnectionCableNetwork | "Other"; - - export interface SlotTemplate { - typ: SlotType; - occupant?: SlotOccupantTemplate; - } - - export interface DeviceTemplate { - id?: number; - name?: string; - prefab_name?: string; - slots: SlotTemplate[]; - // reagents: { [key: string]: float} - connections: Connection[]; - fields: { [key in LogicType]?: LogicField }; - } - export interface FrozenIC { - device: number; - id: number; - registers: number[]; - ip: number; - ic: number; - stack: number[]; - aliases: Aliases; - defines: Defines; - pins: Pins; - state: string; - code: string; - } - } - - export namespace V2 { - export interface VMState { - activeIC: number; - vm: FrozenVM; - } - - function objectFromIC(ic: SessionDB.V1.FrozenIC): FrozenObject { - return { - obj_info: { - name: undefined, - id: ic.id, - prefab: "ItemIntegratedCircuit10", - prefab_hash: crc32("ItemIntegratedCircuit10"), - memory: ic.stack, - source_code: ic.code, - compile_errors: undefined, - circuit: { - instruction_pointer: ic.ip, - yield_instruction_count: ic.ic, - state: ic.state as ICState, - aliases: ic.aliases, - defines: ic.defines, - labels: new Map(), - registers: ic.registers, - }, - - // unused - slots: undefined, - parent_slot: undefined, - root_parent_human: undefined, - damage: undefined, - device_pins: undefined, - connections: undefined, - reagents: undefined, - logic_values: undefined, - slot_logic_values: undefined, - entity: undefined, - socketed_ic: undefined, - visible_devices: undefined, - }, - database_template: true, - template: undefined, - }; - } - function objectsFromV1Template( - template: SessionDB.V1.DeviceTemplate, - idFn: () => number, - socketedIcFn: (id: number) => number | undefined, - ): FrozenObject[] { - const slotOccupantsPairs = new Map( - template.slots.flatMap((slot, index) => { - if (typeof slot.occupant !== "undefined") { - return [ - [ - index, - [ - { - obj_info: { - name: undefined, - id: slot.occupant.id ?? idFn(), - prefab: undefined, - prefab_hash: slot.occupant.fields.PrefabHash?.value, - damage: slot.occupant.fields.Damage?.value, - - socketed_ic: undefined, - // unused - memory: undefined, - source_code: undefined, - compile_errors: undefined, - circuit: undefined, - slots: undefined, - device_pins: undefined, - connections: undefined, - reagents: undefined, - logic_values: undefined, - slot_logic_values: undefined, - entity: undefined, - visible_devices: undefined, - }, - database_template: true, - template: undefined, - }, - slot.occupant.fields.Quantity ?? 1, - ], - ], - ] as [number, [FrozenObject, number]][]; - } else { - return [] as [number, [FrozenObject, number]][]; - } - }), - ); - const frozen: FrozenObject = { - obj_info: { - name: template.name, - id: template.id, - prefab: template.prefab_name, - prefab_hash: undefined, - slots: new Map( - Array.from(slotOccupantsPairs.entries()).map( - ([index, [obj, quantity]]) => [ - index, - { - quantity, - id: obj.obj_info.id, - }, - ], - ), - ), - socketed_ic: socketedIcFn(template.id), - - logic_values: new Map( - Object.entries(template.fields).map(([key, val]) => { - return [key as LogicType, val.value]; - }), - ), - - // unused - memory: undefined, - source_code: undefined, - compile_errors: undefined, - circuit: undefined, - parent_slot: undefined, - root_parent_human: undefined, - damage: undefined, - device_pins: undefined, - connections: undefined, - reagents: undefined, - slot_logic_values: undefined, - entity: undefined, - visible_devices: undefined, - }, - database_template: true, - template: undefined, - }; - return [ - ...Array.from(slotOccupantsPairs.entries()).map( - ([_index, [obj, _quantity]]) => obj, - ), - frozen, - ]; - } - - export function fromV1State(v1State: SessionDB.V1.VMState): VMState { - const highestObjetId = Math.max( - ...v1State.vm.devices - .map((device) => device.id ?? -1) - .concat(v1State.vm.ics.map((ic) => ic.id ?? -1)), - ); - let nextId = highestObjetId + 1; - const deviceIcs = new Map( - v1State.vm.ics.map((ic) => [ic.device, objectFromIC(ic)]), - ); - const objects = v1State.vm.devices.flatMap((device) => { - return objectsFromV1Template( - device, - () => nextId++, - (id) => deviceIcs.get(id)?.obj_info.id ?? undefined, - ); - }); - const vm: FrozenVM = { - objects, - circuit_holders: objects.flatMap((obj) => - "socketed_ic" in obj.obj_info && - typeof obj.obj_info.socketed_ic !== "undefined" - ? [obj.obj_info.id] - : [], - ), - program_holders: objects.flatMap((obj) => - "source_code" in obj.obj_info && - typeof obj.obj_info.source_code !== "undefined" - ? [obj.obj_info.id] - : [], - ), - default_network_key: v1State.vm.default_network, - networks: v1State.vm.networks as FrozenCableNetwork[], - wireless_receivers: [], - wireless_transmitters: [], - }; - const v2State: VMState = { - activeIC: v1State.activeIC, - vm, - }; - return v2State; - } - } - - export enum DBVersion { - V1 = 1, - V2 = 2, - } - - export const LOCAL_DB_VERSION = DBVersion.V2 as const; - export type CurrentDBSchema = AppDBSchemaV2; - export type CurrentDBVmState = V2.VMState; - export const LOCAL_DB_SESSION_STORE = "sessionsV2" as const; - - export interface AppDBSchemaV1 extends DBSchema { - sessions: { - key: string; - value: { - name: string; - date: Date; - session: V1.VMState; - }; - indexes: { - "by-date": Date; - "by-name": string; - }; - }; - } - - export interface AppDBSchemaV2 extends DBSchema { - sessions: { - key: string; - value: { - name: string; - date: Date; - session: V1.VMState; - }; - indexes: { - "by-date": Date; - "by-name": string; - }; - }; - sessionsV2: { - key: string; - value: { - name: string; - date: Date; - version: DBVersion.V2; - session: V2.VMState; - }; - indexes: { - "by-date": Date; - "by-name": string; - }; - }; - } -} export interface OldPrograms { programs: [number, string][]; diff --git a/www/src/ts/sessionDB.ts b/www/src/ts/sessionDB.ts new file mode 100644 index 0000000..23fd3bf --- /dev/null +++ b/www/src/ts/sessionDB.ts @@ -0,0 +1,345 @@ +import type { + ICError, + FrozenVM, + RegisterSpec, + DeviceSpec, + LogicType, + LogicSlotType, + LogicField, + Class as SlotType, + FrozenCableNetwork, + FrozenObject, + ObjectInfo, + ICState, + ObjectID, +} from "ic10emu_wasm"; +import { DBSchema } from "idb"; +import { crc32 } from "utils"; + +export namespace SessionDB { + export namespace V1 { + export interface VMState { + activeIC: number; + vm: FrozenVM; + } + + export interface FrozenVM { + ics: FrozenIC[]; + devices: DeviceTemplate[]; + networks: FrozenNetwork[]; + default_network: number; + } + + export interface FrozenNetwork { + id: number; + devices: number[]; + power_only: number[]; + channels: number[]; + } + export type RegisterSpec = { + readonly RegisterSpec: { + readonly indirection: number; + readonly target: number; + }; + }; + export type DeviceSpec = { + readonly DeviceSpec: { + readonly device: + | "Db" + | { readonly Numbered: number } + | { + readonly Indirect: { + readonly indirection: number; + readonly target: number; + }; + }; + readonly connection: number | undefined; + }; + }; + export type Alias = RegisterSpec | DeviceSpec; + + export type Aliases = Map; + + export type Defines = Map; + + export type Pins = (number | undefined)[]; + export interface SlotOccupantTemplate { + id?: number; + fields: { [key in LogicSlotType]?: LogicField }; + } + export interface ConnectionCableNetwork { + CableNetwork: { + net: number | undefined; + typ: string; + }; + } + export type Connection = ConnectionCableNetwork | "Other"; + + export interface SlotTemplate { + typ: SlotType; + occupant?: SlotOccupantTemplate; + } + + export interface DeviceTemplate { + id?: number; + name?: string; + prefab_name?: string; + slots: SlotTemplate[]; + // reagents: { [key: string]: float} + connections: Connection[]; + fields: { [key in LogicType]?: LogicField }; + } + export interface FrozenIC { + device: number; + id: number; + registers: number[]; + ip: number; + ic: number; + stack: number[]; + aliases: Aliases; + defines: Defines; + pins: Pins; + state: string; + code: string; + } + } + + export namespace V2 { + export interface VMState { + activeIC: number; + vm: FrozenVM; + } + + function objectFromIC(ic: SessionDB.V1.FrozenIC): FrozenObject { + return { + obj_info: { + name: undefined, + id: ic.id, + prefab: "ItemIntegratedCircuit10", + prefab_hash: crc32("ItemIntegratedCircuit10"), + memory: ic.stack, + source_code: ic.code, + compile_errors: undefined, + circuit: { + instruction_pointer: ic.ip, + yield_instruction_count: ic.ic, + state: ic.state as ICState, + aliases: ic.aliases, + defines: ic.defines, + labels: new Map(), + registers: ic.registers, + }, + + // unused + slots: undefined, + parent_slot: undefined, + root_parent_human: undefined, + damage: undefined, + device_pins: undefined, + connections: undefined, + reagents: undefined, + logic_values: undefined, + slot_logic_values: undefined, + entity: undefined, + socketed_ic: undefined, + visible_devices: undefined, + }, + database_template: true, + template: undefined, + }; + } + function objectsFromV1Template( + template: SessionDB.V1.DeviceTemplate, + idFn: () => number, + socketedIcFn: (id: number) => number | undefined, + ): FrozenObject[] { + const slotOccupantsPairs = new Map( + template.slots.flatMap((slot, index) => { + if (typeof slot.occupant !== "undefined") { + return [ + [ + index, + [ + { + obj_info: { + name: undefined, + id: slot.occupant.id ?? idFn(), + prefab: undefined, + prefab_hash: slot.occupant.fields.PrefabHash?.value, + damage: slot.occupant.fields.Damage?.value, + + socketed_ic: undefined, + // unused + memory: undefined, + source_code: undefined, + compile_errors: undefined, + circuit: undefined, + slots: undefined, + device_pins: undefined, + connections: undefined, + reagents: undefined, + logic_values: undefined, + slot_logic_values: undefined, + entity: undefined, + visible_devices: undefined, + }, + database_template: true, + template: undefined, + }, + slot.occupant.fields.Quantity ?? 1, + ], + ], + ] as [number, [FrozenObject, number]][]; + } else { + return [] as [number, [FrozenObject, number]][]; + } + }), + ); + const frozen: FrozenObject = { + obj_info: { + name: template.name, + id: template.id, + prefab: template.prefab_name, + prefab_hash: undefined, + slots: new Map( + Array.from(slotOccupantsPairs.entries()).map( + ([index, [obj, quantity]]) => [ + index, + { + quantity, + id: obj.obj_info.id, + }, + ], + ), + ), + socketed_ic: socketedIcFn(template.id), + + logic_values: new Map( + Object.entries(template.fields).map(([key, val]) => { + return [key as LogicType, val.value]; + }), + ), + + // unused + memory: undefined, + source_code: undefined, + compile_errors: undefined, + circuit: undefined, + parent_slot: undefined, + root_parent_human: undefined, + damage: undefined, + device_pins: undefined, + connections: undefined, + reagents: undefined, + slot_logic_values: undefined, + entity: undefined, + visible_devices: undefined, + }, + database_template: true, + template: undefined, + }; + return [ + ...Array.from(slotOccupantsPairs.entries()).map( + ([_index, [obj, _quantity]]) => obj, + ), + frozen, + ]; + } + + export function fromV1State(v1State: SessionDB.V1.VMState): VMState { + const highestObjetId = Math.max( + ...v1State.vm.devices + .map((device) => device.id ?? -1) + .concat(v1State.vm.ics.map((ic) => ic.id ?? -1)), + ); + let nextId = highestObjetId + 1; + const deviceIcs = new Map( + v1State.vm.ics.map((ic) => [ic.device, objectFromIC(ic)]), + ); + const objects = v1State.vm.devices.flatMap((device) => { + return objectsFromV1Template( + device, + () => nextId++, + (id) => deviceIcs.get(id)?.obj_info.id ?? undefined, + ); + }); + const vm: FrozenVM = { + objects, + circuit_holders: objects.flatMap((obj) => + "socketed_ic" in obj.obj_info && + typeof obj.obj_info.socketed_ic !== "undefined" + ? [obj.obj_info.id] + : [], + ), + program_holders: objects.flatMap((obj) => + "source_code" in obj.obj_info && + typeof obj.obj_info.source_code !== "undefined" + ? [obj.obj_info.id] + : [], + ), + default_network_key: v1State.vm.default_network, + networks: v1State.vm.networks as FrozenCableNetwork[], + wireless_receivers: [], + wireless_transmitters: [], + }; + const v2State: VMState = { + activeIC: v1State.activeIC, + vm, + }; + return v2State; + } + } + + export enum DBVersion { + V1 = 1, + V2 = 2, + } + + export const LOCAL_DB_VERSION = DBVersion.V2 as const; + export type CurrentDBSchema = AppDBSchemaV2; + export type CurrentDBVmState = V2.VMState; + export const LOCAL_DB_SESSION_STORE = "sessionsV2" as const; + + export interface AppDBSchemaV1 extends DBSchema { + sessions: { + key: string; + value: { + name: string; + date: Date; + session: V1.VMState; + }; + indexes: { + "by-date": Date; + "by-name": string; + }; + }; + } + + export interface AppDBSchemaV2 extends DBSchema { + sessions: { + key: string; + value: { + name: string; + date: Date; + session: V1.VMState; + }; + indexes: { + "by-date": Date; + "by-name": string; + }; + }; + sessionsV2: { + key: string; + value: { + name: string; + date: Date; + version: DBVersion.V2; + session: V2.VMState; + }; + indexes: { + "by-date": Date; + "by-name": string; + }; + }; + } +} diff --git a/www/src/ts/virtualMachine/index.ts b/www/src/ts/virtualMachine/index.ts index 8094e02..f14c7ec 100644 --- a/www/src/ts/virtualMachine/index.ts +++ b/www/src/ts/virtualMachine/index.ts @@ -23,15 +23,10 @@ export interface ToastMessage { id: string; } import { - signal, computed, - effect, - batch, } from '@lit-labs/preact-signals'; -import type { Signal } from '@lit-labs/preact-signals'; import { getJsonContext } from "./jsonErrorUtils"; import { VMState } from "./state"; -import { Obj } from "@popperjs/core"; export interface VirtualMachineEventMap { "vm-template-db-loaded": CustomEvent; @@ -94,19 +89,19 @@ class VirtualMachine extends TypedEventTarget() { } async updateCode() { - const progs = this.app.session.programs.peek(); + const progs = this.app.session.programs; for (const id of progs.keys()) { const attempt = Date.now().toString(16); - const circuitHolder = this.state.getObject(id); - const prog = progs.get(id); + const vmProg = this.state.getObjectProgramSource(id).peek(); + const prog = progs.get(id).peek(); if ( - circuitHolder && + vmProg && prog && - circuitHolder.peek().obj_info.source_code !== prog + vmProg !== prog ) { try { console.time(`CompileProgram_${id}_${attempt}`); - await this.ic10vm.setCodeInvalid(id, progs.get(id)!); + await this.ic10vm.setCodeInvalid(id, prog); const errors = await this.ic10vm.getCompileErrors(id); this.app.session.setProgramErrors(id, errors); this.dispatchCustomEvent("vm-object-modified", id); diff --git a/www/src/ts/virtualMachine/state.ts b/www/src/ts/virtualMachine/state.ts index 80b13ac..aba2540 100644 --- a/www/src/ts/virtualMachine/state.ts +++ b/www/src/ts/virtualMachine/state.ts @@ -593,7 +593,16 @@ export class VMState { const key = `obj:${id},source`; if (!this.signalCacheHas(key)) { const s = computed(() => { - return this.getObject(id).value?.obj_info.source_code ?? null; + if (this.circuitHolderIds.value?.includes(id)) { + const circuit = this.getObject(id).value; + const ic = this.getObject(circuit?.obj_info.socketed_ic).value; + return ic?.obj_info.source_code ?? null; + } else if (this.programHolderIds.value?.includes(id)) { + return this.getObject(id).value?.obj_info.source_code ?? null; + } else { + console.error(`(objectId: ${id}) does not refer to a object with a known program interface`) + return null; + } }) this.signalCacheSet(key, s); return s; diff --git a/xtask/src/generate/instructions.rs b/xtask/src/generate/instructions.rs index e596b51..21695f4 100644 --- a/xtask/src/generate/instructions.rs +++ b/xtask/src/generate/instructions.rs @@ -89,6 +89,7 @@ fn write_instructions_enum( #[strum(use_phf, serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] pub enum InstructionOp { + #[strum(props( example = "", desc = "No Operation", operands = "0" ))] Nop, #(#inst_variants)*