From 43ad6844b1c66af1903771b9129aad6ca273ccb9 Mon Sep 17 00:00:00 2001 From: burhankhanzada Date: Wed, 30 Sep 2026 02:12:31 +0200 Subject: [PATCH 01/11] Let I2C devices answer reads from a register bank Every read on the bus used to return 0xFF, so no I2C sensor could be modelled: a part could only receive what the sketch wrote. A read has to be answered mid-frame, while the CPU clocks it, and part logic only runs between frames. So a part now publishes registers and the bus answers from them, which is how I2C sensors are built: - context.i2c.serve(address, size:, pointerBytes:) declares a bank and claims the address. The first pointerBytes of a write set the register pointer (MSB first); reads return the register under it and advance, wrapping. pointerBytes: 0 is for devices with nothing to select, like a PCF8574, whose reads start at 0. - context.i2c.setRegisters(address, offset, bytes) publishes readings. - Bytes the sketch writes after the pointer are stored, so a register reads back what the sketch set, and still arrive through drain(). - Serving the same shape again keeps registers and pointer, so a part can declare itself every frame. The register logic lives in I2cRecorder (native) and is mirrored in web/avr_bridge.js, so a read never crosses into Dart on either platform. An address nothing serves still reads 0xFF and is NACKed. Tested at three levels: I2cRecorder unit tests for each rule; the same scenarios run against the JS copy in Node (not checked in); and a real Wire sketch (test/fixtures/i2c_read), compiled with arduino-cli, that reads one register, six in a row after a repeated start, reads back a write, and is refused at an absent address. PDL.md's context.i2c section documents the API. Also drops the empty `()` the Dart 3.13 migration left on PartI2cApi and I2cRecorder, the last two. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../pinbench_parts/lib/logic/part_logic.dart | 46 ++- .../pinbench_parts/test/part_logic_test.dart | 6 + packages/pinbench_pdl | 2 +- .../pinbench_sim/lib/core/avr_interop_io.dart | 16 +- .../lib/core/avr_interop_web.dart | 14 + .../pinbench_sim/lib/core/engine_pin_api.dart | 8 + .../pinbench_sim/lib/core/i2c_recorder.dart | 137 ++++++++- .../test/fixtures/i2c_read/i2c_read.hex | 287 ++++++++++++++++++ .../test/fixtures/i2c_read/i2c_read.ino | 58 ++++ packages/pinbench_sim/test/i2c_read_test.dart | 42 +++ .../pinbench_sim/test/i2c_recorder_test.dart | 124 ++++++++ web/avr_bridge.js | 70 ++++- 12 files changed, 786 insertions(+), 24 deletions(-) create mode 100644 packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.hex create mode 100644 packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.ino create mode 100644 packages/pinbench_sim/test/i2c_read_test.dart diff --git a/packages/pinbench_parts/lib/logic/part_logic.dart b/packages/pinbench_parts/lib/logic/part_logic.dart index 3327d6b..71f24ea 100644 --- a/packages/pinbench_parts/lib/logic/part_logic.dart +++ b/packages/pinbench_parts/lib/logic/part_logic.dart @@ -173,13 +173,20 @@ abstract interface class PartPinApi { /// bytes would show a corrupt picture forever after. So this hands over a /// queue rather than a reading, and hands it over exactly once. /// -/// Narrow in the same way as [PartPinApi]: a part may listen at its own -/// address. It cannot see another device's traffic, and it cannot write to the -/// bus — a peripheral that could put bytes on the wire would be modelling the -/// sketch's side of the conversation. -// One method today, and still an interface: it is the seam a fake bus is -// installed at in tests, and the shape the other capability APIs here take. -abstract interface class PartI2cApi() { +/// A part talks back the way real I²C devices do: through **registers**. It +/// [serve]s a bank of them at its address and keeps them current with +/// [setRegisters]; the sketch writes a register pointer, then reads, and the +/// pointer advances after every byte. The bus answers from those registers +/// the moment the sketch clocks a read, mid-frame, which a part running +/// between frames could not. A reading refreshed every frame is at most 16 ms +/// old — fresher than any real sensor's conversion. +/// +/// Narrow in the same way as [PartPinApi]: a part may listen at and serve its +/// own address. It cannot see another device's traffic, and it never drives +/// the bus itself — the sketch is always the one clocking bytes. +// An interface, not a concrete class: it is the seam a fake bus is installed +// at in tests, and the shape the other capability APIs here take. +abstract interface class PartI2cApi { /// Every transaction the sketch has addressed to [address] since the last /// call, oldest first, each holding the bytes that followed the address. /// @@ -188,6 +195,25 @@ abstract interface class PartI2cApi() { /// nobody listens on costs nothing. The first answer after a fresh claim is /// therefore empty, and a display simply keeps the frame it already had. List> drain(int address); + + /// Makes [address] answer the sketch's reads from a bank of [size] + /// registers, and claims it, so the address is acknowledged. + /// + /// [pointerBytes] is how many bytes at the start of a write select the + /// register: 1 for most sensors (MPU6050, DS1307, BME280), 2 for large + /// EEPROMs, 0 for devices with nothing to select (a PCF8574), whose every + /// read starts at register 0. Bytes the sketch writes after the pointer are + /// stored, so reading back a register returns what the sketch put there — + /// which is what setting an RTC's clock relies on. + /// + /// Safe to call every frame: the same shape keeps the registers and the + /// pointer, a different one starts over, zero-filled. + void serve(int address, {int size = 256, int pointerBytes = 1}); + + /// Writes [bytes] into [address]'s registers from [offset], wrapping at the + /// end of the bank — how a part publishes a new reading. Ignored unless the + /// address is [serve]d. + void setRegisters(int address, int offset, List bytes); } /// The bus a part sees when there is no emulator behind it — in a unit test, @@ -195,6 +221,12 @@ abstract interface class PartI2cApi() { class const NoI2cBus() implements PartI2cApi { @override List> drain(int address) => const []; + + @override + void serve(int address, {int size = 256, int pointerBytes = 1}) {} + + @override + void setRegisters(int address, int offset, List bytes) {} } /// Maps the name in a `LOGIC` line to an implementation. diff --git a/packages/pinbench_parts/test/part_logic_test.dart b/packages/pinbench_parts/test/part_logic_test.dart index 5fc8cea..7894db2 100644 --- a/packages/pinbench_parts/test/part_logic_test.dart +++ b/packages/pinbench_parts/test/part_logic_test.dart @@ -689,6 +689,12 @@ class _FakeBus(final Map>> traffic) implements PartI2cApi { drained.add(address); return traffic[address] ?? const []; } + + @override + void serve(int address, {int size = 256, int pointerBytes = 1}) {} + + @override + void setRegisters(int address, int offset, List bytes) {} } /// A minimal definition for the helper tests — nothing about it matters except diff --git a/packages/pinbench_pdl b/packages/pinbench_pdl index 3b2956d..e834b31 160000 --- a/packages/pinbench_pdl +++ b/packages/pinbench_pdl @@ -1 +1 @@ -Subproject commit 3b2956d2a42c20515c70d4cd76e09ef994cf8a61 +Subproject commit e834b3125007086075a05048af22779bc0865ee1 diff --git a/packages/pinbench_sim/lib/core/avr_interop_io.dart b/packages/pinbench_sim/lib/core/avr_interop_io.dart index 5bcced0..aadafb9 100644 --- a/packages/pinbench_sim/lib/core/avr_interop_io.dart +++ b/packages/pinbench_sim/lib/core/avr_interop_io.dart @@ -52,6 +52,15 @@ class AVRBridge { /// Every transaction written to [address] since the last call. static List> drainI2c(int address) => _i2c.drain(address); + /// Makes [address] answer the sketch's reads from a bank of [size] + /// registers. See [I2cRecorder.serve]. + static void serveI2c(int address, {int size = 256, int pointerBytes = 1}) => + _i2c.serve(address, size: size, pointerBytes: pointerBytes); + + /// Publishes [bytes] into [address]'s registers from [offset]. + static void setI2cRegisters(int address, int offset, List bytes) => + _i2c.setRegisters(address, offset, bytes); + static void Function(double?)? onBuzzerFrequencyChanged; /// The Arduino pin number connected to the piezo buzzer. @@ -338,9 +347,8 @@ class _RecordingTwiHandler(final AVRTWI twi, final I2cRecorder recorder) @override void readByte(bool ack) { - // Nothing on the canvas talks back yet. 0xFF is what an idle bus reads, - // pulled up and undriven, which is also what a sketch reading from a - // device that is not there would see. - twi.completeRead(0xFF); + // From the registers a part serves, or 0xFF — what a pulled-up, undriven + // bus reads — when nothing on the canvas answers at this address. + twi.completeRead(recorder.readByte()); } } diff --git a/packages/pinbench_sim/lib/core/avr_interop_web.dart b/packages/pinbench_sim/lib/core/avr_interop_web.dart index 73e24b5..85cd5c2 100644 --- a/packages/pinbench_sim/lib/core/avr_interop_web.dart +++ b/packages/pinbench_sim/lib/core/avr_interop_web.dart @@ -1,4 +1,5 @@ import 'dart:js_interop'; +import 'dart:typed_data'; /// Web [AVRBridge] backed by `avr8js` via the `window.AVR8` bridge defined in /// `web/avr_bridge.js`. Mirrors the native `avr_interop_io.dart` API but runs @@ -27,6 +28,8 @@ extension type _Avr8._(JSObject _) implements JSObject { external bool getPin13State(); external void listenI2c(int address); external JSInt32Array drainI2c(int address); + external void serveI2c(int address, int size, int pointerBytes); + external void setI2cRegisters(int address, int offset, JSInt32Array bytes); } class AVRBridge._() { @@ -93,6 +96,17 @@ class AVRBridge._() { /// traffic is kept for [drainI2c]. static void listenI2c(int address) => _avr8.listenI2c(address); + /// Makes [address] answer the sketch's reads from a bank of [size] + /// registers. The registers live in `avr_bridge.js`, next to the CPU, so a + /// read is answered without crossing into Dart. + static void serveI2c(int address, {int size = 256, int pointerBytes = 1}) => + _avr8.serveI2c(address, size, pointerBytes); + + /// Publishes [bytes] into [address]'s registers from [offset], as one + /// typed-array copy. + static void setI2cRegisters(int address, int offset, List bytes) => + _avr8.setI2cRegisters(address, offset, Int32List.fromList(bytes).toJS); + /// Every transaction written to [address] since the last call. /// /// The bridge hands the whole frame's traffic over as one flat typed array — diff --git a/packages/pinbench_sim/lib/core/engine_pin_api.dart b/packages/pinbench_sim/lib/core/engine_pin_api.dart index 35c667b..1afafd4 100644 --- a/packages/pinbench_sim/lib/core/engine_pin_api.dart +++ b/packages/pinbench_sim/lib/core/engine_pin_api.dart @@ -141,6 +141,14 @@ class const EngineI2cApi() implements PartI2cApi { AVRBridge.listenI2c(address); return AVRBridge.drainI2c(address); } + + @override + void serve(int address, {int size = 256, int pointerBytes = 1}) => + AVRBridge.serveI2c(address, size: size, pointerBytes: pointerBytes); + + @override + void setRegisters(int address, int offset, List bytes) => + AVRBridge.setI2cRegisters(address, offset, bytes); } /// [PartSpiceApi] backed by the running solver. diff --git a/packages/pinbench_sim/lib/core/i2c_recorder.dart b/packages/pinbench_sim/lib/core/i2c_recorder.dart index 0c0c5fd..48b7a3b 100644 --- a/packages/pinbench_sim/lib/core/i2c_recorder.dart +++ b/packages/pinbench_sim/lib/core/i2c_recorder.dart @@ -1,4 +1,7 @@ -/// Records what the sketch writes onto the I²C bus, per slave address. +import 'dart:typed_data'; + +/// Records what the sketch writes onto the I²C bus, per slave address, and +/// answers what it reads from devices that [serve] registers. /// /// The emulator's TWI peripheral reports the bus as *events* — start, address, /// byte, stop — while a peripheral wants *transactions*, because the first byte @@ -11,11 +14,20 @@ /// refresh — a thousand bytes — can arrive between two looks. Sampling would /// lose all but the last byte and a display would show noise. /// +/// **Reads are answered here, not by the part.** A read has to be answered +/// the moment the sketch clocks it, in the middle of the CPU's frame, and a +/// part only runs between frames. So a part that talks back publishes its +/// registers with [serve] and [setRegisters], and the bus answers from them. +/// That is how almost every I²C sensor is built anyway: the sketch writes a +/// register pointer, then reads, and the pointer advances after every byte. +/// Values a part refreshes each frame are at most one frame (16 ms) old, far +/// fresher than any real sensor's conversion time. +/// /// Kept out of the emulator facades because both of them need it and neither /// owns it: the native bridge feeds this from `avr8_dart`'s TWI callbacks, the -/// web bridge from `avr8js`'s, and the frame loop drains the same shape either -/// way. -class I2cRecorder() { +/// web bridge from `avr8js`'s (`web/avr_bridge.js` mirrors it, so reads never +/// cross into Dart), and the frame loop drains the same shape either way. +class I2cRecorder { /// Addresses some part has claimed by draining them. /// /// This gates the *acknowledgement*, not the recording, and the difference @@ -50,22 +62,89 @@ class I2cRecorder() { List? _current; int? _currentAddress; + /// Devices that answer reads, by address. + final Map _devices = {}; + + /// The device the open transaction talks to, if it serves registers. + I2cRegisterDevice? _device; + + /// Whether the open transaction is a write, while [_device] is set. + var _writing = false; + + /// Pointer bytes still to come in the open write transaction. A write + /// starts by setting the register pointer, most significant byte first; + /// everything after that is data. + var _pointerBytesLeft = 0; + /// Claims [address] so its traffic is recorded from now on. void listenAt(int address) => _listening.add(address); bool isListening(int address) => _listening.contains(address); + /// Makes [address] answer reads from a bank of [size] registers. + /// + /// [pointerBytes] is how many bytes of a write set the register pointer: + /// 1 for most sensors, 2 for large EEPROMs, and 0 for devices with no + /// registers to choose between (a PCF8574 port expander), whose every read + /// starts again at register 0. + /// + /// Serving claims the address, like [listenAt]. Calling it again with the + /// same shape keeps the registers and the pointer, so a part can declare + /// itself every frame without resetting what the sketch wrote; a different + /// shape starts over, zero-filled. + void serve(int address, {int size = 256, int pointerBytes = 1}) { + assert( + size > 0 && pointerBytes >= 0 && pointerBytes <= 2, + 'size $size, pointerBytes $pointerBytes', + ); + _listening.add(address); + final existing = _devices[address]; + if (existing != null && + existing.registers.length == size && + existing.pointerBytes == pointerBytes) { + return; + } + _devices[address] = I2cRegisterDevice(size: size, pointerBytes: pointerBytes); + } + + /// Overwrites [address]'s registers from [offset], wrapping at the end of + /// the bank. How a part publishes fresh readings. Does nothing for an + /// address nobody [serve]s. + void setRegisters(int address, int offset, List bytes) { + final registers = _devices[address]?.registers; + if (registers == null) return; + for (var i = 0; i < bytes.length; i++) { + registers[(offset + i) % registers.length] = bytes[i] & 0xFF; + } + } + + /// [address]'s registers, or null when nothing serves it. For tests and + /// for parts that want to read back what the sketch wrote. + List? registersOf(int address) => _devices[address]?.registers; + /// Whether any device on the canvas answers at [address] — what the TWI /// peripheral needs in order to ACK or NACK the address byte, and therefore /// what makes `Wire.endTransmission()` report success or failure to the /// sketch exactly as a real bus would. bool acknowledges(int address) => _listening.contains(address); - /// Starts recording a write transaction to [address]. A read is not - /// recorded — nothing on the canvas talks back yet — but it still closes - /// whatever transaction was open. + /// Starts a transaction with [address]. A write is recorded, and also + /// updates the register pointer and registers of a device that [serve]s; + /// a read is not recorded, but it still closes whatever transaction was + /// open, and a pointer-less device starts its read at register 0. void beginTransaction(int address, {required bool write}) { endTransaction(); + final device = _devices[address]; + _device = device; + _writing = write; + if (device != null) { + if (write) { + _pointerBytesLeft = device.pointerBytes; + if (device.pointerBytes > 0) device.pointer = 0; + } else if (device.pointerBytes == 0) { + device.pointer = 0; + } + } if (!write) return; if (!_pending.containsKey(address) && _pending.length >= maxRecordedAddresses) return; _currentAddress = address; @@ -74,7 +153,32 @@ class I2cRecorder() { /// Records one byte of the open transaction. Ignored when the bus is idle or /// addressing a device nobody listens for. - void writeByte(int value) => _current?.add(value & 0xFF); + void writeByte(int value) { + final byte = value & 0xFF; + _current?.add(byte); + final device = _device; + if (device == null || !_writing) return; + if (_pointerBytesLeft > 0) { + device.pointer = ((device.pointer << 8) | byte) % device.registers.length; + _pointerBytesLeft--; + } else if (device.pointerBytes > 0) { + // A pointer-less device's writes drive its outputs, not a register the + // sketch can read back, so only register devices store them. + device.registers[device.pointer] = byte; + device.advance(); + } + } + + /// The byte the sketch reads next from the open read transaction: the + /// register under the pointer, which then advances. 0xFF — a pulled-up, + /// undriven bus — when nothing serves the address. + int readByte() { + final device = _device; + if (device == null || _writing) return 0xFF; + final value = device.registers[device.pointer]; + device.advance(); + return value; + } /// Closes the open transaction and queues it for its address. void endTransaction() { @@ -82,6 +186,8 @@ class I2cRecorder() { final bytes = _current; _currentAddress = null; _current = null; + _device = null; + _pointerBytesLeft = 0; if (address == null || bytes == null || bytes.isEmpty) return; final queue = _pending.putIfAbsent(address, () => []); @@ -106,7 +212,22 @@ class I2cRecorder() { _listening.clear(); _pending.clear(); _bufferedBytes.clear(); + _devices.clear(); _current = null; _currentAddress = null; + _device = null; + _pointerBytesLeft = 0; } } + +/// One device's registers and its pointer, as [I2cRecorder.serve] made them. +class I2cRegisterDevice({required int size, required final int pointerBytes}) { + final registers = Uint8List(size); + + /// The register the next read or write touches. + var pointer = 0; + + /// Moves to the next register, wrapping at the end of the bank the way an + /// auto-incrementing device does. + void advance() => pointer = (pointer + 1) % registers.length; +} diff --git a/packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.hex b/packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.hex new file mode 100644 index 0000000..1b4733f --- /dev/null +++ b/packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.hex @@ -0,0 +1,287 @@ +:100000000C9458000C9480000C9480000C94800098 +:100010000C9480000C9480000C9480000C94800060 +:100020000C9480000C9480000C9480000C94800050 +:100030000C9480000C9480000C9480000C94800040 +:100040000C9496050C9480000C9406060C94E00524 +:100050000C9480000C9480000C9480000C94800020 +:100060000C9438060C948000000000002500280045 +:100070002B00040404040404040402020202020229 +:10008000030303030303010204081020408001025C +:1000900004081020010204081020000000080002DB +:1000A00001000003040700000000000000005707E3 +:1000B00011241FBECFEFD8E0DEBFCDBF11E0A0E01E +:1000C000B1E0E4E8F1E102C005900D92A434B1077B +:1000D000D9F722E0A4E4B1E001C01D92A03BB20731 +:1000E000E1F710E0C8E5D0E004C02197FE010E94CE +:1000F000BA08C735D107C9F70E9490070C94C00809 +:100100000C94000090E0FC01E656FF4F2491FC01A6 +:10011000EA57FF4F3491FC01EE58FF4FE491EE2374 +:10012000C9F0222339F0233001F1A8F4213019F16C +:10013000223029F1F0E0EE0FFF1FE859FF4FA591A3 +:10014000B4918FB7F894EC91611126C030953E239D +:100150003C938FBF08952730A9F02830C9F0243090 +:1001600049F7809180008F7D03C0809180008F7758 +:1001700080938000DFCF84B58F7784BDDBCF84B5DB +:100180008F7DFBCF8091B0008F778093B000D2CF6E +:100190008091B0008F7DF9CF3E2BDACF3FB7F89436 +:1001A0008091B3019091B401A091B501B091B601D5 +:1001B00026B5A89B05C02F3F19F00196A11DB11DC2 +:1001C0003FBFBA2FA92F982F8827BC01CD01620FFE +:1001D000711D811D911D42E0660F771F881F991FB9 +:1001E0004A95D1F7089508950895AF92BF92CF929E +:1001F000DF92EF92FF920F931F93CF93DF936C01E7 +:100200007B018B01040F151FEB015E01AE18BF08C7 +:10021000C017D10759F06991D601ED91FC91019079 +:10022000F081E02DC6010995892B79F7C501DF9191 +:10023000CF911F910F91FF90EF90DF90CF90BF90E3 +:10024000AF900895FC01538D448D252F30E0842F0D +:1002500090E0821B930B541710F0CF9608950197EE +:100260000895FC01918D828D981761F0A28DAE0FDB +:10027000BF2FB11D5D968C91928D9F5F9F73928F62 +:1002800090E008958FEF9FEF0895FC01918D828D8E +:10029000981731F0828DE80FF11D858D90E008955B +:1002A0008FEF9FEF0895FC01918D228D892F90E0B3 +:1002B000805C9F4F821B91098F739927089587E077 +:1002C00092E00E94530121E0892B09F420E0822F63 +:1002D000089580E090E0892B29F00E945F01811150 +:1002E0000C9400000895FC01A48DA80FB92FB11D36 +:1002F000A35ABF4F2C91848D90E001968F7399275C +:10030000848FA689B7892C93A089B1898C91837039 +:1003100080648C93938D848D981306C00288F38932 +:10032000E02D80818F7D80830895EF92FF920F935F +:100330001F93CF93DF93EC0181E0888F9B8D8C8D91 +:1003400098131AC0E889F989808185FF15C09FB785 +:10035000F894EE89FF896083E889F9898081837048 +:10036000806480839FBF81E090E0DF91CF911F91F7 +:100370000F91FF90EF900895F62E0B8D10E00F5F18 +:100380001F4F0F731127E02E8C8D8E110CC00FB6EE +:1003900007FCFACFE889F989808185FFF5CFCE0186 +:1003A0000E947301F1CFEB8DEC0FFD2FF11DE35A8D +:1003B000FF4FF0829FB7F8940B8FEA89FB89808109 +:1003C0008062CFCFCF93DF93EC01888D8823B9F083 +:1003D000AA89BB89E889F9898C9185FD03C0808150 +:1003E00086FD0DC00FB607FCF7CF8C9185FFF2CFCD +:1003F000808185FFEDCFCE010E947301E9CFDF91AF +:10040000CF91089520918501260F3327331F213284 +:100410003105ECF42091DD01FC0190E080E0243016 +:1004200069F082E00895A09185012191AC014B59BA +:100430005E4FA40FB52FB11D2C930196861798F32C +:1004400080918501680F6093850180E0089581E0C7 +:1004500008951092DD0181E08093DC011092B701D4 +:1004600061E082E10E94820061E083E10E948200FB +:10047000E9EBF0E080818E7F808380818D7F8083B7 +:1004800088E48093B80085E48093BC000895CF93FE +:10049000DF9391E0909364018823B9F0C091B80094 +:1004A000D091BA008091BC008A7B8093BC0060E050 +:1004B00082E10E94820060E083E10E9482000E944B +:1004C0002902D093BA00C093B800DF91CF9108956C +:1004D0008F929F92AF92BF92CF92DF92EF92FF9254 +:1004E0000F931F93CF93DF93D0910602D13208F080 +:1004F000D7C0182FC09105020E94CE006B017C016D +:100500008091DD0181116BC082E08093DD01109349 +:10051000DC018FEF8093DB011092DA01D093D901D7 +:10052000A5EEB1E0E9EBF1E080E0D81391C01092C4 +:10053000B8018091B801CC0FC82BC093B80180914D +:10054000B701813009F088C01092B7010E94CE0037 +:100550006B017C018091B8018093BB008091DF0129 +:100560009091E001A091E101B091E201892B8A2BE9 +:100570008B2BA1F00E94CE000091DF011091E001D1 +:100580002091E1013091E2016C197D098E099F09EA +:10059000061717072807390708F442C08091BC00E6 +:1005A00083FDD8CF85EC8093BC000E94CE006B0108 +:1005B0007C018091DD01823009F450C08091DB0123 +:1005C0008F3F09F46FC08091DB01803209F46CC069 +:1005D0008091DB01803309F469C084E026C08091FA +:1005E000DF019091E001A091E101B091E201892B3E +:1005F0008A2B8B2B09F484CF0E94CE008090DF01E0 +:100600009090E001A090E101B090E2016C197D09A9 +:100610008E099F0986169706A806B90608F070CFBE +:100620008091DE010E94470285E01092E401109261 +:1006300006021092E301DF91CF911F910F91FF907D +:10064000EF90DF90CF90BF90AF909F908F900895E4 +:100650009D9191938F5F69CF85EEA5CF8091DF014A +:100660009091E001A091E101B091E201892B8A2BE8 +:100670008B2B09F49ECF0E94CE000091DF011091D8 +:10068000E0012091E1013091E2016C197D098E09B0 +:100690009F09061717072807390708F08ACFC0CF28 +:1006A00081E0C3CF80E0C1CF82E0BFCF83E0BDCF88 +:1006B0000895E091AD018091AC01E81730F4F0E0CD +:1006C000EC5BFE4F808190E008958FEF9FEF0895DF +:1006D0009091AD018091AC012FEF3FEF981748F456 +:1006E000E92FF0E0EC5BFE4F208130E09F5F9093BC +:1006F000AD01C90108958091AC019091AD01891BB4 +:10070000990B0895CF92DF92EF92FF920F931F9370 +:10071000CF93DF937C01CB018A012091E301222357 +:1007200089F0EB016B01C40ED51ECC15DD0569F017 +:100730006991D701ED91FC910190F081E02DC70105 +:100740000995F3CF642F0E940202C801DF91CF9177 +:100750001F910F91FF90EF90DF90CF900895CF936E +:10076000DF931F92CDB7DEB769832091E301222387 +:10077000F9F020910602203258F021E030E0FC012F +:100780003383228390E080E00F90DF91CF91089532 +:100790008091E401E82FF0E0EB51FE4F99819083C6 +:1007A0008F5F8093E4018093060281E090E0ECCFBC +:1007B00061E0CE0101960E940202F7CF90E080E056 +:1007C0000895FC0101900020E9F73197AF01481B23 +:1007D000590BBC0187E092E00C94F5008F929F9238 +:1007E000AF92BF920F931F93CF93DF93CDB7DEB736 +:1007F000A1970FB6F894DEBF0FBECDBF19A242304D +:1008000008F44AE08E010F5D1F4F842E912CB12C0D +:10081000A12CA50194010E949808E62FB901CA01F4 +:10082000EA30F4F4E05DD801EE938D01232B242B04 +:10083000252B79F790E080E0109719F0CD010E9408 +:10084000E103A1960FB6F894DEBF0FBECDBFDF91D6 +:10085000CF911F910F91BF90AF909F908F9008956F +:10086000E95CE1CFEF92FF920F931F93CF93DF9359 +:10087000F62E172F042F0E94E103CF2DD12F0C0F3E +:100880001D2F111DC017D10799F07E01888180317D +:1008900028F460E387E092E00E9495012196F70139 +:1008A000608170E090E080E040E10E94EE03EACFDA +:1008B00084E291E0DF91CF911F910F91FF90EF9033 +:1008C0000C94E1034F925F926F927F92BF92CF920E +:1008D000DF92EF92FF920F931F93CF93DF93D62F68 +:1008E000B72E142F91E09093E30198E690930502C0 +:1008F0001092E40110920602682F84EA92E00E94AE +:10090000AF0380E00E946802C82F8111C7C00E9417 +:10091000CE006B017C018091DD01811163C081E01B +:100920008093DD018093DC019FEF9093DB011092B7 +:10093000DA01910F9093D9018093B8018091B801A9 +:10094000806D8093B8018091B701813009F082C039 +:100950001092B7010E94CE006B017C018091B8011A +:100960008093BB008091DF019091E001A091E101B3 +:10097000B091E201892B8A2B8B2BA1F00E94CE0033 +:100980004090DF015090E0016090E1017090E20141 +:100990006C197D098E099F09461657066806790667 +:1009A00008F441C08091BC0083FDD8CF85EC8093D2 +:1009B000BC000E94CE006B017C018091DD01813082 +:1009C00009F44AC08091DA01811710F41091DA011C +:1009D000A9EBB1E0E4E4F1E0C11749F18D91819315 +:1009E000CF5FFACF8091DF019091E001A091E1010A +:1009F000B091E201892B8A2B8B2B09F48CCF0E94BA +:100A0000CE004090DF015090E0016090E1017090D5 +:100A1000E2016C197D098E099F0946165706680682 +:100A2000790608F078CF8091DE010E94470210928B +:100A3000AD01C093AC01FD2E0D2F1B2D802F8F1902 +:100A40008C1768F584EA92E00E946803F8018193AC +:100A50008F01F4CF85EEABCF8091DF019091E00163 +:100A6000A091E101B091E201892B8A2B8B2B09F433 +:100A7000A4CF0E94CE004090DF015090E001609032 +:100A8000E1017090E2016C197D098E099F094616FB +:100A900057066806790608F090CFC5CFC0E08C2FC6 +:100AA000DF91CF911F910F91FF90EF90DF90CF904A +:100AB000BF907F906F905F904F90089585ED8093E9 +:100AC000BC008091DF019091E001A091E101B09123 +:100AD000E2010796A11DB11D23E0B695A795979554 +:100AE00087952A95D1F72091BC0024FD03C0109270 +:100AF000DD0108954091DF015091E0016091E10135 +:100B00007091E201452B462B472B69F30097A10515 +:100B1000B10541F02AE22A95F1F700C00197A10939 +:100B2000B109E1CF8091DE010C9447021F920F9230 +:100B30000FB60F9211242F933F938F939F93AF93F0 +:100B4000BF938091AF019091B001A091B101B0919C +:100B5000B2013091AE0123E0230F2D3758F50196F5 +:100B6000A11DB11D2093AE018093AF019093B00100 +:100B7000A093B101B093B2018091B3019091B401FF +:100B8000A091B501B091B6010196A11DB11D809350 +:100B9000B3019093B401A093B501B093B601BF9196 +:100BA000AF919F918F913F912F910F900FBE0F901A +:100BB0001F90189526E8230F0296A11DB11DD2CFD4 +:100BC0001F920F920FB60F9211242F933F934F93C2 +:100BD0005F936F937F938F939F93AF93BF93EF93A5 +:100BE000FF9387E092E00E947301FF91EF91BF9124 +:100BF000AF919F918F917F916F915F914F913F91B5 +:100C00002F910F900FBE0F901F9018951F920F926B +:100C10000FB60F9211242F938F939F93EF93FF930F +:100C2000E0911702F09118028081E0911D02F0918D +:100C30001E0282FD1BC09081809120028F5F8F7306 +:100C400020912102821741F0E0912002F0E0E95F5B +:100C5000FD4F958F80932002FF91EF919F918F918F +:100C60002F910F900FBE0F901F9018958081F4CF99 +:100C70001F920F920FB60F9211242F933F934F9311 +:100C80005F936F937F938F939F93AF93BF93EF93F4 +:100C9000FF938091B900887F803609F44AC0F0F54F +:100CA000883209F4A7C018F5803109F49BC0B8F464 +:100CB000882309F4F9C0883009F494C0FF91EF91BA +:100CC000BF91AF919F918F917F916F915F914F9164 +:100CD0003F912F910F900FBE0F901F901895883164 +:100CE00009F488C0803251F78093DB0114C080344E +:100CF00009F49DC040F48033B9F38833F9F680934A +:100D0000DB0185ECB0C0803509F485C0883509F475 +:100D100096C0883499F60E945E05D0CF883909F4D0 +:100D20008CC038F5883729F050F4883611F08037B8 +:100D300029F683E08093DD011092A70157C088381F +:100D400009F47BC0803919F0803809F0B7CF809161 +:100D5000A701803208F071C0E091A70181E08E0FF9 +:100D60008093A7018091BB00F0E0E957FE4F80839C +:100D70003DC0803B39F0E0F4803A09F479C0883A0C +:100D800009F09CCF84E08093DD01109286011092DF +:100D90008501E091AA01F091AB010995809185014F +:100DA00081110FC081E0809385011092650109C017 +:100DB000803C09F4A6CF883C09F4A3CF883B09F016 +:100DC0007DCFE091860181E08E0F80938601F0E077 +:100DD000EB59FE4F80818093BB00909186018091FA +:100DE000850129C08091B8018093BB0085EC809378 +:100DF000BC0064CF9091DA018091D901981758F521 +:100E0000E091DA0181E08E0F8093DA01F0E0E7549F +:100E1000FE4F8081E9CFE091DA0181E08E0F80936F +:100E2000DA018091BB00F0E0E754FE4F808390919F +:100E3000DA018091D9019817C8F285E8D8CFE091FE +:100E4000DA0181E08E0F8093DA018091BB00F0E03F +:100E5000E754FE4F80838091DC0181115CCF81E0FB +:100E60008093B70184EA8093BC001092DD0126CF05 +:100E700085EC8093BC001092DD018091A701803247 +:100E800030F4E091A701F0E0E957FE4F1082609145 +:100E9000A70170E0E091A801F091A90187E891E035 +:100EA00009951092A7010ACF1092DB0134CFE7E039 +:100EB000F2E01382128288EE93E0A0E0B0E0848337 +:100EC0009583A683B78324E031E03183208325EC2A +:100ED00030E03587248724EC30E03787268720EC04 +:100EE00030E0318B208B21EC30E0338B228B22ECF5 +:100EF00030E0358B248B26EC30E0378B268B118E3F +:100F0000128E138E148EE4EAF2E01382128284832E +:100F10009583A683B78386E191E0918380830895CA +:100F2000CF93DF9300D000D000D0CDB7DEB7789458 +:100F300084B5826084BD84B5816084BD85B58260DE +:100F400085BD85B5816085BD80916E00816080938F +:100F50006E001092810080918100826080938100F8 +:100F60008091810081608093810080918000816008 +:100F7000809380008091B10084608093B100809163 +:100F8000B00081608093B00080917A00846080938B +:100F90007A0080917A00826080937A0080917A0052 +:100FA000816080937A0080917A00806880937A00D3 +:100FB0001092C100E0911702F091180282E0808344 +:100FC000E0911302F09114021082E0911502F09169 +:100FD000160280E1808310921F02E0911B02F091C3 +:100FE0001C0286E08083E0911902F0911A02808150 +:100FF00080618083E0911902F0911A0280818860FB +:101000008083E0911902F0911A02808180688083C8 +:10101000E0911902F0911A0280818F7D80831092F5 +:10102000AD011092AC011092E401109206020E94F0 +:10103000290283EF90E09093AB018093AA0184EFA3 +:1010400090E09093A9018093A80141E0BE016F5FF9 +:101050007F4F85E70E946204482FBE016F5F7F4F7C +:1010600087E291E00E94320446E0BE016F5F7F4F4D +:101070008BE30E946204482FBE016F5F7F4F8CE2BA +:1010800091E00E94320411E01093E30188E680931E +:1010900005021092E401109206026BE684EA92E0E7 +:1010A0000E94AF036AE584EA92E00E94AF0381E008 +:1010B0000E94680241E0BE016F5F7F4F8BE60E9495 +:1010C0006204482FBE016F5F7F4F81E391E00E9471 +:1010D00032041093E30180E5809305021092E4014D +:1010E0001092060287E391E00E94E10381E00E94F2 +:1010F0006802682F70E090E080E04AE00E94EE0312 +:1011000084E291E00E94E1038FE391E00E94E10319 +:1011100084E291E00E94E10300E010E00115110576 +:10112000E9F30E945F018823C9F30E940000F6CF13 +:10113000A1E21A2EAA1BBB1BFD010DC0AA1FBB1FDB +:10114000EE1FFF1FA217B307E407F50720F0A21B4D +:10115000B30BE40BF50B661F771F881F991F1A94BA +:1011600069F760957095809590959B01AC01BD01E4 +:10117000CF010895EE0FFF1F0590F491E02D099423 +:04118000F894FFCF11 +:10118400000000009501F5002201E2015301310144 +:10119400450100000000AF038203DE0358037B0314 +:1011A400680359030D0A0057484F3D004143433D2E +:1011B400004241434B3D00414253454E543D00449F +:0411C4004F4E450045 +:00000001FF diff --git a/packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.ino b/packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.ino new file mode 100644 index 0000000..f863eb5 --- /dev/null +++ b/packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.ino @@ -0,0 +1,58 @@ +// Fixture for i2c_read_test.dart: reads a served I2C device the way real +// sensor libraries do, and reports each result over Serial. +// +// Rebuild after editing (from this folder): +// arduino-cli compile --fqbn arduino:avr:uno --output-dir . i2c_read.ino +// mv i2c_read.ino.hex i2c_read.hex && rm -f i2c_read.ino.* + +#include + +const uint8_t DEVICE = 0x68; // served by the test +const uint8_t ABSENT = 0x50; // nothing on the bus + +// Write the register pointer, then read [count] bytes after a repeated start. +uint8_t readRegisters(uint8_t reg, uint8_t *out, uint8_t count) { + Wire.beginTransmission(DEVICE); + Wire.write(reg); + if (Wire.endTransmission(false) != 0) return 0; + uint8_t got = Wire.requestFrom(DEVICE, count); + for (uint8_t i = 0; i < got; i++) out[i] = Wire.read(); + return got; +} + +void printHex(const char *label, const uint8_t *bytes, uint8_t count) { + Serial.print(label); + for (uint8_t i = 0; i < count; i++) { + if (bytes[i] < 0x10) Serial.print('0'); + Serial.print(bytes[i], HEX); + } + Serial.println(); +} + +void setup() { + Serial.begin(115200); + Wire.begin(); + uint8_t buf[6]; + + // One register, like a WHO_AM_I check. + printHex("WHO=", buf, readRegisters(0x75, buf, 1)); + + // Six consecutive registers in one read, like an accelerometer sample. + printHex("ACC=", buf, readRegisters(0x3B, buf, 6)); + + // Write a register, then read it back, like setting an RTC. + Wire.beginTransmission(DEVICE); + Wire.write(0x6B); + Wire.write(0x5A); + Wire.endTransmission(); + printHex("BACK=", buf, readRegisters(0x6B, buf, 1)); + + // An address with nothing behind it is not acknowledged. + Wire.beginTransmission(ABSENT); + Serial.print("ABSENT="); + Serial.println(Wire.endTransmission()); + + Serial.println("DONE"); +} + +void loop() {} diff --git a/packages/pinbench_sim/test/i2c_read_test.dart b/packages/pinbench_sim/test/i2c_read_test.dart new file mode 100644 index 0000000..8f1604b --- /dev/null +++ b/packages/pinbench_sim/test/i2c_read_test.dart @@ -0,0 +1,42 @@ +/// End-to-end I²C reads: a real sketch built on the Arduino `Wire` library, +/// compiled for the Uno (`test/fixtures/i2c_read/`), runs on the emulated +/// ATmega328P and reads a device the test serves. +/// +/// The recorder's own tests cover the register logic; this proves the part +/// that only the real library can: that `Wire.requestFrom` after a repeated +/// start, a multi-byte read and a write-then-read-back all reach the served +/// registers through the TWI peripheral, and that an absent address is still +/// NACKed while another one answers. +library; + +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinbench_sim/core/avr_interop.dart'; + +void main() { + test('a Wire sketch reads, reads back and is refused where nothing answers', () { + final hex = File('test/fixtures/i2c_read/i2c_read.hex').readAsStringSync(); + final lines = []; + // `Serial.println` ends lines with CRLF; the bridge splits on the LF. + AVRBridge.loadHex(hex, onSerialPrint: (line) => lines.add(line.trimRight())); + + // Served after loading: a load resets the bus, so a new run never sees + // the previous one's devices. + AVRBridge.serveI2c(0x68); + AVRBridge.setI2cRegisters(0x68, 0x75, [0x68]); + AVRBridge.setI2cRegisters(0x68, 0x3B, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]); + + // Two simulated seconds is ample for setup() at 115200 baud; stop early + // once the sketch reports it is done. + const cyclesPerStep = 160000; // 10 ms at 16 MHz + for (var step = 0; step < 200 && !lines.contains('DONE'); step++) { + AVRBridge.tick(cyclesPerStep); + } + + expect( + lines, + containsAllInOrder(['WHO=68', 'ACC=123456789ABC', 'BACK=5A', 'ABSENT=2', 'DONE']), + ); + }); +} diff --git a/packages/pinbench_sim/test/i2c_recorder_test.dart b/packages/pinbench_sim/test/i2c_recorder_test.dart index 7602667..788ff56 100644 --- a/packages/pinbench_sim/test/i2c_recorder_test.dart +++ b/packages/pinbench_sim/test/i2c_recorder_test.dart @@ -120,4 +120,128 @@ void main() { expect(recorder.drain(0x3C), isEmpty); expect(recorder.acknowledges(0x3C), isFalse); }); + + group('answering reads', () { + /// A register read the way `Wire` does it: write the pointer, then a + /// repeated start and a read of [count] bytes. + List readRegisters(I2cRecorder bus, int address, int register, int count) { + bus.beginTransaction(address, write: true); + bus.writeByte(register); + bus.beginTransaction(address, write: false); // repeated start + final bytes = [for (var i = 0; i < count; i++) bus.readByte()]; + bus.endTransaction(); + return bytes; + } + + test('a read returns the register the sketch pointed at', () { + final bus = I2cRecorder() + ..serve(0x68) + ..setRegisters(0x68, 0x75, [0x68]); // MPU6050 WHO_AM_I + + expect(readRegisters(bus, 0x68, 0x75, 1), [0x68]); + }); + + test('the pointer advances after every byte and wraps at the end', () { + // How a sensor hands over a multi-byte reading in one read: the + // accelerometer's six bytes start at 0x3B and follow each other. + final bus = I2cRecorder() + ..serve(0x68, size: 4) + ..setRegisters(0x68, 0, [0xA0, 0xA1, 0xA2, 0xA3]); + + expect(readRegisters(bus, 0x68, 2, 4), [0xA2, 0xA3, 0xA0, 0xA1]); + }); + + test('what the sketch writes after the pointer can be read back', () { + // Setting an RTC's clock and reading it again depends on this. + final bus = I2cRecorder()..serve(0x68); + bus.beginTransaction(0x68, write: true); + [0x00, 0x30, 0x15, 0x09].forEach(bus.writeByte); + bus.endTransaction(); + + expect(readRegisters(bus, 0x68, 0x00, 3), [0x30, 0x15, 0x09]); + }); + + test('a two-byte pointer selects the register most significant byte first', () { + final bus = I2cRecorder() + ..serve(0x50, size: 32 * 1024, pointerBytes: 2) // 24LC256 EEPROM + ..setRegisters(0x50, 0x1234, [0x5A]); + bus.beginTransaction(0x50, write: true); + bus + ..writeByte(0x12) + ..writeByte(0x34); + bus.beginTransaction(0x50, write: false); + + expect(bus.readByte(), 0x5A); + }); + + test('a pointer-less device starts every read at register 0', () { + // A PCF8574 has one register, its port; writes drive the pins rather + // than change what a read returns. + final bus = I2cRecorder() + ..serve(0x20, size: 1, pointerBytes: 0) + ..setRegisters(0x20, 0, [0xF0]); + bus.beginTransaction(0x20, write: true); + bus.writeByte(0x0F); + bus.beginTransaction(0x20, write: false); + final first = bus.readByte(); + bus.beginTransaction(0x20, write: false); + + expect([first, bus.readByte()], [0xF0, 0xF0]); + }); + + test('an address nothing serves reads as an idle bus', () { + final bus = I2cRecorder()..listenAt(0x3C); + bus.beginTransaction(0x3C, write: false); + + expect(bus.readByte(), 0xFF, reason: 'a display listens but does not answer'); + }); + + test('serving claims the address, so it is acknowledged', () { + final bus = I2cRecorder()..serve(0x68); + + expect(bus.acknowledges(0x68), isTrue); + expect(bus.acknowledges(0x69), isFalse); + }); + + test('serving the same shape again keeps the registers and the pointer', () { + // Parts declare themselves every frame; that must not wipe what the + // sketch wrote a frame ago. + final bus = I2cRecorder()..serve(0x68); + bus.beginTransaction(0x68, write: true); + [0x10, 0x42].forEach(bus.writeByte); + bus.endTransaction(); + bus.serve(0x68); + + expect(bus.registersOf(0x68)![0x10], 0x42); + bus.serve(0x68, size: 128); + expect(bus.registersOf(0x68)![0x10], 0, reason: 'a new shape starts over'); + }); + + test('writes to a served device still reach drain', () { + // A part reacts to configuration (a power-on command, a range setting) + // by reading the transactions, exactly as a display does. + final bus = I2cRecorder()..serve(0x68); + bus.beginTransaction(0x68, write: true); + [0x6B, 0x00].forEach(bus.writeByte); + bus.endTransaction(); + + expect(bus.drain(0x68), [ + [0x6B, 0x00], + ]); + }); + + test('setRegisters is ignored for an address nothing serves', () { + final bus = I2cRecorder()..setRegisters(0x68, 0, [1, 2, 3]); + + expect(bus.registersOf(0x68), isNull); + }); + + test('reset forgets every served device', () { + final bus = I2cRecorder()..serve(0x68); + bus.reset(); + + expect(bus.registersOf(0x68), isNull); + expect(bus.acknowledges(0x68), isFalse); + }); + }); } diff --git a/web/avr_bridge.js b/web/avr_bridge.js index 7f3d2fe..a2fe665 100644 --- a/web/avr_bridge.js +++ b/web/avr_bridge.js @@ -143,7 +143,7 @@ function handlePortChange(pinOffset, value, oldValue) { } } -// --- I2C bus recording (port of I2cRecorder) -------------------------------- +// --- I2C bus recording and register serving (port of I2cRecorder) --------- // // Bus traffic is queued rather than sampled: the frame loop runs the CPU for a // whole frame before any part looks, and a screen refresh is a thousand bytes. @@ -158,17 +158,55 @@ const i2c = { buffered: new Map(), // address -> queued byte count current: null, currentAddress: null, + // Devices that answer reads: address -> { registers: Uint8Array, + // pointerBytes, pointer }. Kept here, beside the CPU, so a read is answered + // mid-frame without crossing into Dart. + devices: new Map(), + device: null, // the device the open transaction talks to + writing: false, + pointerBytesLeft: 0, reset() { this.listening.clear(); this.pending.clear(); this.buffered.clear(); + this.devices.clear(); this.current = null; this.currentAddress = null; + this.device = null; + this.pointerBytesLeft = 0; + }, + + // Same shape again keeps registers and pointer; a new shape starts over. + serve(address, size, pointerBytes) { + this.listening.add(address); + const existing = this.devices.get(address); + if (existing && existing.registers.length === size && existing.pointerBytes === pointerBytes) return; + this.devices.set(address, { registers: new Uint8Array(size), pointerBytes, pointer: 0 }); + }, + + setRegisters(address, offset, bytes) { + const device = this.devices.get(address); + if (!device) return; + const registers = device.registers; + for (let i = 0; i < bytes.length; i++) { + registers[(offset + i) % registers.length] = bytes[i] & 0xff; + } }, begin(address, write) { this.end(); + const device = this.devices.get(address) || null; + this.device = device; + this.writing = write; + if (device) { + if (write) { + this.pointerBytesLeft = device.pointerBytes; + if (device.pointerBytes > 0) device.pointer = 0; + } else if (device.pointerBytes === 0) { + device.pointer = 0; + } + } if (!write) return; if (!this.pending.has(address) && this.pending.size >= MAX_I2C_ADDRESSES) return; this.currentAddress = address; @@ -176,7 +214,28 @@ const i2c = { }, write(value) { - if (this.current) this.current.push(value & 0xff); + const byte = value & 0xff; + if (this.current) this.current.push(byte); + const device = this.device; + if (!device || !this.writing) return; + if (this.pointerBytesLeft > 0) { + device.pointer = ((device.pointer << 8) | byte) % device.registers.length; + this.pointerBytesLeft--; + } else if (device.pointerBytes > 0) { + // A pointer-less device's writes drive outputs, not readable registers. + device.registers[device.pointer] = byte; + device.pointer = (device.pointer + 1) % device.registers.length; + } + }, + + // The register under the pointer, which then advances; 0xff (a pulled-up, + // undriven bus) when nothing serves the address. + read() { + const device = this.device; + if (!device || this.writing) return 0xff; + const value = device.registers[device.pointer]; + device.pointer = (device.pointer + 1) % device.registers.length; + return value; }, end() { @@ -184,6 +243,8 @@ const i2c = { const bytes = this.current; this.currentAddress = null; this.current = null; + this.device = null; + this.pointerBytesLeft = 0; if (address === null || !bytes || !bytes.length) return; if (!this.pending.has(address)) this.pending.set(address, []); @@ -229,14 +290,15 @@ function attachTwi() { twi.completeConnect(i2c.listening.has(addr)); }, writeByte(value) { i2c.write(value); twi.completeWrite(true); }, - // Nothing on the canvas talks back yet; 0xff is what an idle bus reads. - readByte() { twi.completeRead(0xff); }, + readByte() { twi.completeRead(i2c.read()); }, }; } window.AVR8 = { listenI2c(address) { i2c.listening.add(address); }, drainI2c(address) { return i2c.drain(address); }, + serveI2c(address, size, pointerBytes) { i2c.serve(address, size, pointerBytes); }, + setI2cRegisters(address, offset, bytes) { i2c.setRegisters(address, offset, bytes); }, setSerialPrint(cb) { onSerialPrint = cb; }, setBuzzerFrequencyCallback(cb) { onBuzzerFrequency = cb; }, From 6b915c4c3e5f6250b70c9bcf60656d6d2b016cc2 Mon Sep 17 00:00:00 2001 From: burhankhanzada Date: Wed, 30 Sep 2026 02:29:05 +0200 Subject: [PATCH 02/11] Run the desktop emulator for cycles, not instructions AVRBridge.tick(cycles) looped `cycles` times over avrInstruction, so it ran that many instructions. Most AVR instructions take one cycle but branches, calls and memory access take two to four, so a frame asked for 256,000 cycles ran about 345,000 and sketches ran roughly 30% fast on desktop: delay(1000) lasted about 0.77 s. The web bridge already looped until cpu.cycles reached the target; native now does the same. avr_tick_test pins it: each frame advances the requested cycles, give or take the last instruction. It fails on the old loop (frame 0 ran 345,030 cycles). Co-Authored-By: Claude Opus 5.5 (1M context) --- .../pinbench_sim/lib/core/avr_interop_io.dart | 10 ++++++- packages/pinbench_sim/test/avr_tick_test.dart | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 packages/pinbench_sim/test/avr_tick_test.dart diff --git a/packages/pinbench_sim/lib/core/avr_interop_io.dart b/packages/pinbench_sim/lib/core/avr_interop_io.dart index aadafb9..0171204 100644 --- a/packages/pinbench_sim/lib/core/avr_interop_io.dart +++ b/packages/pinbench_sim/lib/core/avr_interop_io.dart @@ -191,6 +191,13 @@ class AVRBridge { } } + /// Runs the CPU for [cycles] clock cycles — simulated time, which the frame + /// loop derives from wall-clock time. + /// + /// Counted in *cycles*, not instructions: most AVR instructions take one + /// cycle but branches, calls and memory access take two to four, so a loop + /// over instructions ran typical sketches about 30% fast (`delay(1000)` + /// lasted 0.77 s). The web bridge counts the same way. static void tick(int cycles) { // Reset PWM duty accumulators for this frame. _dutyTotalSamples = 0; @@ -198,7 +205,8 @@ class AVRBridge { _dutyHighSamples[p] = 0; } - for (var i = 0; i < cycles; i++) { + final limit = _cpu.cycles + cycles; + for (var i = 0; _cpu.cycles < limit; i++) { avrInstruction(_cpu); // Periodically sample digital pin levels to estimate PWM duty cycles. diff --git a/packages/pinbench_sim/test/avr_tick_test.dart b/packages/pinbench_sim/test/avr_tick_test.dart new file mode 100644 index 0000000..e119980 --- /dev/null +++ b/packages/pinbench_sim/test/avr_tick_test.dart @@ -0,0 +1,27 @@ +/// `AVRBridge.tick` advances simulated time by exactly the cycles it is asked +/// for, which is what keeps `delay()` and `millis()` in step with the wall +/// clock. +library; + +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinbench_sim/core/avr_interop.dart'; + +void main() { + test('tick(n) advances n cycles, not n instructions', () { + // Any real firmware will do; this one mixes Wire, Serial and delay loops, + // so its instructions average well over one cycle each. + AVRBridge.loadHex(File('test/fixtures/i2c_read/i2c_read.hex').readAsStringSync()); + const frame = 256000; // one 16 ms frame at 16 MHz + + for (var f = 0; f < 50; f++) { + final before = AVRBridge.getCycles(); + AVRBridge.tick(frame); + final ran = AVRBridge.getCycles() - before; + // The last instruction may finish a few cycles past the boundary (the + // longest AVR instructions take 4 cycles, 5 with an interrupt entry). + expect(ran, inInclusiveRange(frame, frame + 5), reason: 'frame $f ran $ran cycles'); + } + }); +} From 90009d3adb610f014651930adceff6c82ee88287 Mon Sep 17 00:00:00 2001 From: burhankhanzada Date: Wed, 30 Sep 2026 02:50:30 +0200 Subject: [PATCH 03/11] Run the web's AVR on avr8_dart, compiled by dart2js The web used avr8js from the unpkg CDN, so the desktop and the web ran two different emulators and every feature was written twice: the I2C reads in web/avr_bridge.js had to mirror I2cRecorder by hand. Now web/avr_bridge.js is the native AVRBridge itself, compiled by dart2js and exported as window.AVR8 (web_bridge/avr_bridge_main.dart), so both platforms run the same code and the CDN dependency is gone. dart2js rather than calling the bridge from the app: the app ships as WebAssembly, and dart2wasm runs this loop at about half the speed (2.1x real time against 4.4x on the OLED firmware). To match avr8js's speed, tick() reads its statics into locals (a lazily initialised static costs a check on every access in dart2js) and samples PWM duty from the port registers instead of 14 pinState calls; the masks agree with pinState on every template (4.7M samples, 0 differences). Measured warm in Chrome 154: 2.71x real time against avr8js's 2.56x. The bridge no-ops before a sketch is loaded, as the JS one did, and no longer logs from tick (sim_log pulls in Flutter-side code). tools/build_avr_bridge.sh builds it against the app's committed lockfile; CI checks the committed file is current and runs tools/test_avr_bridge.mjs against it (I2C reads, cycle-accurate ticks, blink timing, real-time speed). Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 10 + README.md | 2 +- .../pinbench_sim/lib/core/avr_interop.dart | 12 +- .../pinbench_sim/lib/core/avr_interop_io.dart | 98 +- .../lib/core/avr_interop_web.dart | 8 +- .../pinbench_sim/lib/core/i2c_recorder.dart | 6 +- .../web_bridge/avr_bridge_main.dart | 102 + tools/build_avr_bridge.sh | 42 + tools/test_avr_bridge.mjs | 70 + web/avr_bridge.js | 5349 +++++++++++++++-- web/index.html | 6 +- 11 files changed, 5266 insertions(+), 439 deletions(-) create mode 100644 packages/pinbench_sim/web_bridge/avr_bridge_main.dart create mode 100755 tools/build_avr_bridge.sh create mode 100644 tools/test_avr_bridge.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad0c02a..5b98acb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,6 +148,16 @@ jobs: echo "::endgroup::" done + # web/avr_bridge.js is the web's emulator: the native AVRBridge compiled + # by dart2js, committed so a web build needs no extra step. A change to + # the bridge that forgot to rebuild it would ship the old emulator to + # the web alone, so check it matches, then run the compiled file itself: + # I²C reads, cycle-accurate ticks, and real-time speed. + - name: Check the web emulator build + run: | + tools/build_avr_bridge.sh --check + node tools/test_avr_bridge.mjs + # The figure, where it is seen: the run's summary page. It counts the # lines of files the tests load, so files no test touches are missing # from both sides — read it as a trend, not an absolute. diff --git a/README.md b/README.md index d2c2588..c8206d3 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ forking is welcome and always will be, just under your own name. | State | Riverpod 3.x with codegen | | Canvas | Custom `CustomPainter` | | Editor | `re_editor` | -| AVR emulation | `avr8_dart` (native), `avr8js` (web) | +| AVR emulation | `avr8_dart` (native; compiled by dart2js on the web) | | Analog solver | `ngspice_dart` — a pure-Dart port, no FFI or native libngspice | | Audio | `flutter_soloud` (native), Web Audio (web) | | Multi-window | `multiview_desktop` | diff --git a/packages/pinbench_sim/lib/core/avr_interop.dart b/packages/pinbench_sim/lib/core/avr_interop.dart index 6db0a5c..25873a4 100644 --- a/packages/pinbench_sim/lib/core/avr_interop.dart +++ b/packages/pinbench_sim/lib/core/avr_interop.dart @@ -1,8 +1,10 @@ // Selects the AVR emulator backend at compile time. // -// Native platforms use the pure-Dart `avr8_dart` port. The web delegates to -// `avr8js` (optimized JS, real-time on the 16 MHz ATmega328P) via the bridge in -// `web/avr_bridge.js`, because `avr8_dart` compiled by dart2js is too slow to -// keep real time (so time-based sketches lag). Both expose the same `AVRBridge` -// static API used by the simulation engine. +// Both run the same code: the pure-Dart `avr8_dart` port behind the native +// `AVRBridge` in `avr_interop_io.dart`. Native platforms call it directly. The +// web app is WebAssembly, where that integer-heavy loop runs at about half the +// speed of JavaScript, so the web instead calls a dart2js build of the same +// bridge (`web/avr_bridge.js`, built by `tools/build_avr_bridge.sh`) through +// the `window.AVR8` facade in `avr_interop_web.dart`. Both expose the same +// `AVRBridge` static API used by the simulation engine. export 'avr_interop_io.dart' if (dart.library.js_interop) 'avr_interop_web.dart'; diff --git a/packages/pinbench_sim/lib/core/avr_interop_io.dart b/packages/pinbench_sim/lib/core/avr_interop_io.dart index 0171204..a55353d 100644 --- a/packages/pinbench_sim/lib/core/avr_interop_io.dart +++ b/packages/pinbench_sim/lib/core/avr_interop_io.dart @@ -3,7 +3,6 @@ import 'dart:typed_data'; import 'package:avr8_dart/avr8_dart.dart'; -import 'sim_log.dart'; import 'frequency_detector.dart'; import 'i2c_recorder.dart'; @@ -15,9 +14,19 @@ import 'i2c_recorder.dart'; /// RX queueing ([queueSerialInput]) and TX (via onSerialPrint), and buzzer /// frequency detection. State is static because the emulator is a singleton per /// process; [loadHex] fully re-creates the CPU on each run. +/// +/// **The same code runs on the web.** This file imports nothing from Flutter, +/// so `tools/build_avr_bridge.sh` compiles it with dart2js into +/// `web/avr_bridge.js` (see `web_bridge/avr_bridge_main.dart`), which the web +/// build's [AVRBridge] facade calls. Keep it that way: an import of Flutter, +/// or of anything that imports Flutter, breaks the web build of the emulator. class AVRBridge { - static const _log = SimLog('app.simulation.avr'); static late CPU _cpu; + + /// Whether a program has been loaded. Everything that reads the CPU answers + /// a harmless default before then, because the canvas asks for pin states + /// before the first Run. + static var _loaded = false; static CPU get cpu => _cpu; static late AVRIOPort _portB; static late AVRIOPort _portC; @@ -107,6 +116,7 @@ class AVRBridge { /// Fraction of the last [tick] window that [pin] was driven high (0.0–1.0). /// Falls back to the instantaneous level if no samples were taken yet. static double getPinDuty(int pin) { + if (!_loaded) return 0; if (pin < 0 || pin >= _dutyPinCount) return getPinState(pin) ? 1.0 : 0.0; if (_dutyTotalSamples == 0) return getPinState(pin) ? 1.0 : 0.0; return _dutyHighSamples[pin] / _dutyTotalSamples; @@ -144,9 +154,11 @@ class AVRBridge { _twi.eventHandler = _RecordingTwiHandler(_twi, _i2c); _freqDetector.reset(); + _loaded = true; } static bool isPinOutput(int pin) { + if (!_loaded) return false; if (pin >= 0 && pin <= 7) { return (_cpu.data[portDConfig.DDR] & (1 << pin)) != 0; } else if (pin >= 8 && pin <= 13) { @@ -199,60 +211,89 @@ class AVRBridge { /// over instructions ran typical sketches about 30% fast (`delay(1000)` /// lasted 0.77 s). The web bridge counts the same way. static void tick(int cycles) { + if (!_loaded) return; + // Everything the loop touches is read into a local first. `_cpu` and the + // other statics are lazily initialised, so every access checks whether + // they are set yet; in the dart2js build of this file (the web's + // emulator) that check is a call, and the loop runs about 60 million + // times a second. + final cpu = _cpu; + final usart = _usart; + final portB = _portB; + final portD = _portD; + final rxQueue = _rxQueue; + final freqDetector = _freqDetector; + final dutyHigh = _dutyHighSamples; + final ddrD = portDConfig.DDR; + final ddrB = portBConfig.DDR; + // Reset PWM duty accumulators for this frame. - _dutyTotalSamples = 0; + var dutyTotal = 0; for (var p = 0; p < _dutyPinCount; p++) { - _dutyHighSamples[p] = 0; + dutyHigh[p] = 0; } - final limit = _cpu.cycles + cycles; - for (var i = 0; _cpu.cycles < limit; i++) { - avrInstruction(_cpu); + final limit = cpu.cycles + cycles; + for (var i = 0; cpu.cycles < limit; i++) { + avrInstruction(cpu); - // Periodically sample digital pin levels to estimate PWM duty cycles. + // Periodically sample digital pin levels to estimate PWM duty cycles: + // pins 0-7 are PORTD, 8-13 are PORTB. A pin is driven high exactly when + // `AVRIOPort.pinState` would say `High` — an output, last written 1, not + // open-collector — and computing that as one mask per port instead of + // fourteen enum lookups keeps the sampler off the profile. if ((i & (_dutySampleInterval - 1)) == 0) { - _dutyTotalSamples++; - for (var p = 0; p < _dutyPinCount; p++) { - if (getPinState(p)) _dutyHighSamples[p]++; + dutyTotal++; + final highD = cpu.data[ddrD] & portD.lastValue & ~portD.openCollector; + final highB = cpu.data[ddrB] & portB.lastValue & ~portB.openCollector; + if (highD != 0) { + for (var p = 0; p < 8; p++) { + if ((highD & (1 << p)) != 0) dutyHigh[p]++; + } + } + if (highB != 0) { + for (var p = 8; p < _dutyPinCount; p++) { + if ((highB & (1 << (p - 8))) != 0) dutyHigh[p]++; + } } } // Process all pending clock events for the current CPU cycle - while (_cpu.nextClockEvent != null && _cpu.nextClockEvent!.cycles <= _cpu.cycles) { - final event = _cpu.nextClockEvent!; - _cpu.nextClockEvent = event.next; + while (cpu.nextClockEvent != null && cpu.nextClockEvent!.cycles <= cpu.cycles) { + final event = cpu.nextClockEvent!; + cpu.nextClockEvent = event.next; event.callback(); - if (_cpu.clockEventPool.length < 10) { - _cpu.clockEventPool.add(event); + if (cpu.clockEventPool.length < 10) { + cpu.clockEventPool.add(event); } } // Feed queued serial input into the receiver as it frees up. writeByte // returns false while the USART is busy or RX is disabled, so the byte // stays queued until the sketch is ready for it. - if (_rxQueue.isNotEmpty && !_usart.rxBusy && _usart.writeByte(_rxQueue.first)) { - _rxQueue.removeFirst(); + if (rxQueue.isNotEmpty && !usart.rxBusy && usart.writeByte(rxQueue.first)) { + rxQueue.removeFirst(); } // Process interrupts - if (_cpu.interruptsEnabled && _cpu.nextInterrupt >= 0) { - final interrupt = _cpu.pendingInterrupts[_cpu.nextInterrupt]; + if (cpu.interruptsEnabled && cpu.nextInterrupt >= 0) { + final interrupt = cpu.pendingInterrupts[cpu.nextInterrupt]; if (interrupt != null) { - avrInterrupt(_cpu, interrupt.address); + avrInterrupt(cpu, interrupt.address); if (!interrupt.constant) { - _cpu.clearInterrupt(interrupt); + cpu.clearInterrupt(interrupt); } } } - if (_freqDetector.checkTimeout(_cpu.cycles)) { - _log.trace('Tone stopped at cycle ${_cpu.cycles}'); + if (freqDetector.checkTimeout(cpu.cycles)) { onBuzzerFrequencyChanged?.call(null); } } + _dutyTotalSamples = dutyTotal; } - static bool getPin13State() => _portB.pinState(5) == PinState.High; + static bool getPin13State() => _loaded && _portB.pinState(5) == PinState.High; /// Whether the Arduino Uno's on-board LED is currently lit. /// @@ -264,6 +305,7 @@ class AVRBridge { static bool get isBuiltinLedOn => getPin13State(); static bool getPinState(int pin) { + if (!_loaded) return false; if (pin >= 0 && pin <= 7) { return _portD.pinState(pin) == PinState.High; } else if (pin >= 8 && pin <= 13) { @@ -275,12 +317,14 @@ class AVRBridge { } static void setAnalogVoltage(int channel, double voltage) { + if (!_loaded) return; if (channel >= 0 && channel < _adc.channelValues.length) { _adc.channelValues[channel] = voltage; } } static void setDigitalPin(int pin, {required bool isHigh}) { + if (!_loaded) return; if (pin >= 0 && pin <= 7) { _portD.setPin(pin, isHigh); } else if (pin >= 8 && pin <= 13) { @@ -290,9 +334,9 @@ class AVRBridge { } } - static int getPortBValue() => _cpu.data[0x25]; // PORTB address + static int getPortBValue() => _loaded ? _cpu.data[0x25] : 0; // PORTB address - static int getCycles() => _cpu.cycles; + static int getCycles() => _loaded ? _cpu.cycles : 0; static void _parseHex(String hexString, Uint16List flash) { flash.fillRange(0, flash.length, 0); diff --git a/packages/pinbench_sim/lib/core/avr_interop_web.dart b/packages/pinbench_sim/lib/core/avr_interop_web.dart index 85cd5c2..ca11101 100644 --- a/packages/pinbench_sim/lib/core/avr_interop_web.dart +++ b/packages/pinbench_sim/lib/core/avr_interop_web.dart @@ -1,9 +1,11 @@ import 'dart:js_interop'; import 'dart:typed_data'; -/// Web [AVRBridge] backed by `avr8js` via the `window.AVR8` bridge defined in -/// `web/avr_bridge.js`. Mirrors the native `avr_interop_io.dart` API but runs -/// the emulator in optimized JS so the 16 MHz AVR keeps real time. +/// Web [AVRBridge]: a facade over `window.AVR8`, which is the native +/// `avr_interop_io.dart` bridge itself compiled by dart2js into +/// `web/avr_bridge.js` (`web_bridge/avr_bridge_main.dart`). The emulator is the +/// same `avr8_dart` code on every platform; it runs as JavaScript here because +/// dart2js runs it about twice as fast as the app's WebAssembly. @JS('AVR8') external _Avr8 get _avr8; diff --git a/packages/pinbench_sim/lib/core/i2c_recorder.dart b/packages/pinbench_sim/lib/core/i2c_recorder.dart index 48b7a3b..018e03b 100644 --- a/packages/pinbench_sim/lib/core/i2c_recorder.dart +++ b/packages/pinbench_sim/lib/core/i2c_recorder.dart @@ -24,9 +24,9 @@ import 'dart:typed_data'; /// fresher than any real sensor's conversion time. /// /// Kept out of the emulator facades because both of them need it and neither -/// owns it: the native bridge feeds this from `avr8_dart`'s TWI callbacks, the -/// web bridge from `avr8js`'s (`web/avr_bridge.js` mirrors it, so reads never -/// cross into Dart), and the frame loop drains the same shape either way. +/// owns it: the bridge feeds this from `avr8_dart`'s TWI callbacks (on the web +/// too, inside the dart2js build in `web/avr_bridge.js`, so reads never cross +/// into the app), and the frame loop drains the same shape either way. class I2cRecorder { /// Addresses some part has claimed by draining them. /// diff --git a/packages/pinbench_sim/web_bridge/avr_bridge_main.dart b/packages/pinbench_sim/web_bridge/avr_bridge_main.dart new file mode 100644 index 0000000..853ce31 --- /dev/null +++ b/packages/pinbench_sim/web_bridge/avr_bridge_main.dart @@ -0,0 +1,102 @@ +/// The web's ATmega328P: the same `avr8_dart` emulator and [AVRBridge] the +/// desktop runs, compiled by dart2js into `web/avr_bridge.js` and exposed as +/// `window.AVR8` for the app's web facade (`lib/core/avr_interop_web.dart`). +/// +/// Why a separate dart2js build rather than calling [AVRBridge] from the app +/// directly: the web app ships as WebAssembly, and dart2wasm runs this +/// integer-heavy loop at about half the speed of dart2js (≈2× real time +/// against ≈4.4×, measured on the OLED template's firmware). JavaScript is the +/// faster home for the CPU, so the CPU lives there, and the app crosses into +/// it once per frame, exactly as it did when this was `avr8js`. +/// +/// Build with `tools/build_avr_bridge.sh`; never edit `web/avr_bridge.js` by +/// hand. +library; + +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import 'dart:typed_data'; + +import 'package:pinbench_sim/core/avr_interop_io.dart'; + +void main() { + globalContext['AVR8'] = createJSInteropWrapper(_Avr8()); +} + +/// The `window.AVR8` object. Method names and argument order are the contract +/// with the web facade's `_Avr8` extension type; change both together. +@JSExport() +class _Avr8 { + JSFunction? _serialPrint; + + void setSerialPrint(JSFunction callback) => _serialPrint = callback; + + void setBuzzerFrequencyCallback(JSFunction callback) { + AVRBridge.onBuzzerFrequencyChanged = (hz) => callback.callAsFunction(null, hz?.toJS); + } + + void setBuzzerPin(JSNumber? pin) => AVRBridge.buzzerPin = pin?.toDartInt; + + void setServoPins(JSArray? pins) => + AVRBridge.servoPins = [for (final pin in pins?.toDart ?? const []) pin.toDartInt]; + + double getServoPulseUs(int pin) => AVRBridge.getServoPulseUs(pin); + + void loadHex(String hex) => AVRBridge.loadHex(hex, onSerialPrint: _printLine); + + /// `Serial.println` ends lines with CRLF and the USART splits on the LF, so + /// the CR is dropped here, where the web always dropped it. + void _printLine(String line) { + final text = line.endsWith('\r') ? line.substring(0, line.length - 1) : line; + _serialPrint?.callAsFunction(null, text.toJS); + } + + void queueSerialInput(String text) => AVRBridge.queueSerialInput(text); + + void tick(int cycles) => AVRBridge.tick(cycles); + + bool getPinState(int pin) => AVRBridge.getPinState(pin); + + double getPinDuty(int pin) => AVRBridge.getPinDuty(pin); + + bool isPinOutput(int pin) => AVRBridge.isPinOutput(pin); + + // Positional, like the facade's call. + // ignore: avoid_positional_boolean_parameters + void setDigitalPin(int pin, bool isHigh) => AVRBridge.setDigitalPin(pin, isHigh: isHigh); + + void setAnalogVoltage(int channel, double voltage) => + AVRBridge.setAnalogVoltage(channel, voltage); + + int getPortBValue() => AVRBridge.getPortBValue(); + + int getCycles() => AVRBridge.getCycles(); + + bool getPin13State() => AVRBridge.getPin13State(); + + void listenI2c(int address) => AVRBridge.listenI2c(address); + + /// The frame's traffic as one flat typed array, `[length, ...bytes]` per + /// transaction, so it crosses into the app as a single copy. + JSInt32Array drainI2c(int address) { + final transactions = AVRBridge.drainI2c(address); + var total = 0; + for (final t in transactions) { + total += t.length + 1; + } + final flat = Int32List(total); + var at = 0; + for (final t in transactions) { + flat[at++] = t.length; + flat.setAll(at, t); + at += t.length; + } + return flat.toJS; + } + + void serveI2c(int address, int size, int pointerBytes) => + AVRBridge.serveI2c(address, size: size, pointerBytes: pointerBytes); + + void setI2cRegisters(int address, int offset, JSInt32Array bytes) => + AVRBridge.setI2cRegisters(address, offset, bytes.toDart); +} diff --git a/tools/build_avr_bridge.sh b/tools/build_avr_bridge.sh new file mode 100755 index 0000000..ab3bf9d --- /dev/null +++ b/tools/build_avr_bridge.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Builds web/avr_bridge.js — the web's ATmega328P — from the same Dart the +# desktop runs (packages/pinbench_sim/lib/core/avr_interop_io.dart), with +# dart2js. The output is committed so `flutter run -d chrome` and the website +# build need no extra step; CI rebuilds it and fails if it drifted. +# +# tools/build_avr_bridge.sh rebuild web/avr_bridge.js +# tools/build_avr_bridge.sh --check exit 1 if the committed file is stale +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +pkg="$root/packages/pinbench_sim" +out="$root/web/avr_bridge.js" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT + +# Resolved by the app, not by pinbench_sim: the app's pubspec.lock is +# committed and pins avr8_dart, so a new avr8_dart release cannot make --check +# fail on a commit that changed nothing. +(cd "$root" && flutter pub get --enforce-lockfile >/dev/null) +# -O4 is safe here: the bridge does no type tests that -O4's assumptions +# could break, and it is the build the benchmark's 4.4x real time came from. +(cd "$pkg" && dart compile js -O4 --no-source-maps \ + --packages="$root/.dart_tool/package_config.json" \ + -o "$tmp/avr_bridge.js" web_bridge/avr_bridge_main.dart >/dev/null) + +{ + echo "// GENERATED by tools/build_avr_bridge.sh from" + echo "// packages/pinbench_sim/web_bridge/avr_bridge_main.dart — do not edit." + cat "$tmp/avr_bridge.js" +} > "$tmp/avr_bridge.out.js" + +if [[ "${1:-}" == "--check" ]]; then + if ! cmp -s "$tmp/avr_bridge.out.js" "$out"; then + echo "web/avr_bridge.js is stale: run tools/build_avr_bridge.sh and commit it." >&2 + exit 1 + fi + echo "web/avr_bridge.js is up to date." +else + mv "$tmp/avr_bridge.out.js" "$out" + echo "Wrote web/avr_bridge.js ($(wc -c < "$out" | tr -d ' ') bytes)." +fi diff --git a/tools/test_avr_bridge.mjs b/tools/test_avr_bridge.mjs new file mode 100644 index 0000000..acbee69 --- /dev/null +++ b/tools/test_avr_bridge.mjs @@ -0,0 +1,70 @@ +// Runs the compiled web emulator (web/avr_bridge.js) in Node against real +// firmware, so the artefact the browser loads is tested, not only the Dart it +// was built from. Usage: node tools/test_avr_bridge.mjs +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import vm from 'node:vm'; + +const root = fileURLToPath(new URL('..', import.meta.url)); +vm.runInThisContext(readFileSync(root + 'web/avr_bridge.js', 'utf8'), { filename: 'avr_bridge.js' }); +const avr = globalThis.AVR8; +if (!avr) throw new Error('avr_bridge.js did not define AVR8'); + +let failures = 0; +const check = (name, ok, detail) => { + console.log(`${ok ? 'ok ' : 'FAIL'} ${name}${detail ? ' — ' + detail : ''}`); + if (!ok) failures++; +}; +const CLOCK = 16e6; + +// 1. Calls before any program is loaded answer defaults instead of throwing. +check('safe before load', avr.getPinState(13) === false && avr.getCycles() === 0); + +// 2. I2C reads, read-back and NACK, through the real Wire library. +{ + const lines = []; + avr.setSerialPrint((line) => lines.push(line)); + avr.loadHex(readFileSync(root + 'packages/pinbench_sim/test/fixtures/i2c_read/i2c_read.hex', 'utf8')); + avr.serveI2c(0x68, 256, 1); + avr.setI2cRegisters(0x68, 0x75, new Int32Array([0x68])); + avr.setI2cRegisters(0x68, 0x3b, new Int32Array([0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc])); + for (let i = 0; i < 200 && !lines.includes('DONE'); i++) avr.tick(160000); + const want = ['WHO=68', 'ACC=123456789ABC', 'BACK=5A', 'ABSENT=2', 'DONE']; + check('I2C reads', want.every((w, i) => lines[i] === w), JSON.stringify(lines)); +} + +// 3. Time: a frame's worth of cycles is exactly that, so delay() keeps pace. +{ + avr.loadHex(readFileSync(root + 'assets/templates/blink/blink.ino.hex', 'utf8')); + let exact = true; + for (let f = 0; f < 50; f++) { + const before = avr.getCycles(); + avr.tick(256000); + const ran = avr.getCycles() - before; + if (ran < 256000 || ran > 256005) exact = false; + } + check('tick counts cycles', exact); + // The blink template toggles pin 13 every 500 ms: 8 edges in 4 simulated s. + let edges = 0, last = avr.getPin13State(); + for (let f = 0; f < 250; f++) { + avr.tick(256000); + const now = avr.getPin13State(); + if (now !== last) edges++; + last = now; + } + check('blink keeps real time', edges >= 7 && edges <= 9, `${edges} edges in 4 s (delay(500): 8 expected)`); +} + +// 4. Speed: the reason this is dart2js and not the app's Wasm. +{ + avr.loadHex(readFileSync(root + 'assets/templates/oled/oled.ino.hex', 'utf8')); + const t0 = performance.now(); + for (let f = 0; f < 312; f++) avr.tick(256000); // ~5 simulated seconds + const x = avr.getCycles() / ((performance.now() - t0) / 1000) / CLOCK; + // The bar is real time itself, not the ~3.5x a laptop gets: a shared CI + // runner is slower, and what the web needs is only never to fall behind. + check('faster than real time', x > 1, `x${x.toFixed(2)} real time`); +} + +console.log(failures ? `${failures} failed` : 'all passed'); +process.exit(failures ? 1 : 0); diff --git a/web/avr_bridge.js b/web/avr_bridge.js index a2fe665..b8a5c92 100644 --- a/web/avr_bridge.js +++ b/web/avr_bridge.js @@ -1,411 +1,4962 @@ -// Full AVR bridge backed by avr8js (hand-optimized JS, ~16 MHz+ in-browser). -// -// On the web the pure-Dart `avr8_dart` emulator (compiled by dart2js) tops out -// around ~10 MHz — too slow to run the 16 MHz ATmega328P in real time, which -// makes time-based sketches (clap rhythm, delays) lag. avr8js runs the same -// core in optimized JS at real-time speed, so the web build delegates the AVR -// emulation here. This mirrors `lib/features/simulation/core/avr_interop.dart`. -import { - CPU, - avrInstruction, - AVRIOPort, - portBConfig, - portCConfig, - portDConfig, - PinState, - AVRTimer, - timer0Config, - timer1Config, - timer2Config, - AVRADC, - adcConfig, - AVRUSART, - usart0Config, - AVRTWI, - twiConfig, -} from 'https://unpkg.com/avr8js@0.21.0/dist/esm/index.js'; - -let cpu = null; -let portB = null, portC = null, portD = null; -let adc = null, usart = null, twi = null; -// Timers must be kept referenced so they keep ticking. -// eslint-disable-next-line no-unused-vars -let timer0 = null, timer1 = null, timer2 = null; - -let buzzerPin = null; -let onSerialPrint = null; // (String) => void (Dart callback) -let onBuzzerFrequency = null; // (double|null) => void (Dart callback) - -// Servo signal pins (set from the traced netlist, like buzzerPin). HIGH-pulse -// widths are measured only on these pins. -let servoPins = []; -const servoRise = new Float64Array(14).fill(-1); -const servoPulseUs = new Float64Array(14); - -const rxQueue = []; -let txLine = ''; - -// --- PWM duty sampling (pins 0..13) ----------------------------------------- -const DUTY_PINS = 14; -const DUTY_SAMPLE_MASK = 63; // sample every 64 cycles -const dutyHigh = new Int32Array(DUTY_PINS); -let dutyTotal = 0; - -// --- Frequency detector (port of FrequencyDetector) ------------------------- -const CLOCK = 16000000; -const fd = { - lastToggleCycle: 0, prevDelta: 0, stableDelta: 0, stableCount: 0, - timeoutCycles: 160000, playing: false, - reset() { - this.lastToggleCycle = 0; this.prevDelta = 0; this.stableDelta = 0; - this.stableCount = 0; this.timeoutCycles = 160000; this.playing = false; - }, - onToggle(cycle) { - if (this.lastToggleCycle !== 0) { - const d = cycle - this.lastToggleCycle; - if (d > 400 && d < 400000) { - if (this.stableDelta === 0 || Math.abs(this.stableDelta - d) > 2000) { - this.stableDelta = d; this.stableCount = 1; - } else { - this.stableDelta = ((this.stableDelta * 3) + d + 2) >> 2; - this.stableCount++; - } - this.timeoutCycles = Math.max(this.stableDelta * 4, 4000); - if (this.stableCount >= 4) { - if (!this.playing || Math.abs(this.prevDelta - this.stableDelta) > 2000) { - this.prevDelta = this.stableDelta; - let freq = CLOCK / (2.0 * this.stableDelta); - if (Math.abs(freq - 2000.0) < 20.0) freq = 2000.0; - this.playing = true; - if (onBuzzerFrequency) onBuzzerFrequency(freq); - } - } - } - } - this.lastToggleCycle = cycle; - }, - checkTimeout(cycles) { - if (this.playing && this.lastToggleCycle !== 0 && - (cycles - this.lastToggleCycle) > this.timeoutCycles) { - this.reset(); - return true; - } - return false; - }, -}; - -function parseHex(hexString) { - const flash = new Uint16Array(0x4000); // 32KB flash = 16K x 16-bit words - const lines = hexString.split('\n'); - for (let line of lines) { - line = line.trim(); - if (line.length === 0 || line[0] !== ':') continue; - if (line.substr(7, 2) !== '00') continue; // data records only - const length = parseInt(line.substr(1, 2), 16); - const addr = parseInt(line.substr(3, 4), 16); - for (let i = 0; i < length; i += 2) { - const lsb = parseInt(line.substr(9 + i * 2, 2), 16); - let msb = 0; - if (i + 1 < length) msb = parseInt(line.substr(11 + i * 2, 2), 16); - flash[(addr + i) >> 1] = (msb << 8) | lsb; - } - } - return flash; +// GENERATED by tools/build_avr_bridge.sh from +// packages/pinbench_sim/web_bridge/avr_bridge_main.dart — do not edit. +(function dartProgram(){function copyProperties(a,b){var t=Object.keys(a) +for(var s=0;s=0)return true +if(typeof version=="function"&&version.length==0){var r=version() +if(/^\d+\.\d+\.\d+\.\d+$/.test(r))return true}}catch(q){}return false}() +function inherit(a,b){a.prototype.constructor=a +a.prototype["$i"+a.name]=a +if(b!=null){if(z){Object.setPrototypeOf(a.prototype,b.prototype) +return}var t=Object.create(b.prototype) +copyProperties(a.prototype,t) +a.prototype=t}}function inheritMany(a,b){for(var t=0;t4294967295)throw A.d(A.X(a,0,4294967295,"length",null)) +return J.fo(new Array(a),b)}, +fn(a,b){if(a<0)throw A.d(A.aF("Length must be a non-negative integer: "+a)) +return A.F(new Array(a),b.m("k<0>"))}, +fo(a,b){var t=A.F(a,b.m("k<0>")) +t.$flags=1 +return t}, +e6(a){if(a<256)switch(a){case 9:case 10:case 11:case 12:case 13:case 32:case 133:case 160:return!0 +default:return!1}switch(a){case 5760:case 8192:case 8193:case 8194:case 8195:case 8196:case 8197:case 8198:case 8199:case 8200:case 8201:case 8202:case 8232:case 8233:case 8239:case 8287:case 12288:case 65279:return!0 +default:return!1}}, +fp(a,b){var t,s +for(t=a.length;b0;b=t){t=b-1 +s=a.charCodeAt(t) +if(s!==32&&s!==13&&!J.e6(s))break}return b}, +ac(a){if(typeof a=="number"){if(Math.floor(a)==a)return J.aK.prototype +return J.bw.prototype}if(typeof a=="string")return J.aj.prototype +if(a==null)return J.aL.prototype +if(typeof a=="boolean")return J.bv.prototype +if(Array.isArray(a))return J.k.prototype +if(typeof a!="object"){if(typeof a=="function")return J.v.prototype +if(typeof a=="symbol")return J.al.prototype +if(typeof a=="bigint")return J.ak.prototype +return a}if(a instanceof A.m)return a +return J.dc(a)}, +ad(a){if(typeof a=="string")return J.aj.prototype +if(a==null)return a +if(Array.isArray(a))return J.k.prototype +if(typeof a!="object"){if(typeof a=="function")return J.v.prototype +if(typeof a=="symbol")return J.al.prototype +if(typeof a=="bigint")return J.ak.prototype +return a}if(a instanceof A.m)return a +return J.dc(a)}, +a0(a){if(a==null)return a +if(Array.isArray(a))return J.k.prototype +if(typeof a!="object"){if(typeof a=="function")return J.v.prototype +if(typeof a=="symbol")return J.al.prototype +if(typeof a=="bigint")return J.ak.prototype +return a}if(a instanceof A.m)return a +return J.dc(a)}, +dP(a){if(a==null)return a +if(typeof a!="object"){if(typeof a=="function")return J.v.prototype +if(typeof a=="symbol")return J.al.prototype +if(typeof a=="bigint")return J.ak.prototype +return a}if(a instanceof A.m)return a +return J.dc(a)}, +bR(a,b){if(a==null)return b==null +if(typeof a!="object")return b!=null&&a===b +return J.ac(a).a_(a,b)}, +eK(a,b){if(typeof b==="number")if(Array.isArray(a)||typeof a=="string"||A.eB(a,a[v.dispatchPropertyName]))if(b>>>0===b&&b>>0===b&&b").a7(c).m("b4<1,2>")) +return new A.a3(a,b.m("@<0>").a7(c).m("a3<1,2>"))}, +cN(a){return new A.aP("Field '"+a+"' has not been initialized.")}, +fr(a){return new A.aP("Field '"+a+"' has already been initialized.")}, +dR(a){var t,s +for(t=$.aa.length,s=0;s"))}, +eb(a,b,c){var t="count" +if(u.X.b(a)){A.cx(b,t) +A.O(b,t) +return new A.ai(a,b,c.m("ai<0>"))}A.cx(b,t) +A.O(b,t) +return new A.P(a,b,c.m("P<0>"))}, +e3(){return new A.b0("No element")}, +e4(){return new A.b0("Too few elements")}, +Y:function Y(){}, +bo:function bo(a,b){this.a=a +this.$ti=b}, +a3:function a3(a,b){this.a=a +this.$ti=b}, +b4:function b4(a,b){this.a=a +this.$ti=b}, +b3:function b3(){}, +aH:function aH(a,b){this.a=a +this.$ti=b}, +aP:function aP(a){this.a=a}, +bp:function bp(a){this.a=a}, +e:function e(){}, +z:function z(){}, +b1:function b1(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.$ti=d}, +am:function am(a,b,c){var _=this +_.a=a +_.b=b +_.c=0 +_.d=null +_.$ti=c}, +aT:function aT(a,b,c){this.a=a +this.b=b +this.$ti=c}, +P:function P(a,b,c){this.a=a +this.b=b +this.$ti=c}, +ai:function ai(a,b,c){this.a=a +this.b=b +this.$ti=c}, +bH:function bH(a,b){this.a=a +this.b=b}, +aJ:function aJ(){}, +bJ:function bJ(){}, +ar:function ar(){}, +be:function be(){}, +eH(a){var t=A.eG(a) +if(t!=null)return t +return"minified:"+a}, +eB(a,b){var t +if(b!=null){t=b.x +if(t!=null)return t}return u.G.b(a)}, +q(a){var t +if(typeof a=="string")return a +if(typeof a=="number"){if(a!==0)return""+a}else if(!0===a)return"true" +else if(!1===a)return"false" +else if(a==null)return"null" +t=J.ax(a) +return t}, +aZ(a){var t,s=$.e8 +if(s==null)s=$.e8=Symbol("identityHashCode") +t=a[s] +if(t==null){t=Math.random()*0x3fffffff|0 +a[s]=t}return t}, +fx(a,b){var t,s,r,q,p,o=null,n=/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(a) +if(n==null)return o +t=n[3] +if(b<2||b>36)throw A.d(A.X(b,2,36,"radix",o)) +if(b===10&&t!=null)return parseInt(a,10) +if(b<10||t==null){s=b<=10?47+b:86+b +r=n[1] +for(q=r.length,p=0;ps)return o}return parseInt(a,b)}, +bE(a){var t,s,r,q +if(a instanceof A.m)return A.B(A.av(a),null) +t=J.ac(a) +if(t===B.a7||t===B.a9||u.o.b(a)){s=B.n(a) +if(s!=="Object"&&s!=="")return s +r=a.constructor +if(typeof r=="function"){q=r.name +if(typeof q=="string"&&q!=="Object"&&q!=="")return q}}return A.B(A.av(a),null)}, +fy(a){var t,s,r +if(typeof a=="number"||A.dM(a))return J.ax(a) +if(typeof a=="string")return JSON.stringify(a) +if(a instanceof A.a4)return a.j(0) +t=$.eJ() +for(s=0;s<1;++s){r=t[s].cA(a) +if(r!=null)return r}return"Instance of '"+A.bE(a)+"'"}, +fz(a){var t +if(0<=a){if(a<=65535)return String.fromCharCode(a) +if(a<=1114111){t=a-65536 +return String.fromCharCode((B.a.A(t,10)|55296)>>>0,t&1023|56320)}}throw A.d(A.X(a,0,1114111,null,null))}, +dO(a,b){var t,s="index" +if(!A.eu(b))return new A.ah(!0,b,s,null) +t=J.S(a) +if(b<0||b>=t)return A.bs(b,t,a,null,s) +return A.e9(b,s)}, +hD(a){return new A.ah(!0,a,null,null)}, +d(a){return A.t(a,new Error())}, +t(a,b){var t +if(a==null)a=new A.cW() +b.dartException=a +t=A.hW +if("defineProperty" in Object){Object.defineProperty(b,"message",{get:t}) +b.name=""}else b.toString=t +return b}, +hW(){return J.ax(this.dartException)}, +af(a,b){throw A.t(a,b==null?new Error():b)}, +b(a,b,c){var t +if(b==null)b=0 +if(c==null)c=0 +t=Error() +A.af(A.hd(a,b,c),t)}, +hd(a,b,c){var t,s,r,q,p,o,n,m,l +if(typeof b=="string")t=b +else{s="[]=;add;removeWhere;retainWhere;removeRange;setRange;setInt8;setInt16;setInt32;setUint8;setUint16;setUint32;setFloat32;setFloat64".split(";") +r=s.length +q=b +if(q>r){c=q/r|0 +q%=r}t=s[q]}p=typeof c=="string"?c:"modify;remove from;add to".split(";")[c] +o=u.j.b(a)?"list":"ByteData" +n=a.$flags|0 +m="a " +if((n&4)!==0)l="constant " +else if((n&2)!==0){l="unmodifiable " +m="an "}else l=(n&1)!==0?"fixed-length ":"" +return new A.bK("'"+t+"': Cannot "+p+" "+m+l+o)}, +dm(a){throw A.d(A.aI(a))}, +eD(a){if(a==null)return J.bS(a) +if(typeof a=="object")return A.aZ(a) +return J.bS(a)}, +hI(a){if(typeof a=="number")return B.p.gC(a) +if(a instanceof A.bP)return A.aZ(a) +return A.eD(a)}, +hK(a,b){var t,s,r,q=a.length +for(t=0;t>>0!==a||a>=c)throw A.d(A.dO(b,a))}, +an:function an(){}, +aU:function aU(){}, +d7:function d7(a){this.a=a}, +bx:function bx(){}, +ao:function ao(){}, +W:function W(){}, +A:function A(){}, +by:function by(){}, +bz:function bz(){}, +bA:function bA(){}, +H:function H(){}, +bB:function bB(){}, +aV:function aV(){}, +bC:function bC(){}, +aW:function aW(){}, +aX:function aX(){}, +b6:function b6(){}, +b7:function b7(){}, +b8:function b8(){}, +b9:function b9(){}, +dG(a,b){var t=b.c +return t==null?b.c=A.bc(a,"e2",[b.x]):t}, +ea(a){var t=a.w +if(t===6||t===7)return A.ea(a.x) +return t===11||t===12}, +fA(a){return a.as}, +a_(a){return A.d5(v.typeUniverse,a,!1)}, +a9(a0,a1,a2,a3){var t,s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a=a1.w +switch(a){case 5:case 1:case 2:case 3:case 4:return a1 +case 6:t=a1.x +s=A.a9(a0,t,a2,a3) +if(s===t)return a1 +return A.ek(a0,s,!0) +case 7:t=a1.x +s=A.a9(a0,t,a2,a3) +if(s===t)return a1 +return A.ej(a0,s,!0) +case 8:r=a1.y +q=A.at(a0,r,a2,a3) +if(q===r)return a1 +return A.bc(a0,a1.x,q) +case 9:p=a1.x +o=A.a9(a0,p,a2,a3) +n=a1.y +m=A.at(a0,n,a2,a3) +if(o===p&&m===n)return a1 +return A.dI(a0,o,m) +case 10:l=a1.x +k=a1.y +j=A.at(a0,k,a2,a3) +if(j===k)return a1 +return A.el(a0,l,j) +case 11:i=a1.x +h=A.a9(a0,i,a2,a3) +g=a1.y +f=A.hA(a0,g,a2,a3) +if(h===i&&f===g)return a1 +return A.ei(a0,h,f) +case 12:e=a1.y +a3+=e.length +d=A.at(a0,e,a2,a3) +p=a1.x +o=A.a9(a0,p,a2,a3) +if(d===e&&o===p)return a1 +return A.dJ(a0,o,d,!0) +case 13:c=a1.x +if(c=0)q+=" "+s[r];++r}return q+"})"}, +eq(a0,a1,a2){var t,s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b=", ",a=null +if(a2!=null){t=a2.length +if(a1==null)a1=A.F([],u.s) +else a=a1.length +s=a1.length +for(r=t;r>0;--r)a1.push("T"+(s+r)) +for(q=u.O,p="<",o="",r=0;r0){d+=c+"[" +for(c="",r=0;r0){d+=c+"{" +for(c="",r=0;r "+e}, +B(a,b){var t,s,r,q,p,o,n=a.w +if(n===5)return"erased" +if(n===2)return"dynamic" +if(n===3)return"void" +if(n===1)return"Never" +if(n===4)return"any" +if(n===6){t=a.x +s=A.B(t,b) +r=t.w +return(r===11||r===12?"("+s+")":s)+"?"}if(n===7)return"FutureOr<"+A.B(a.x,b)+">" +if(n===8){q=A.hC(a.x) +p=a.y +return p.length>0?q+("<"+A.ex(p,b)+">"):q}if(n===10)return A.hw(a,b) +if(n===11)return A.eq(a,b,null) +if(n===12)return A.eq(a.x,b,a.y) +if(n===13){o=a.x +return b[b.length-1-o]}return"?"}, +hC(a){var t=A.eG(a) +if(t!=null)return t +return"minified:"+a}, +fX(a,b){var t=a.tR[b] +while(typeof t=="string")t=a.tR[t] +return t}, +fW(a,b){var t,s,r,q,p,o=a.eT,n=o[b] +if(n==null)return A.d5(a,b,!1) +else if(typeof n=="number"){t=n +s=A.bd(a,5,"#") +r=A.d8(t) +for(q=0;q0)q+="<"+A.bb(c)+">" +t=a.eC.get(q) +if(t!=null)return t +s=new A.G(null,null) +s.w=8 +s.x=b +s.y=c +if(c.length>0)s.c=c[0] +s.as=q +r=A.Z(a,s) +a.eC.set(q,r) +return r}, +dI(a,b,c){var t,s,r,q,p,o +if(b.w===9){t=b.x +s=b.y.concat(c)}else{s=c +t=b}r=t.as+(";<"+A.bb(s)+">") +q=a.eC.get(r) +if(q!=null)return q +p=new A.G(null,null) +p.w=9 +p.x=t +p.y=s +p.as=r +o=A.Z(a,p) +a.eC.set(r,o) +return o}, +el(a,b,c){var t,s,r="+"+(b+"("+A.bb(c)+")"),q=a.eC.get(r) +if(q!=null)return q +t=new A.G(null,null) +t.w=10 +t.x=b +t.y=c +t.as=r +s=A.Z(a,t) +a.eC.set(r,s) +return s}, +ei(a,b,c){var t,s,r,q,p,o=b.as,n=c.a,m=n.length,l=c.b,k=l.length,j=c.c,i=j.length,h="("+A.bb(n) +if(k>0){t=m>0?",":"" +h+=t+"["+A.bb(l)+"]"}if(i>0){t=m>0?",":"" +h+=t+"{"+A.fO(j)+"}"}s=o+(h+")") +r=a.eC.get(s) +if(r!=null)return r +q=new A.G(null,null) +q.w=11 +q.x=b +q.y=c +q.as=s +p=A.Z(a,q) +a.eC.set(s,p) +return p}, +dJ(a,b,c,d){var t,s=b.as+("<"+A.bb(c)+">"),r=a.eC.get(s) +if(r!=null)return r +t=A.fQ(a,b,c,s,d) +a.eC.set(s,t) +return t}, +fQ(a,b,c,d,e){var t,s,r,q,p,o,n,m +if(e){t=c.length +s=A.d8(t) +for(r=0,q=0;q0){o=A.a9(a,b,s,0) +n=A.at(a,c,s,0) +return A.dJ(a,o,n,c!==n)}}m=new A.G(null,null) +m.w=12 +m.x=b +m.y=c +m.as=d +return A.Z(a,m)}, +fG(a,b,c,d){return{u:a,e:b,r:c,s:[],p:0,n:d}}, +fM(a){var t,s,r,q,p,o,n,m=a.r,l=a.s +for(t=m.length,s=0;s=48&&r<=57)s=A.fI(s+1,r,m,l) +else if((((r|32)>>>0)-97&65535)<26||r===95||r===36||r===124)s=A.eg(a,s,m,l,!1) +else if(r===46)s=A.eg(a,s,m,l,!0) +else{++s +switch(r){case 44:break +case 58:l.push(!1) +break +case 33:l.push(!0) +break +case 59:l.push(A.a7(a.u,a.e,l.pop())) +break +case 94:l.push(A.fS(a.u,l.pop())) +break +case 35:l.push(A.bd(a.u,5,"#")) +break +case 64:l.push(A.bd(a.u,2,"@")) +break +case 126:l.push(A.bd(a.u,3,"~")) +break +case 60:l.push(a.p) +a.p=l.length +break +case 62:A.fK(a,l) +break +case 38:A.fJ(a,l) +break +case 63:q=a.u +l.push(A.ek(q,A.a7(q,a.e,l.pop()),a.n)) +break +case 47:q=a.u +l.push(A.ej(q,A.a7(q,a.e,l.pop()),a.n)) +break +case 40:l.push(-3) +l.push(a.p) +a.p=l.length +break +case 41:A.fH(a,l) +break +case 91:l.push(a.p) +a.p=l.length +break +case 93:p=l.splice(a.p) +A.eh(a.u,a.e,p) +a.p=l.pop() +l.push(p) +l.push(-1) +break +case 123:l.push(a.p) +a.p=l.length +break +case 125:p=l.splice(a.p) +A.fN(a.u,a.e,p) +a.p=l.pop() +l.push(p) +l.push(-2) +break +case 43:o=m.indexOf("(",s) +l.push(m.substring(s,o)) +l.push(-4) +l.push(a.p) +a.p=l.length +s=o+1 +break +default:throw"Bad character "+r}}}n=l.pop() +return A.a7(a.u,a.e,n)}, +fI(a,b,c,d){var t,s,r=b-48 +for(t=c.length;a=48&&s<=57))break +r=r*10+(s-48)}d.push(r) +return a}, +eg(a,b,c,d,e){var t,s,r,q,p,o,n=b+1 +for(t=c.length;n>>0)-97&65535)<26||s===95||s===36||s===124))r=s>=48&&s<=57 +else r=!0 +if(!r)break}}q=c.substring(b,n) +if(e){t=a.u +p=a.e +if(p.w===9)p=p.x +o=A.fX(t,p.x)[q] +if(o==null)A.af('No "'+q+'" in "'+A.fA(p)+'"') +d.push(A.d6(t,p,o))}else d.push(q) +return n}, +fK(a,b){var t,s=a.u,r=A.ef(a,b),q=b.pop() +if(typeof q=="string")b.push(A.bc(s,q,r)) +else{t=A.a7(s,a.e,q) +switch(t.w){case 11:b.push(A.dJ(s,t,r,a.n)) +break +default:b.push(A.dI(s,t,r)) +break}}}, +fH(a,b){var t,s,r,q=a.u,p=b.pop(),o=null,n=null +if(typeof p=="number")switch(p){case-1:o=b.pop() +break +case-2:n=b.pop() +break +default:b.push(p) +break}else b.push(p) +t=A.ef(a,b) +p=b.pop() +switch(p){case-3:p=b.pop() +if(o==null)o=q.sEA +if(n==null)n=q.sEA +s=A.a7(q,a.e,p) +r=new A.bM() +r.a=t +r.b=o +r.c=n +b.push(A.ei(q,s,r)) +return +case-4:b.push(A.el(q,b.pop(),t)) +return +default:throw A.d(A.bn("Unexpected state under `()`: "+A.q(p)))}}, +fJ(a,b){var t=b.pop() +if(0===t){b.push(A.bd(a.u,1,"0&")) +return}if(1===t){b.push(A.bd(a.u,4,"1&")) +return}throw A.d(A.bn("Unexpected extended operation "+A.q(t)))}, +ef(a,b){var t=b.splice(a.p) +A.eh(a.u,a.e,t) +a.p=b.pop() +return t}, +a7(a,b,c){if(typeof c=="string")return A.bc(a,c,a.sEA) +else if(typeof c=="number"){b.toString +return A.fL(a,b,c)}else return c}, +eh(a,b,c){var t,s=c.length +for(t=0;to)return!1 +n=o-p +m=t.b +l=s.b +k=m.length +j=l.length +if(p+k=e)return!1 +a0=g[c] +c+=3 +if(a0?new Array(r):v.typeUniverse.sEA +for(p=0;p0?new Array(a):v.typeUniverse.sEA}, +G:function G(a,b){var _=this +_.a=a +_.b=b +_.r=_.f=_.d=_.c=null +_.w=0 +_.as=_.Q=_.z=_.y=_.x=null}, +bM:function bM(){this.c=this.b=this.a=null}, +bP:function bP(a){this.a=a}, +d1:function d1(){}, +bQ:function bQ(a){this.a=a}, +aQ(a,b){return new A.a5(a.m("@<0>").a7(b).m("a5<1,2>"))}, +dE(a){return new A.b5(a.m("b5<0>"))}, +dH(){var t=Object.create(null) +t[""]=t +delete t[""] +return t}, +bN(a,b,c){var t=new A.as(a,b,c.m("as<0>")) +t.c=a.e +return t}, +dF(a){var t,s +if(A.dR(a))return"{...}" +t=new A.bI("") +try{s={} +$.aa.push(a) +t.a+="{" +s.a=!0 +a.al(0,new A.cP(s,t)) +t.a+="}"}finally{$.aa.pop()}s=t.a +return s.charCodeAt(0)==0?s:s}, +fs(a){return 8}, +ft(a){var t +a=(a<<1>>>0)-1 +for(;;a=t){t=(a&a-1)>>>0 +if(t===0)return a}}, +b5:function b5(a){var _=this +_.a=0 +_.f=_.e=_.d=_.c=_.b=null +_.r=0 +_.$ti=a}, +d3:function d3(a){this.a=a +this.c=this.b=null}, +as:function as(a,b,c){var _=this +_.a=a +_.b=b +_.d=_.c=null +_.$ti=c}, +h:function h(){}, +aS:function aS(){}, +cP:function cP(a,b){this.a=a +this.b=b}, +aR:function aR(a,b){var _=this +_.a=a +_.d=_.c=_.b=0 +_.$ti=b}, +bO:function bO(a,b,c,d,e){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=null +_.$ti=e}, +aq:function aq(){}, +ba:function ba(){}, +dg(a,b){var t=A.fx(a,b) +if(t!=null)return t +throw A.d(new A.cG(a))}, +V(a,b,c,d){var t,s=c?J.fn(a,d):J.e5(a,d) +if(a!==0&&b!=null)for(t=0;t")) +for(t=a.gD(a);t.n();)s.push(t.gt()) +return s}, +ec(a,b,c){var t=J.dt(b) +if(!t.n())return a +if(c.length===0){do a+=A.q(t.gt()) +while(t.n())}else{a+=A.q(t.gt()) +while(t.n())a=a+c+A.q(t.gt())}return a}, +cF(a){if(typeof a=="number"||A.dM(a)||a==null)return J.ax(a) +if(typeof a=="string")return JSON.stringify(a) +return A.fy(a)}, +bn(a){return new A.cy(a)}, +aF(a){return new A.ah(!1,null,null,a)}, +cx(a,b){return a}, +e9(a,b){return new A.bF(null,null,!0,a,b,"Value not in range")}, +X(a,b,c,d,e){return new A.bF(b,c,!0,a,d,"Invalid value")}, +bG(a,b,c){if(0>a||a>c)throw A.d(A.X(a,0,c,"start",null)) +if(b!=null){if(a>b||b>c)throw A.d(A.X(b,a,c,"end",null)) +return b}return c}, +O(a,b){if(a<0)throw A.d(A.X(a,0,null,b,null)) +return a}, +bs(a,b,c,d,e){return new A.cK(b,!0,a,e,"Index out of range")}, +J(a){return new A.bK(a)}, +ed(a){return new A.cY(a)}, +fB(a){return new A.b0(a)}, +aI(a){return new A.cD(a)}, +fm(a,b,c){var t,s +if(A.dR(a)){if(b==="("&&c===")")return"(...)" +return b+"..."+c}t=A.F([],u.s) +$.aa.push(a) +try{A.hv(a,t)}finally{$.aa.pop()}s=A.ec(b,t,", ")+c +return s.charCodeAt(0)==0?s:s}, +cL(a,b,c){var t,s +if(A.dR(a))return b+"..."+c +t=new A.bI(b) +$.aa.push(a) +try{s=t +s.a=A.ec(s.a,a,", ")}finally{$.aa.pop()}t.a+=c +s=t.a +return s.charCodeAt(0)==0?s:s}, +hv(a,b){var t,s,r,q,p,o,n,m=a.gD(a),l=0,k=0 +for(;;){if(!(l<80||k<3))break +if(!m.n())return +t=A.q(m.gt()) +b.push(t) +l+=t.length+2;++k}if(!m.n()){if(k<=5)return +s=b.pop() +r=b.pop()}else{q=m.gt();++k +if(!m.n()){if(k<=4){b.push(A.q(q)) +return}s=A.q(q) +r=b.pop() +l+=s.length+2}else{p=m.gt();++k +for(;m.n();q=p,p=o){o=m.gt();++k +if(k>100){for(;;){if(!(l>75&&k>3))break +l-=b.pop().length+2;--k}b.push("...") +return}}r=A.q(q) +s=A.q(p) +l+=s.length+r.length+4}}if(k>b.length+2){l+=5 +n="..."}else n=null +for(;;){if(!(l>80&&b.length>3))break +l-=b.pop().length+2 +if(n==null){l+=5 +n="..."}}if(n!=null)b.push(n) +b.push(r) +b.push(s)}, +d0:function d0(){}, +cE:function cE(){}, +cy:function cy(a){this.a=a}, +cW:function cW(){}, +ah:function ah(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +bF:function bF(a,b,c,d,e,f){var _=this +_.e=a +_.f=b +_.a=c +_.b=d +_.c=e +_.d=f}, +cK:function cK(a,b,c,d,e){var _=this +_.f=a +_.a=b +_.b=c +_.c=d +_.d=e}, +bK:function bK(a){this.a=a}, +cY:function cY(a){this.a=a}, +b0:function b0(a){this.a=a}, +cD:function cD(a){this.a=a}, +cG:function cG(a){this.a=a}, +l:function l(){}, +aY:function aY(){}, +m:function m(){}, +bI:function bI(a){this.a=a}, +fb(a){var t,s,r=u.S,q=A.V(128,null,!1,u.r) +r=new A.cz(A.aQ(r,u.M),A.aQ(r,u.J),q,A.F([],u.l),A.dE(u.F),A.V(256,null,!0,u.W),new A.cA(),a) +t=r.a=new Uint8Array(8448) +r.b=J.eO(B.u.gai(t),0,null) +t=r.c=J.eN(B.u.gai(t),0,null) +s=J.eP(B.t.gai(a),0,null) +r.d=s +r.y=s.length>131072 +t.$flags&2&&A.b(t,10) +t.setUint16(93,8447,!0) +B.b.X(q,0,128,null) +return r}, +u:function u(a,b,c,d,e,f){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e +_.f=f}, +bk:function bk(a,b){this.a=a +this.b=b +this.c=null}, +cz:function cz(a,b,c,d,e,f,g,h){var _=this +_.d=_.c=_.b=_.a=$ +_.e=a +_.f=b +_.r=c +_.w=null +_.x=d +_.y=$ +_.z=e +_.Q=f +_.as=g +_.ax=_.at=0 +_.ay=-1 +_.ch=0 +_.CW=h}, +cA:function cA(){}, +eU(a,b){var t=new A.bU(a,b) +t.bJ(a,b) +return t}, +a1:function a1(a,b){this.a=a +this.b=b}, +az:function az(a,b){this.a=a +this.b=b}, +M:function M(){}, +ay:function ay(){}, +ag:function ag(a){this.b=a}, +D:function D(a){this.b=a}, +bT:function bT(){}, +bU:function bU(a,b){var _=this +_.a=a +_.b=b +_.f=_.c=$ +_.r=!1 +_.w=25 +_.x=$}, +bX:function bX(a){this.a=a}, +bW:function bW(a,b){this.a=a +this.b=b}, +bY:function bY(a){this.a=a}, +bV:function bV(a){this.a=a}, +dB(a,b){var t=new A.aB(a,b,A.aQ(u.S,u.c),A.F([],u.f)) +t.bK(a,b) +return t}, +a2:function a2(a,b,c){this.d=a +this.e=b +this.f=c}, +aC:function aC(a,b,c){this.a=a +this.d=b +this.e=c}, +aD:function aD(a,b,c,d,e){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e}, +ap:function ap(a,b){this.a=a +this.b=b}, +a6:function a6(a,b){this.a=a +this.b=b}, +aB:function aB(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.e=_.d=$ +_.f=d +_.r=0 +_.w=255 +_.Q=_.z=_.y=_.x=0}, +c2:function c2(a){this.a=a}, +c3:function c3(a){this.a=a}, +c4:function c4(a){this.a=a}, +c5:function c5(){}, +c6:function c6(a){this.a=a}, +c7:function c7(a,b){this.a=a +this.b=b}, +c1:function c1(a,b,c){this.a=a +this.b=b +this.c=c}, +hH(a){switch(a){case 1:return B.j +case 2:return B.i +case 3:return B.h +default:return B.f}}, +dC(a,b){var t=new A.bl(a,b) +t.bM(a,b) +return t}, +aE:function aE(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t){var _=this +_.a=a +_.b=b +_.d=c +_.e=d +_.r=e +_.w=f +_.x=g +_.y=h +_.Q=i +_.as=j +_.at=k +_.ax=l +_.ay=m +_.ch=n +_.fy=o +_.go=p +_.id=q +_.k1=r +_.k4=s +_.ok=t}, +o:function o(a,b,c,d,e){var _=this +_.a=a +_.b=b +_.c=c +_.d=d +_.e=e}, +bl:function bl(a,b){var _=this +_.a=a +_.b=b +_.c=$ +_.w=_.r=_.f=_.e=_.d=0 +_.x=$ +_.ax=_.at=_.as=_.Q=_.z=_.y=0 +_.ay=255 +_.db=_.cy=_.cx=_.CW=_.ch=0 +_.dy=_.dx=!1 +_.fr=!0 +_.fx=0 +_.fy=null +_.go=!1 +_.id=0 +_.k4=_.k3=_.k2=_.k1=$}, +cb:function cb(a){this.a=a}, +cc:function cc(a){this.a=a}, +cd:function cd(a){this.a=a}, +ce:function ce(a){this.a=a}, +cf:function cf(a){this.a=a}, +cm:function cm(a){this.a=a}, +cl:function cl(a){this.a=a}, +cg:function cg(a){this.a=a}, +ch:function ch(a){this.a=a}, +ci:function ci(a){this.a=a}, +cj:function cj(a){this.a=a}, +ck:function ck(a){this.a=a}, +f7(a,b,c){var t=new A.c8(a,b) +t.bL(a,b,c) +return t}, +cU:function cU(){}, +cQ:function cQ(a){this.a=a}, +c8:function c8(a,b){var _=this +_.a=a +_.b=b +_.d=$ +_.e=!1 +_.f=$}, +ca:function ca(a){this.a=a}, +c9:function c9(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.d=d}, +f8(a,b,c){var t=new A.cn(a,b) +t.bN(a,b,c) +return t}, +cX:function cX(){}, +cn:function cn(a,b){var _=this +_.a=a +_.b=b +_.e=null +_.w=!1 +_.x=0 +_.y="" +_.as=_.Q=_.z=$}, +cp:function cp(a){this.a=a}, +cq:function cq(a){this.a=a}, +cr:function cr(a){this.a=a}, +cs:function cs(a){this.a=a}, +ct:function ct(a){this.a=a}, +co:function co(a){this.a=a}, +cu:function cu(a){this.a=a}, +cv:function cv(a){this.a=a}, +cw:function cw(a,b){this.a=a +this.b=b}, +f1(a){var t +if(!$.N)return 0 +if(a<0||a>=14)return A.dz(a)?1:0 +t=$.dU +if(t===0)return A.dz(a)?1:0 +return $.dT()[a]/t}, +f3(a,b){var t,s,r +$.dp().W(0) +B.b.X($.dr(),0,14,-1) +B.b.X($.dq(),0,14,0) +t=new Uint16Array(16384) +A.f0(a,t) +$.p.b=A.fb(t) +$.eV.b=A.dC($.p.k(),B.W) +$.eW.b=A.dC($.p.k(),B.X) +$.eX.b=A.dC($.p.k(),B.V) +$.dx.b=A.f8($.p.k(),B.a6,16e6) +$.dx.k().e=new A.c0(b) +$.aA.b=A.dB($.p.k(),B.U) +$.aA.k().f.push(A.hE()) +$.bZ.b=A.dB($.p.k(),B.S) +$.bZ.k().f.push(A.hF()) +$.bj.b=A.dB($.p.k(),B.T) +$.bj.k().f.push(A.hG()) +$.dv.b=A.eU($.p.k(),B.Y) +s=$.bh() +r=s.a +if(r.a>0){r.b=r.c=r.d=r.e=r.f=null +r.a=0 +r.aE()}s.b.W(0) +s.c.W(0) +s.f.W(0) +s.r=s.e=s.d=null +s.x=0 +$.dw.b=A.f7($.p.k(),B.a5,16e6) +$.dw.k().d=new A.d4($.dw.k(),s) +$.dn().b4() +$.N=!0}, +f2(a){var t +if(!$.N)return!1 +if(a>=0&&a<=7){t=$.p.k().a +t===$&&A.a() +return(t[42]&B.a.K(1,a))!==0}else if(a>=8&&a<=13){t=$.p.k().a +t===$&&A.a() +return(t[36]&B.a.K(1,a-8))!==0}else if(a>=14&&a<=19){t=$.p.k().a +t===$&&A.a() +return(t[39]&B.a.K(1,a-14))!==0}return!1}, +f_(a,b){return A.dy(0,a,b)}, +eY(a,b){return A.dy(8,a,b)}, +eZ(a,b){return A.dy(14,a,b)}, +dy(a,b,c){var t,s,r,q,p,o,n,m,l,k,j,i,h,g=(b^c)>>>0 +if(g===0)return +t=$.dV +if(t!=null&&t>=a&&t>>0!==0){s=$.dn() +r=$.p.k().ax +q=s.a +if(q!==0){p=r-q +if(p>400&&p<4e5){q=s.c +if(q===0||Math.abs(q-p)>2000){s.c=p +q=s.d=1 +o=q +q=p}else{q=s.c=B.a.ag(q*3+p+2,4) +o=++s.d}n=q*4 +s.e=n +if(n<4000)s.e=4000 +if(o>=4)if(!s.f||Math.abs(s.b-q)>2000){s.b=q +m=16e6/(2*q) +if(Math.abs(m-2000)<20)m=2000 +s.f=!0 +new A.c_().$1(m)}}}s.a=r}for(s=$.dW,r=s.length,q=a+8,l=0;l=q)continue +j=B.a.K(1,k-a) +if((g&j)>>>0===0)continue +if((b&j)>>>0!==0){o=$.dr() +n=$.p.b +if(n===$.p)A.af(A.cN("")) +o[k]=n.ax}else{i=$.dr()[k] +if(i>=0){o=$.p.b +if(o===$.p)A.af(A.cN("")) +h=(o.ax-i)/16 +if(h>=400&&h<=3000)$.dq()[k]=h}}}}, +f6(e2){var t,s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,d0,d1,d2,d3,d4,d5,d6,d7,d8,d9,e0,e1 +if(!$.N)return +t=$.p.k() +s=$.dx.k() +r=$.aA.k() +q=$.bj.k() +p=$.dp() +o=$.dn() +n=$.dT() +for(m=0;m<14;++m)n[m]=0 +l=t.ax+e2 +for(k=t.x,j=t.CW,i=p.$ti.c,h=t.r,g=0,f=0;e=t.ax,e>>4&31 +a=e[d] +a0=e[c&15|c>>>5&16] +a1=B.a.b6(a+a0+(e[95]&1)) +a2=a1&255 +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a4=((a2^a0)&(a^a2)&128)!==0?8:0 +a5=d&192|b|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=(a1&256)!==0?1:0 +a3=~a2 +a3=((a&a0|a0&a3|a3&a)&1)!==0?32:0 +e[95]=a5|d|b|a3}else if(b===3072){e=t.a +e===$&&A.a() +d=c>>>4&31 +a=e[d] +a0=e[c&15|c>>>5&16] +b=a+a0 +a2=b&255 +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +a3=a2!==0?0:2 +a4=(a2&128)!==0?4:0 +a6=((a2^a0)&(a2^a)&128)!==0?8:0 +a5=d&192|a3|a4|a6 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=(b&256)!==0?1:0 +a3=~a2 +a3=((a&a0|a0&a3|a3&a)&1)!==0?32:0 +e[95]=a5|d|b|a3}else{a3=c&65280 +if(a3===38400){a7=2*(c>>>4&3)+24 +e=t.c +e===$&&A.a() +a8=e.getUint16(a7,!0) +a2=a8+(c&15|c>>>2&48)&65535 +e.$flags&2&&A.b(e,10) +e.setUint16(a7,a2,!0) +e=t.a +e===$&&A.a() +d=e[95] +b=a2!==0?0:2 +a3=(a2&32768)!==0?4:0 +a4=(~a8&a2&32768)!==0?8:0 +a5=d&224|b|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=(~a2&a8&32768)!==0?1:0 +e.$flags&2&&A.b(e) +e[95]=a5|d|b;++t.ax}else if(b===8192){e=t.a +e===$&&A.a() +d=c>>>4&31 +a2=e[d]&e[c&15|c>>>5&16] +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=d&225|b|a3 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else{a4=c&61440 +if(a4===28672){e=t.a +e===$&&A.a() +d=c>>>4 +b=(d&15)+16 +a2=e[b]&(c&15|d&240) +e.$flags&2&&A.b(e) +e[b]=a2 +b=e[95] +d=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=b&225|d|a3 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else{a6=c&65039 +if(a6===37893){e=t.a +e===$&&A.a() +d=c>>>4&31 +a8=e[d] +a2=a8>>>1|a8&128 +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=d&224|b|a3|a8&1 +a5|=(a5>>>2&1^a5&1)!==0?8:0 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else{a9=c&65423 +if(a9===38024){e=t.a +e===$&&A.a() +d=e[95] +e.$flags&2&&A.b(e) +e[95]=d&~(1<<(c>>>4&7))}else{b0=c&65032 +if(b0===63488){b1=c&7 +a=c>>>4&31 +e=t.a +e===$&&A.a() +d=e[a] +b=e[95] +e.$flags&2&&A.b(e) +e[a]=(~(1<>>6&1)<>>0}else if(b===62464){b=t.a +b===$&&A.a() +if((b[95]&1<<(c&7))===0){b=(c&512)!==0?64:0 +t.at=d+((c>>>3&63)-b) +t.ax=e+1}}else if(b===61440){b=t.a +b===$&&A.a() +if((b[95]&1<<(c&7))!==0){b=(c&512)!==0?64:0 +t.at=d+((c>>>3&63)-b) +t.ax=e+1}}else if(a9===37896){e=t.a +e===$&&A.a() +d=e[95] +e.$flags&2&&A.b(e) +e[95]=(d|1<<(c>>>4&7))>>>0}else if(b0===64e3){e=t.a +e===$&&A.a() +a=e[c>>>4&31] +d=e[95] +b=(a>>>(c&7)&1)!==0?64:0 +e.$flags&2&&A.b(e) +e[95]=d&191|b}else{a9=c&65038 +if(a9===37902){e=j[d+1] +b2=d+2 +d=t.c +d===$&&A.a() +b3=d.getUint16(93,!0) +b=t.y +b===$&&A.a() +a3=t.a +a3===$&&A.a() +a3.$flags&2&&A.b(a3) +a3[b3]=b2&255 +a3[b3-1]=B.a.A(b2,8)&255 +if(b)a3[b3-2]=B.a.A(b2,16)&255 +a3=b?3:2 +d.$flags&2&&A.b(d,10) +d.setUint16(93,b3-a3,!0) +t.at=(e|(c&1)<<16|(c&496)<<13)-1 +e=t.ax +t.ax=e+(b?4:3)}else if(a3===38912){e=(c>>>3&31)+32 +b4=1<<(c&7)>>>0 +t.au(e,(t.p(e)&~b4)>>>0,b4)}else if(a6===37888){a=c>>>4&31 +e=t.a +e===$&&A.a() +a2=B.a.b6(255-e[a]) +e.$flags&2&&A.b(e) +e[a]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=d&225|b|a3|1 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else if(b===5120){e=t.a +e===$&&A.a() +b5=e[c>>>4&31] +b6=e[c&15|c>>>5&16] +a2=b5-b6 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=d&192|b|a3|8 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=b6>b5?1:0 +a3=~b5 +a3=((a3&b6|b6&a2|a2&a3)&1)!==0?32:0 +e.$flags&2&&A.b(e) +e[95]=a5|d|b|a3}else if(b===1024){e=t.a +e===$&&A.a() +b7=e[c>>>4&31] +b8=e[c&15|c>>>5&16] +a5=e[95] +d=a5&1 +a0=b7-b8-d +b=a0===0&&(a5>>>1&1)!==0?2:0 +d=b8+d>b7?1:0 +a3=(a0&128)!==0?4:0 +a4=((b7^b8)&(b7^a0)&128)!==0?8:0 +a5=a5&192|b|d|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=~b7 +b=((b&b8|b8&a0|a0&b)&1)!==0?32:0 +e.$flags&2&&A.b(e) +e[95]=a5|d|b}else if(a4===12288){e=t.a +e===$&&A.a() +d=c>>>4 +b7=e[(d&15)+16] +b8=c&15|d&240 +a0=b7-b8 +d=e[95] +b=a0!==0?0:2 +a3=(a0&128)!==0?4:0 +a4=((b7^b8)&(b7^a0)&128)!==0?8:0 +a5=d&192|b|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=b8>b7?1:0 +a3=~b7 +a3=((a3&b8|b8&a0|a0&a3)&1)!==0?32:0 +e.$flags&2&&A.b(e) +e[95]=a5|d|b|a3}else if(b===4096){b=t.a +b===$&&A.a() +if(b[c>>>4&31]===b[c&15|c>>>5&16]){b9=j[d+1] +b=b9&65039 +a3=!0 +if(b!==36864)if(b!==37376){b=b9&65038 +b=b===37902||b===37900}else b=a3 +else b=a3 +c0=b?2:1 +t.at=d+c0 +t.ax=e+c0}}else if(a6===37898){e=t.a +e===$&&A.a() +d=c>>>4&31 +a2=e[d]-1 +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=d&225|b|a3|8 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else if(c===38169){c1=d+1 +e=t.c +e===$&&A.a() +b3=e.getUint16(93,!0) +d=t.a +d===$&&A.a() +c2=d[92] +d.$flags&2&&A.b(d) +d[b3]=c1&255 +d[b3-1]=B.a.A(c1,8)&255 +d[b3-2]=B.a.A(c1,16)&255 +e.$flags&2&&A.b(e,10) +e.setUint16(93,b3-3,!0) +t.at=((c2<<16|e.getUint16(30,!0))>>>0)-1 +t.ax+=3}else if(c===37913){e=t.a +e===$&&A.a() +c2=e[92] +e=t.c +e===$&&A.a() +t.at=((c2<<16|e.getUint16(30,!0))>>>0)-1;++t.ax}else if(c===38360){e=t.a +e===$&&A.a() +c3=e[91] +d=t.d +d===$&&A.a() +b=t.c +b===$&&A.a() +b=d[(c3<<16|b.getUint16(30,!0))>>>0] +e.$flags&2&&A.b(e) +e[0]=b +t.ax+=2}else if(a6===36870){e=t.a +e===$&&A.a() +c3=e[91] +d=t.d +d===$&&A.a() +b=t.c +b===$&&A.a() +b=d[(c3<<16|b.getUint16(30,!0))>>>0] +e.$flags&2&&A.b(e) +e[c>>>4&31]=b +t.ax+=2}else if(a6===36871){e=t.a +e===$&&A.a() +c3=e[91] +d=t.c +d===$&&A.a() +c4=d.getUint16(30,!0) +b=t.d +b===$&&A.a() +a3=b[(c3<<16|c4)>>>0] +e.$flags&2&&A.b(e) +e[c>>>4&31]=a3 +d.$flags&2&&A.b(d,10) +d.setUint16(30,c4+1,!0) +if(c4===65535)e[91]=(c3+1)%(b.length>>>16) +t.ax+=2}else if(b===9216){e=t.a +e===$&&A.a() +d=c>>>4&31 +a2=e[d]^e[c&15|c>>>5&16] +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=d&225|b|a3 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else{c5=c&65416 +if(c5===776){e=t.a +e===$&&A.a() +d=e[(c>>>4&7)+16]*e[(c&7)+16] +a2=d<<1>>>0 +b=t.c +b===$&&A.a() +b.$flags&2&&A.b(b,10) +b.setUint16(0,a2,!0) +b=e[95] +a3=(a2&65535)!==0?0:2 +d=(d&32768)!==0?1:0 +e.$flags&2&&A.b(e) +e[95]=b&252|a3|d;++t.ax}else if(c5===896){e=t.c +e===$&&A.a() +d=e.getInt8((c>>>4&7)+16)*e.getInt8((c&7)+16) +a2=d<<1>>>0 +e.$flags&2&&A.b(e,7) +e.setInt16(0,a2,!0) +e=t.a +e===$&&A.a() +b=e[95] +a3=(a2&65535)!==0?0:2 +d=(d&32768)!==0?1:0 +e.$flags&2&&A.b(e) +e[95]=b&252|a3|d;++t.ax}else if(c5===904){e=t.c +e===$&&A.a() +c6=e.getInt8((c>>>4&7)+16) +d=t.a +d===$&&A.a() +b=c6*d[(c&7)+16] +a2=b<<1>>>0 +e.$flags&2&&A.b(e,7) +e.setInt16(0,a2,!0) +e=d[95] +a3=(a2&65535)!==0?2:0 +b=(b&32768)!==0?1:0 +d.$flags&2&&A.b(d) +d[95]=e&252|a3|b;++t.ax}else if(c===38153){c1=d+1 +e=t.c +e===$&&A.a() +b3=e.getUint16(93,!0) +d=t.y +d===$&&A.a() +b=t.a +b===$&&A.a() +b.$flags&2&&A.b(b) +b[b3]=c1&255 +b[b3-1]=B.a.A(c1,8)&255 +if(d)b[b3-2]=B.a.A(c1,16)&255 +b=d?3:2 +e.$flags&2&&A.b(e,10) +e.setUint16(93,b3-b,!0) +t.at=e.getUint16(30,!0)-1 +e=t.ax +t.ax=e+(d?3:2)}else if(c===37897){e=t.c +e===$&&A.a() +t.at=e.getUint16(30,!0)-1;++t.ax}else{c7=c&63488 +if(c7===45056){c4=t.p((c&15|c>>>5&48)+32) +e=t.a +e===$&&A.a() +e.$flags&2&&A.b(e) +e[c>>>4&31]=c4}else if(a6===37891){e=t.a +e===$&&A.a() +d=c>>>4&31 +a=e[d] +a0=a+1&255 +e.$flags&2&&A.b(e) +e[d]=a0 +d=e[95] +b=a0!==0?0:2 +a3=(a0&128)!==0?4:0 +a4=127===a?8:0 +a5=d&225|b|a3|a4 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else if(a9===37900){t.at=(j[d+1]|(c&1)<<16|(c&496)<<13)-1 +t.ax=e+2}else if(a6===37382){a0=c>>>4&31 +e=t.a +e===$&&A.a() +c8=e[a0] +d=t.c +d===$&&A.a() +a8=t.p(d.getUint16(30,!0)) +t.B(d.getUint16(30,!0),(a8&255-c8)>>>0) +e.$flags&2&&A.b(e) +e[a0]=a8}else if(a6===37381){a0=c>>>4&31 +e=t.a +e===$&&A.a() +c9=e[a0] +d=t.c +d===$&&A.a() +a8=t.p(d.getUint16(30,!0)) +t.B(d.getUint16(30,!0),(a8|c9)>>>0) +e.$flags&2&&A.b(e) +e[a0]=a8}else if(a6===37383){e=t.a +e===$&&A.a() +d=c>>>4&31 +a0=e[d] +b=t.c +b===$&&A.a() +a2=t.p(b.getUint16(30,!0)) +t.B(b.getUint16(30,!0),(a0^a2)>>>0) +e.$flags&2&&A.b(e) +e[d]=a2}else if(a4===57344){e=t.a +e===$&&A.a() +d=c>>>4 +e.$flags&2&&A.b(e) +e[(d&15)+16]=c&15|d&240}else if(a6===36864){t.ax=e+1 +a8=t.p(j[d+1]) +e=t.a +e===$&&A.a() +e.$flags&2&&A.b(e) +e[c>>>4&31]=a8;++t.at}else if(a6===36876){t.ax=e+1 +e=t.a +e===$&&A.a() +d=t.c +d===$&&A.a() +d=t.p(d.getUint16(26,!0)) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(a6===36877){e=t.c +e===$&&A.a() +d0=e.getUint16(26,!0);++t.ax +d=t.a +d===$&&A.a() +b=t.p(d0) +d.$flags&2&&A.b(d) +d[c>>>4&31]=b +e.$flags&2&&A.b(e,10) +e.setUint16(26,d0+1,!0)}else if(a6===36878){e=t.c +e===$&&A.a() +d0=e.getUint16(26,!0)-1 +e.$flags&2&&A.b(e,10) +e.setUint16(26,d0,!0);++t.ax +e=t.a +e===$&&A.a() +d=t.p(d0) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(a6===32776){t.ax=e+1 +e=t.a +e===$&&A.a() +d=t.c +d===$&&A.a() +d=t.p(d.getUint16(28,!0)) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(a6===36873){e=t.c +e===$&&A.a() +d1=e.getUint16(28,!0);++t.ax +d=t.a +d===$&&A.a() +b=t.p(d1) +d.$flags&2&&A.b(d) +d[c>>>4&31]=b +e.$flags&2&&A.b(e,10) +e.setUint16(28,d1+1,!0)}else if(a6===36874){e=t.c +e===$&&A.a() +d1=e.getUint16(28,!0)-1 +e.$flags&2&&A.b(e,10) +e.setUint16(28,d1,!0);++t.ax +e=t.a +e===$&&A.a() +d=t.p(d1) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else{a9=c&53768 +if(a9===32776&&(c&7|c>>>7&24|c>>>8&32)!==0){t.ax=e+1 +e=t.a +e===$&&A.a() +d=t.c +d===$&&A.a() +d=t.p(d.getUint16(28,!0)+(c&7|c>>>7&24|c>>>8&32)) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(a6===32768){t.ax=e+1 +e=t.a +e===$&&A.a() +d=t.c +d===$&&A.a() +d=t.p(d.getUint16(30,!0)) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(a6===36865){e=t.c +e===$&&A.a() +d2=e.getUint16(30,!0);++t.ax +d=t.a +d===$&&A.a() +b=t.p(d2) +d.$flags&2&&A.b(d) +d[c>>>4&31]=b +e.$flags&2&&A.b(e,10) +e.setUint16(30,d2+1,!0)}else if(a6===36866){e=t.c +e===$&&A.a() +d2=e.getUint16(30,!0)-1 +e.$flags&2&&A.b(e,10) +e.setUint16(30,d2,!0);++t.ax +e=t.a +e===$&&A.a() +d=t.p(d2) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(a9===32768&&(c&7|c>>>7&24|c>>>8&32)!==0){t.ax=e+1 +e=t.a +e===$&&A.a() +d=t.c +d===$&&A.a() +d=t.p(d.getUint16(30,!0)+(c&7|c>>>7&24|c>>>8&32)) +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(c===38344){e=t.a +e===$&&A.a() +d=t.d +d===$&&A.a() +b=t.c +b===$&&A.a() +b=d[b.getUint16(30,!0)] +e.$flags&2&&A.b(e) +e[0]=b +t.ax+=2}else if(a6===36868){e=t.a +e===$&&A.a() +d=t.d +d===$&&A.a() +b=t.c +b===$&&A.a() +b=d[b.getUint16(30,!0)] +e.$flags&2&&A.b(e) +e[c>>>4&31]=b +t.ax+=2}else if(a6===36869){e=t.c +e===$&&A.a() +c4=e.getUint16(30,!0) +d=t.a +d===$&&A.a() +b=t.d +b===$&&A.a() +b=b[c4] +d.$flags&2&&A.b(d) +d[c>>>4&31]=b +e.$flags&2&&A.b(e,10) +e.setUint16(30,c4+1,!0) +t.ax+=2}else if(a6===37894){e=t.a +e===$&&A.a() +d=c>>>4&31 +a8=e[d] +a2=a8>>>1 +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a5=d&224|b|a8&1 +a5|=(a5>>>2&1^a5&1)!==0?8:0 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else if(b===11264){e=t.a +e===$&&A.a() +d=e[c&15|c>>>5&16] +e.$flags&2&&A.b(e) +e[c>>>4&31]=d}else if(a3===256){d3=2*(c&15) +d4=2*(c>>>4&15) +e=t.a +e===$&&A.a() +d=e[d3] +e.$flags&2&&A.b(e) +e[d4]=d +e[d4+1]=e[d3+1]}else if(b===39936){e=t.a +e===$&&A.a() +a2=e[c>>>4&31]*e[c&15|c>>>5&16] +d=t.c +d===$&&A.a() +d.$flags&2&&A.b(d,10) +d.setUint16(0,a2,!0) +d=e[95] +b=(a2&65535)!==0?0:2 +a3=(a2&32768)!==0?1:0 +e.$flags&2&&A.b(e) +e[95]=d&252|b|a3;++t.ax}else if(a3===512){e=t.c +e===$&&A.a() +a2=e.getInt8((c>>>4&15)+16)*e.getInt8((c&15)+16) +e.$flags&2&&A.b(e,7) +e.setInt16(0,a2,!0) +e=t.a +e===$&&A.a() +d=e[95] +b=(a2&65535)!==0?0:2 +a3=(a2&32768)!==0?1:0 +e.$flags&2&&A.b(e) +e[95]=d&252|b|a3;++t.ax}else if(c5===768){e=t.c +e===$&&A.a() +d=e.getInt8((c>>>4&7)+16) +b=t.a +b===$&&A.a() +a2=d*b[(c&7)+16] +e.$flags&2&&A.b(e,7) +e.setInt16(0,a2,!0) +e=b[95] +d=(a2&65535)!==0?0:2 +a3=(a2&32768)!==0?1:0 +b.$flags&2&&A.b(b) +b[95]=e&252|d|a3;++t.ax}else if(a6===37889){a=c>>>4&31 +e=t.a +e===$&&A.a() +a8=e[a] +a2=0-a8 +e.$flags&2&&A.b(e) +e[a]=a2 +d=e[95] +b=a2!==0 +a3=b?0:2 +a4=(a2&128)!==0?4:0 +a6=128===a2?8:0 +a5=d&192|a3|a4|a6 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=b?1:0 +a3=((a2|a8)&1)!==0?32:0 +e[95]=a5|d|b|a3}else if(c!==0)if(b===10240){e=t.a +e===$&&A.a() +d=c>>>4&31 +a2=e[d]|e[c&15|c>>>5&16] +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=d&225|b|a3 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else if(a4===24576){e=t.a +e===$&&A.a() +d=c>>>4 +b=(d&15)+16 +a2=e[b]|c&15|d&240 +e.$flags&2&&A.b(e) +e[b]=a2 +b=e[95] +d=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a5=b&225|d|a3 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else if(c7===47104){e=t.a +e===$&&A.a() +t.B((c&15|c>>>5&48)+32,e[c>>>4&31])}else if(a6===36879){e=t.c +e===$&&A.a() +a8=e.getUint16(93,!0)+1 +e.$flags&2&&A.b(e,10) +e.setUint16(93,a8,!0) +e=t.a +e===$&&A.a() +d=e[a8] +e.$flags&2&&A.b(e) +e[c>>>4&31]=d;++t.ax}else if(a6===37391){e=t.c +e===$&&A.a() +a8=e.getUint16(93,!0) +d=t.a +d===$&&A.a() +b=d[c>>>4&31] +d.$flags&2&&A.b(d) +d[a8]=b +e.$flags&2&&A.b(e,10) +e.setUint16(93,a8-1,!0);++t.ax}else if(a4===53248){e=(c&2048)!==0?2048:0 +d5=(c&2047)-e +c1=d+1 +d=t.c +d===$&&A.a() +b3=d.getUint16(93,!0) +e=t.y +e===$&&A.a() +b=t.a +b===$&&A.a() +b.$flags&2&&A.b(b) +b[b3]=c1&255 +b[b3-1]=B.a.A(c1,8)&255 +if(e)b[b3-2]=B.a.A(c1,16)&255 +b=e?3:2 +d.$flags&2&&A.b(d,10) +d.setUint16(93,b3-b,!0) +t.at+=d5 +b=t.ax +t.ax=b+(e?3:2)}else if(c===38152){e=t.y +e===$&&A.a() +d=t.c +d===$&&A.a() +b=d.getUint16(93,!0) +c4=b+(e?3:2) +d.$flags&2&&A.b(d,10) +d.setUint16(93,c4,!0) +d=t.a +d===$&&A.a() +b=(d[c4-1]<<8>>>0)+d[c4]-1 +t.at=b +if(e)t.at=(b|d[c4-2]<<16)>>>0 +d=t.ax +t.ax=d+(e?4:3)}else if(c===38168){e=t.y +e===$&&A.a() +d=t.c +d===$&&A.a() +b=d.getUint16(93,!0) +c4=b+(e?3:2) +d.$flags&2&&A.b(d,10) +d.setUint16(93,c4,!0) +d=t.a +d===$&&A.a() +b=(d[c4-1]<<8>>>0)+d[c4]-1 +t.at=b +if(e)t.at=(b|d[c4-2]<<16)>>>0 +b=t.ax +t.ax=b+(e?4:3) +b=d[95] +d.$flags&2&&A.b(d) +d[95]=b|128}else if(a4===49152){b=(c&2048)!==0?2048:0 +t.at=d+((c&2047)-b) +t.ax=e+1}else if(a6===37895){e=t.a +e===$&&A.a() +d=c>>>4&31 +a=e[d] +a0=a>>>1|(e[95]&1)<<7 +e.$flags&2&&A.b(e) +e[d]=a0 +d=e[95] +b=a0!==0?0:2 +a3=(a0&128)!==0?4:0 +a4=(a&1)!==0?1:0 +a5=d&224|b|a3|a4 +a5|=(a5>>>2&1^a5&1)!==0?8:0 +e[95]=a5|((a5>>>2&1^a5>>>3&1)!==0?16:0)}else if(b===2048){e=t.a +e===$&&A.a() +d=c>>>4&31 +b5=e[d] +b6=e[c&15|c>>>5&16] +a5=e[95] +b=a5&1 +a2=b5-b6-b +e.$flags&2&&A.b(e) +e[d]=a2 +d=a2===0&&(a5>>>1&1)!==0?2:0 +b=b6+b>b5?1:0 +a3=(a2&128)!==0?4:0 +a4=((b5^b6)&(b5^a2)&128)!==0?8:0 +a5=a5&192|d|b|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=~b5 +b=((b&b6|b6&a2|a2&b)&1)!==0?32:0 +e[95]=a5|d|b}else if(a4===16384){e=t.a +e===$&&A.a() +d=c>>>4 +b=(d&15)+16 +b5=e[b] +b6=c&15|d&240 +a5=e[95] +d=a5&1 +a2=b5-b6-d +e.$flags&2&&A.b(e) +e[b]=a2 +b=a2===0&&(a5>>>1&1)!==0?2:0 +d=b6+d>b5?1:0 +a3=(a2&128)!==0?4:0 +a4=((b5^b6)&(b5^a2)&128)!==0?8:0 +a5=a5&192|b|d|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=~b5 +b=((b&b6|b6&a2|a2&b)&1)!==0?32:0 +e[95]=a5|d|b}else if(a3===39424){d6=(c>>>3&31)+32 +b4=1<<(c&7)>>>0 +t.au(d6,(t.p(d6)|b4)>>>0,b4);++t.ax}else if(a3===39168){if((t.p((c>>>3&31)+32)&1<<(c&7))>>>0===0){e=t.at +b9=j[e+1] +d=b9&65039 +b=!0 +if(d!==36864)if(d!==37376){d=b9&65038 +d=d===37902||d===37900}else d=b +else d=b +c0=d?2:1 +t.ax+=c0 +t.at=e+c0}}else if(a3===39680){if((t.p((c>>>3&31)+32)&1<<(c&7))>>>0!==0){e=t.at +b9=j[e+1] +d=b9&65039 +b=!0 +if(d!==36864)if(d!==37376){d=b9&65038 +d=d===37902||d===37900}else d=b +else d=b +c0=d?2:1 +t.ax+=c0 +t.at=e+c0}}else if(a3===38656){c4=2*(c>>>4&3)+24 +e=t.c +e===$&&A.a() +d7=e.getUint16(c4,!0) +d8=c&15|c>>>2&48 +a2=d7-d8 +e.$flags&2&&A.b(e,10) +e.setUint16(c4,a2,!0) +e=t.a +e===$&&A.a() +d=e[95] +b=a2!==0?0:2 +a3=(a2&32768)!==0?4:0 +a4=(d7&~a2&32768)!==0?8:0 +a5=d&192|b|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=d8>d7?1:0 +a3=~d7 +a3=((a3&d8|d8&a2|a2&a3)&1)!==0?32:0 +e.$flags&2&&A.b(e) +e[95]=a5|d|b|a3;++t.ax}else if(b0===64512){b=t.a +b===$&&A.a() +if((b[c>>>4&31]&1<<(c&7))===0){b9=j[d+1] +b=b9&65039 +a3=!0 +if(b!==36864)if(b!==37376){b=b9&65038 +b=b===37902||b===37900}else b=a3 +else b=a3 +c0=b?2:1 +t.ax=e+c0 +t.at=d+c0}}else if(b0===65024){b=t.a +b===$&&A.a() +if((b[c>>>4&31]&1<<(c&7))!==0){b9=j[d+1] +b=b9&65039 +a3=!0 +if(b!==36864)if(b!==37376){b=b9&65038 +b=b===37902||b===37900}else b=a3 +else b=a3 +c0=b?2:1 +t.ax=e+c0 +t.at=d+c0}}else if(c!==38280)if(c!==38376)if(c!==38392)if(a6===37376){e=t.a +e===$&&A.a() +a8=e[c>>>4&31] +t.B(j[d+1],a8);++t.at;++t.ax}else if(a6===37388){e=t.c +e===$&&A.a() +e=e.getUint16(26,!0) +d=t.a +d===$&&A.a() +t.B(e,d[c>>>4&31]);++t.ax}else if(a6===37389){e=t.c +e===$&&A.a() +d0=e.getUint16(26,!0) +d=t.a +d===$&&A.a() +t.B(d0,d[c>>>4&31]) +e.$flags&2&&A.b(e,10) +e.setUint16(26,d0+1,!0);++t.ax}else if(a6===37390){e=t.a +e===$&&A.a() +c4=e[c>>>4&31] +e=t.c +e===$&&A.a() +d0=e.getUint16(26,!0)-1 +e.$flags&2&&A.b(e,10) +e.setUint16(26,d0,!0) +t.B(d0,c4);++t.ax}else if(a6===33288){e=t.c +e===$&&A.a() +e=e.getUint16(28,!0) +d=t.a +d===$&&A.a() +t.B(e,d[c>>>4&31]);++t.ax}else if(a6===37385){e=t.a +e===$&&A.a() +c4=e[c>>>4&31] +e=t.c +e===$&&A.a() +d1=e.getUint16(28,!0) +t.B(d1,c4) +e.$flags&2&&A.b(e,10) +e.setUint16(28,d1+1,!0);++t.ax}else if(a6===37386){e=t.a +e===$&&A.a() +c4=e[c>>>4&31] +e=t.c +e===$&&A.a() +d1=e.getUint16(28,!0)-1 +e.$flags&2&&A.b(e,10) +e.setUint16(28,d1,!0) +t.B(d1,c4);++t.ax}else if(a9===33288&&(c&7|c>>>7&24|c>>>8&32)!==0){e=t.c +e===$&&A.a() +e=e.getUint16(28,!0) +d=t.a +d===$&&A.a() +t.B(e+(c&7|c>>>7&24|c>>>8&32),d[c>>>4&31]);++t.ax}else if(a6===33280){e=t.c +e===$&&A.a() +e=e.getUint16(30,!0) +d=t.a +d===$&&A.a() +t.B(e,d[c>>>4&31]);++t.ax}else if(a6===37377){e=t.c +e===$&&A.a() +d2=e.getUint16(30,!0) +d=t.a +d===$&&A.a() +t.B(d2,d[c>>>4&31]) +e.$flags&2&&A.b(e,10) +e.setUint16(30,d2+1,!0);++t.ax}else if(a6===37378){e=t.a +e===$&&A.a() +c4=e[c>>>4&31] +e=t.c +e===$&&A.a() +d2=e.getUint16(30,!0)-1 +e.$flags&2&&A.b(e,10) +e.setUint16(30,d2,!0) +t.B(d2,c4);++t.ax}else if(a9===33280&&(c&7|c>>>7&24|c>>>8&32)!==0){e=t.c +e===$&&A.a() +e=e.getUint16(30,!0) +d=t.a +d===$&&A.a() +t.B(e+(c&7|c>>>7&24|c>>>8&32),d[c>>>4&31]);++t.ax}else if(b===6144){e=t.a +e===$&&A.a() +d=c>>>4&31 +b5=e[d] +b6=e[c&15|c>>>5&16] +a2=b5-b6 +e.$flags&2&&A.b(e) +e[d]=a2 +d=e[95] +b=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a4=((b5^b6)&(b5^a2)&128)!==0?8:0 +a5=d&192|b|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=b6>b5?1:0 +a3=~b5 +a3=((a3&b6|b6&a2|a2&a3)&1)!==0?32:0 +e[95]=a5|d|b|a3}else if(a4===20480){e=t.a +e===$&&A.a() +d=c>>>4 +b=(d&15)+16 +b5=e[b] +b6=c&15|d&240 +a2=b5-b6 +e.$flags&2&&A.b(e) +e[b]=a2 +b=e[95] +d=a2!==0?0:2 +a3=(a2&128)!==0?4:0 +a4=((b5^b6)&(b5^a2)&128)!==0?8:0 +a5=b&192|d|a3|a4 +d=(a5>>>2&1^a5>>>3&1)!==0?16:0 +b=b6>b5?1:0 +a3=~b5 +a3=((a3&b6|b6&a2|a2&a3)&1)!==0?32:0 +e[95]=a5|d|b|a3}else if(a6===37890){a=c>>>4&31 +e=t.a +e===$&&A.a() +c4=e[a] +e.$flags&2&&A.b(e) +e[a]=(c4&15)<<4|c4>>>4&15}else if(c===38312)t.as.$0() +else if(a6===37380){a0=c>>>4&31 +e=t.a +e===$&&A.a() +b5=e[a0] +d=t.c +d===$&&A.a() +b6=e[d.getUint16(30,!0)] +d=d.getUint16(30,!0) +e.$flags&2&&A.b(e) +e[d]=b5 +e[a0]=b6}}}}}}}}}}t.at=B.a.a0(t.at+1,16384);++t.ax +if((f&63)===0){++g +e=t.a +e===$&&A.a() +d9=e[42]&q.y +e0=e[36]&r.y +if(d9!==0)for(m=0;m<8;++m)if((d9&B.a.G(1,m))!==0)n[m]=n[m]+1 +if(e0!==0)for(m=8;m<14;++m)if((e0&B.a.K(1,m-8))!==0)n[m]=n[m]+1}for(;;){e=t.w +if(!(e!=null&&e.a<=t.ax))break +t.w=e.c +e.b.$0() +if(k.length<10)k.push(e)}e=!1 +if(!p.gcc(0))if(!s.w){e=p.b +if(e===p.c)A.af(A.e3()) +e=p.a[e] +e=s.U(e==null?i.a(e):e)}if(e){e=p.b +if(e===p.c)A.af(A.e3());++p.d +d=p.a +b=d[e] +if(b==null)i.a(b) +d[e]=null +p.b=(e+1&d.length-1)>>>0}e=t.a +e===$&&A.a() +if((e[95]&128)!==0&&t.ay>=0){e1=h[t.ay] +if(e1!=null){d=e1.a +b=t.c +b===$&&A.a() +b3=b.getUint16(93,!0) +a3=t.at +e.$flags&2&&A.b(e) +e[b3]=a3&255 +e[b3-1]=B.a.A(a3,8)&255 +a4=t.y +a4===$&&A.a() +if(a4)e[b3-2]=B.a.A(a3,16)&255 +a3=a4?3:2 +b.$flags&2&&A.b(b,10) +b.setUint16(93,b3-a3,!0) +e[95]=e[95]&127 +t.ax+=2 +t.at=d +if(!e1.f)t.S(e1)}}if(o.c0(t.ax)){e=$.dA +if(e!=null)e.$1(null)}}$.dU=g}, +dz(a){if(!$.N)return!1 +if(a>=0&&a<=7)return $.bj.k().a4(a)===B.e +else if(a>=8&&a<=13)return $.aA.k().a4(a-8)===B.e +else if(a>=14&&a<=19)return $.bZ.k().a4(a-14)===B.e +return!1}, +f4(a,b){var t +if(!$.N)return +if(a>=0){t=$.dv.k().c +t===$&&A.a() +t=a=0&&a<=7)$.bj.k().a6(a,b) +else if(a>=8&&a<=13)$.aA.k().a6(a-8,b) +else if(a>=14&&a<=19)$.bZ.k().a6(a-14,b)}, +f0(a,b){var t,s,r,q,p,o,n,m,l,k,j,i +B.t.X(b,0,16384,0) +t=a.split("\n") +for(s=t.length,r=b.$flags|0,q=0;q>>0}}}}, +c0:function c0(a){this.a=a}, +c_:function c_(){}, +d4:function d4(a,b){this.a=a +this.b=b}, +cH:function cH(){var _=this +_.d=_.c=_.b=_.a=0 +_.e=16e4 +_.f=!1}, +cI:function cI(a,b,c,d){var _=this +_.a=a +_.b=b +_.c=c +_.e=_.d=null +_.f=d +_.r=null +_.w=!1 +_.x=0}, +cJ:function cJ(){}, +br:function br(a,b){this.a=a +this.b=b +this.c=0}, +hT(){v.G.AVR8=new A.dk().$0()}, +di:function di(a){this.a=a}, +dj:function dj(a){this.a=a}, +dk:function dk(){}, +bL:function bL(){this.a=null}, +cZ:function cZ(a){this.a=a}, +eG(a){return v.mangledGlobalNames[a]}, +d9(a){var t +if(typeof a=="function")throw A.d(A.aF("Attempting to rewrap a JS function.")) +t=function(b,c){return function(){return b(c)}}(A.h9,a) +t[$.bi()]=a +return t}, +x(a){var t +if(typeof a=="function")throw A.d(A.aF("Attempting to rewrap a JS function.")) +t=function(b,c){return function(d){return b(c,d,arguments.length)}}(A.ha,a) +t[$.bi()]=a +return t}, +er(a){var t +if(typeof a=="function")throw A.d(A.aF("Attempting to rewrap a JS function.")) +t=function(b,c){return function(d,e){return b(c,d,e,arguments.length)}}(A.hb,a) +t[$.bi()]=a +return t}, +es(a){var t +if(typeof a=="function")throw A.d(A.aF("Attempting to rewrap a JS function.")) +t=function(b,c){return function(d,e,f){return b(c,d,e,f,arguments.length)}}(A.hc,a) +t[$.bi()]=a +return t}, +h9(a){return a.$0()}, +ha(a,b,c){if(c>=1)return a.$1(b) +return a.$0()}, +hb(a,b,c,d){if(d>=2)return a.$2(b,c) +if(d===1)return a.$1(b) +return a.$0()}, +hc(a,b,c,d,e){if(e>=3)return a.$3(b,c,d) +if(e===2)return a.$2(b,c) +if(e===1)return a.$1(b) +return a.$0()}},B={} +var w=[A,J,B] +var $={} +A.dD.prototype={} +J.bt.prototype={ +a_(a,b){return a===b}, +gC(a){return A.aZ(a)}, +j(a){return"Instance of '"+A.bE(a)+"'"}, +gu(a){return A.ab(A.dL(this))}} +J.bv.prototype={ +j(a){return String(a)}, +gC(a){return a?519018:218159}, +gu(a){return A.ab(u.y)}, +$ij:1, +$iw:1} +J.aL.prototype={ +a_(a,b){return null==b}, +j(a){return"null"}, +gC(a){return 0}, +$ij:1} +J.aN.prototype={$in:1} +J.U.prototype={ +gC(a){return 0}, +j(a){return String(a)}} +J.bD.prototype={} +J.b2.prototype={} +J.v.prototype={ +j(a){var t=a[$.eI()] +if(t==null)t=a[$.bi()] +if(t==null)return this.bH(a) +return"JavaScript function for "+J.ax(t)}} +J.ak.prototype={ +gC(a){return 0}, +j(a){return String(a)}} +J.al.prototype={ +gC(a){return 0}, +j(a){return String(a)}} +J.k.prototype={ +E(a,b){a.$flags&1&&A.b(a,29) +a.push(b)}, +T(a,b){var t +a.$flags&1&&A.b(a,"removeAt",1) +t=a.length +if(b>=t)throw A.d(A.e9(b,null)) +return a.splice(b,1)[0]}, +I(a,b){return A.cT(a,b,null,A.a8(a).c)}, +F(a,b){return a[b]}, +q(a,b,c,d,e){var t,s,r,q,p +a.$flags&2&&A.b(a,5) +A.bG(b,c,a.length) +t=c-b +if(t===0)return +A.O(e,"skipCount") +if(u.j.b(d)){s=d +r=e}else{s=J.du(d,e).ap(0,!1) +r=0}q=J.ad(s) +if(r+t>q.gi(s))throw A.d(A.e4()) +if(r=0;--p)a[b+p]=q.l(s,r+p) +else for(p=0;p"))}, +gC(a){return A.aZ(a)}, +gi(a){return a.length}, +si(a,b){a.$flags&1&&A.b(a,"set length","change the length of") +if(b<0)throw A.d(A.X(b,0,null,"newLength",null)) +if(b>a.length)A.a8(a).c.a(null) +a.length=b}, +l(a,b){if(!(b>=0&&b=0&&b=q){s.d=null +return!1}s.d=r[t] +s.c=t+1 +return!0}} +J.aM.prototype={ +b6(a){var t +if(a>=-2147483648&&a<=2147483647)return a|0 +if(isFinite(a)){t=a<0?Math.ceil(a):Math.floor(a) +return t+0}throw A.d(A.J(""+a+".toInt()"))}, +c8(a){var t,s +if(a>=0){if(a<=2147483647)return a|0}else if(a>=-2147483648){t=a|0 +return a===t?t:t-1}s=Math.floor(a) +if(isFinite(s))return s +throw A.d(A.J(""+a+".floor()"))}, +j(a){if(a===0&&1/a<0)return"-0.0" +else return""+a}, +gC(a){var t,s,r,q,p=a|0 +if(a===p)return p&536870911 +t=Math.abs(a) +s=Math.log(t)/0.6931471805599453|0 +r=Math.pow(2,s) +q=t<1?t/r:r/t +return((q*9007199254740992|0)+(q*3542243181176521|0))*599197+s*1259&536870911}, +a0(a,b){var t=a%b +if(t===0)return 0 +if(t>0)return t +if(b<0)return t-b +else return t+b}, +bI(a,b){if((a|0)===a)if(b>=1)return a/b|0 +return this.aJ(a,b)}, +ag(a,b){return(a|0)===a?a/b|0:this.aJ(a,b)}, +aJ(a,b){var t=a/b +if(t>=-2147483648&&t<=2147483647)return t|0 +if(t>0){if(t!==1/0)return Math.floor(t)}else if(t>-1/0)return Math.ceil(t) +throw A.d(A.J("Result of truncating division is "+A.q(t)+": "+A.q(a)+" ~/ "+b))}, +K(a,b){if(b<0)throw A.d(A.hD(b)) +return b>31?0:a<>>0}, +G(a,b){return b>31?0:a<>>0}, +A(a,b){var t +if(a>0)t=this.af(a,b) +else{t=b>31?31:b +t=a>>t>>>0}return t}, +af(a,b){return b>31?0:a>>>b}, +gu(a){return A.ab(u.H)}, +$ii:1} +J.aK.prototype={ +gu(a){return A.ab(u.S)}, +$ij:1, +$ic:1} +J.bw.prototype={ +gu(a){return A.ab(u.i)}, +$ij:1} +J.aj.prototype={ +c5(a,b){var t=b.length,s=a.length +if(t>s)return!1 +return b===this.bG(a,s-t)}, +N(a,b,c){return a.substring(b,A.bG(b,c,a.length))}, +bG(a,b){return this.N(a,b,null)}, +cz(a){var t,s,r,q=a.trim(),p=q.length +if(p===0)return q +if(q.charCodeAt(0)===133){t=J.fp(q,1) +if(t===p)return""}else t=0 +s=p-1 +r=q.charCodeAt(s)===133?J.fq(q,s):p +if(t===0&&r===p)return q +return q.substring(t,r)}, +j(a){return a}, +gC(a){var t,s,r +for(t=a.length,s=0,r=0;r>6}s=s+((s&67108863)<<3)&536870911 +s^=s>>11 +return s+((s&16383)<<15)&536870911}, +gu(a){return A.ab(u.N)}, +gi(a){return a.length}, +$ij:1, +$iI:1} +A.Y.prototype={ +gD(a){return new A.bo(J.dt(this.gR()),A.K(this).m("bo<1,2>"))}, +gi(a){return J.S(this.gR())}, +I(a,b){var t=A.K(this) +return A.e0(J.du(this.gR(),b),t.c,t.y[1])}, +F(a,b){return A.K(this).y[1].a(J.ds(this.gR(),b))}, +j(a){return J.ax(this.gR())}} +A.bo.prototype={ +n(){return this.a.n()}, +gt(){return this.$ti.y[1].a(this.a.gt())}} +A.a3.prototype={ +gR(){return this.a}} +A.b4.prototype={$ie:1} +A.b3.prototype={ +l(a,b){return this.$ti.y[1].a(J.eK(this.a,b))}, +h(a,b,c){J.eL(this.a,b,this.$ti.c.a(c))}, +si(a,b){J.eS(this.a,b)}, +E(a,b){J.eM(this.a,this.$ti.c.a(b))}, +T(a,b){return this.$ti.y[1].a(J.eR(this.a,b))}, +q(a,b,c,d,e){var t=this.$ti +J.eT(this.a,b,c,A.e0(d,t.y[1],t.c),e)}, +V(a,b,c,d){return this.q(0,b,c,d,0)}, +$ie:1, +$if:1} +A.aH.prototype={ +gR(){return this.a}} +A.aP.prototype={ +j(a){return"LateInitializationError: "+this.a}} +A.bp.prototype={ +gi(a){return this.a.length}, +l(a,b){return this.a.charCodeAt(b)}} +A.e.prototype={} +A.z.prototype={ +gD(a){var t=this +return new A.am(t,t.gi(t),A.K(t).m("am"))}, +I(a,b){return A.cT(this,b,null,A.K(this).m("z.E"))}} +A.b1.prototype={ +gbR(){var t=J.S(this.a) +return t}, +gbV(){var t=J.S(this.a),s=this.b +if(s>t)return t +return s}, +gi(a){var t=J.S(this.a),s=this.b +if(s>=t)return 0 +return t-s}, +F(a,b){var t=this,s=t.gbV()+b +if(b<0||s>=t.gbR())throw A.d(A.bs(b,t.gi(0),t,null,"index")) +return J.ds(t.a,s)}, +I(a,b){var t=this +A.O(b,"count") +return A.cT(t.a,t.b+b,t.c,t.$ti.c)}, +ap(a,b){var t,s,r=this,q=r.b,p=r.a,o=J.ad(p),n=o.gi(p),m=n-q +if(m<=0){p=J.e5(0,r.$ti.c) +return p}t=A.V(m,o.F(p,q),!1,r.$ti.c) +for(s=1;s=p){s.d=null +return!1}s.d=q.F(r,t);++s.c +return!0}} +A.aT.prototype={ +gi(a){return J.S(this.a)}, +F(a,b){return this.b.$1(J.ds(this.a,b))}} +A.P.prototype={ +I(a,b){A.cx(b,"count") +A.O(b,"count") +return new A.P(this.a,this.b+b,A.K(this).m("P<1>"))}, +gD(a){var t=this.a +return new A.bH(t.gD(t),this.b)}} +A.ai.prototype={ +gi(a){var t=this.a,s=t.gi(t)-this.b +if(s>=0)return s +return 0}, +I(a,b){A.cx(b,"count") +A.O(b,"count") +return new A.ai(this.a,this.b+b,this.$ti)}, +$ie:1} +A.bH.prototype={ +n(){var t,s +for(t=this.a,s=0;s")) +A.hK(t.a,s) +t.$map=s}return s}, +l(a,b){return this.ab().l(0,b)}, +al(a,b){this.ab().al(0,b)}, +gi(a){return this.ab().a}} +A.b_.prototype={} +A.a4.prototype={ +j(a){var t=this.constructor,s=t==null?null:t.name +return"Closure '"+A.eH(s==null?"unknown":s)+"'"}, +gcC(){return this}, +$C:"$1", +$R:1, +$D:null} +A.cB.prototype={$C:"$0",$R:0} +A.cC.prototype={$C:"$2",$R:2} +A.cV.prototype={} +A.cS.prototype={ +j(a){var t=this.$static_name +if(t==null)return"Closure of unknown static method" +return"Closure '"+A.eH(t)+"'"}} +A.aG.prototype={ +a_(a,b){if(b==null)return!1 +if(this===b)return!0 +if(!(b instanceof A.aG))return!1 +return this.$_target===b.$_target&&this.a===b.a}, +gC(a){return(A.eD(this.a)^A.aZ(this.$_target))>>>0}, +j(a){return"Closure '"+this.$_name+"' of "+("Instance of '"+A.bE(this.a)+"'")}} +A.cR.prototype={ +j(a){return"RuntimeError: "+this.a}} +A.a5.prototype={ +gi(a){return this.a}, +ak(a){var t +if((a&0x3fffffff)===a){t=this.c +if(t==null)return!1 +return t[a]!=null}else return this.c9(a)}, +c9(a){var t=this.d +if(t==null)return!1 +return this.Y(this.aG(t,a),a)>=0}, +l(a,b){var t,s,r,q,p=null +if(typeof b=="string"){t=this.b +if(t==null)return p +s=t[b] +r=s==null?p:s.b +return r}else if(typeof b=="number"&&(b&0x3fffffff)===b){q=this.c +if(q==null)return p +s=q[b] +r=s==null?p:s.b +return r}else return this.ca(b)}, +ca(a){var t,s,r=this.d +if(r==null)return null +t=this.aG(r,a) +s=this.Y(t,a) +if(s<0)return null +return t[s].b}, +h(a,b,c){var t,s,r,q,p,o,n=this +if(typeof b=="string"){t=n.b +n.aB(t==null?n.b=n.ad():t,b,c)}else if(typeof b=="number"&&(b&0x3fffffff)===b){s=n.c +n.aB(s==null?n.c=n.ad():s,b,c)}else{r=n.d +if(r==null)r=n.d=n.ad() +q=n.a3(b) +p=r[q] +if(p==null)r[q]=[n.ae(b,c)] +else{o=n.Y(p,b) +if(o>=0)p[o].b=c +else p.push(n.ae(b,c))}}}, +co(a,b){var t,s,r=this +if(r.ak(a)){t=r.l(0,a) +return t==null?A.K(r).y[1].a(t):t}s=b.$0() +r.h(0,a,s) +return s}, +b3(a,b){if((b&0x3fffffff)===b)return this.bU(this.c,b) +else return this.cb(b)}, +cb(a){var t,s,r,q,p=this,o=p.d +if(o==null)return null +t=p.a3(a) +s=o[t] +r=p.Y(s,a) +if(r<0)return null +q=s.splice(r,1)[0] +p.aK(q) +if(s.length===0)delete o[t] +return q.b}, +W(a){var t=this +if(t.a>0){t.b=t.c=t.d=t.e=t.f=null +t.a=0 +t.ac()}}, +al(a,b){var t=this,s=t.e,r=t.r +while(s!=null){b.$2(s.a,s.b) +if(r!==t.r)throw A.d(A.aI(t)) +s=s.c}}, +aB(a,b,c){var t=a[b] +if(t==null)a[b]=this.ae(b,c) +else t.b=c}, +bU(a,b){var t +if(a==null)return null +t=a[b] +if(t==null)return null +this.aK(t) +delete a[b] +return t.b}, +ac(){this.r=this.r+1&1073741823}, +ae(a,b){var t,s=this,r=new A.cO(a,b) +if(s.e==null)s.e=s.f=r +else{t=s.f +t.toString +r.d=t +s.f=t.c=r}++s.a +s.ac() +return r}, +aK(a){var t=this,s=a.d,r=a.c +if(s==null)t.e=r +else s.c=r +if(r==null)t.f=s +else r.d=s;--t.a +t.ac()}, +a3(a){return J.bS(a)&1073741823}, +aG(a,b){return a[this.a3(b)]}, +Y(a,b){var t,s +if(a==null)return-1 +t=a.length +for(s=0;s"]=t +delete t[""] +return t}} +A.cO.prototype={} +A.aO.prototype={ +a3(a){return A.hI(a)&1073741823}, +Y(a,b){var t,s +if(a==null)return-1 +t=a.length +for(s=0;s>>0!==b||b>c)this.bS(a,b,c,d)}} +A.d7.prototype={ +aO(a,b,c){var t=A.fw(this.a,b,c) +t.$flags=3 +return t}, +aN(a,b,c){var t=A.fv(this.a,b,c) +t.$flags=3 +return t}, +aM(a,b,c){var t=A.fu(this.a,b,c) +t.$flags=3 +return t}} +A.bx.prototype={ +gu(a){return B.ap}, +$ij:1} +A.ao.prototype={ +gi(a){return a.length}, +aI(a,b,c,d,e){var t,s,r=a.length +this.aC(a,b,r,"start") +this.aC(a,c,r,"end") +if(b>c)throw A.d(A.X(b,0,c,null,null)) +t=c-b +s=d.length +if(s-e")) +s.c=t.e +return s}, +gi(a){return this.a}, +c1(a,b){var t +if((b&1073741823)===b){t=this.c +if(t==null)return!1 +return t[b]!=null}else return this.bQ(b)}, +bQ(a){var t=this.d +if(t==null)return!1 +return this.aF(t[B.a.gC(a)&1073741823],a)>=0}, +E(a,b){var t,s,r=this +if(typeof b=="string"&&b!=="__proto__"){t=r.b +return r.aD(t==null?r.b=A.dH():t,b)}else if(typeof b=="number"&&(b&1073741823)===b){s=r.c +return r.aD(s==null?r.c=A.dH():s,b)}else return r.bO(b)}, +bO(a){var t,s,r=this,q=r.d +if(q==null)q=r.d=A.dH() +t=J.bS(a)&1073741823 +s=q[t] +if(s==null)q[t]=[r.a8(a)] +else{if(r.aF(s,a)>=0)return!1 +s.push(r.a8(a))}return!0}, +aD(a,b){if(a[b]!=null)return!1 +a[b]=this.a8(b) +return!0}, +aE(){this.r=this.r+1&1073741823}, +a8(a){var t,s=this,r=new A.d3(a) +if(s.e==null)s.e=s.f=r +else{t=s.f +t.toString +r.c=t +s.f=t.b=r}++s.a +s.aE() +return r}, +aF(a,b){var t,s +if(a==null)return-1 +t=a.length +for(s=0;s"))}, +F(a,b){return this.l(a,b)}, +I(a,b){return A.cT(a,b,null,A.av(a).m("h.E"))}, +E(a,b){var t=this.gi(a) +this.si(a,t+1) +this.h(a,t,b)}, +bP(a,b,c){var t,s=this,r=s.gi(a),q=c-b +for(t=c;tq.gi(r))throw A.d(A.e4()) +if(s=0;--p)this.h(a,b+p,q.l(r,s+p)) +else for(p=0;p"))}, +gcc(a){return this.b===this.c}, +gi(a){return(this.c-this.b&this.a.length-1)>>>0}, +F(a,b){var t=this,s=t.gi(0) +if(0>b||b>=s)A.af(A.bs(b,s,t,null,"index")) +s=t.a +s=s[(t.b+b&s.length-1)>>>0] +return s==null?t.$ti.c.a(s):s}, +bX(a,b){var t,s,r,q=this,p=b.a.length,o=q.gi(0),n=o+p,m=q.a,l=m.length +if(n>=l){t=A.V(A.ft(n+(n>>>1)),null,!1,q.$ti.m("1?")) +q.c=q.bW(t) +q.a=t +q.b=0 +B.b.q(t,o,n,b,0) +q.c+=p}else{n=q.c +s=l-n +if(p>>0)t[q]=null +r.b=r.c=0;++r.d}}, +j(a){return A.cL(this,"{","}")}, +bW(a){var t,s,r=this,q=r.b,p=r.c,o=r.a +if(q<=p){t=p-q +B.b.q(a,0,t,o,q) +return t}else{s=o.length-q +B.b.q(a,0,s,o,q) +B.b.q(a,s,s+r.c,r.a,0) +return r.c+s}}} +A.bO.prototype={ +gt(){var t=this.e +return t==null?this.$ti.c.a(t):t}, +n(){var t,s=this,r=s.a +if(s.c!==r.d)A.af(A.aI(r)) +t=s.d +if(t===s.b){s.e=null +return!1}r=r.a +s.e=r[t] +s.d=(t+1&r.length-1)>>>0 +return!0}} +A.aq.prototype={ +j(a){return A.cL(this,"{","}")}, +I(a,b){return A.eb(this,b,this.$ti.c)}, +F(a,b){var t,s,r,q=this +A.O(b,"index") +t=A.bN(q,q.r,q.$ti.c) +for(s=b;t.n();){if(s===0){r=t.d +return r==null?t.$ti.c.a(r):r}--s}throw A.d(A.bs(b,b-s,q,null,"index"))}, +$ie:1} +A.ba.prototype={} +A.d0.prototype={ +j(a){return this.a1()}} +A.cE.prototype={} +A.cy.prototype={ +j(a){var t=this.a +if(t!=null)return"Assertion failed: "+A.cF(t) +return"Assertion failed"}} +A.cW.prototype={} +A.ah.prototype={ +gaa(){return"Invalid argument"+(!this.a?"(s)":"")}, +ga9(){return""}, +j(a){var t=this,s=t.c,r=s==null?"":" ("+s+")",q=t.d,p=q==null?"":": "+A.q(q),o=t.gaa()+r+p +if(!t.a)return o +return o+t.ga9()+": "+A.cF(t.gam())}, +gam(){return this.b}} +A.bF.prototype={ +gam(){return this.b}, +gaa(){return"RangeError"}, +ga9(){var t,s=this.e,r=this.f +if(s==null)t=r!=null?": Not less than or equal to "+A.q(r):"" +else if(r==null)t=": Not greater than or equal to "+A.q(s) +else if(r>s)t=": Not in inclusive range "+A.q(s)+".."+A.q(r) +else t=r=32&&this.e.ak(a))return this.e.l(0,a).$1(a) +t=this.a +t===$&&A.a() +return t[a]}, +au(a,b,c){var t,s=this.f.l(0,a) +if(s!=null){t=this.a +t===$&&A.a() +if(s.$4(b,t[a],a,c))return}t=this.a +t===$&&A.a() +t.$flags&2&&A.b(t) +t[a]=b}, +B(a,b){return this.au(a,b,255)}, +v(a){var t,s,r=this.a +r===$&&A.a() +t=a.d +s=r[t] +r.$flags&2&&A.b(r) +r[t]=(s|a.e)>>>0 +if((r[a.b]&a.c)!==0)this.an(a)}, +H(a,b){var t +if((b&a.c)>>>0!==0){t=this.a +t===$&&A.a() +t=t[a.d] +if((t&a.e)!==0)this.an(a)}else this.aj(a,!1)}, +an(a){var t,s=this,r=a.a +s.r[r]=a +t=s.ay +if(t===-1||t>r)s.ay=r +if(r>s.ch)s.ch=r}, +aj(a,b){var t,s,r,q,p,o=this +if(b){t=o.a +t===$&&A.a() +s=a.d +r=t[s] +t.$flags&2&&A.b(t) +t[s]=r&~a.e}q=a.a +t=o.r +if(t[q]==null)return +t[q]=null +if(o.ay===q){o.ay=-1 +for(p=q+1,s=o.ch;p<=s;++p)if(t[p]!=null){o.ay=p +break}}}, +S(a){return this.aj(a,!0)}, +O(a,b){var t,s,r,q=a.e +if((b&q)>>>0!==0){t=this.a +t===$&&A.a() +s=a.d +r=t[s] +t.$flags&2&&A.b(t) +t[s]=r&~q +this.S(a)}}, +J(a,b){var t,s,r,q,p=this,o=p.ax,n=o+(b>1?b:1) +o=p.x +if(o.length!==0){t=o.pop() +t.a=n +t.b=a +t.c=null}else t=new A.bk(n,a) +s=p.w +r=null +for(;;){if(!(s!=null&&s.a>>6&3 +if(t<4)return B.aa[t] +return B.m}, +gcs(){switch(this.gct().a){case 0:return 5 +case 1:return 5 +case 2:return 1.1 +case 3:return 2.56 +default:return 5}}, +cm(a){return this.gcl().$1(a)}} +A.bX.prototype={ +$1(a){var t,s +if(a instanceof A.ag)t=a.b +else if(a instanceof A.D){s=this.a.c +s===$&&A.a() +t=s[a.b]}else t=a instanceof A.ay?0.378125:0 +s=this.a +s.a.J(new A.bW(s,Math.min(Math.max(B.p.c8(t/s.gcs()*1024),0),1023)),s.w*s.gb1())}, +$S:15} +A.bW.prototype={ +$0(){return this.a.aS(this.b)}, +$S:1} +A.bY.prototype={ +$4(a,b,c,d){var t,s,r,q,p,o,n=(a&128)===0 +if(!n&&(b&128)===0)this.a.w=25 +t=this.a +s=t.a +r=s.a +r===$&&A.a() +r.$flags&2&&A.b(r) +r[122]=a +q=t.x +q===$&&A.a() +s.H(q,a) +if(!t.r&&(a&64)!==0){if(n){s.J(new A.bV(t),t.w*t.gb1()) +return!0}p=r[124]&31 +o=B.ah.l(0,((r[123]&8)!==0?p|32:p)&15) +if(o==null)o=B.l +t.r=!0 +t.cm(o) +return!0}return!1}, +$S:0} +A.bV.prototype={ +$0(){return this.a.aS(0)}, +$S:1} +A.a2.prototype={} +A.aC.prototype={} +A.aD.prototype={} +A.ap.prototype={ +a1(){return"PinState."+this.b}} +A.a6.prototype={ +a1(){return"PinOverrideMode."+this.b}} +A.aB.prototype={ +bK(a,b){var t,s,r,q,p,o,n,m,l,k,j,i,h=this,g=h.a +g.z.E(0,h) +t=h.b +s=t.c +g.Q[s]=h +g=g.f +g.h(0,t.b,new A.c2(h)) +g.h(0,s,new A.c3(h)) +g.h(0,t.a,new A.c4(h)) +r=t.e +s=A.a8(r).m("aT<1,u?>") +s=A.e7(new A.aT(r,new A.c5(),s),s.m("z.E")) +h.d!==$&&A.C() +h.d=s +q=A.dE(u.S) +for(s=r.length,p=0;p>>0!==0?B.e:B.al +else return p}, +a6(a,b){var t=this,s=B.a.K(1,a),r=(t.r&~s)>>>0 +t.r=r +if(b)t.r=(r|s)>>>0 +r=t.a.a +r===$&&A.a() +t.Z(r[t.b.b])}, +ao(a,b){var t,s=this,r=B.a.G(1,a),q=s.w,p=~r +if(b===B.d){s.w=(q|r)>>>0 +s.x=(s.x&p)>>>0}else{s.w=(q&p)>>>0 +switch(b.a){case 1:q=(s.x&p)>>>0 +s.x=q +p=s.a.a +p===$&&A.a() +s.x=(q|p[s.b.c]&r)>>>0 +break +case 2:s.x=(s.x|r)>>>0 +break +case 3:s.x=(s.x&p)>>>0 +break +case 4:s.x=(s.x^r)>>>0 +break +default:break}}q=s.a.a +q===$&&A.a() +p=s.b +t=q[p.b] +s.a5(q[p.c],t) +s.Z(t)}, +Z(a){var t,s,r,q,p=this,o=(p.r&~a|p.y&a)>>>0,n=p.a.a +n===$&&A.a() +n.$flags&2&&A.b(n) +n[p.b.a]=o +if(p.Q!==o){for(n=p.c,t=0;t<8;++t){s=B.a.G(1,t) +r=(o&s)>>>0 +if(r!==(p.Q&s)>>>0){q=r!==0 +p.cw(t,q) +s=n.l(0,t) +if(s!=null)s.$1(q)}}p.Q=o}}, +cw(a,b){var t,s,r,q,p,o,n,m,l=this,k=l.b,j=k.e +if(a=s)continue +q=i[r] +if(q==null)continue +p=(this.Q&B.a.G(1,r))>>>0!==0 +o=q.e +n=q.d +m=q.f +l=t.a +l===$&&A.a() +k=l[61] +j=B.a.G(1,o) +if((k&j)===0||p)continue +if((B.a.af(l[105],n)&3)===0)t.an(new A.u(m,61,j,60,j,!0))}}, +a5(a,b){var t,s,r,q=this,p=((a&q.w|q.x)&b|a&~b)>>>0,o=q.y +if(p!==o||b!==q.z){q.y=p +q.z=b +for(t=q.f,s=t.length,r=0;r>>0 +n.$flags&2&&A.b(n) +n[s]=p +o.a5(p,q) +o.Z(q) +return!0}, +$S:0} +A.c5.prototype={ +$1(a){var t,s +if(a!=null){t=a.f +s=B.a.G(1,a.e) +s=new A.u(t,61,s,60,s,!1) +t=s}else t=null +return t}, +$S:16} +A.c6.prototype={ +$4(a,b,c,d){var t,s,r,q +for(t=this.a.a,s=t.z,s=A.bN(s,s.r,s.$ti.c),r=s.$ti.c;s.n();){q=s.d +q=(q==null?r.a(q):q).e +q===$&&A.a() +if(q!=null)t.O(q,a)}return!0}, +$S:0} +A.c7.prototype={ +$4(a,b,c,d){var t,s,r=this.a.a,q=r.a +q===$&&A.a() +q.$flags&2&&A.b(q) +q[this.b]=a +for(q=r.z,q=A.bN(q,q.r,q.$ti.c),t=q.$ti.c;q.n();){s=q.d +s=(s==null?t.a(s):s).e +s===$&&A.a() +if(s!=null)r.H(s,a)}return!0}, +$S:0} +A.c1.prototype={ +$4(a,b,c,d){var t,s,r,q,p,o,n,m,l,k=this,j=k.b,i=j==="flag" +if(!i){t=k.a.a.a +t===$&&A.a() +t.$flags&2&&A.b(t) +t[k.c]=a}for(t=k.a.a,s=t.z,s=A.bN(s,s.r,s.$ti.c),j=j==="mask",r=s.$ti.c;s.n();){q=s.d +if(q==null)q=r.a(q) +p=q.d +p===$&&A.a() +o=p.length +n=0 +for(;n>>1|t&3] +l=m.ax=r.a +m.ay=r.b +m.Q=r.c +m.as=r.d +q=l===3||l===1||l===4 +p=m.cx +l=m.cx=t>>>6&3 +l=(l===1&&q&&(r.e&1)===0?m.cx=0:l)!==0 +if(p!==0!==l)m.b7(l?B.f:B.d) +o=m.cy +l=m.cy=t>>>4&3 +l=(l===1&&q?m.cy=0:l)!==0 +if(o!==0!==l)m.b8(l?B.f:B.d) +l=m.x +l===$&&A.a() +if(l){n=m.db +l=m.db=t>>>2&3 +l=(l===1&&q?m.db=0:l)!==0 +if(n!==0!==l)m.b9(l?B.f:B.d)}}, +c2(){this.aY(!0)}, +aZ(a,b){var t,s,r,q,p,o,n,m,l=this,k=l.a,j=k.ax,i=l.d,h=j-i,g=l.fx +if(g!==0&&h>=g||a){t=a?1:B.a.bI(h,g) +l.d=i+t*g +s=l.ch +i=l.ax +r=i===1||i===4 +q=r?l.cn(s,t):B.a.a0(s+t,l.gP()+1) +p=s+t>l.gP() +if(!l.dx){l.ch=q +if(!r)l.b5(q,s)}if(!r){if(l.ax===3&&p){i=l.cx +if(i!==0)l.aq(i,"A",!0) +i=l.cy +if(i!==0)l.aq(i,"B",!0)}if(l.Q===2&&p){l.e=l.f +l.r=l.w +l.y=l.z}if(p)if(l.as!==1){i=l.gP() +g=l.c +g===$&&A.a() +g=i===g +i=g}else i=!0 +else i=!1 +if(i){i=l.k1 +i===$&&A.a() +k.v(i)}}}if(l.dx){i=l.ch=l.CW +l.dx=!1 +if(!(i===0&&l.Q===2))i=i===l.gP()&&l.Q===1 +else i=!0 +if(i){l.e=l.f +l.r=l.w +l.y=l.z}}if(l.dy){i=k.a +i===$&&A.a() +g=l.b +o=i[g.ax]&7 +n=g.ok +m=g.b.l(0,o) +if(m==null)m=0 +i=m!==0 +l.d=i?k.ax:0 +l.dy=!1 +l.fx=m +g=g.k4 +if(g!==0&&l.fy==null)l.fy=k.Q[g] +g=l.fy +if(g!=null)g.c.h(0,n,null) +if(i)k.J(l.ga2(),l.d+m-k.ax) +else{k=l.fy +if(k!=null)i=o===6||o===7 +else i=!1 +if(i){k.c.h(0,n,l.gc6()) +l.go=o===7}}return}if(b&&l.fx!==0)k.J(l.ga2(),l.d+l.fx-k.ax)}, +aY(a){return this.aZ(!1,a)}, +c7(a){if(a===this.go)this.aZ(!0,!1)}, +cn(a,b){var t,s,r=this +if(a===0&&r.gP()===0){if(r.Q===1){r.e=r.f +r.r=r.w +r.y=r.z}b=0}for(t=r.a;b>0;){if(r.fr){++a +if(a===r.gP()&&!r.dx){r.fr=!1 +if(r.Q===1){r.e=r.f +r.r=r.w +r.y=r.z}}}else{--a +if(a===0&&!r.dx){r.fr=!0 +s=r.k1 +s===$&&A.a() +t.v(s) +if(r.Q===2){r.e=r.f +r.r=r.w +r.y=r.z}}}if(!r.dx){if(a===r.e){s=r.k2 +s===$&&A.a() +t.v(s) +s=r.cx +if(s!==0)r.L(s,"A")}if(a===r.r){s=r.k3 +s===$&&A.a() +t.v(s) +s=r.cy +if(s!==0)r.L(s,"B")}s=r.x +s===$&&A.a() +if(s&&a===r.y){s=r.k4 +s===$&&A.a() +t.v(s) +s=r.db +if(s!==0)r.L(s,"C")}}--b}t=r.c +t===$&&A.a() +return a&t}, +b5(a,b){var t=this,s=b>a,r=t.e,q=b=r))r=q&&s +else r=!0 +if(r){r=t.k2 +r===$&&A.a() +t.a.v(r) +r=t.cx +if(r!==0)t.L(r,"A")}r=t.r +q=b=r))r=q&&s +else r=!0 +if(r){r=t.k3 +r===$&&A.a() +t.a.v(r) +r=t.cy +if(r!==0)t.L(r,"B")}r=t.x +r===$&&A.a() +if(r){r=t.y +q=b=r))r=q&&s +else r=!0}else r=!1 +if(r){r=t.k4 +r===$&&A.a() +t.a.v(r) +r=t.db +if(r!==0)t.L(r,"C")}}, +aQ(a){var t=this,s=t.ax +if(s===3||s===1||s===4)return +if((a&128)!==0)t.L(t.cx,"A") +if((a&64)!==0)t.L(t.cy,"B")}, +aq(a,b,c){var t,s=this,r=a===3,q=s.fr +switch(s.ax){case 0:case 2:t=A.hH(a) +break +case 3:if(a===1)t=c?B.d:B.j +else t=r!==c?B.h:B.i +break +case 1:case 4:if(a===1)t=B.j +else t=q===r?B.h:B.i +break +default:t=B.d}if(t!==B.d)if(b==="A")s.b7(t) +else if(b==="B")s.b8(t) +else s.b9(t)}, +L(a,b){return this.aq(a,b,!1)}, +b7(a){var t=this.b,s=this.a.Q[t.fy] +if(s!=null)s.ao(t.go,a)}, +b8(a){var t=this.b,s=this.a.Q[t.id] +if(s!=null)s.ao(t.k1,a)}, +b9(a){var t=this.a.Q[0] +if(t!=null)t.ao(0,a)}} +A.cb.prototype={ +$1(a){var t,s,r=this.a +r.aY(!1) +if(r.b.a===16){t=r.a.a +t===$&&A.a() +s=B.a.A(r.ch,8) +t.$flags&2&&A.b(t) +t[a+1]=s}t=r.a.a +t===$&&A.a() +r=r.ch&255 +t.$flags&2&&A.b(t) +t[a]=r +return r}, +$S:4} +A.cc.prototype={ +$4(a,b,c,d){var t,s=this.a +s.CW=(s.id<<8|a)>>>0 +s.dx=s.fr=!0 +s.a.cB(s.ga2(),0) +if(s.fx!==0){t=s.CW +s.b5(t,t)}return!1}, +$S:0} +A.cd.prototype={ +$4(a,b,c,d){var t=this.a,s=(t.id<<8|a)>>>0 +t.f=s +if(t.Q===0)t.e=s +return!1}, +$S:0} +A.ce.prototype={ +$4(a,b,c,d){var t=this.a,s=(t.id<<8|a)>>>0 +t.w=s +if(t.Q===0)t.r=s +return!1}, +$S:0} +A.cf.prototype={ +$4(a,b,c,d){var t=this.a +t.at=(t.id<<8|a)>>>0 +return!1}, +$S:0} +A.cm.prototype={ +$4(a,b,c,d){this.a.id=a +return!1}, +$S:0} +A.cl.prototype={ +$4(a,b,c,d){var t=this.a,s=(a&B.a.A(t.gck(),8))>>>0 +t.id=s +t=t.a.a +t===$&&A.a() +t.$flags&2&&A.b(t) +t[c]=s +return!0}, +$S:0} +A.cg.prototype={ +$4(a,b,c,d){var t=this.a,s=t.a.a +s===$&&A.a() +s.$flags&2&&A.b(s) +s[t.b.at]=a +t.ar() +return!0}, +$S:0} +A.ch.prototype={ +$4(a,b,c,d){var t,s,r=this.a,q=r.b +if(q.ay===0){r.aQ(a) +a=(a&4294967103)>>>0}t=r.a +s=t.a +s===$&&A.a() +s.$flags&2&&A.b(s) +s[q.ax]=a +r.dy=!0 +q=r.ga2() +t.aR(q) +t.J(q,0) +r.ar() +return!0}, +$S:0} +A.ci.prototype={ +$4(a,b,c,d){this.a.aQ(a) +return!1}, +$S:0} +A.cj.prototype={ +$4(a,b,c,d){var t=this.a,s=t.a,r=s.a +r===$&&A.a() +r.$flags&2&&A.b(r) +r[t.b.w]=a +r=t.k1 +r===$&&A.a() +s.O(r,a) +r=t.k2 +r===$&&A.a() +s.O(r,a) +t=t.k3 +t===$&&A.a() +s.O(t,a) +return!0}, +$S:0} +A.ck.prototype={ +$4(a,b,c,d){var t=this.a,s=t.a,r=t.k1 +r===$&&A.a() +s.H(r,a) +r=t.k2 +r===$&&A.a() +s.H(r,a) +t=t.k3 +t===$&&A.a() +s.H(t,a) +return!1}, +$S:0} +A.cU.prototype={} +A.cQ.prototype={ +av(a){this.a.aV()}, +az(){this.a.aW()}, +aX(a,b){this.a.aT(!1)}, +U(a){var t=this.a +t.e=!1 +t.M(48)}, +b2(a){this.a.aU(255)}} +A.c8.prototype={ +bL(a,b,c){var t=this +t.d=new A.cQ(t) +t.f!==$&&A.C() +t.f=new A.u(48,188,1,188,128,!1) +t.M(248) +t.a.f.h(0,188,new A.ca(t))}, +aV(){this.e=!1 +this.M(this.gaw()===248?8:16)}, +aW(){var t,s +this.e=!1 +t=this.a.a +t===$&&A.a() +s=t[188] +t.$flags&2&&A.b(t) +t[188]=s&4294967279 +this.M(248)}, +aT(a){var t,s=this +s.e=!1 +t=s.a.a +t===$&&A.a() +if((t[187]&1)!==0)s.M(a?64:72) +else s.M(a?24:32)}, +aU(a){var t,s +this.e=!1 +t=this.a.a +t===$&&A.a() +s=t[188] +t.$flags&2&&A.b(t) +t[187]=a +this.M((s&64)!==0?80:88)}, +gaw(){var t=this.a.a +t===$&&A.a() +return t[185]&248}, +M(a){var t,s=this.a,r=s.a +r===$&&A.a() +t=r[185] +r.$flags&2&&A.b(r) +r[185]=t&4294967047|a +t=this.f +t===$&&A.a() +s.v(t)}} +A.ca.prototype={ +$4(a,b,c,d){var t,s,r=this.a,q=r.a,p=q.a +p===$&&A.a() +p.$flags&2&&A.b(p) +p[188]=a +t=r.f +t===$&&A.a() +q.O(t,a) +q.H(t,a) +s=r.gaw() +if((a&128)!==0&&(a&4)!==0&&!r.e){q.J(new A.c9(r,a,s,p[187]),0) +return!0}return!1}, +$S:0} +A.c9.prototype={ +$0(){var t,s=this,r=s.b +if((r&32)!==0){r=s.a +r.e=!0 +r=r.d +r===$&&A.a() +r.av(s.c!==248)}else if((r&16)!==0){r=s.a +r.e=!0 +r=r.d +r===$&&A.a() +r.az()}else{t=s.c +if(t===8||t===16){r=s.a +r.e=!0 +r=r.d +r===$&&A.a() +t=s.d +r.aX(t>>>1,(t&1)===0)}else if(t===24||t===40){r=s.a +r.e=!0 +r=r.d +r===$&&A.a() +r.U(s.d)}else if(t===64||t===80){t=s.a +t.e=!0 +t=t.d +t===$&&A.a() +t.b2((r&64)!==0)}}}, +$S:1} +A.cX.prototype={} +A.cn.prototype={ +bN(a,b,c){var t,s,r=this +r.z!==$&&A.C() +r.z=new A.u(36,193,128,192,128,!0) +r.Q!==$&&A.C() +r.Q=new A.u(38,193,32,192,32,!1) +r.as!==$&&A.C() +r.as=new A.u(40,193,64,192,64,!1) +t=r.a +s=t.a +s===$&&A.a() +s.$flags&2&&A.b(s) +s[192]=32 +s[193]=0 +s[194]=6 +r.w=!1 +r.x=0 +r.y="" +s=t.f +s.h(0,192,new A.cp(r)) +s.h(0,193,new A.cq(r)) +s.h(0,194,new A.cr(r)) +t.e.h(0,198,new A.cs(r)) +s.h(0,198,new A.ct(r)) +s.h(0,197,new A.cu(r)) +s.h(0,196,new A.cv(r))}, +ba(a,b){var t,s,r=this +if(!r.w){t=r.a.a +t===$&&A.a() +t=(t[193]&16)===0}else t=!0 +if(t)return!1 +t=r.a +if(b){r.x=a +s=r.z +s===$&&A.a() +t.v(s) +return!0}else{r.w=!0 +t.J(new A.cw(r,a),r.gb_()) +return!0}}, +U(a){return this.ba(a,!1)}, +gb_(){var t,s,r,q,p=this.gaP(),o=this.a.a +o===$&&A.a() +t=o[194] +s=(t&8)!==0?2:1 +t=(t&32)!==0?1:0 +r=o[197] +q=o[196] +o=(o[192]&2)!==0?8:16 +return(((r<<8|q)>>>0)+1)*o*(1+p+s+t)}, +gaP(){var t=this.a.a +t===$&&A.a() +switch(t[194]>>>1&3|t[193]&4){case 0:return 5 +case 1:return 6 +case 2:return 7 +case 3:return 8 +case 7:return 9 +default:return 9}}} +A.cp.prototype={ +$4(a,b,c,d){var t=this.a,s=t.a,r=s.a +r===$&&A.a() +r.$flags&2&&A.b(r) +r[192]=a&3 +t=t.as +t===$&&A.a() +s.O(t,a) +return!0}, +$S:0} +A.cq.prototype={ +$4(a,b,c,d){var t,s=this.a,r=s.a,q=s.z +q===$&&A.a() +r.H(q,a) +t=s.Q +t===$&&A.a() +r.H(t,a) +s=s.as +s===$&&A.a() +r.H(s,a) +if((a&16)!==0&&(b&16)!==0)r.S(q) +if((a&8)!==0&&(b&8)===0)r.v(t) +s=r.a +s===$&&A.a() +s.$flags&2&&A.b(s) +s[193]=a +return!0}, +$S:0} +A.cr.prototype={ +$4(a,b,c,d){var t=this.a.a.a +t===$&&A.a() +t.$flags&2&&A.b(t) +t[194]=a +return!0}, +$S:0} +A.cs.prototype={ +$1(a){var t,s,r=this.a,q=B.ai.l(0,r.gaP()) +if(q==null)q=255 +t=r.x +r.x=0 +s=r.z +s===$&&A.a() +r.a.S(s) +return t&q}, +$S:4} +A.ct.prototype={ +$4(a,b,c,d){var t,s,r,q=this.a +if(q.e!=null){t=A.fz(a) +s=q.y +if(t==="\n"){q.e.$1(s) +q.y=""}else q.y=s+t}s=q.a +s.J(new A.co(q),q.gb_()) +r=q.as +r===$&&A.a() +s.S(r) +q=q.Q +q===$&&A.a() +s.S(q) +return!1}, +$S:0} +A.co.prototype={ +$0(){var t=this.a,s=t.a,r=t.Q +r===$&&A.a() +s.v(r) +t=t.as +t===$&&A.a() +s.v(t)}, +$S:1} +A.cu.prototype={ +$4(a,b,c,d){var t=this.a.a.a +t===$&&A.a() +t.$flags&2&&A.b(t) +t[197]=a +return!0}, +$S:0} +A.cv.prototype={ +$4(a,b,c,d){var t=this.a.a.a +t===$&&A.a() +t.$flags&2&&A.b(t) +t[196]=a +return!0}, +$S:0} +A.cw.prototype={ +$0(){var t=this.a +t.w=!1 +t.ba(this.b,!0)}, +$S:1} +A.c0.prototype={ +$1(a){this.a.$1(a)}, +$S:2} +A.c_.prototype={ +$1(a){var t=$.dA +return t==null?null:t.$1(a)}, +$S:18} +A.d4.prototype={ +av(a){return this.a.aV()}, +az(){this.b.b0() +this.a.aW()}, +aX(a,b){var t=this.b +t.bZ(a,b) +this.a.aT(t.a.c1(0,a))}, +U(a){var t +this.b.U(a) +t=this.a +t.e=!1 +t.M(40)}, +b2(a){this.a.aU(this.b.cr())}} +A.cH.prototype={ +b4(){var t=this +t.d=t.c=t.b=t.a=0 +t.e=16e4 +t.f=!1}, +c0(a){var t,s=this +if(s.f){t=s.a +t=t!==0&&a-t>s.e}else t=!1 +if(t){s.b4() +return!0}return!1}} +A.cI.prototype={ +bn(a,b,c){var t,s +this.a.E(0,a) +t=this.f +s=t.l(0,a) +if(s!=null&&s.b.length===c&&s.a===b)return +t.h(0,a,new A.br(b,new Uint8Array(c)))}, +bB(a,b,c){var t,s,r,q,p,o=this.f.l(0,a),n=o==null?null:o.b +if(n==null)return +for(o=c.length,t=n.length,s=n.$flags|0,r=0;r0)t.c=0}else if(t.a===0)t.c=0 +if(!b)return +s=r.b +if(!s.ak(a)&&s.a>=16)return +r.e=a +r.d=A.F([],u.t)}, +U(a){var t,s,r=this,q=a&255,p=r.d +if(p!=null)p.push(q) +t=r.r +if(t==null||!r.w)return +p=r.x +if(p>0){t.c=B.a.a0((t.c<<8|q)>>>0,t.b.length) +r.x=p-1}else if(t.a>0){p=t.b +s=t.c +p.$flags&2&&A.b(p) +p[s]=q +t.aL()}}, +cr(){var t,s=this.r +if(s==null||this.w)return 255 +t=s.b[s.c] +s.aL() +return t}, +b0(){var t,s,r,q,p,o=this,n=o.e,m=o.d +o.r=o.d=o.e=null +o.x=0 +if(n==null||m==null||m.length===0)return +t=o.b.co(n,new A.cJ()) +s=J.a0(t) +s.E(t,m) +r=o.c +q=r.l(0,n) +if(q==null)q=0 +p=q+m.length +for(;;){if(!(p>65536&&s.gi(t)>1))break +p-=J.S(s.T(t,0))}r.h(0,n,p)}} +A.cJ.prototype={ +$0(){return A.F([],u.p)}, +$S:19} +A.br.prototype={ +aL(){return this.c=(this.c+1)%this.b.length}} +A.di.prototype={ +$0(){return this.a.a}, +$S:20} +A.dj.prototype={ +$1(a){this.a.a=a}, +$S:21} +A.dk.prototype={ +$0(){var t,s=new A.bL(),r=v.G,q=A.bf(r.Object),p=A.bf(q.create.apply(q,[null])) +p.setSerialPrint=A.x(s.gbC()) +p.setBuzzerFrequencyCallback=A.x(s.gbt()) +p.setBuzzerPin=A.x(s.gbv()) +p.setServoPins=A.x(s.gbE()) +p.getServoPulseUs=A.x(s.gbl()) +p.loadHex=A.x(s.gci()) +p._printLine=A.x(s.gaH()) +p.queueSerialInput=A.x(s.gcp()) +p.tick=A.x(s.gcu()) +p.getPinState=A.x(s.gbh()) +p.getPinDuty=A.x(s.gbf()) +p.isPinOutput=A.x(s.gcd()) +p.setDigitalPin=A.er(s.gbx()) +p.setAnalogVoltage=A.er(s.gbr()) +p.getPortBValue=A.d9(s.gbj()) +p.getCycles=A.d9(s.gbb()) +p.getPin13State=A.d9(s.gbd()) +p.listenI2c=A.x(s.gcf()) +p.drainI2c=A.x(s.gc3()) +p.serveI2c=A.es(s.gbo()) +p.setI2cRegisters=A.es(s.gbz()) +q=A.bf(r.Object) +t=A.bf(q.create.apply(q,[null])) +t.get=A.d9(new A.di(s)) +t.set=A.x(new A.dj(s)) +r=A.bf(r.Object) +r.defineProperty.apply(r,[p,"_serialPrint",t]) +return p}, +$S:22} +A.bL.prototype={ +bD(a){return this.a=a}, +bu(a){$.dA=new A.cZ(a)}, +bw(a){return $.dV=a==null?null:A.dK(a)}, +bF(a){var t,s=A.F([],u.t) +if(a==null)t=null +else t=u.q.b(a)?a:new A.aH(a,A.a8(a).m("aH<1,i>")) +t=J.dt(t==null?B.ad:t) +while(t.n())s.push(A.dK(t.gt())) +return $.dW=s}, +bm(a){return a>=0&&a<14?$.dq()[a]:0}, +cj(a){return A.f3(a,this.gaH())}, +bT(a){var t=B.c.c5(a,"\r")?B.c.N(a,0,a.length-1):a,s=this.a +if(s!=null)s.call(null,t)}, +cq(a){$.dp().bX(0,new A.bp(a)) +return null}, +cv(a){return A.f6(a)}, +bi(a){return A.dz(a)}, +bg(a){return A.f1(a)}, +ce(a){return A.f2(a)}, +by(a,b){return A.f5(a,b)}, +bs(a,b){return A.f4(a,b)}, +bk(){if($.N){var t=$.p.k().a +t===$&&A.a() +t=t[37]}else t=0 +return t}, +bc(){return $.N?$.p.k().ax:0}, +be(){return $.N&&$.aA.k().a4(5)===B.e}, +cg(a){return $.bh().a.E(0,a)}, +c4(a){var t,s,r,q,p,o,n,m=$.bh(),l=m.b.b3(0,a) +m.c.b3(0,a) +t=l==null?B.ac:l +for(m=J.a0(t),s=m.gD(t),r=0;s.n();)r+=J.S(s.gt())+1 +q=new Int32Array(r) +for(m=m.gD(t),p=0;m.n();){s=m.gt() +o=p+1 +n=J.ad(s) +q[p]=n.gi(s) +B.ak.bq(q,o,s) +p=o+n.gi(s)}return q}, +bp(a,b,c){return $.bh().bn(a,c,b)}, +bA(a,b,c){return $.bh().bB(a,b,c)}} +A.cZ.prototype={ +$1(a){var t=a==null?null:a +return this.a.call(null,t)}, +$S:6};(function aliases(){var t=J.U.prototype +t.bH=t.j +t=A.h.prototype +t.aA=t.q})();(function installTearOffs(){var t=hunkHelpers._instance_0u,s=hunkHelpers._instance_1u,r=hunkHelpers._static_2,q=hunkHelpers._instance_2u,p=hunkHelpers.installInstanceTearOff +var o +t(o=A.bl.prototype,"ga2","c2",1) +s(o,"gc6","c7",17) +r(A,"hG","f_",3) +r(A,"hE","eY",3) +r(A,"hF","eZ",3) +s(o=A.bL.prototype,"gbC","bD",5) +s(o,"gbt","bu",5) +s(o,"gbv","bw",6) +s(o,"gbE","bF",23) +s(o,"gbl","bm",7) +s(o,"gci","cj",2) +s(o,"gaH","bT",2) +s(o,"gcp","cq",2) +s(o,"gcu","cv",8) +s(o,"gbh","bi",9) +s(o,"gbf","bg",7) +s(o,"gcd","ce",9) +q(o,"gbx","by",24) +q(o,"gbr","bs",25) +t(o,"gbj","bk",10) +t(o,"gbb","bc",10) +t(o,"gbd","be",26) +s(o,"gcf","cg",8) +s(o,"gc3","c4",27) +p(o,"gbo",0,3,null,["$3"],["bp"],28,0,0) +p(o,"gbz",0,3,null,["$3"],["bA"],29,0,0)})();(function inheritance(){var t=hunkHelpers.mixin,s=hunkHelpers.inherit,r=hunkHelpers.inheritMany +s(A.m,null) +r(A.m,[A.dD,J.bt,A.b_,J.bm,A.l,A.bo,A.cE,A.h,A.am,A.bH,A.aJ,A.bJ,A.bq,A.a4,A.aS,A.cO,A.d_,A.d7,A.G,A.bM,A.bP,A.aq,A.d3,A.as,A.bO,A.d0,A.cG,A.aY,A.bI,A.u,A.bk,A.cz,A.M,A.bT,A.bU,A.a2,A.aC,A.aD,A.aB,A.aE,A.o,A.bl,A.cU,A.cQ,A.c8,A.cX,A.cn,A.d4,A.cH,A.cI,A.br,A.bL]) +r(J.bt,[J.bv,J.aL,J.aN,J.ak,J.al,J.aM,J.aj]) +r(J.aN,[J.U,J.k,A.an,A.aU]) +r(J.U,[J.bD,J.b2,J.v]) +s(J.bu,A.b_) +s(J.cM,J.k) +r(J.aM,[J.aK,J.bw]) +r(A.l,[A.Y,A.e,A.P]) +r(A.Y,[A.a3,A.be]) +s(A.b4,A.a3) +s(A.b3,A.be) +s(A.aH,A.b3) +r(A.cE,[A.aP,A.cR,A.d1,A.cy,A.cW,A.ah,A.bK,A.cY,A.b0,A.cD]) +s(A.ar,A.h) +s(A.bp,A.ar) +s(A.z,A.e) +r(A.z,[A.b1,A.aT,A.aR]) +s(A.ai,A.P) +s(A.T,A.bq) +r(A.a4,[A.cB,A.cC,A.cV,A.dd,A.df,A.bX,A.bY,A.c2,A.c3,A.c4,A.c5,A.c6,A.c7,A.c1,A.cb,A.cc,A.cd,A.ce,A.cf,A.cm,A.cl,A.cg,A.ch,A.ci,A.cj,A.ck,A.ca,A.cp,A.cq,A.cr,A.cs,A.ct,A.cu,A.cv,A.c0,A.c_,A.dj,A.cZ]) +r(A.cV,[A.cS,A.aG]) +s(A.a5,A.aS) +s(A.aO,A.a5) +r(A.cC,[A.de,A.cP]) +r(A.aU,[A.bx,A.ao]) +r(A.ao,[A.b6,A.b8]) +s(A.b7,A.b6) +s(A.W,A.b7) +s(A.b9,A.b8) +s(A.A,A.b9) +r(A.W,[A.by,A.bz]) +r(A.A,[A.bA,A.H,A.bB,A.aV,A.bC,A.aW,A.aX]) +s(A.bQ,A.d1) +s(A.ba,A.aq) +s(A.b5,A.ba) +r(A.ah,[A.bF,A.cK]) +r(A.cB,[A.cA,A.bW,A.bV,A.c9,A.co,A.cw,A.cJ,A.di,A.dk]) +r(A.d0,[A.a1,A.az,A.ap,A.a6]) +r(A.M,[A.ay,A.ag,A.D]) +t(A.ar,A.bJ) +t(A.be,A.h) +t(A.b6,A.h) +t(A.b7,A.aJ) +t(A.b8,A.h) +t(A.b9,A.aJ)})() +var v={G:typeof self!="undefined"?self:globalThis,typeUniverse:{eC:new Map(),tR:{},eT:{},tPV:{},sEA:[]},mangledGlobalNames:{c:"int",i:"double",eC:"num",I:"String",w:"bool",aY:"Null",f:"List",m:"Object",i7:"Map",n:"JSObject"},mangledNames:{},types:["w(c,c,c,c)","~()","~(I)","~(c,c)","c(c)","~(v)","~(i?)","i(c)","~(c)","w(c)","c()","@(@)","@(@,I)","@(I)","~(m?,m?)","~(M)","u?(a2?)","~(w)","~(i)","f>()","v?()","~(v?)","n()","~(k?)","~(c,w)","~(c,i)","w()","H(c)","~(c,c,c)","~(c,c,H)"],interceptorsByTag:null,leafTags:null,arrayRti:Symbol("$ti")} +A.fU(v.typeUniverse,JSON.parse('{"v":"U","bD":"U","b2":"U","i8":"an","k":{"f":["1"],"e":["1"],"n":[]},"bv":{"w":[],"j":[]},"aL":{"j":[]},"aN":{"n":[]},"U":{"n":[]},"bu":{"b_":[]},"cM":{"k":["1"],"f":["1"],"e":["1"],"n":[]},"aM":{"i":[]},"aK":{"i":[],"c":[],"j":[]},"bw":{"i":[],"j":[]},"aj":{"I":[],"j":[]},"Y":{"l":["2"]},"a3":{"Y":["1","2"],"l":["2"],"l.E":"2"},"b4":{"a3":["1","2"],"Y":["1","2"],"e":["2"],"l":["2"],"l.E":"2"},"b3":{"h":["2"],"f":["2"],"Y":["1","2"],"e":["2"],"l":["2"]},"aH":{"b3":["1","2"],"h":["2"],"f":["2"],"Y":["1","2"],"e":["2"],"l":["2"],"h.E":"2","l.E":"2"},"bp":{"h":["c"],"f":["c"],"e":["c"],"h.E":"c"},"e":{"l":["1"]},"z":{"e":["1"],"l":["1"]},"b1":{"z":["1"],"e":["1"],"l":["1"],"z.E":"1","l.E":"1"},"aT":{"z":["2"],"e":["2"],"l":["2"],"z.E":"2","l.E":"2"},"P":{"l":["1"],"l.E":"1"},"ai":{"P":["1"],"e":["1"],"l":["1"],"l.E":"1"},"ar":{"h":["1"],"f":["1"],"e":["1"]},"a5":{"aS":["1","2"]},"aO":{"a5":["1","2"],"aS":["1","2"]},"H":{"A":[],"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[],"j":[],"h.E":"c"},"an":{"n":[],"j":[]},"aU":{"n":[]},"bx":{"n":[],"j":[]},"ao":{"y":["1"],"n":[]},"W":{"h":["i"],"f":["i"],"y":["i"],"e":["i"],"n":[]},"A":{"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[]},"by":{"W":[],"h":["i"],"f":["i"],"y":["i"],"e":["i"],"n":[],"j":[],"h.E":"i"},"bz":{"W":[],"h":["i"],"f":["i"],"y":["i"],"e":["i"],"n":[],"j":[],"h.E":"i"},"bA":{"A":[],"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[],"j":[],"h.E":"c"},"bB":{"A":[],"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[],"j":[],"h.E":"c"},"aV":{"A":[],"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[],"j":[],"h.E":"c"},"bC":{"A":[],"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[],"j":[],"h.E":"c"},"aW":{"A":[],"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[],"j":[],"h.E":"c"},"aX":{"A":[],"h":["c"],"f":["c"],"y":["c"],"e":["c"],"n":[],"j":[],"h.E":"c"},"b5":{"aq":["1"],"e":["1"]},"h":{"f":["1"],"e":["1"]},"aR":{"z":["1"],"e":["1"],"l":["1"],"z.E":"1","l.E":"1"},"aq":{"e":["1"]},"ba":{"aq":["1"],"e":["1"]},"f":{"e":["1"]},"ay":{"M":[]},"ag":{"M":[]},"D":{"M":[]},"fl":{"f":["c"],"e":["c"]},"fF":{"f":["c"],"e":["c"]},"fE":{"f":["c"],"e":["c"]},"fj":{"f":["c"],"e":["c"]},"fC":{"f":["c"],"e":["c"]},"fk":{"f":["c"],"e":["c"]},"fD":{"f":["c"],"e":["c"]},"fh":{"f":["i"],"e":["i"]},"fi":{"f":["i"],"e":["i"]}}')) +A.fT(v.typeUniverse,JSON.parse('{"bH":1,"aJ":1,"bJ":1,"ar":1,"be":2,"bq":2,"ao":1,"ba":1}')) +var u=(function rtii(){var t=A.a_ +return{F:t("aB"),X:t("e<@>"),Z:t("i6"),C:t("T"),l:t("k"),p:t("k>"),s:t("k"),D:t("k"),b:t("k<@>"),t:t("k"),U:t("k"),f:t("k<~(c,c)>"),T:t("aL"),m:t("n"),g:t("v"),G:t("y<@>"),q:t("f"),j:t("f<@>"),Q:t("W"),E:t("A"),P:t("aY"),K:t("m"),L:t("i9"),N:t("I"),R:t("j"),o:t("b2"),y:t("w"),J:t("w(c,c,c,c)"),i:t("i"),S:t("c"),M:t("c(c)"),W:t("aB?"),r:t("u?"),V:t("e2?"),z:t("n?"),O:t("m?"),v:t("I?"),u:t("w?"),w:t("i?"),I:t("c?"),n:t("eC?"),c:t("~(w)?"),H:t("eC")}})();(function constants(){var t=hunkHelpers.makeConstList +B.a7=J.bt.prototype +B.b=J.k.prototype +B.a=J.aK.prototype +B.p=J.aM.prototype +B.c=J.aj.prototype +B.a8=J.v.prototype +B.a9=J.aN.prototype +B.ak=A.H.prototype +B.t=A.aV.prototype +B.u=A.aX.prototype +B.v=J.bD.prototype +B.k=J.b2.prototype +B.aO=new A.az(2,"Constant") +B.l=new A.ag(0) +B.m=new A.a1(4,"Reserved") +B.Q=new A.aC(1,108,8) +B.q=t([],u.U) +B.S=new A.aD(38,39,40,B.Q,B.q) +B.R=new A.aC(2,109,10) +B.N=new A.a2(0,0,2) +B.O=new A.a2(2,1,4) +B.ag=t([null,null,B.N,B.O],u.U) +B.T=new A.aD(41,42,43,B.R,B.ag) +B.P=new A.aC(0,107,6) +B.U=new A.aD(35,36,37,B.P,B.q) +B.aj=new A.T([0,0,1,1,2,8,3,32,4,64,5,128,6,256,7,1024],u.C) +B.V=new A.aE(8,B.aj,14,16,18,55,179,180,0,178,176,177,0,112,37,3,43,3,0,0) +B.r=new A.T([0,0,1,1,2,8,3,64,4,256,5,1024,6,0,7,0],u.C) +B.W=new A.aE(8,B.r,28,30,32,53,71,72,0,70,68,69,0,110,43,6,43,5,43,4) +B.X=new A.aE(16,B.r,22,24,26,54,136,138,134,132,128,129,130,111,37,1,37,2,43,5) +B.aN=new A.az(0,"SingleEnded") +B.C=new A.D(0) +B.D=new A.D(1) +B.E=new A.D(2) +B.F=new A.D(3) +B.G=new A.D(4) +B.H=new A.D(5) +B.I=new A.D(6) +B.J=new A.D(7) +B.aP=new A.az(3,"Temperature") +B.Z=new A.ay() +B.B=new A.ag(1.1) +B.ah=new A.T([0,B.C,1,B.D,2,B.E,3,B.F,4,B.G,5,B.H,6,B.I,7,B.J,8,B.Z,14,B.B,15,B.l],A.a_("T")) +B.L=new A.a1(1,"AREF") +B.K=new A.a1(0,"AVCC") +B.M=new A.a1(2,"Internal1V1") +B.aa=t([B.L,B.K,B.m,B.M],A.a_("k")) +B.Y=new A.bT() +B.n=function getTagFallback(o) { + var s = Object.prototype.toString.call(o); + return s.substring(8, s.length - 1); } - -function pinState(pin) { - if (pin >= 0 && pin <= 7) return portD.pinState(pin) === PinState.High; - if (pin >= 8 && pin <= 13) return portB.pinState(pin - 8) === PinState.High; - if (pin >= 14 && pin <= 19) return portC.pinState(pin - 14) === PinState.High; - return false; -} - -function handlePortChange(pinOffset, value, oldValue) { - const changed = value ^ oldValue; - if (changed === 0) return; - - if (buzzerPin !== null && buzzerPin >= pinOffset && buzzerPin < pinOffset + 8 && - (changed & (1 << (buzzerPin - pinOffset))) !== 0) { - fd.onToggle(cpu.cycles); +B.a_=function() { + var toStringFunction = Object.prototype.toString; + function getTag(o) { + var s = toStringFunction.call(o); + return s.substring(8, s.length - 1); } - - for (const sPin of servoPins) { - if (sPin < pinOffset || sPin >= pinOffset + 8) continue; - const bit = 1 << (sPin - pinOffset); - if ((changed & bit) === 0) continue; - if ((value & bit) !== 0) { - servoRise[sPin] = cpu.cycles; - } else if (servoRise[sPin] >= 0) { - const us = (cpu.cycles - servoRise[sPin]) / 16.0; // 16 MHz clock - // Servo control pulses are ~0.4-3 ms; ignore anything else on the pin. - if (us >= 400 && us <= 3000) servoPulseUs[sPin] = us; + function getUnknownTag(object, tag) { + if (/^HTML[A-Z].*Element$/.test(tag)) { + var name = toStringFunction.call(object); + if (name == "[object Object]") return null; + return "HTMLElement"; } } + function getUnknownTagGenericBrowser(object, tag) { + if (object instanceof HTMLElement) return "HTMLElement"; + return getUnknownTag(object, tag); + } + function prototypeForTag(tag) { + if (typeof window == "undefined") return null; + if (typeof window[tag] == "undefined") return null; + var constructor = window[tag]; + if (typeof constructor != "function") return null; + return constructor.prototype; + } + function discriminator(tag) { return null; } + var isBrowser = typeof HTMLElement == "function"; + return { + getTag: getTag, + getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag, + prototypeForTag: prototypeForTag, + discriminator: discriminator }; } - -// --- I2C bus recording and register serving (port of I2cRecorder) --------- -// -// Bus traffic is queued rather than sampled: the frame loop runs the CPU for a -// whole frame before any part looks, and a screen refresh is a thousand bytes. -// See `packages/pinbench_sim/lib/core/i2c_recorder.dart` for the full reasoning — -// this is the same model, kept in JS so the per-byte callbacks never cross into -// Dart. -const MAX_I2C_BYTES_PER_ADDRESS = 64 * 1024; -const MAX_I2C_ADDRESSES = 16; -const i2c = { - listening: new Set(), - pending: new Map(), // address -> array of transactions (arrays of bytes) - buffered: new Map(), // address -> queued byte count - current: null, - currentAddress: null, - // Devices that answer reads: address -> { registers: Uint8Array, - // pointerBytes, pointer }. Kept here, beside the CPU, so a read is answered - // mid-frame without crossing into Dart. - devices: new Map(), - device: null, // the device the open transaction talks to - writing: false, - pointerBytesLeft: 0, - - reset() { - this.listening.clear(); - this.pending.clear(); - this.buffered.clear(); - this.devices.clear(); - this.current = null; - this.currentAddress = null; - this.device = null; - this.pointerBytesLeft = 0; - }, - - // Same shape again keeps registers and pointer; a new shape starts over. - serve(address, size, pointerBytes) { - this.listening.add(address); - const existing = this.devices.get(address); - if (existing && existing.registers.length === size && existing.pointerBytes === pointerBytes) return; - this.devices.set(address, { registers: new Uint8Array(size), pointerBytes, pointer: 0 }); - }, - - setRegisters(address, offset, bytes) { - const device = this.devices.get(address); - if (!device) return; - const registers = device.registers; - for (let i = 0; i < bytes.length; i++) { - registers[(offset + i) % registers.length] = bytes[i] & 0xff; - } - }, - - begin(address, write) { - this.end(); - const device = this.devices.get(address) || null; - this.device = device; - this.writing = write; - if (device) { - if (write) { - this.pointerBytesLeft = device.pointerBytes; - if (device.pointerBytes > 0) device.pointer = 0; - } else if (device.pointerBytes === 0) { - device.pointer = 0; +B.a4=function(getTagFallback) { + return function(hooks) { + if (typeof navigator != "object") return hooks; + var userAgent = navigator.userAgent; + if (typeof userAgent != "string") return hooks; + if (userAgent.indexOf("DumpRenderTree") >= 0) return hooks; + if (userAgent.indexOf("Chrome") >= 0) { + function confirm(p) { + return typeof window == "object" && window[p] && window[p].name == p; } + if (confirm("Window") && confirm("HTMLElement")) return hooks; } - if (!write) return; - if (!this.pending.has(address) && this.pending.size >= MAX_I2C_ADDRESSES) return; - this.currentAddress = address; - this.current = []; - }, - - write(value) { - const byte = value & 0xff; - if (this.current) this.current.push(byte); - const device = this.device; - if (!device || !this.writing) return; - if (this.pointerBytesLeft > 0) { - device.pointer = ((device.pointer << 8) | byte) % device.registers.length; - this.pointerBytesLeft--; - } else if (device.pointerBytes > 0) { - // A pointer-less device's writes drive outputs, not readable registers. - device.registers[device.pointer] = byte; - device.pointer = (device.pointer + 1) % device.registers.length; - } - }, - - // The register under the pointer, which then advances; 0xff (a pulled-up, - // undriven bus) when nothing serves the address. - read() { - const device = this.device; - if (!device || this.writing) return 0xff; - const value = device.registers[device.pointer]; - device.pointer = (device.pointer + 1) % device.registers.length; - return value; - }, - - end() { - const address = this.currentAddress; - const bytes = this.current; - this.currentAddress = null; - this.current = null; - this.device = null; - this.pointerBytesLeft = 0; - if (address === null || !bytes || !bytes.length) return; - - if (!this.pending.has(address)) this.pending.set(address, []); - const queue = this.pending.get(address); - queue.push(bytes); - let buffered = (this.buffered.get(address) || 0) + bytes.length; - while (buffered > MAX_I2C_BYTES_PER_ADDRESS && queue.length > 1) { - buffered -= queue.shift().length; - } - this.buffered.set(address, buffered); - }, - - // Flattened as [length, ...bytes] per transaction, so the whole frame's - // traffic crosses into Dart as one typed array rather than a thousand - // boxed numbers. - drain(address) { - const queue = this.pending.get(address); - this.pending.delete(address); - this.buffered.delete(address); - if (!queue || !queue.length) return new Int32Array(0); - let total = 0; - for (const transaction of queue) total += transaction.length + 1; - const flat = new Int32Array(total); - let at = 0; - for (const transaction of queue) { - flat[at++] = transaction.length; - flat.set(transaction, at); - at += transaction.length; - } - return flat; - }, -}; - -function attachTwi() { - twi = new AVRTWI(cpu, twiConfig, CLOCK); - twi.eventHandler = { - start() { twi.completeStart(); }, - stop() { i2c.end(); twi.completeStop(); }, - connectToSlave(addr, write) { - i2c.begin(addr, write); - // The ACK is the only answer that reaches the sketch, so a device that - // is not on the canvas must not answer for one that is. - twi.completeConnect(i2c.listening.has(addr)); - }, - writeByte(value) { i2c.write(value); twi.completeWrite(true); }, - readByte() { twi.completeRead(i2c.read()); }, + hooks.getTag = getTagFallback; }; } - -window.AVR8 = { - listenI2c(address) { i2c.listening.add(address); }, - drainI2c(address) { return i2c.drain(address); }, - serveI2c(address, size, pointerBytes) { i2c.serve(address, size, pointerBytes); }, - setI2cRegisters(address, offset, bytes) { i2c.setRegisters(address, offset, bytes); }, - - setSerialPrint(cb) { onSerialPrint = cb; }, - setBuzzerFrequencyCallback(cb) { onBuzzerFrequency = cb; }, - setBuzzerPin(pin) { buzzerPin = (pin === undefined || pin === null) ? null : pin; }, - setServoPins(pins) { servoPins = pins || []; }, - getServoPulseUs(pin) { - return (pin >= 0 && pin < servoPulseUs.length) ? servoPulseUs[pin] : 0; - }, - - loadHex(hexString) { - rxQueue.length = 0; - txLine = ''; - servoRise.fill(-1); - servoPulseUs.fill(0); - const program = parseHex(hexString); - cpu = new CPU(program); - timer0 = new AVRTimer(cpu, timer0Config); - timer1 = new AVRTimer(cpu, timer1Config); - timer2 = new AVRTimer(cpu, timer2Config); - usart = new AVRUSART(cpu, usart0Config, CLOCK); - usart.onByteTransmit = (value) => { - const ch = String.fromCharCode(value); - if (ch === '\n') { - if (onSerialPrint) onSerialPrint(txLine); - txLine = ''; - } else if (ch !== '\r') { - txLine += ch; - } - }; - portB = new AVRIOPort(cpu, portBConfig); - portB.addListener((v, o) => handlePortChange(8, v, o)); - portC = new AVRIOPort(cpu, portCConfig); - portC.addListener((v, o) => handlePortChange(14, v, o)); - portD = new AVRIOPort(cpu, portDConfig); - portD.addListener((v, o) => handlePortChange(0, v, o)); - adc = new AVRADC(cpu, adcConfig); - // The bus is rebuilt with the program: claims belong to the parts of the - // run that just ended, and its traffic to the sketch that just stopped. - i2c.reset(); - attachTwi(); - fd.reset(); - }, - - queueSerialInput(text) { - for (let i = 0; i < text.length; i++) rxQueue.push(text.charCodeAt(i) & 0xff); - }, - - tick(cycles) { - if (!cpu) return; - dutyTotal = 0; - dutyHigh.fill(0); - const limit = cpu.cycles + cycles; - let i = 0; - while (cpu.cycles < limit) { - avrInstruction(cpu); - cpu.tick(); - - if ((i & DUTY_SAMPLE_MASK) === 0) { - dutyTotal++; - for (let p = 0; p < DUTY_PINS; p++) if (pinState(p)) dutyHigh[p]++; - } - - // Feed queued serial input as the receiver frees up. - if (rxQueue.length && usart.writeByte && usart.writeByte(rxQueue[0])) { - rxQueue.shift(); - } - - if (fd.checkTimeout(cpu.cycles)) { - if (onBuzzerFrequency) onBuzzerFrequency(null); - } - i++; +B.a0=function(hooks) { + if (typeof dartExperimentalFixupGetTag != "function") return hooks; + hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag); +} +B.a3=function(hooks) { + if (typeof navigator != "object") return hooks; + var userAgent = navigator.userAgent; + if (typeof userAgent != "string") return hooks; + if (userAgent.indexOf("Firefox") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "GeoGeolocation": "Geolocation", + "Location": "!Location", + "WorkerMessageEvent": "MessageEvent", + "XMLDocument": "!Document"}; + function getTagFirefox(o) { + var tag = getTag(o); + return quickMap[tag] || tag; + } + hooks.getTag = getTagFirefox; +} +B.a2=function(hooks) { + if (typeof navigator != "object") return hooks; + var userAgent = navigator.userAgent; + if (typeof userAgent != "string") return hooks; + if (userAgent.indexOf("Trident/") == -1) return hooks; + var getTag = hooks.getTag; + var quickMap = { + "BeforeUnloadEvent": "Event", + "DataTransfer": "Clipboard", + "HTMLDDElement": "HTMLElement", + "HTMLDTElement": "HTMLElement", + "HTMLPhraseElement": "HTMLElement", + "Position": "Geoposition" + }; + function getTagIE(o) { + var tag = getTag(o); + var newTag = quickMap[tag]; + if (newTag) return newTag; + if (tag == "Object") { + if (window.DataView && (o instanceof window.DataView)) return "DataView"; } - }, - - getPinState(pin) { return cpu ? pinState(pin) : false; }, - - getPinDuty(pin) { - if (!cpu) return 0; - if (pin < 0 || pin >= DUTY_PINS) return pinState(pin) ? 1.0 : 0.0; - if (dutyTotal === 0) return pinState(pin) ? 1.0 : 0.0; - return dutyHigh[pin] / dutyTotal; - }, - - isPinOutput(pin) { - if (!cpu) return false; - if (pin >= 0 && pin <= 7) return (cpu.data[portDConfig.DDR] & (1 << pin)) !== 0; - if (pin >= 8 && pin <= 13) return (cpu.data[portBConfig.DDR] & (1 << (pin - 8))) !== 0; - if (pin >= 14 && pin <= 19) return (cpu.data[portCConfig.DDR] & (1 << (pin - 14))) !== 0; - return false; - }, - - setDigitalPin(pin, isHigh) { - if (!cpu) return; - if (pin >= 0 && pin <= 7) portD.setPin(pin, isHigh); - else if (pin >= 8 && pin <= 13) portB.setPin(pin - 8, isHigh); - else if (pin >= 14 && pin <= 19) portC.setPin(pin - 14, isHigh); - }, - - setAnalogVoltage(channel, voltage) { - if (adc && channel >= 0 && channel < adc.channelValues.length) { - adc.channelValues[channel] = voltage; + return tag; + } + function prototypeForTagIE(tag) { + var constructor = window[tag]; + if (constructor == null) return null; + return constructor.prototype; + } + hooks.getTag = getTagIE; + hooks.prototypeForTag = prototypeForTagIE; +} +B.a1=function(hooks) { + var getTag = hooks.getTag; + var prototypeForTag = hooks.prototypeForTag; + function getTagFixed(o) { + var tag = getTag(o); + if (tag == "Document") { + if (!!o.xmlVersion) return "!Document"; + return "!HTMLDocument"; } - }, - - getPortBValue() { return cpu ? cpu.data[0x25] : 0; }, - getCycles() { return cpu ? cpu.cycles : 0; }, - getPin13State() { return portB ? portB.pinState(5) === PinState.High : false; }, -}; + return tag; + } + function prototypeForTagFixed(tag) { + if (tag == "Document") return null; + return prototypeForTag(tag); + } + hooks.getTag = getTagFixed; + hooks.prototypeForTag = prototypeForTagFixed; +} +B.o=function(hooks) { return hooks; } -console.log('AVR8 (avr8js) bridge loaded.'); +B.a5=new A.cU() +B.a6=new A.cX() +B.aD=new A.o(0,65535,0,0,0) +B.y=new A.o(1,255,1,2,0) +B.aC=new A.o(1,511,1,2,0) +B.aM=new A.o(1,1023,1,2,0) +B.x=new A.o(2,-1,0,0,0) +B.aE=new A.o(3,255,2,1,0) +B.aI=new A.o(3,511,2,1,0) +B.aF=new A.o(3,1023,2,1,0) +B.aH=new A.o(4,-2,2,2,0) +B.aL=new A.o(4,-1,2,2,1) +B.aB=new A.o(1,-2,1,2,0) +B.w=new A.o(1,-1,1,2,1) +B.aG=new A.o(2,-2,0,0,0) +B.aJ=new A.o(5,65535,0,0,0) +B.az=new A.o(3,-2,2,1,1) +B.A=new A.o(3,-1,2,1,1) +B.ab=t([B.aD,B.y,B.aC,B.aM,B.x,B.aE,B.aI,B.aF,B.aH,B.aL,B.aB,B.w,B.aG,B.aJ,B.az,B.A],u.D) +B.ac=t([],u.p) +B.ad=t([],A.a_("k")) +B.ae=t([],u.t) +B.aK=new A.o(0,255,0,0,0) +B.aA=new A.o(3,255,2,0,0) +B.z=new A.o(5,255,0,0,0) +B.af=t([B.aK,B.y,B.x,B.aA,B.z,B.w,B.z,B.A],u.D) +B.ai=new A.T([5,31,6,63,7,127,8,255,9,255],u.C) +B.d=new A.a6(0,"None") +B.f=new A.a6(1,"Enable") +B.h=new A.a6(2,"Set") +B.i=new A.a6(3,"Clear") +B.j=new A.a6(4,"Toggle") +B.al=new A.ap(0,"Low") +B.e=new A.ap(1,"High") +B.am=new A.ap(2,"Input") +B.an=new A.ap(3,"InputPullUp") +B.ao=A.L("i2") +B.ap=A.L("i3") +B.aq=A.L("fh") +B.ar=A.L("fi") +B.as=A.L("fj") +B.at=A.L("fk") +B.au=A.L("fl") +B.av=A.L("fC") +B.aw=A.L("fD") +B.ax=A.L("fE") +B.ay=A.L("fF")})();(function staticFields(){$.d2=null +$.aa=A.F([],A.a_("k")) +$.e8=null +$.dZ=null +$.dY=null +$.eA=null +$.ey=null +$.eF=null +$.da=null +$.dh=null +$.dQ=null +$.p=A.Q() +$.N=!1 +$.aA=A.Q() +$.bZ=A.Q() +$.bj=A.Q() +$.dv=A.Q() +$.eV=A.Q() +$.eW=A.Q() +$.eX=A.Q() +$.dx=A.Q() +$.dw=A.Q() +$.dA=null +$.dV=null +$.dW=B.ae +$.dU=0})();(function lazyInitializers(){var t=hunkHelpers.lazyFinal +t($,"i5","eI",()=>A.db("_$dart_dartClosure")) +t($,"i4","bi",()=>A.db("_$dart_dartClosure_dartJSInterop")) +t($,"ia","eJ",()=>A.F([new J.bu()],A.a_("k"))) +t($,"hZ","bh",()=>{var s=u.S +return new A.cI(A.dE(s),A.aQ(s,A.a_("f>")),A.aQ(s,s),A.aQ(s,A.a_("br")))}) +t($,"i1","dr",()=>A.V(14,-1,!1,u.S)) +t($,"i0","dq",()=>A.V(14,0,!1,u.i)) +t($,"hY","dn",()=>new A.cH()) +t($,"i_","dp",()=>new A.aR(A.V(A.fs(null),null,!1,u.I),A.a_("aR"))) +t($,"hX","dT",()=>A.V(14,0,!1,u.S))})();(function nativeSupport(){!function(){var t=function(a){var n={} +n[a]=1 +return Object.keys(hunkHelpers.convertToFastObject(n))[0]} +v.getIsolateTag=function(a){return t("___dart_"+a+v.isolateTag)} +var s="___dart_isolate_tags_" +var r=Object[s]||(Object[s]=Object.create(null)) +var q="_ZxYxX" +for(var p=0;;p++){var o=t(q+"_"+p+"_") +if(!(o in r)){r[o]=1 +v.isolateTag=o +break}}v.dispatchPropertyName=v.getIsolateTag("dispatch_record")}() +hunkHelpers.setOrUpdateInterceptorsByTag({ArrayBuffer:A.an,SharedArrayBuffer:A.an,ArrayBufferView:A.aU,DataView:A.bx,Float32Array:A.by,Float64Array:A.bz,Int16Array:A.bA,Int32Array:A.H,Int8Array:A.bB,Uint16Array:A.aV,Uint32Array:A.bC,Uint8ClampedArray:A.aW,CanvasPixelArray:A.aW,Uint8Array:A.aX}) +hunkHelpers.setOrUpdateLeafTags({ArrayBuffer:true,SharedArrayBuffer:true,ArrayBufferView:false,DataView:true,Float32Array:true,Float64Array:true,Int16Array:true,Int32Array:true,Int8Array:true,Uint16Array:true,Uint32Array:true,Uint8ClampedArray:true,CanvasPixelArray:true,Uint8Array:false}) +A.ao.$nativeSuperclassTag="ArrayBufferView" +A.b6.$nativeSuperclassTag="ArrayBufferView" +A.b7.$nativeSuperclassTag="ArrayBufferView" +A.W.$nativeSuperclassTag="ArrayBufferView" +A.b8.$nativeSuperclassTag="ArrayBufferView" +A.b9.$nativeSuperclassTag="ArrayBufferView" +A.A.$nativeSuperclassTag="ArrayBufferView"})() +Function.prototype.$0=function(){return this()} +Function.prototype.$1=function(a){return this(a)} +Function.prototype.$2=function(a,b){return this(a,b)} +Function.prototype.$3=function(a,b,c){return this(a,b,c)} +Function.prototype.$4=function(a,b,c,d){return this(a,b,c,d)} +convertAllToFastObject(w) +convertToFastObject($);(function(a){if(typeof document==="undefined"){a(null) +return}if(typeof document.currentScript!="undefined"){a(document.currentScript) +return}var t=document.scripts +function onLoad(b){for(var r=0;r
Loading PinBench…
- + +