diff --git a/CLAUDE.MD b/CLAUDE.MD new file mode 100644 index 0000000000..c6f1069a94 --- /dev/null +++ b/CLAUDE.MD @@ -0,0 +1,280 @@ +AM32 ESC Flasher Module - Implementation Plan + + Context + + PX4 has no upstream support for AM32 ESC firmware updates. Matt's branch (jake/matt_firefly_am32_flasher) proves the concept works but has architectural issues: firmware baked into the build as a C + array, ~100-state machine with poor error recovery, tight coupling to DShot via a 4-message uORB handshake, no GCS progress reporting, and ~826 lines of flashing logic injected into the DShot driver. + + Goal: Clean, upstream-quality esc_flasher module that reads firmware from SD card, is triggered via MAVLink command from QGC, reports per-ESC progress via a custom MAVLink message, and requires a + reboot to resume normal DShot operation. Structured to allow future USB passthrough (4-way interface) extension. + + Architecture Overview + + QGC PX4 + │ │ + ├─COMMAND_LONG──────────────►│ vehicle_command uORB + │ MAV_CMD_ESC_FW_UPDATE │ │ + │ │ esc_flasher module + │ │ (dormant at boot, wakes on cmd) + │ │ │ + │ │ 1. Validate (disarmed, files exist) + │ │ 2. ACK command + │ │ 3. Stop DShot module + │ │ 4. Take over motor GPIOs + │ │ 5. For each ESC: + │ │ - Bootloader handshake + │ │ - Read device info → pick FW file + │◄─ESC_FW_UPDATE_STATUS──────│ - Flash 256-byte chunks + │ (per-ESC progress) │ - Send RUN_APP + │ │ 6. Publish REBOOT_REQUIRED + │◄─COMMAND_ACK (ACCEPTED)────│ 7. Go idle (reboot needed) + + + New Files + + 1. src/modules/esc_flasher/EscFlasher.hpp + + Module class inheriting ModuleBase + ModuleParams + ScheduledWorkItem. + + Key members: + - _vehicle_command_sub — polls for MAV_CMD trigger + - _esc_fw_update_status_pub — publishes per-ESC progress + - _command_ack_pub — ACKs the initial command + - State enum: Idle, StoppingDShot, InitGPIOs, ConnectingESC, ReadingInfo, Flashing, RunApp, Complete, Failed + - Per-ESC state: _current_esc_index, _esc_count, _bytes_written, _firmware_size + - GPIO pin array: uint32_t _gpio_pins[MAX_MOTORS] + - File handle for current firmware + + 2. src/modules/esc_flasher/EscFlasher.cpp + + Main module implementation (~600-800 lines, much simpler than Matt's ~1,900). + + Module lifecycle: + - Auto-started at boot on supported boards (Kconfig opt-in) + - Runs at 1Hz when idle (vehicle_command poll only — negligible overhead) + - Transitions to 100Hz active polling during flashing + - Returns to idle after completion (reboot required to restore DShot) + + DShot shutdown: + - Calls system("dshot stop") via NuttX shell — DShot destructor calls up_dshot_arm(false) + - Waits up to 2s for DShot to exit, then proceeds + - Simple, no coupling — DShot module has no knowledge of the flasher + + GPIO takeover: + - After DShot stops, configures motor pins as GPIO outputs via px4_arch_configgpio(io_timer_channel_get_gpio_output(ch)) + - Drives all pins HIGH for 600ms to force ESCs into bootloader mode on reboot + - ESC power cycle not required — AM32 bootloader detects signal-high at startup + + Flashing loop (per ESC): + 1. Select ESC (drive target pin, keep others HIGH) + 2. Send bootloader handshake → get 9-byte device info + 3. Read firmware tag (CMD_READ_FLASH at tag address) → extract hardware target string + 4. Open /fs/microsd/am32/.bin (e.g., AM32_G071_48K.bin) + 5. For each 256-byte chunk: + - CMD_SET_ADDRESS + - CMD_SET_BUFFER + data + CRC + - CMD_PROG_FLASH → wait ACK + - Publish progress (chunk_index / total_chunks * 100) + 6. Write firmware tag section + 7. CMD_RUN → ESC reboots to application + 8. Move to next ESC + + 3. src/modules/esc_flasher/am32_protocol.hpp / .cpp + + Extracted and cleaned AM32 bootloader protocol implementation. + + Reused from Matt's code (protocol is correct): + - crc16_ccitt() — CRC-16 with polynomial 0xA001 + - bitbang_send_packet() — bit-banged UART TX/RX at 19200 baud + - 52µs bit time, 26µs half-bit for sampling + - px4_enter_critical_section() for timing accuracy + - hrt_absolute_time() busy-wait loops + - Bootloader handshake packet: {0x00 * 8, 0x0D, 'B','L','H','e','l','i', CRC} + - Command encoding: SET_ADDRESS(0xFF), SET_BUFFER(0xFE), PROG_FLASH(0x01), RUN(0x00), READ_FLASH(0x03) + + Cleaned up vs Matt's code: + - Function-based API instead of inline state machine code + - Proper error return codes instead of magic numbers + - Configurable timeouts instead of hardcoded values + - Separated from module state machine logic + + Public API: + namespace am32 { + int handshake(uint32_t gpio, uint8_t device_info[9]); + int set_address(uint32_t gpio, uint16_t address); + int write_chunk(uint32_t gpio, const uint8_t *data, uint16_t len); + int read_flash(uint32_t gpio, uint16_t address, uint8_t *buf, uint16_t len); + int run_app(uint32_t gpio); + uint16_t crc16(const uint8_t *buf, uint16_t len); + } + + 4. src/modules/esc_flasher/CMakeLists.txt + + px4_add_module( + MODULE modules__esc_flasher + MAIN esc_flasher + SRCS + EscFlasher.cpp + am32_protocol.cpp + DEPENDS + px4_work_queue + ) + + 5. src/modules/esc_flasher/Kconfig + + menuconfig MODULES_ESC_FLASHER + bool "esc_flasher" + default n + ---help--- + AM32 ESC firmware update module + + + 6. msg/EscFirmwareUpdateStatus.msg + + uint64 timestamp + + uint8 esc_index # Current ESC being flashed (0-based) + uint8 esc_count # Total ESCs being flashed + uint8 progress_pct # 0-100 for current ESC + uint8 overall_progress_pct # 0-100 for entire operation + + uint8 status + uint8 STATUS_IDLE = 0 + uint8 STATUS_CONNECTING = 1 + uint8 STATUS_READING_INFO = 2 + uint8 STATUS_ERASING = 3 + uint8 STATUS_WRITING = 4 + uint8 STATUS_COMPLETE = 5 + uint8 STATUS_FAILED = 6 + uint8 STATUS_REBOOT_REQUIRED = 7 + + uint8 error + uint8 ERROR_NONE = 0 + uint8 ERROR_NOT_DISARMED = 1 + uint8 ERROR_NO_FIRMWARE_FILE = 2 + uint8 ERROR_COMMS_TIMEOUT = 3 + uint8 ERROR_CRC_MISMATCH = 4 + uint8 ERROR_VERIFICATION_FAILED = 5 + uint8 ERROR_UNSUPPORTED_HARDWARE = 6 + uint8 ERROR_DSHOT_STOP_FAILED = 7 + + uint32 current_firmware_version # Currently installed version + uint32 new_firmware_version # Version being written + + + Modified Files + + 7. msg/versioned/VehicleCommand.msg + + Add command constant in PX4-internal range (for development; upstream MAVLink later): + + uint32 VEHICLE_CMD_ESC_FIRMWARE_UPDATE = 100002 + + + - param1: ESC index (0-based, 255 = all ESCs) + - param2: ESC type (0 = AM32) + + 8. MAVLink message definition (development.xml in mavlink submodule) + + Add ESC_FIRMWARE_UPDATE_STATUS message definition. This is in the MAVLink submodule — we'll fork/PR it separately. For initial development, stream the uORB message as a generic STATUSTEXT fallback. + + 9. src/modules/mavlink/streams/ESC_FIRMWARE_UPDATE_STATUS.hpp + + New MAVLink stream class that subscribes to esc_firmware_update_status uORB and sends the MAVLink message. Follow the pattern of existing streams like ESC_STATUS.hpp. + + 10. src/modules/mavlink/mavlink_messages.cpp + + Register the new stream. + + 11. Board configs + + Enable MODULES_ESC_FLASHER on boards that use AM32 ESCs (e.g., ARK boards). Add esc_flasher start to the board's startup script (rc.board_defaults or similar). + + Key Design Decisions + + Why stop DShot entirely (not just pause)? + - DShot uses DMA + timer hardware that conflicts with GPIO bit-bang + - Clean shutdown via destructor guarantees all DMA released, timers freed + - Reboot to restore is simple and safe — avoids complex reinit edge cases + - ESCs need power cycle anyway after reflash + + Why system("dshot stop") instead of uORB handshake? + - Zero coupling — DShot module doesn't know flasher exists + - Works with any output driver (PWM, OneShot, future drivers) + - DShot's existing ModuleBase::stop() + destructor already handles cleanup + - Matt's 4-message uORB handshake added 826 lines to DShot for this + + Why auto-start + idle instead of on-demand spawn? + - Module is already loaded and subscribed to vehicle_command — instant response + - 1Hz idle poll is negligible overhead + - Avoids complexity of commander spawning external modules + - Kconfig opt-in means boards that don't need it pay zero cost + + Why separate am32_protocol from module? + - Clean separation of protocol logic from module orchestration + - Protocol code can be reused by future 4-way passthrough implementation + - Easier to test protocol functions in isolation + + Firmware File Convention + + Files in /fs/microsd/am32/: + - Named by AM32 hardware target: AM32_G071_48K.bin, AM32_F051_32K.bin, etc. + - The flasher connects to each ESC bootloader, reads the firmware tag area to determine hardware target + - Maps tag → filename, opens file, flashes + - If no matching file found, reports ERROR_NO_FIRMWARE_FILE for that ESC and moves on + + State Machine (simplified) + + Idle ──(MAV_CMD)──► StoppingDShot ──(dshot stopped)──► InitGPIOs + │ + (pins HIGH, 600ms) + │ + ┌──► ConnectESC + │ │ + │ (handshake OK) + │ │ + │ ReadInfo ──► LoadFirmware ──► Flash + │ │ + │ (all chunks done) + │ │ + │ RunApp ◄──────────────────────┘ + │ │ + │ (next ESC?) + │ │ + └──YES─┘ + │ NO + ▼ + Complete + (REBOOT_REQUIRED) + + + Error from any flashing state → Failed (with error code), skip to next ESC or abort. + + Implementation Order + + 1. am32_protocol.cpp/hpp — Extract and clean up Matt's protocol code (CRC, bitbang, commands) + 2. EscFirmwareUpdateStatus.msg — uORB message definition + 3. VehicleCommand.msg — Add command constant + 4. EscFlasher.hpp/cpp — Module skeleton with state machine + 5. CMakeLists.txt + Kconfig — Build integration + 6. MAVLink stream — ESC_FIRMWARE_UPDATE_STATUS.hpp + register in mavlink_messages.cpp + 7. Board config — Enable on a test board + 8. Test — SITL smoke test (state machine logic), then hardware test + + Verification + + 1. Build: make ark_fmu-v6x_default (or target board) — verify clean compile + 2. SITL smoke test: Module starts, responds to vehicle command, reports error (no GPIOs in SITL) + 3. Hardware test: + - Place AM32 firmware on SD card + - Send MAV_CMD_ESC_FIRMWARE_UPDATE via commander shell or MAVLink + - Verify DShot stops, ESCs enter bootloader, firmware flashes, progress reported + - Reboot, verify ESCs running new firmware + 4. QGC integration: Verify COMMAND_ACK and status messages appear in MAVLink inspector + + Future Extensions (not in this PR) + + - 4-way interface passthrough: Add USB serial passthrough mode using the same am32_protocol infrastructure. The flasher module would proxy between USB serial (4-way protocol) and GPIO bit-bang. + - Runtime DShot restart: Instead of requiring reboot, add dshot start after flashing. Needs careful timer/DMA reinit validation. + - ESC settings read/write: Extend am32_protocol with EEPROM read/write commands. + - Orchestrator module: If hot-switching between DShot and passthrough becomes needed, add a motor_output_mgr similar to CDC ACM. diff --git a/msg/CMakeLists.txt b/msg/CMakeLists.txt index 9465aaabec..0cdbc63683 100644 --- a/msg/CMakeLists.txt +++ b/msg/CMakeLists.txt @@ -68,10 +68,7 @@ set(msg_files DistanceSensorModeChangeRequest.msg DronecanNodeStatus.msg Ekf2Timestamps.msg - EscFlasherRequest.msg - EscFlasherRequestAck.msg - EscFlasherStatus.msg - EscFlasherVersions.msg + EscFirmwareUpdateStatus.msg EscReport.msg EscStatus.msg EstimatorAidSource1d.msg diff --git a/msg/EscFirmwareUpdateStatus.msg b/msg/EscFirmwareUpdateStatus.msg new file mode 100644 index 0000000000..f4a2d12d5d --- /dev/null +++ b/msg/EscFirmwareUpdateStatus.msg @@ -0,0 +1,29 @@ +uint64 timestamp # time since system start (microseconds) + +uint8 esc_index # Current ESC being flashed (0-based) +uint8 esc_count # Total ESCs being flashed +uint8 progress_pct # 0-100 for current ESC +uint8 overall_progress_pct # 0-100 for entire operation + +uint8 status +uint8 STATUS_IDLE = 0 +uint8 STATUS_CONNECTING = 1 +uint8 STATUS_READING_INFO = 2 +uint8 STATUS_ERASING = 3 +uint8 STATUS_WRITING = 4 +uint8 STATUS_COMPLETE = 5 +uint8 STATUS_FAILED = 6 +uint8 STATUS_REBOOT_REQUIRED = 7 + +uint8 error +uint8 ERROR_NONE = 0 +uint8 ERROR_NOT_DISARMED = 1 +uint8 ERROR_NO_FIRMWARE_FILE = 2 +uint8 ERROR_COMMS_TIMEOUT = 3 +uint8 ERROR_CRC_MISMATCH = 4 +uint8 ERROR_VERIFICATION_FAILED = 5 +uint8 ERROR_UNSUPPORTED_HARDWARE = 6 +uint8 ERROR_DSHOT_STOP_FAILED = 7 + +uint32 current_firmware_version # Currently installed version +uint32 new_firmware_version # Version being written diff --git a/msg/EscFlasherRequest.msg b/msg/EscFlasherRequest.msg deleted file mode 100644 index 5308685182..0000000000 --- a/msg/EscFlasherRequest.msg +++ /dev/null @@ -1,16 +0,0 @@ -uint64 timestamp # time since system start (microseconds) - -uint32 msg_id # Give a unique message identifier so we can compare when getting an ACK - -uint8 request # Request type -# use these items as enum values, they can never change -uint8 REQUEST_ESC_INFO = 0 # Request ESC_INFO from one or more ESCs -uint8 REQUEST_FLASHING = 1 # Request flashing for one or more ESCs -uint8 REQUEST_CANCEL = 2 # Cancel any pending request and return control to DShot -uint8 REQUEST_FLASHING_COMPLETE = 3 # Flashing complete, return control to DShot - -uint8 motor_flags # Flags for which motors are being requested -# motor_flags bit 0 : Set to 1 if ESC0 is being flashed -# motor_flags bit 1 : Set to 1 if ESC1 is being flashed -# motor_flags bit 2 : Set to 1 if ESC2 is being flashed -# motor_flags bit 3 : Set to 1 if ESC3 is being flashed diff --git a/msg/EscFlasherRequestAck.msg b/msg/EscFlasherRequestAck.msg deleted file mode 100644 index 9fb454fe1b..0000000000 --- a/msg/EscFlasherRequestAck.msg +++ /dev/null @@ -1,31 +0,0 @@ -# ESC Flasher Request Acknowledgement uORB message. -# Used for acknowledging the ESC Flasher command being received. -# Follows the MAVLink COMMAND_ACK message definition - -uint64 timestamp # time since system start (microseconds) - -uint32 msg_id # Message identifier that we are responding to - -uint8 request # Request type -# use these items as enum values, they can never change -uint8 REQUEST_ESC_INFO = 0 # Request ESC_INFO from one or more ESCs -uint8 REQUEST_FLASHING = 1 # Request flashing for one or more ESCs -uint8 REQUEST_CANCEL = 2 # Cancel any pending request and return control to DShot -uint8 REQUEST_FLASHING_COMPLETE = 3 # Flashing complete, return control to DShot - -uint8 result # Command acknowledge result -# Result cases. This follows the MAVLink MAV_RESULT enum definition -uint8 ESC_FLASHER_CMD_RESULT_ACCEPTED = 0 # Command ACCEPTED and EXECUTED | -uint8 ESC_FLASHER_CMD_RESULT_TEMPORARILY_REJECTED = 1 # Command TEMPORARY REJECTED/DENIED | -uint8 ESC_FLASHER_CMD_RESULT_DENIED = 2 # Command PERMANENTLY DENIED | -uint8 ESC_FLASHER_CMD_RESULT_UNSUPPORTED = 3 # Command UNKNOWN/UNSUPPORTED | -uint8 ESC_FLASHER_CMD_RESULT_FAILED = 4 # Command executed, but failed | -uint8 ESC_FLASHER_CMD_RESULT_IN_PROGRESS = 5 # Command being executed | -uint8 ESC_FLASHER_CMD_RESULT_CANCELLED = 6 # Command Canceled - -uint8[16] fw_major # Firmware versions major -uint8[16] fw_minor # Firmware versions minor -uint16 fw_flags # Bit flags for valid fw versions - -uint32[16] gpio_pins # GPIO pin definitions -uint16 gpio_flags # Bit flags for valid gpio pins diff --git a/msg/EscFlasherStatus.msg b/msg/EscFlasherStatus.msg deleted file mode 100644 index 944f8578b9..0000000000 --- a/msg/EscFlasherStatus.msg +++ /dev/null @@ -1,7 +0,0 @@ -uint64 timestamp # time since system start (microseconds) - -bool esc_flashing_in_progress - -uint8 version_major - -uint8 version_minor diff --git a/msg/EscFlasherVersions.msg b/msg/EscFlasherVersions.msg deleted file mode 100644 index 186424ce82..0000000000 --- a/msg/EscFlasherVersions.msg +++ /dev/null @@ -1,25 +0,0 @@ -# ESC Flasher Versions uOrb message -# -uint64 timestamp # time since system start (microseconds) - -uint8 result -# use these items as enum values, they can never change -uint8 RESULT_UNKNOWN = 0 # -uint8 RESULT_SUCCESS = 1 # -uint8 RESULT_NO_DATA = 2 # -uint8 RESULT_MISMATCH = 3 # -uint8 RESULT_NEEDS_UPDATE = 4 # -uint8 RESULT_BROKEN_ESC = 5 # - -uint16 versions_valid # Bit-flags to identify valid version information - -uint8[16] esc_type -# use these items as enum values, they can never change -uint8 ESC_TYPE_UNKNOWN = 0 # Unknown -uint8 ESC_TYPE_AM32 = 1 # AM32 2.18+ -uint8 ESC_TYPE_BLHELI = 2 # BLHeli -uint8 ESC_TYPE_AM32_OLD = 3 # AM32 2.16 -uint8 ESC_TYPE_BLUEJAY = 4 # BlueJay - -uint8[16] versions_major # Firmware versions major -uint8[16] versions_minor # Firmware versions minor diff --git a/msg/versioned/VehicleCommand.msg b/msg/versioned/VehicleCommand.msg index 669a4b8af9..80cb791b89 100644 --- a/msg/versioned/VehicleCommand.msg +++ b/msg/versioned/VehicleCommand.msg @@ -111,6 +111,7 @@ uint16 VEHICLE_CMD_EXTERNAL_WIND_ESTIMATE = 43004 uint32 VEHICLE_CMD_PX4_INTERNAL_START = 65537 # Start of PX4 internal only vehicle commands (> UINT16_MAX). uint32 VEHICLE_CMD_SET_GPS_GLOBAL_ORIGIN = 100000 # Sets the GPS coordinates of the vehicle local origin (0,0,0) position. |Unused|Unused|Unused|Unused|Latitude (WGS-84)|Longitude (WGS-84)|[m] Altitude (AMSL from GNSS, positive above ground)| uint32 VEHICLE_CMD_SET_NAV_STATE = 100001 # Change mode by specifying nav_state directly. |nav_state|Unused|Unused|Unused|Unused|Unused|Unused| +uint32 VEHICLE_CMD_ESC_FIRMWARE_UPDATE = 100002 # Trigger AM32 ESC firmware update from SD card. |ESC index (0-based, 255=all)|ESC type (0=AM32)|Unused|Unused|Unused|Unused|Unused| uint8 VEHICLE_MOUNT_MODE_RETRACT = 0 # Load and keep safe position (Roll,Pitch,Yaw) from permanent memory and stop stabilization. uint8 VEHICLE_MOUNT_MODE_NEUTRAL = 1 # Load and keep neutral position (Roll,Pitch,Yaw) from permanent memory. diff --git a/src/drivers/dshot/DShot.cpp b/src/drivers/dshot/DShot.cpp index 33e714f1fd..92fec8e76e 100644 --- a/src/drivers/dshot/DShot.cpp +++ b/src/drivers/dshot/DShot.cpp @@ -38,6 +38,7 @@ #include char DShot::_telemetry_device[] {}; +bool DShot::_telemetry_swap_rxtx{false}; px4::atomic_bool DShot::_request_telemetry_init{false}; DShot::DShot() : @@ -57,6 +58,9 @@ DShot::~DShot() up_dshot_arm(false); perf_free(_cycle_perf); + perf_free(_bdshot_rpm_perf); + perf_free(_dshot_telem_perf); + delete _telemetry; } @@ -97,7 +101,7 @@ int DShot::task_spawn(int argc, char *argv[]) void DShot::enable_dshot_outputs(const bool enabled) { - if (enabled && !_outputs_initialized && esc_flasher_flashing_state == 0) { + if (enabled && !_outputs_initialized) { unsigned int dshot_frequency = 0; uint32_t dshot_frequency_param = 0; @@ -143,9 +147,6 @@ void DShot::enable_dshot_outputs(const bool enabled) _bidirectional_dshot_enabled = _param_bidirectional_enable.get(); - // Get ESC_TYPE parameter at startup - _esc_type = (ESCType)_param_esc_type.get(); - int ret = up_dshot_init(_output_mask, dshot_frequency, _bidirectional_dshot_enabled); if (ret < 0) { @@ -192,7 +193,7 @@ void DShot::update_num_motors() _num_motors = motor_count; } -void DShot::init_telemetry(const char *device) +void DShot::init_telemetry(const char *device, bool swap_rxtx) { if (!_telemetry) { _telemetry = new DShotTelemetry{}; @@ -204,7 +205,7 @@ void DShot::init_telemetry(const char *device) } if (device != NULL) { - int ret = _telemetry->init(device); + int ret = _telemetry->init(device, swap_rxtx); if (ret != 0) { PX4_ERR("telemetry init failed (%i)", ret); @@ -246,27 +247,12 @@ int DShot::handle_new_telemetry_data(const int telemetry_index, const DShotTelem ++esc_status.counter; ret = 1; // Indicate we wrapped, so we publish data -#if 1 - // Flag get_esc_info state machine to start ESC detection - if (get_esc_info_boot == 0 && get_esc_info_start == 0) { - // Use get_esc_info_boot so we only start ESC detection at boot ONCE - // It can be restarted via ESC_Flasher.cpp, using get_esc_info_start = 1 - get_esc_info_boot = 1; - get_esc_info_start = 1; - get_esc_info_time = hrt_absolute_time() + 3_s; - // Set _esc_type_temp to the first enum, AM32 - if (_esc_type == ESCType::Unknown) { - _esc_type_temp = ESCType::AM32; - } - else { - _esc_type_temp = _esc_type; - } - } -#endif } _last_telemetry_index = telemetry_index; + perf_count(_dshot_telem_perf); + return ret; } @@ -274,39 +260,14 @@ void DShot::publish_esc_status(void) { esc_status_s &esc_status = esc_status_pub.get(); int telemetry_index = 0; - int num_active_motors = _num_motors; // clear data of the esc that are offline for (int index = 0; (index < _last_telemetry_index); index++) { if ((esc_status.esc_online_flags & (1 << index)) == 0) { memset(&esc_status.esc[index], 0, sizeof(struct esc_report_s)); - num_active_motors--; } } - if (num_active_motors != _num_motors && esc_flasher_flashing_state == 0) { - _broken_esc++; - // 50 times will take 2 seconds, since this runs at 25 Hz - if (_broken_esc >= 50 && _broken_esc % 50 == 0) { - //PX4_INFO("Broken ESC detected! Hardware fix required."); - if (!_broken_esc_flag) { - PX4_INFO("Broken ESC detected! Hardware fix required."); - _broken_esc_flag = true; - - // Send esc_versions uOrb with result BROKEN - esc_flasher_versions_s esc_flasher_versions{0}; - esc_flasher_versions.result = esc_flasher_versions_s::RESULT_BROKEN_ESC; - // Flag versions_valid so the message gets sent out in PRE_SHOW_STATUS.hpp - esc_flasher_versions.versions_valid = 1; - esc_flasher_versions.timestamp = hrt_absolute_time(); - _esc_flasher_versions_pub.publish(esc_flasher_versions); - } - } - } - else { - _broken_esc = 0; - } - // FIXME: mark all UART Telemetry ESC's as online, otherwise commander complains even for a single dropout esc_status.esc_count = _num_motors; esc_status.esc_online_flags = (1 << esc_status.esc_count) - 1; @@ -327,12 +288,6 @@ void DShot::publish_esc_status(void) } } - // If ESC flashing is in progress, mark all ESCs as offline so vehicle cannot arm - if (esc_flasher_flashing_state > 0) { - esc_status.esc_online_flags = 0; - esc_status.esc_armed_flags = 0; - } - if (!esc_status_pub.advertised()) { esc_status_pub.advertise(); @@ -373,10 +328,10 @@ int DShot::handle_new_bdshot_erpm(void) ++telemetry_index; } - - } + perf_count(_bdshot_rpm_perf); + return num_erpms; } @@ -392,13 +347,6 @@ int DShot::send_command_thread_safe(const dshot_command_t command, const int num cmd.motor_mask = 1 << motor_index; } - ESCType temp_esc_type = _esc_type; - if (get_esc_info_state != 0) { - temp_esc_type = _esc_type_temp; - } - if (temp_esc_type == ESCType::AM32) { - if (cmd.command == 9 || cmd.command == 10) cmd.save = true; - } cmd.num_repetitions = num_repetitions; _new_command.store(&cmd); @@ -419,128 +367,13 @@ int DShot::send_command_thread_safe(const dshot_command_t command, const int num return 0; } -void DShot::retrieve_and_print_esc_info_thread_safe(const int motor_index) -{ - if (_request_esc_info.load() != nullptr) { - // already in progress (not expected to ever happen) - return; - } - - DShotTelemetry::OutputBuffer output_buffer{}; - output_buffer.motor_index = motor_index; - - // start the request - _request_esc_info.store(&output_buffer); - - // wait until processed - int max_time = 1000; - - while (_request_esc_info.load() != nullptr && max_time-- > 0) { - px4_usleep(1000); - } - - _request_esc_info.store(nullptr); // just in case we time out... - _waiting_for_esc_info = false; - - if (output_buffer.buf_pos == 0) { - PX4_ERR("No data received. If telemetry is setup correctly, try again"); - return; - } - - DShotTelemetry::decodeAndPrintEscInfoPacket(output_buffer); -} - -int DShot::retrieve_and_print_esc_info_non_blocking(const int motor_index) -{ - if (_request_esc_info.load() != nullptr) { - // already in progress (not expected to ever happen) - return -1; - } - - esc_flasher_output_buffer.buf_pos = 0; - esc_flasher_output_buffer.motor_index = motor_index; - - // start the request - _request_esc_info.store(&esc_flasher_output_buffer); - - return 0; -} - -int DShot::retrieve_and_print_esc_info_check_result(uint8_t* fw_ver_major, uint8_t* fw_ver_minor) -{ - // Return 0 only if AM32 ESC - // Return -1 if still waiting for data - // Return -2 for timeout or CRC error or other error - // Return -3 for BLHeli ESC - - if (_request_esc_info.load() != nullptr) { - // Data not ready yet - return -1; - } - - _request_esc_info.store(nullptr); // just in case we time out... - _waiting_for_esc_info = false; - - if (esc_flasher_output_buffer.buf_pos == 0) { - PX4_ERR("No data received. If telemetry is setup correctly, try again"); - return -2; - } - - int retval = DShotTelemetry::decodeEscInfoPacketFwVersion(esc_flasher_output_buffer, fw_ver_major, fw_ver_minor); - if (retval == 0) { - // AM32 ESC success - return retval; - } - else if (retval == -1) { - // No data or CRC error or other error - return -2; - } - else if (retval == -2) { - // BLHeli ESC success - //PX4_ERR("AM32 ESC not found"); - return -3; - } - - return retval; -} - -int DShot::request_esc_info() -{ - _telemetry->redirectOutput(*_request_esc_info.load()); - _waiting_for_esc_info = true; - - int motor_index = _request_esc_info.load()->motor_index; - - _current_command.motor_mask = 1 << motor_index; - - ESCType temp_esc_type = _esc_type; - if (get_esc_info_state != 0) { - temp_esc_type = _esc_type_temp; - } - if (temp_esc_type == ESCType::AM32 || temp_esc_type == ESCType::AM32_Old) _current_command.num_repetitions = 6; - else _current_command.num_repetitions = 1; - - _current_command.command = DShot_cmd_esc_info; - _current_command.save = false; - - PX4_DEBUG("Requesting ESC info for motor %i", motor_index); - return motor_index; -} - void DShot::mixerChanged() { update_num_motors(); - } bool DShot::updateOutputs(uint16_t outputs[MAX_ACTUATORS], unsigned num_outputs, unsigned num_control_groups_updated) -{ - return false; -} - -bool DShot::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], - unsigned num_outputs, unsigned num_control_groups_updated) { if (!_outputs_on) { return false; @@ -549,99 +382,47 @@ bool DShot::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], int requested_telemetry_index = -1; if (_telemetry) { - // check for an ESC info request. We only process it when we're not expecting other telemetry data - if (_request_esc_info.load() != nullptr && !_waiting_for_esc_info && stop_motors - && !_telemetry->expectingData() && !_current_command.valid()) { - requested_telemetry_index = request_esc_info(); - } else { - requested_telemetry_index = _telemetry->getRequestMotorIndex(); - } + requested_telemetry_index = _telemetry->getRequestMotorIndex(); } - if (stop_motors) { + int telemetry_index = 0; - int telemetry_index = 0; + for (int i = 0; i < (int)num_outputs; i++) { + + uint16_t output = outputs[i]; + + if (output == DSHOT_DISARM_VALUE) { - // when motors are stopped we check if we have other commands to send - for (int i = 0; i < (int)num_outputs; i++) { if (_current_command.valid() && (_current_command.motor_mask & (1 << i))) { - // for some reason we need to always request telemetry when sending a command - // unless command is esc_info - ESCType temp_esc_type = _esc_type; - if (get_esc_info_state != 0) { - temp_esc_type = _esc_type_temp; - } - if ((temp_esc_type == ESCType::AM32 || temp_esc_type == ESCType::AM32_Old) && _current_command.command == 6) { - up_dshot_motor_command(i, _current_command.command, false); - } - else up_dshot_motor_command(i, _current_command.command, true); + up_dshot_motor_command(i, _current_command.command, true); + } else { up_dshot_motor_command(i, DShot_cmd_motor_stop, telemetry_index == requested_telemetry_index); } - telemetry_index += _mixing_output.isFunctionSet(i); - } + } else { - if (_current_command.valid()) { - --_current_command.num_repetitions; - - if (_current_command.num_repetitions == 0 && _current_command.save) { - _current_command.save = false; - _current_command.num_repetitions = 10; - _current_command.command = dshot_command_t::DShot_cmd_save_settings; - } - } - - } else { - int telemetry_index = 0; - - for (int i = 0; i < (int)num_outputs; i++) { - - uint16_t output = outputs[i]; - - if (output == DSHOT_DISARM_VALUE) { - up_dshot_motor_command(i, DShot_cmd_motor_stop, telemetry_index == requested_telemetry_index); - - } else { - - // DShot 3D splits the throttle ranges in two. - // This is in terms of DShot values, code below is in terms of actuator_output - // Direction 1) 48 is the slowest, 1047 is the fastest. - // Direction 2) 1049 is the slowest, 2047 is the fastest. - if (_param_dshot_3d_enable.get() || (_reversible_outputs & (1u << i))) { - if (output >= _param_dshot_3d_dead_l.get() && output < _param_dshot_3d_dead_h.get()) { - output = DSHOT_DISARM_VALUE; - - } else { - bool upper_range = output >= 1000; - - if (upper_range) { - output -= 1000; - - } else { - output = 999 - output; // lower range is inverted - } - - float max_output = 999.f; - float min_output = max_output * _param_dshot_min.get(); - output = math::min(max_output, (min_output + output * (max_output - min_output) / max_output)); - - if (upper_range) { - output += 1000; - } - - } - } - - up_dshot_motor_data_set(i, math::min(output, static_cast(DSHOT_MAX_THROTTLE)), - telemetry_index == requested_telemetry_index); + if (_param_dshot_3d_enable.get() || (_reversible_outputs & (1u << i))) { + output = convert_output_to_3d_scaling(output); } - telemetry_index += _mixing_output.isFunctionSet(i); + up_dshot_motor_data_set(i, math::min(output, static_cast(DSHOT_MAX_THROTTLE)), + telemetry_index == requested_telemetry_index); } - // clear commands when motors are running - _current_command.clear(); + telemetry_index += _mixing_output.isFunctionSet(i); + } + + // Decrement the command counter + if (_current_command.valid()) { + --_current_command.num_repetitions; + + // Queue a save command after the burst if save has been requested + if (_current_command.num_repetitions == 0 && _current_command.save) { + _current_command.save = false; + _current_command.num_repetitions = 10; + _current_command.command = dshot_command_t::DShot_cmd_save_settings; + } } up_dshot_trigger(); @@ -649,6 +430,36 @@ bool DShot::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], return true; } +uint16_t DShot::convert_output_to_3d_scaling(uint16_t output) +{ + // DShot 3D splits the throttle ranges in two. + // This is in terms of DShot values, code below is in terms of actuator_output + // Direction 1) 48 is the slowest, 1047 is the fastest. + // Direction 2) 1049 is the slowest, 2047 is the fastest. + if (output >= _param_dshot_3d_dead_l.get() && output < _param_dshot_3d_dead_h.get()) { + return DSHOT_DISARM_VALUE; + } + + bool upper_range = output >= 1000; + + if (upper_range) { + output -= 1000; + + } else { + output = 999 - output; // lower range is inverted + } + + float max_output = 999.f; + float min_output = max_output * _param_dshot_min.get(); + output = math::min(max_output, (min_output + output * (max_output - min_output) / max_output)); + + if (upper_range) { + output += 1000; + } + + return output; +} + void DShot::Run() { if (should_exit()) { @@ -673,14 +484,7 @@ void DShot::Run() if (_telemetry) { const int telem_update = _telemetry->update(_num_motors); - // Are we waiting for ESC info? - if (_waiting_for_esc_info) { - if (telem_update != -1) { - _request_esc_info.store(nullptr); - _waiting_for_esc_info = false; - } - - } else if (telem_update >= 0) { + if (telem_update >= 0) { const int need_to_publish = handle_new_telemetry_data(telem_update, _telemetry->latestESCData(), _bidirectional_dshot_enabled); @@ -706,7 +510,7 @@ void DShot::Run() // telemetry device update request? if (_request_telemetry_init.load()) { - init_telemetry(_telemetry_device); + init_telemetry(_telemetry_device, _telemetry_swap_rxtx); _request_telemetry_init.store(false); } @@ -722,10 +526,6 @@ void DShot::Run() handle_vehicle_commands(); - handle_esc_flasher_requests(); - - run_get_esc_info(); - if (!_mixing_output.armed().armed) { if (_reversible_outputs != _mixing_output.reversibleOutputs()) { _reversible_outputs = _mixing_output.reversibleOutputs(); @@ -815,438 +615,6 @@ void DShot::handle_vehicle_commands() } } -void DShot::handle_esc_flasher_requests() -{ - static uint8_t fw_version_major; - static uint8_t fw_version_minor; - static uint32_t gpio; - int retval = 0; - - while (!_current_command.valid() && _esc_flasher_request_sub.update(&_esc_flasher_request)) { - PX4_INFO("DShot received request from ESC Flasher"); - - if (_esc_flasher_request.request == esc_flasher_request_s::REQUEST_CANCEL || - _esc_flasher_request.request == esc_flasher_request_s::REQUEST_FLASHING_COMPLETE) { - // Cancel any requests, re-enable DShot - esc_flasher_esc_info_state = 0; - esc_flasher_esc_info_motor_index = 0; - esc_flasher_flashing_state = 0; - enable_dshot_outputs(true); - - memset(&_esc_flasher_response, 0, sizeof(_esc_flasher_response)); - _esc_flasher_response.msg_id = _esc_flasher_request.msg_id; - _esc_flasher_response.request = _esc_flasher_request.request; - _esc_flasher_response.result = esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_ACCEPTED; - _esc_flasher_response.timestamp = hrt_absolute_time(); - - if (_esc_flasher_request.request == esc_flasher_request_s::REQUEST_CANCEL) { - PX4_WARN("Canceling ESC Flasher requests and re-enabling DShot"); - } - else { - PX4_INFO("Flashing completed by ESC Flasher, re-enabling DShot"); - PX4_INFO("Restarting ESC detection to update ESC versions"); - - // Restart ESC detection, so new version is saved and send via esc_versions uOrb - get_esc_info_start = 1; - get_esc_info_time = hrt_absolute_time() + 2_s; - _esc_type_temp = _esc_type; - } - _esc_flasher_request_ack_pub.publish(_esc_flasher_response); - - // Clear broken esc flags on CANCEL or COMPLETE - _broken_esc = 0; - _broken_esc_flag = false; - - break; - } - - if (esc_flasher_esc_info_state) { - // ESC_INFO request is in progress - memset(&_esc_flasher_response, 0, sizeof(_esc_flasher_response)); - _esc_flasher_response.msg_id = _esc_flasher_request.msg_id; - _esc_flasher_response.request = _esc_flasher_request.request; - _esc_flasher_response.result = esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_IN_PROGRESS; - _esc_flasher_response.timestamp = hrt_absolute_time(); - - PX4_ERR("ESC INFO request already in progress"); - _esc_flasher_request_ack_pub.publish(_esc_flasher_response); - - break; - } - if (esc_flasher_flashing_state) { - // FLASHING request is in progress - memset(&_esc_flasher_response, 0, sizeof(_esc_flasher_response)); - _esc_flasher_response.msg_id = _esc_flasher_request.msg_id; - _esc_flasher_response.request = _esc_flasher_request.request; - _esc_flasher_response.result = esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_IN_PROGRESS; - _esc_flasher_response.timestamp = hrt_absolute_time(); - - PX4_ERR("FLASHING request already in progress"); - _esc_flasher_request_ack_pub.publish(_esc_flasher_response); - - break; - } - - if (_esc_flasher_request.request == esc_flasher_request_s::REQUEST_ESC_INFO) { - if (telemetry_enabled()) { - esc_flasher_esc_info_state = 1; - esc_flasher_esc_info_motor_index = 0; - memset(&_esc_flasher_response, 0, sizeof(_esc_flasher_response)); - _esc_flasher_response.msg_id = _esc_flasher_request.msg_id; - _esc_flasher_response.request = _esc_flasher_request.request; - } - else { - PX4_ERR("Telemetry is not enabled, but required to get ESC info"); - } - } - else if (_esc_flasher_request.request == esc_flasher_request_s::REQUEST_FLASHING) { - // When ESC Flasher requests flashing, disable all DShot outputs until - // ESC Flasher notifies completion or cancellation - // Return via uORB all GPIO pin definitions, since ESC Flasher will need those for bit-banging UART - esc_flasher_flashing_state = 1; - memset(&_esc_flasher_response, 0, sizeof(_esc_flasher_response)); - _esc_flasher_response.msg_id = _esc_flasher_request.msg_id; - _esc_flasher_response.request = _esc_flasher_request.request; - } - } - - switch (esc_flasher_esc_info_state) { - case 0: - break; - case 1: - // Get ESC_INFO from the requested motors - if (_esc_flasher_request.motor_flags & (1 << esc_flasher_esc_info_motor_index)) { - retrieve_and_print_esc_info_non_blocking(esc_flasher_esc_info_motor_index); - esc_flasher_esc_info_state = 2; - } - else { - // Move on to next motor - esc_flasher_esc_info_motor_index++; - if (esc_flasher_esc_info_motor_index >= 4) { - esc_flasher_esc_info_state = 3; - } - } - - break; - case 2: - // Wait for ESC_INFO to finish - retval = retrieve_and_print_esc_info_check_result(&fw_version_major, &fw_version_minor); - if (retval == 0) { - - // Save the versions to our response struct - _esc_flasher_response.fw_flags |= (1 << esc_flasher_esc_info_motor_index); - _esc_flasher_response.fw_major[esc_flasher_esc_info_motor_index] = fw_version_major; - _esc_flasher_response.fw_minor[esc_flasher_esc_info_motor_index] = fw_version_minor; - - // Move on to next motor - esc_flasher_esc_info_motor_index++; - if (esc_flasher_esc_info_motor_index >= 4) { - esc_flasher_esc_info_state = 3; - } - else { - esc_flasher_esc_info_state = 1; - } - } - else if (retval == -1) { - // Still waiting for response or timeout - } - else if (retval == -2) { - // No valid response from ESC - // Move on to next motor - esc_flasher_esc_info_motor_index++; - if (esc_flasher_esc_info_motor_index >= 4) { - esc_flasher_esc_info_state = 3; - } - else { - esc_flasher_esc_info_state = 1; - } - } - else if (retval == -3) { - // Non-AM32 ESC detected - // Publish response message - _esc_flasher_response.fw_flags = 0; - _esc_flasher_response.timestamp = hrt_absolute_time(); - _esc_flasher_response.result = esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_UNSUPPORTED; - - _esc_flasher_request_ack_pub.publish(_esc_flasher_response); - - esc_flasher_esc_info_state = 0; - esc_flasher_esc_info_motor_index = 0; - } - - break; - case 3: - // Publish response message - _esc_flasher_response.timestamp = hrt_absolute_time(); - _esc_flasher_response.result = esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_ACCEPTED; - - _esc_flasher_request_ack_pub.publish(_esc_flasher_response); - - esc_flasher_esc_info_state = 0; - esc_flasher_esc_info_motor_index = 0; - - break; - } - - switch (esc_flasher_flashing_state) { - case 0: - break; - case 1: - // Disable all DShot outputs - enable_dshot_outputs(false); - _outputs_initialized = false; - esc_flasher_flashing_state = 2; - - break; - case 2: - // Send GPIO pins to ESC Flasher - for (uint32_t i = 0; i < 16; i++) { - if (_esc_flasher_request.motor_flags & (1 << i)) { - gpio = io_timer_channel_get_gpio_output(i); - // Clear unwanted flags from GPIO pin definition - gpio &= (GPIO_PORT_MASK | GPIO_PIN_MASK); - _esc_flasher_response.gpio_pins[i] = gpio; - _esc_flasher_response.gpio_flags |= (1 << i); - } - } - - esc_flasher_flashing_state = 3; - - break; - case 3: - // Publish response message - _esc_flasher_response.timestamp = hrt_absolute_time(); - _esc_flasher_response.result = esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_ACCEPTED; - - _esc_flasher_request_ack_pub.publish(_esc_flasher_response); - - esc_flasher_flashing_state = 4; - - break; - case 4: - // Wait state for ESC Flasher, ensure DShot outputs stay off - - break; - } - - // Update ESC status if flashing is active, so the vehicle cannot arm - if (esc_flasher_flashing_state > 0) { - publish_esc_status(); - } -} - -void DShot::run_get_esc_info() -{ - int retval = 0; - - if (_num_motors && get_esc_info_start && (esc_flasher_flashing_state == 0) && (esc_flasher_esc_info_state == 0)) { - // Once we have _num_motors, mixers have been initialized, try to get esc_info from all motors - - if (get_esc_info_state == 0 && (hrt_absolute_time() > get_esc_info_time)) { - // Start state machine - PX4_INFO("Starting ESC detection, attempting ESC type %d", (int)_esc_type_temp); - get_esc_info_state = 1; - get_esc_info_motor_index = 0; - - memset(&_esc_info_save, 0, sizeof(_esc_info_save)); - - // This ESC_TYPE test is setup to run shortly after bootup - // It will start out at the param ESC_TYPE and move up the list until the end, no loop - // If DShot gets a response to esc_info command, then it is a success and the new ESC_TYPE is saved in the param - // If no response then no change to ESC_TYPE param - // To restart the test, reset the param ESC_TYPE to 0 and reboot PX4 - } - - switch (get_esc_info_state) { - case 0: - // Idle state - break; - case 1: - // Check motor_index against _num_motors - if (get_esc_info_motor_index < (uint32_t)_num_motors) { - get_esc_info_state = 2; - } - else { - // Done, count how many motors gave the esc_info - int motor_count = 0; - for (int i = 0; i < _num_motors; i++) { - if (_esc_info_save[i].type != ESCType::Unknown) { - motor_count++; - } - } - - if (motor_count == _num_motors) { - get_esc_info_state = 10; - PX4_INFO("ESC_INFO received for %d out of %d motors", (int)motor_count, (int)_num_motors); - } - else if (motor_count == 0) { - - // Move to next ESC_TYPE during the bootup test - int temp_type = (int)_esc_type_temp; - temp_type++; - _esc_type_temp = (ESCType)temp_type; - - if (_esc_type_temp == ESCType::BlueJay) { - // End of test, fail - get_esc_info_state = 11; - PX4_ERR("No ESC_INFO received, please set param ESC_TYPE to 0 and reboot"); - break; - } - - // No data received, try again in 1 second - if (get_esc_info_tries++ < 3) { - get_esc_info_time = hrt_absolute_time() + 1_s; - get_esc_info_state = 0; - PX4_WARN("No ESC_INFO received, trying again in 1 second with ESC type %d", (int)_esc_type_temp); - } - else { - // After 3 tries, no data received, end state-machine - get_esc_info_state = 11; - PX4_ERR("No ESC_INFO received"); - } - } - else { - // Only received data from some motors - get_esc_info_state = 10; - PX4_INFO("ESC_INFO received for %d out of %d motors", (int)motor_count, (int)_num_motors); - } - } - break; - case 2: - // Get next motor esc_info - retrieve_and_print_esc_info_non_blocking(get_esc_info_motor_index); - get_esc_info_state = 3; - break; - case 3: - // Wait for ESC_INFO to finish - retval = retrieve_and_print_esc_info_check_result(&_esc_info_save[get_esc_info_motor_index].fw_major, &_esc_info_save[get_esc_info_motor_index].fw_minor); - if (retval == 0) { - // AM32 ESC detected - _esc_info_save[get_esc_info_motor_index].type = ESCType::AM32; - get_esc_info_motor_index++; - get_esc_info_state = 1; - break; - } - else if (retval == -1) { - // Still waiting for response or timeout - } - else if (retval == -2) { - // No valid response from ESC - // Move on to next motor - get_esc_info_motor_index++; - get_esc_info_state = 1; - break; - } - else if (retval == -3) { - // Non-AM32 ESC detected - _esc_info_save[get_esc_info_motor_index].type = ESCType::BLHELI32; - get_esc_info_motor_index++; - get_esc_info_state = 1; - break; - } - - break; - case 10: - { - // Done with all ESCs, publish uOrb message - esc_flasher_versions_s esc_flasher_versions{0}; - int motor_count = 0; - bool same_type = true; - bool same_versions = true; - ESCType type = ESCType::Unknown; - uint8_t version_major = 0; - uint8_t version_minor = 0; - - for (int i = 0; i < _num_motors; i++) { - if (_esc_info_save[i].type != ESCType::Unknown) { - if (i == 0) { - // First motor, set type - type = _esc_info_save[i].type; - version_major = _esc_info_save[i].fw_major; - version_minor = _esc_info_save[i].fw_minor; - } - else { - // Subsequent motors, check type and versions against first motor - if (_esc_info_save[i].type != type) { - same_type = false; - } - if (_esc_info_save[i].fw_major != version_major || _esc_info_save[i].fw_minor != version_minor) { - same_versions = false; - } - } - - motor_count++; - esc_flasher_versions.versions_valid |= (1 << i); - esc_flasher_versions.esc_type[i] = (uint8_t)_esc_info_save[i].type; - esc_flasher_versions.versions_major[i] = _esc_info_save[i].fw_major; - esc_flasher_versions.versions_minor[i] = _esc_info_save[i].fw_minor; - } - } - - esc_flasher_versions.result = esc_flasher_versions_s::RESULT_SUCCESS; - if (motor_count != _num_motors) { - esc_flasher_versions.result = esc_flasher_versions_s::RESULT_MISMATCH; - } - if (!same_type) { - esc_flasher_versions.result = esc_flasher_versions_s::RESULT_MISMATCH; - } - if (!same_versions) { - esc_flasher_versions.result = esc_flasher_versions_s::RESULT_MISMATCH; - } - - if (same_type) { - // If all types agree, set param - _param_esc_type.set((int32_t)type); - _param_esc_type.commit_no_notification(); - PX4_INFO("ESC Type detected: %s, saving to param ESC_TYPE", esc_types_strings[(uint32_t)type]); - } - - // Compare AM32 to our PX4-internal flashable AM32 version - esc_flasher_status_s esc_flasher_status; - if (_esc_flasher_status_sub.copy(&esc_flasher_status)) { - if ((esc_flasher_versions.result == esc_flasher_versions_s::RESULT_SUCCESS) && - (type == ESCType::AM32 || type == ESCType::AM32_Old)) { - -#if 0 - // Debug testing hack - // REMOVE when done - // First bootup, give false version so it thinks it needs to update - if (get_esc_info_debug == 0) { - esc_versions.versions_minor[0]--; - get_esc_info_debug = 1; - } - // REMOVE WHEN DONE - // REMOVE WHEN DONE - // -#endif - - if (esc_flasher_versions.versions_major[0] != esc_flasher_status.version_major || - esc_flasher_versions.versions_minor[0] != esc_flasher_status.version_minor) { - // The fw version of AM32 detected on the ESCs does not match our internal flashable version - esc_flasher_versions.result = esc_flasher_versions_s::RESULT_NEEDS_UPDATE; - } - } - } - - esc_flasher_versions.timestamp = hrt_absolute_time(); - - if (!_broken_esc_flag) { - _esc_flasher_versions_pub.publish(esc_flasher_versions); - } - - get_esc_info_state = 11; - } - break; - case 11: - // Done, return to idle state - get_esc_info_state = 0; - get_esc_info_start = 0; - - break; - } - } -} - void DShot::update_params() { parameter_update_s pupdate; @@ -1265,41 +633,50 @@ void DShot::update_params() _mixing_output.minValue(i) = DSHOT_MIN_THROTTLE; } } - - _esc_type = (ESCType)_param_esc_type.get(); } int DShot::custom_command(int argc, char *argv[]) { const char *verb = argv[0]; - if (!strcmp(verb, "telemetry")) { - if (argc > 1) { - // telemetry can be requested before the module is started - strncpy(_telemetry_device, argv[1], sizeof(_telemetry_device) - 1); - _telemetry_device[sizeof(_telemetry_device) - 1] = '\0'; - _request_telemetry_init.store(true); - } - - return 0; - } - int motor_index = -1; // select motor index, default: -1=all int myoptind = 1; + bool swap_rxtx = false; + const char *device_name = nullptr; int ch; const char *myoptarg = nullptr; - while ((ch = px4_getopt(argc, argv, "m:", &myoptind, &myoptarg)) != EOF) { + while ((ch = px4_getopt(argc, argv, "m:xd:", &myoptind, &myoptarg)) != EOF) { switch (ch) { case 'm': motor_index = strtol(myoptarg, nullptr, 10) - 1; break; + case 'x': + swap_rxtx = true; + break; + + case 'd': + device_name = myoptarg; + break; + default: return print_usage("unrecognized flag"); } } + if (!strcmp(verb, "telemetry")) { + if (device_name) { + // telemetry can be requested before the module is started + strncpy(_telemetry_device, device_name, sizeof(_telemetry_device) - 1); + _telemetry_device[sizeof(_telemetry_device) - 1] = '\0'; + _telemetry_swap_rxtx = swap_rxtx; + _request_telemetry_init.store(true); + } + + return 0; + } + struct VerbCommand { const char *name; dshot_command_t command; @@ -1330,26 +707,6 @@ int DShot::custom_command(int argc, char *argv[]) } } - if (!strcmp(verb, "esc_info")) { - if (!is_running()) { - PX4_ERR("module not running"); - return -1; - } - - if (motor_index == -1) { - PX4_ERR("No motor index specified"); - return -1; - } - - if (!get_instance()->telemetry_enabled()) { - PX4_ERR("Telemetry is not enabled, but required to get ESC info"); - return -1; - } - - get_instance()->retrieve_and_print_esc_info_thread_safe(motor_index); - return 0; - } - if (!is_running()) { int ret = DShot::task_spawn(argc, argv); @@ -1367,6 +724,9 @@ int DShot::print_status() PX4_INFO("Outputs used: 0x%" PRIx32, _output_mask); PX4_INFO("Outputs on: %s", _outputs_on ? "yes" : "no"); perf_print_counter(_cycle_perf); + perf_print_counter(_bdshot_rpm_perf); + perf_print_counter(_dshot_telem_perf); + _mixing_output.printStatus(); if (_telemetry) { @@ -1413,7 +773,8 @@ After saving, the reversed direction will be regarded as the normal one. So to r PRINT_MODULE_USAGE_COMMAND("start"); PRINT_MODULE_USAGE_COMMAND_DESCR("telemetry", "Enable Telemetry on a UART"); - PRINT_MODULE_USAGE_ARG("", "UART device", false); + PRINT_MODULE_USAGE_PARAM_STRING('d', nullptr, "", "UART device", false); + PRINT_MODULE_USAGE_PARAM_FLAG('x', "Swap RX/TX pins", true); // DShot commands PRINT_MODULE_USAGE_COMMAND_DESCR("reverse", "Reverse motor direction"); @@ -1437,9 +798,6 @@ After saving, the reversed direction will be regarded as the normal one. So to r PRINT_MODULE_USAGE_COMMAND_DESCR("beep5", "Send Beep pattern 5"); PRINT_MODULE_USAGE_PARAM_INT('m', -1, 0, 16, "Motor index (1-based, default=all)", true); - PRINT_MODULE_USAGE_COMMAND_DESCR("esc_info", "Request ESC information"); - PRINT_MODULE_USAGE_PARAM_INT('m', -1, 0, 16, "Motor index (1-based)", false); - PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); return 0; diff --git a/src/drivers/dshot/DShot.h b/src/drivers/dshot/DShot.h index 4e0728af87..8137e461e2 100644 --- a/src/drivers/dshot/DShot.h +++ b/src/drivers/dshot/DShot.h @@ -42,11 +42,6 @@ #include "DShotTelemetry.h" -#include -#include -#include -#include - using namespace time_literals; #if !defined(DIRECT_PWM_OUTPUT_CHANNELS) @@ -81,8 +76,6 @@ public: /** @see ModuleBase */ static int print_usage(const char *reason = nullptr); - void retrieve_and_print_esc_info_thread_safe(const int motor_index); - /** * Send a dshot command to one or all motors * This is expected to be called from another thread. @@ -100,9 +93,6 @@ public: bool updateOutputs(uint16_t outputs[MAX_ACTUATORS], unsigned num_outputs, unsigned num_control_groups_updated) override; - bool updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], - unsigned num_outputs, unsigned num_control_groups_updated);// override; - private: /** Disallow copy construction and move assignment. */ @@ -131,7 +121,7 @@ private: void enable_dshot_outputs(const bool enabled); - void init_telemetry(const char *device); + void init_telemetry(const char *device, bool swap_rxtx); int handle_new_telemetry_data(const int telemetry_index, const DShotTelemetry::EscData &data, bool ignore_rpm); @@ -139,8 +129,6 @@ private: int handle_new_bdshot_erpm(void); - int request_esc_info(); - void Run() override; void update_params(); @@ -149,13 +137,7 @@ private: void handle_vehicle_commands(); - void handle_esc_flasher_requests(); - - void run_get_esc_info(); - - int retrieve_and_print_esc_info_non_blocking(const int motor_index); - - int retrieve_and_print_esc_info_check_result(uint8_t* fw_ver_major, uint8_t* fw_ver_minor); + uint16_t convert_output_to_3d_scaling(uint16_t output); MixingOutput _mixing_output{PARAM_PREFIX, DIRECT_PWM_OUTPUT_CHANNELS, *this, MixingOutput::SchedulingPolicy::Auto, false, false}; uint32_t _reversible_outputs{}; @@ -165,78 +147,31 @@ private: uORB::PublicationMultiData esc_status_pub{ORB_ID(esc_status)}; static char _telemetry_device[20]; + static bool _telemetry_swap_rxtx; static px4::atomic_bool _request_telemetry_init; px4::atomic _new_command{nullptr}; - px4::atomic _request_esc_info{nullptr}; bool _outputs_initialized{false}; bool _outputs_on{false}; - bool _waiting_for_esc_info{false}; bool _bidirectional_dshot_enabled{false}; - const char *esc_type_unknown = "Unknown"; - const char *esc_type_am32 = "AM32"; - const char *esc_type_blheli32 = "BLHeli32"; - const char *esc_type_am32_old = "AM32_Old"; - const char *esc_type_bluejay = "BlueJay"; - - const char* esc_types_strings[5] = {esc_type_unknown, esc_type_am32, esc_type_blheli32, esc_type_am32_old, esc_type_bluejay}; - - enum class ESCType { - Unknown = 0, - AM32 = 1, - BLHELI32 = 2, - AM32_Old = 3, - BlueJay = 4 - }; - ESCType _esc_type{ESCType::Unknown}; - ESCType _esc_type_temp{ESCType::Unknown}; - - esc_flasher_request_s _esc_flasher_request{0}; - esc_flasher_request_ack_s _esc_flasher_response{0}; - uint32_t esc_flasher_esc_info_state{0}; - uint32_t esc_flasher_esc_info_motor_index; - uint32_t esc_flasher_flashing_state{0}; - DShotTelemetry::OutputBuffer esc_flasher_output_buffer{}; - static constexpr unsigned _num_outputs{DIRECT_PWM_OUTPUT_CHANNELS}; uint32_t _output_mask{0}; int _num_motors{0}; - uint32_t _broken_esc{0}; - bool _broken_esc_flag{0}; - - // run_get_esc_info variables - struct EscInfoSave { - ESCType type; - uint8_t fw_major; - uint8_t fw_minor; - }; - - EscInfoSave _esc_info_save[esc_status_s::CONNECTED_ESC_MAX]; - - uint64_t get_esc_info_time{0}; - uint32_t get_esc_info_state{0}; - uint32_t get_esc_info_motor_index{0}; - uint32_t get_esc_info_start{0}; - uint32_t get_esc_info_boot{0}; - uint32_t get_esc_info_tries{0}; - //uint32_t get_esc_info_debug{0}; perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")}; + perf_counter_t _bdshot_rpm_perf{perf_alloc(PC_COUNT, MODULE_NAME": bdshot rpm")}; + perf_counter_t _dshot_telem_perf{perf_alloc(PC_COUNT, MODULE_NAME": dshot telem")}; Command _current_command{}; uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s}; uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)}; uORB::Publication _command_ack_pub{ORB_ID(vehicle_command_ack)}; - uORB::Subscription _esc_flasher_request_sub{ORB_ID(esc_flasher_request)}; - uORB::Subscription _esc_flasher_status_sub{ORB_ID(esc_flasher_status)}; - uORB::Publication _esc_flasher_request_ack_pub{ORB_ID(esc_flasher_request_ack)}; uint16_t _esc_status_counter{0}; - uORB::Publication _esc_flasher_versions_pub{ORB_ID(esc_flasher_versions)}; DEFINE_PARAMETERS( (ParamFloat) _param_dshot_min, @@ -244,7 +179,6 @@ private: (ParamInt) _param_dshot_3d_dead_h, (ParamInt) _param_dshot_3d_dead_l, (ParamInt) _param_mot_pole_count, - (ParamBool) _param_bidirectional_enable, - (ParamInt) _param_esc_type + (ParamBool) _param_bidirectional_enable ) }; diff --git a/src/drivers/dshot/DShotTelemetry.cpp b/src/drivers/dshot/DShotTelemetry.cpp index 33f0b328ba..0567759226 100644 --- a/src/drivers/dshot/DShotTelemetry.cpp +++ b/src/drivers/dshot/DShotTelemetry.cpp @@ -46,53 +46,38 @@ using namespace time_literals; DShotTelemetry::~DShotTelemetry() { - deinit(); + _uart.close(); } -int DShotTelemetry::init(const char *uart_device) +int DShotTelemetry::init(const char *port, bool swap_rxtx) { - deinit(); - _uart_fd = ::open(uart_device, O_RDONLY | O_NOCTTY); - - if (_uart_fd < 0) { - PX4_ERR("failed to open serial port: %s err: %d", uart_device, errno); - return -errno; + if (!_uart.setPort(port)) { + PX4_ERR("Error configuring port %s", port); + return PX4_ERROR; } - _num_timeouts = 0; - _num_successful_responses = 0; - _current_motor_index_request = -1; - return setBaudrate(DSHOT_TELEMETRY_UART_BAUDRATE); -} - -void DShotTelemetry::deinit() -{ - if (_uart_fd >= 0) { - close(_uart_fd); - _uart_fd = -1; - } -} - -int DShotTelemetry::redirectOutput(OutputBuffer &buffer) -{ - if (expectingData()) { - // Error: cannot override while we already expect data - return -EBUSY; + if (!_uart.setBaudrate(DSHOT_TELEMETRY_UART_BAUDRATE)) { + PX4_ERR("Error setting baudrate on %s", port); + return PX4_ERROR; } - _current_motor_index_request = buffer.motor_index; - _current_request_start = hrt_absolute_time(); - _redirect_output = &buffer; - _redirect_output->buf_pos = 0; - return 0; + if (swap_rxtx) { + if (!_uart.setSwapRxTxMode()) { + PX4_ERR("Error swapping TX/RX"); + return PX4_ERROR; + } + } + + if (! _uart.open()) { + PX4_ERR("Error opening %s", port); + return PX4_ERROR; + } + + return PX4_OK; } int DShotTelemetry::update(int num_motors) { - if (_uart_fd < 0) { - return -1; - } - if (_current_motor_index_request == -1) { // nothing in progress, start a request _current_motor_index_request = 0; @@ -102,10 +87,9 @@ int DShotTelemetry::update(int num_motors) } // read from the uart. This must be non-blocking, so check first if there is data available - int bytes_available = 0; - int ret = ioctl(_uart_fd, FIONREAD, (unsigned long)&bytes_available); + int bytes_available = _uart.bytesAvailable(); - if (ret != 0 || bytes_available <= 0) { + if (bytes_available <= 0) { // no data available. Check for a timeout const hrt_abstime now = hrt_absolute_time(); @@ -127,13 +111,12 @@ int DShotTelemetry::update(int num_motors) return -1; } - const int buf_length = ESC_FRAME_SIZE; - uint8_t buf[buf_length]; + uint8_t buf[ESC_FRAME_SIZE]; + int bytes = _uart.read(buf, sizeof(buf)); - int num_read = read(_uart_fd, buf, buf_length); - ret = -1; + int ret = -1; - for (int i = 0; i < num_read && ret == -1; ++i) { + for (int i = 0; i < bytes && ret == -1; ++i) { if (_redirect_output) { _redirect_output->buffer[_redirect_output->buf_pos++] = buf[i]; @@ -183,6 +166,9 @@ bool DShotTelemetry::decodeByte(uint8_t byte, bool &successful_decoding) _latest_data.erpm); ++_num_successful_responses; successful_decoding = true; + + } else { + ++_num_checksum_errors; } return true; @@ -195,32 +181,30 @@ void DShotTelemetry::printStatus() const { PX4_INFO("Number of successful ESC frames: %i", _num_successful_responses); PX4_INFO("Number of timeouts: %i", _num_timeouts); + PX4_INFO("Number of CRC errors: %i", _num_checksum_errors); } -uint8_t DShotTelemetry::updateCrc8(uint8_t crc, uint8_t crc_seed) -{ - uint8_t crc_u = crc ^ crc_seed; - - for (int i = 0; i < 8; ++i) { - crc_u = (crc_u & 0x80) ? 0x7 ^ (crc_u << 1) : (crc_u << 1); - } - - return crc_u; -} - - uint8_t DShotTelemetry::crc8(const uint8_t *buf, uint8_t len) { + auto update_crc8 = [](uint8_t crc, uint8_t crc_seed) { + uint8_t crc_u = crc ^ crc_seed; + + for (int i = 0; i < 8; ++i) { + crc_u = (crc_u & 0x80) ? 0x7 ^ (crc_u << 1) : (crc_u << 1); + } + + return crc_u; + }; + uint8_t crc = 0; for (int i = 0; i < len; ++i) { - crc = updateCrc8(buf[i], crc); + crc = update_crc8(buf[i], crc); } return crc; } - void DShotTelemetry::requestNextMotor(int num_motors) { _current_motor_index_request = (_current_motor_index_request + 1) % num_motors; @@ -253,7 +237,6 @@ void DShotTelemetry::decodeAndPrintEscInfoPacket(const OutputBuffer &buffer) BLHELI32, KissV1, KissV2, - AM32 }; ESCVersionInfo version; int packet_length; @@ -267,21 +250,8 @@ void DShotTelemetry::decodeAndPrintEscInfoPacket(const OutputBuffer &buffer) packet_length = esc_info_size_kiss_v2; } else { - if (buffer.buf_pos > esc_info_size_kiss_v1) { - version = ESCVersionInfo::AM32; - packet_length = esc_info_size_am32; - } - else { - version = ESCVersionInfo::KissV1; - packet_length = esc_info_size_kiss_v1; - } - } - - // Check for AM32 but with bad data - if (version != ESCVersionInfo::AM32 && buffer.buf_pos == esc_info_size_am32) { - version = ESCVersionInfo::AM32; - packet_length = esc_info_size_am32; - PX4_WARN("Packet length mismatch (%i != %i), reverting to AM32 decoding", buffer.buf_pos, packet_length); + version = ESCVersionInfo::KissV1; + packet_length = esc_info_size_kiss_v1; } if (buffer.buf_pos != packet_length) { @@ -294,337 +264,116 @@ void DShotTelemetry::decodeAndPrintEscInfoPacket(const OutputBuffer &buffer) return; } - if (version == ESCVersionInfo::AM32) { - // AM32 2.18+ ESC_INFO packet sends bytes 0-47 of its EEPROM struct - if (buffer.buf_pos < packet_length) { - PX4_ERR("Not enough data received"); - return; - } + uint8_t esc_firmware_version = 0; + uint8_t esc_firmware_subversion = 0; + uint8_t esc_type = 0; - uint8_t eeprom_version = data[1]; - if (eeprom_version != 2) { - PX4_ERR("Unsupported EEPROM version from ESC"); - return; - } + switch (version) { + case ESCVersionInfo::KissV1: + esc_firmware_version = data[12]; + esc_firmware_subversion = (data[13] & 0x1f) + 97; + esc_type = (data[13] & 0xe0) >> 5; + break; - AM32_EEprom_st* esc_info = (AM32_EEprom_st*)buffer.buffer; - - char firmware_name[sizeof(esc_info->firmware_name) + 1]; - memcpy(firmware_name, esc_info->firmware_name, sizeof(firmware_name)); - firmware_name[sizeof(esc_info->firmware_name)] = 0; - - PX4_INFO("Received ESC_INFO packet from motor %d:", buffer.motor_index + 1); - - PX4_INFO(" ESC firmware version: %d.%d - ESC firmware name: %s", esc_info->version.major, esc_info->version.minor, firmware_name); - - if (esc_info->stuck_rotor_protection) PX4_INFO(" Stuck rotor protection is ENABLED"); - else PX4_INFO(" Stuck rotor protection is DISABLED"); - - if (esc_info->telemetry_on_interval) PX4_INFO(" Auto-Telemetry interval is ENABLED"); - else PX4_INFO(" Auto-Telemetry interval is DISABLED"); - - if (esc_info->bi_direction) PX4_INFO(" Bi-Directional DShot is ENABLED"); - else PX4_INFO(" Bi-Directional DShot is DISABLED"); + case ESCVersionInfo::KissV2: + case ESCVersionInfo::BLHELI32: + esc_firmware_version = data[13]; + esc_firmware_subversion = data[14]; + esc_type = data[15]; + break; } - else { - uint8_t esc_firmware_version = 0; - uint8_t esc_firmware_subversion = 0; - uint8_t esc_type = 0; - switch (version) { - case ESCVersionInfo::KissV1: - esc_firmware_version = data[12]; - esc_firmware_subversion = (data[13] & 0x1f) + 97; - esc_type = (data[13] & 0xe0) >> 5; + const char *esc_type_str = ""; + + switch (version) { + case ESCVersionInfo::KissV1: + case ESCVersionInfo::KissV2: + switch (esc_type) { + case 1: esc_type_str = "KISS8A"; break; - case ESCVersionInfo::KissV2: - case ESCVersionInfo::BLHELI32: - esc_firmware_version = data[13]; - esc_firmware_subversion = data[14]; - esc_type = data[15]; + case 2: esc_type_str = "KISS16A"; + break; + + case 3: esc_type_str = "KISS24A"; + break; + + case 5: esc_type_str = "KISS Ultralite"; + break; + + default: esc_type_str = "KISS (unknown)"; break; - case ESCVersionInfo::AM32: - // AM32 is handled above, should not get to this case - return; } - const char *esc_type_str = ""; + break; - switch (version) { - case ESCVersionInfo::KissV1: - case ESCVersionInfo::KissV2: - switch (esc_type) { - case 1: esc_type_str = "KISS8A"; - break; + case ESCVersionInfo::BLHELI32: { + char *esc_type_mutable = (char *)(data + 31); + esc_type_mutable[32] = 0; + esc_type_str = esc_type_mutable; + } + break; + } - case 2: esc_type_str = "KISS16A"; - break; + PX4_INFO("ESC Type: %s", esc_type_str); - case 3: esc_type_str = "KISS24A"; - break; + PX4_INFO("MCU Serial Number: %02x%02x%02x-%02x%02x%02x-%02x%02x%02x-%02x%02x%02x", + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], + data[9], data[10], data[11]); - case 5: esc_type_str = "KISS Ultralite"; - break; + switch (version) { + case ESCVersionInfo::KissV1: + case ESCVersionInfo::KissV2: + PX4_INFO("Firmware version: %d.%d%c", esc_firmware_version / 100, esc_firmware_version % 100, + (char)esc_firmware_subversion); + break; - default: esc_type_str = "KISS (unknown)"; - break; - } + case ESCVersionInfo::BLHELI32: + PX4_INFO("Firmware version: %d.%d", esc_firmware_version, esc_firmware_subversion); + break; + } + if (version == ESCVersionInfo::KissV2 || version == ESCVersionInfo::BLHELI32) { + PX4_INFO("Rotation Direction: %s", data[16] ? "reversed" : "normal"); + PX4_INFO("3D Mode: %s", data[17] ? "on" : "off"); + } + + if (version == ESCVersionInfo::BLHELI32) { + uint8_t setting = data[18]; + + switch (setting) { + case 0: + PX4_INFO("Low voltage Limit: off"); break; - case ESCVersionInfo::BLHELI32: { - char *esc_type_mutable = (char *)(data + 31); - esc_type_mutable[32] = 0; - esc_type_str = esc_type_mutable; - } + case 255: + PX4_INFO("Low voltage Limit: unsupported"); + break; + + default: + PX4_INFO("Low voltage Limit: %d.%01d V", setting / 10, setting % 10); break; - case ESCVersionInfo::AM32: - // AM32 is handled above, should not get to this case - return; } - PX4_INFO("ESC Type: %s", esc_type_str); + setting = data[19]; - PX4_INFO("MCU Serial Number: %02x%02x%02x-%02x%02x%02x-%02x%02x%02x-%02x%02x%02x", - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], - data[9], data[10], data[11]); - - switch (version) { - case ESCVersionInfo::KissV1: - case ESCVersionInfo::KissV2: - PX4_INFO("Firmware version: %d.%d%c", esc_firmware_version / 100, esc_firmware_version % 100, - (char)esc_firmware_subversion); + switch (setting) { + case 0: + PX4_INFO("Current Limit: off"); break; - case ESCVersionInfo::BLHELI32: - PX4_INFO("Firmware version: %d.%d", esc_firmware_version, esc_firmware_subversion); + case 255: + PX4_INFO("Current Limit: unsupported"); + break; + + default: + PX4_INFO("Current Limit: %d A", setting); break; - case ESCVersionInfo::AM32: - // AM32 is handled above, should not get to this case - return; } - if (version == ESCVersionInfo::KissV2 || version == ESCVersionInfo::BLHELI32) { - PX4_INFO("Rotation Direction: %s", data[16] ? "reversed" : "normal"); - PX4_INFO("3D Mode: %s", data[17] ? "on" : "off"); - } - - if (version == ESCVersionInfo::BLHELI32) { - uint8_t setting = data[18]; - - switch (setting) { - case 0: - PX4_INFO("Low voltage Limit: off"); - break; - - case 255: - PX4_INFO("Low voltage Limit: unsupported"); - break; - - default: - PX4_INFO("Low voltage Limit: %d.%01d V", setting / 10, setting % 10); - break; - } - - setting = data[19]; - - switch (setting) { - case 0: - PX4_INFO("Current Limit: off"); - break; - - case 255: - PX4_INFO("Current Limit: unsupported"); - break; - - default: - PX4_INFO("Current Limit: %d A", setting); - break; - } - - for (int i = 0; i < 4; ++i) { - setting = data[i + 20]; - PX4_INFO("LED %d: %s", i, setting ? (setting == 255 ? "unsupported" : "on") : "off"); - } + for (int i = 0; i < 4; ++i) { + setting = data[i + 20]; + PX4_INFO("LED %d: %s", i, setting ? (setting == 255 ? "unsupported" : "on") : "off"); } } } - -int DShotTelemetry::decodeEscInfoPacketFwVersion(const OutputBuffer &buffer, uint8_t* fw_version_major, uint8_t* fw_version_minor) -{ - // Return 0 for AM32 success - // Return -1 for not enough data (unknown) - // Return -2 for BLHeli success - - static constexpr int version_position = 12; - const uint8_t *data = buffer.buffer; - - if (buffer.buf_pos < version_position) { - PX4_ERR("Not enough data received"); - return -1; - } - - enum class ESCVersionInfo { - BLHELI32, - KissV1, - KissV2, - AM32 - }; - ESCVersionInfo version; - int packet_length; - - if (data[version_position] == 254) { - version = ESCVersionInfo::BLHELI32; - packet_length = esc_info_size_blheli32; - - } else if (data[version_position] == 255) { - version = ESCVersionInfo::KissV2; - packet_length = esc_info_size_kiss_v2; - - } else { - if (buffer.buf_pos > esc_info_size_kiss_v1) { - version = ESCVersionInfo::AM32; - packet_length = esc_info_size_am32; - } - else { - version = ESCVersionInfo::KissV1; - packet_length = esc_info_size_kiss_v1; - } - } - - // Check for AM32 but with bad data - if (version != ESCVersionInfo::AM32 && buffer.buf_pos == esc_info_size_am32) { - version = ESCVersionInfo::AM32; - packet_length = esc_info_size_am32; - PX4_WARN("Packet length mismatch (%i != %i), reverting to AM32 decoding", buffer.buf_pos, packet_length); - } - - if (buffer.buf_pos != packet_length) { - PX4_ERR("Packet length mismatch (%i != %i)", buffer.buf_pos, packet_length); - return -1; - } - - if (DShotTelemetry::crc8(data, packet_length - 1) != data[packet_length - 1]) { - PX4_ERR("Checksum mismatch"); - return -1; - } - - if (version == ESCVersionInfo::AM32) { - // AM32 2.18+ ESC_INFO packet sends bytes 0-47 of its EEPROM struct - if (buffer.buf_pos < packet_length) { - PX4_ERR("Not enough data received"); - return -1; - } - - uint8_t eeprom_version = data[1]; - if (eeprom_version != 2) { - PX4_ERR("Unsupported EEPROM version from ESC"); - return -1; - } - - AM32_EEprom_st* esc_info = (AM32_EEprom_st*)buffer.buffer; - - *fw_version_major = esc_info->version.major; - *fw_version_minor = esc_info->version.minor; - return 0; - } - else { - //PX4_ERR("Unsupported ESC type, this function only supports AM32"); - - switch (version) { - case ESCVersionInfo::KissV1: - *fw_version_major = data[12]; - *fw_version_minor = (data[13] & 0x1f) + 97; - break; - case ESCVersionInfo::KissV2: - case ESCVersionInfo::BLHELI32: - *fw_version_major = data[13]; - *fw_version_minor = data[14]; - break; - case ESCVersionInfo::AM32: - // AM32 is handled above, should not get to this case - return -1; - } - - return -2; - } -} - -int DShotTelemetry::setBaudrate(unsigned baud) -{ - int speed; - - switch (baud) { - case 9600: speed = B9600; break; - - case 19200: speed = B19200; break; - - case 38400: speed = B38400; break; - - case 57600: speed = B57600; break; - - case 115200: speed = B115200; break; - - case 230400: speed = B230400; break; - - default: - return -EINVAL; - } - - struct termios uart_config; - - int termios_state; - - /* fill the struct for the new configuration */ - tcgetattr(_uart_fd, &uart_config); - - // - // Input flags - Turn off input processing - // - // convert break to null byte, no CR to NL translation, - // no NL to CR translation, don't mark parity errors or breaks - // no input parity check, don't strip high bit off, - // no XON/XOFF software flow control - // - uart_config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | - INLCR | PARMRK | INPCK | ISTRIP | IXON); - // - // Output flags - Turn off output processing - // - // no CR to NL translation, no NL to CR-NL translation, - // no NL to CR translation, no column 0 CR suppression, - // no Ctrl-D suppression, no fill characters, no case mapping, - // no local output processing - // - // config.c_oflag &= ~(OCRNL | ONLCR | ONLRET | - // ONOCR | ONOEOT| OFILL | OLCUC | OPOST); - uart_config.c_oflag = 0; - - // - // No line processing - // - // echo off, echo newline off, canonical mode off, - // extended input processing off, signal chars off - // - uart_config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); - - /* no parity, one stop bit, disable flow control */ - uart_config.c_cflag &= ~(CSTOPB | PARENB | CRTSCTS); - - /* set baud rate */ - if ((termios_state = cfsetispeed(&uart_config, speed)) < 0) { - return -errno; - } - - if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) { - return -errno; - } - - if ((termios_state = tcsetattr(_uart_fd, TCSANOW, &uart_config)) < 0) { - return -errno; - } - - return 0; -} - diff --git a/src/drivers/dshot/DShotTelemetry.h b/src/drivers/dshot/DShotTelemetry.h index abf1523d17..ccbe7b1ce0 100644 --- a/src/drivers/dshot/DShotTelemetry.h +++ b/src/drivers/dshot/DShotTelemetry.h @@ -33,55 +33,9 @@ #pragma once +#include #include -typedef struct { - uint8_t reserved_0; //0 - uint8_t eeprom_version; //1 - uint8_t reserved_1; //2 - struct { - uint8_t major; //3 - uint8_t minor; //4 - } version; - char firmware_name[12]; //5-16 - uint8_t dir_reversed; // 17 - uint8_t bi_direction; // 18 - uint8_t use_sine_start; // 19 - uint8_t comp_pwm; // 20 - uint8_t variable_pwm; // 21 - uint8_t stuck_rotor_protection; // 22 - uint8_t advance_level; // 23 - uint8_t pwm_frequency; // 24 - uint8_t startup_power; // 25 - uint8_t motor_kv; // 26 - uint8_t motor_poles; // 27 - uint8_t brake_on_stop; // 28 - uint8_t stall_protection; // 29 - uint8_t beep_volume; // 30 - uint8_t telemetry_on_interval; // 31 - struct { - uint8_t low_threshold; // 32 - uint8_t high_threshold; // 33 - uint8_t neutral; // 34 - uint8_t dead_band; // 35 - } servo; - uint8_t low_voltage_cut_off; // 36 - uint8_t low_cell_volt_cutoff; // 37 - uint8_t rc_car_reverse; // 38 - uint8_t use_hall_sensors; // 39 - uint8_t sine_mode_changeover_thottle_level; // 40 - uint8_t drag_brake_strength; // 41 - uint8_t driving_brake_strength; // 42 - struct { - uint8_t temperature; // 43 - uint8_t current; // 44 - } limits; - uint8_t sine_mode_power; // 45 - uint8_t input_type; // 46 - uint8_t auto_advance; // 47 - uint8_t crc; // 48 -} AM32_EEprom_st; - class DShotTelemetry { public: @@ -97,7 +51,6 @@ public: static constexpr int esc_info_size_blheli32 = 64; static constexpr int esc_info_size_kiss_v1 = 15; static constexpr int esc_info_size_kiss_v2 = 21; - static constexpr int esc_info_size_am32 = 49; static constexpr int max_esc_info_size = esc_info_size_blheli32; struct OutputBuffer { @@ -108,9 +61,7 @@ public: ~DShotTelemetry(); - int init(const char *uart_device); - - void deinit(); + int init(const char *uart_device, bool swap_rxtx); /** * Read telemetry from the UART (non-blocking) and handle timeouts. @@ -119,16 +70,6 @@ public: */ int update(int num_motors); - /** - * Redirect everything that is read into a different buffer. - * Future calls to @update will write to that instead of an internal buffer, until @update returns - * a value different from -1. No decoding is done. - * The caller must ensure the buffer exists until that point. - * @param buffer - * @return 0 on success <0 on error - */ - int redirectOutput(OutputBuffer &buffer); - bool redirectActive() const { return _redirect_output != nullptr; } /** @@ -148,18 +89,9 @@ public: static void decodeAndPrintEscInfoPacket(const OutputBuffer &buffer); - static int decodeEscInfoPacketFwVersion(const OutputBuffer &buffer, uint8_t* fw_version_major, uint8_t* fw_version_minor); - private: static constexpr int ESC_FRAME_SIZE = 10; - /** - * set the Baudrate - * @param baud - * @return 0 on success, <0 on error - */ - int setBaudrate(unsigned baud); - void requestNextMotor(int num_motors); /** @@ -170,10 +102,10 @@ private: */ bool decodeByte(uint8_t byte, bool &successful_decoding); - static inline uint8_t updateCrc8(uint8_t crc, uint8_t crc_seed); static uint8_t crc8(const uint8_t *buf, uint8_t len); - int _uart_fd{-1}; + device::Serial _uart {}; + uint8_t _frame_buffer[ESC_FRAME_SIZE]; int _frame_position{0}; @@ -187,4 +119,5 @@ private: // statistics int _num_timeouts{0}; int _num_successful_responses{0}; + int _num_checksum_errors{0}; }; diff --git a/src/modules/esc_flasher/AM32Firmware.c b/src/modules/esc_flasher/AM32Firmware.c deleted file mode 100644 index 574cea348e..0000000000 --- a/src/modules/esc_flasher/AM32Firmware.c +++ /dev/null @@ -1,1318 +0,0 @@ -#include "AM32Firmware.h" - -#ifdef INCLUDE_AM32_FIRMWARE - -const uint8_t am32_fw_version_major = 2; -const uint8_t am32_fw_version_minor = 189; - -/* D:\Firefly\AM32\FIRMWARE BYTES\2-186\0x08001000.bin (5/15/2025 3:28:57 PM) - StartOffset(h): 00000000, EndOffset(h): 00004EFF, Length(h): 00004F00 */ - -const unsigned char am32_firmware[20752] = { - 0x00, 0x0D, 0x00, 0x20, 0xD1, 0x10, 0x00, 0x08, 0x71, 0x1D, 0x00, 0x08, 0xF9, 0x14, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x1E, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x1D, 0x00, 0x08, 0x63, 0x1E, 0x00, 0x08, - 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, - 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0xE5, 0x14, 0x00, 0x08, - 0xE3, 0x10, 0x00, 0x08, 0x0D, 0x14, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0x39, 0x14, 0x00, 0x08, - 0x4D, 0x12, 0x00, 0x08, 0xCD, 0x1F, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, - 0xF5, 0x1F, 0x00, 0x08, 0x1D, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x1F, 0x00, 0x08, - 0xA1, 0x1F, 0x00, 0x08, 0xC9, 0x1F, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, - 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0xE3, 0x10, 0x00, 0x08, 0x39, 0x20, 0x00, 0x08, - 0xE3, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x10, 0x00, 0x08, 0x03, 0x48, 0x85, 0x46, - 0x00, 0xF0, 0x80, 0xF8, 0x00, 0x48, 0x00, 0x47, 0x55, 0x39, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x20, - 0x04, 0x48, 0x80, 0x47, 0x04, 0x48, 0x00, 0x47, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, - 0xFE, 0xE7, 0xFE, 0xE7, 0x89, 0x1F, 0x00, 0x08, 0xBD, 0x10, 0x00, 0x08, 0x03, 0x46, 0x0B, 0x43, - 0x9B, 0x07, 0x03, 0xD0, 0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, 0x04, 0x2A, 0xFA, 0xD2, - 0x03, 0xE0, 0x0B, 0x78, 0x03, 0x70, 0x40, 0x1C, 0x49, 0x1C, 0x52, 0x1E, 0xF9, 0xD2, 0x70, 0x47, - 0xD2, 0xB2, 0x01, 0xE0, 0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, 0x70, 0x47, 0x00, 0x22, - 0xF6, 0xE7, 0x10, 0xB5, 0x13, 0x46, 0x0A, 0x46, 0x04, 0x46, 0x19, 0x46, 0xFF, 0xF7, 0xF0, 0xFF, - 0x20, 0x46, 0x10, 0xBD, 0x70, 0xB5, 0x04, 0x46, 0x00, 0x29, 0x03, 0xD0, 0x00, 0x20, 0x20, 0x22, - 0x01, 0x23, 0x10, 0xE0, 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xC0, 0x43, 0xC0, 0x46, 0xC0, 0x46, - 0x0D, 0xE0, 0x25, 0x46, 0xD5, 0x40, 0x8D, 0x42, 0x05, 0xD3, 0x0D, 0x46, 0x95, 0x40, 0x64, 0x1B, - 0x1D, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, 0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x21, 0x46, - 0x70, 0xBD, 0x00, 0x00, 0x70, 0xB5, 0x00, 0x25, 0x04, 0x46, 0x2E, 0x46, 0x00, 0x29, 0x11, 0xD0, - 0x00, 0x2C, 0x01, 0xDA, 0x01, 0x25, 0x44, 0x42, 0x00, 0x29, 0x01, 0xDA, 0x01, 0x26, 0x49, 0x42, - 0x20, 0x46, 0xFF, 0xF7, 0xCF, 0xFF, 0xB5, 0x42, 0x00, 0xD0, 0x40, 0x42, 0x00, 0x2D, 0x00, 0xD0, - 0x49, 0x42, 0x70, 0xBD, 0x00, 0x2C, 0x02, 0xDA, 0x01, 0x21, 0xC9, 0x07, 0x02, 0xE0, 0x00, 0x2C, - 0x00, 0xDD, 0x03, 0x49, 0x08, 0x46, 0xC0, 0x46, 0xC0, 0x46, 0x21, 0x46, 0x70, 0xBD, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0x7F, 0x1C, 0xB5, 0x09, 0x48, 0x00, 0x90, 0x09, 0x48, 0x01, 0x90, 0x05, 0x46, - 0x01, 0x26, 0x00, 0x9C, 0x05, 0xE0, 0xE3, 0x68, 0x07, 0xCC, 0x33, 0x43, 0x0C, 0x3C, 0x98, 0x47, - 0x10, 0x34, 0xAC, 0x42, 0xF7, 0xD3, 0xFF, 0xF7, 0x6D, 0xFF, 0x00, 0x00, 0x08, 0x5E, 0x00, 0x08, - 0x28, 0x5E, 0x00, 0x08, 0x70, 0xB5, 0x8C, 0x18, 0x05, 0x78, 0x40, 0x1C, 0x6B, 0x07, 0x5B, 0x0F, - 0x01, 0xD1, 0x03, 0x78, 0x40, 0x1C, 0x2A, 0x11, 0x06, 0xD1, 0x02, 0x78, 0x40, 0x1C, 0x03, 0xE0, - 0x06, 0x78, 0x0E, 0x70, 0x40, 0x1C, 0x49, 0x1C, 0x5B, 0x1E, 0xF9, 0xD1, 0x2B, 0x07, 0x05, 0xD4, - 0x00, 0x23, 0x52, 0x1E, 0x0D, 0xD4, 0x0B, 0x70, 0x49, 0x1C, 0xFA, 0xE7, 0x03, 0x78, 0x40, 0x1C, - 0xCB, 0x1A, 0x92, 0x1C, 0x03, 0xE0, 0x1D, 0x78, 0x0D, 0x70, 0x49, 0x1C, 0x5B, 0x1C, 0x52, 0x1E, - 0xF9, 0xD5, 0xA1, 0x42, 0xD8, 0xD3, 0x00, 0x20, 0x70, 0xBD, 0x00, 0x00, 0xB0, 0xB5, 0xA9, 0x4C, - 0x20, 0x68, 0x80, 0x02, 0x00, 0xD4, 0xB0, 0xBD, 0x01, 0x20, 0x45, 0x05, 0x09, 0x48, 0x00, 0x68, - 0x09, 0x49, 0x09, 0x68, 0x49, 0x08, 0x88, 0x42, 0x03, 0xD9, 0x25, 0x60, 0x02, 0xF0, 0xF2, 0xF9, - 0xB0, 0xBD, 0x01, 0xF0, 0x1B, 0xFF, 0x05, 0x49, 0x09, 0x78, 0x88, 0x42, 0xEB, 0xD1, 0x25, 0x60, - 0xB0, 0xBD, 0xC0, 0x46, 0x24, 0x00, 0x00, 0x40, 0x7C, 0x05, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20, - 0x05, 0x48, 0x81, 0x88, 0x05, 0x4A, 0x11, 0x80, 0x01, 0x88, 0x05, 0x4A, 0x11, 0x80, 0x40, 0x88, - 0x04, 0x49, 0x08, 0x80, 0x70, 0x47, 0xC0, 0x46, 0x9C, 0x05, 0x00, 0x20, 0x5C, 0x05, 0x00, 0x20, - 0x60, 0x05, 0x00, 0x20, 0x46, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x93, 0xB0, 0x00, 0x27, 0x11, 0x97, - 0x10, 0x97, 0x0F, 0x97, 0x0E, 0x97, 0x0D, 0x97, 0x0C, 0x97, 0x0B, 0x97, 0x0A, 0x97, 0x09, 0x97, - 0x08, 0x97, 0x07, 0x97, 0x06, 0x97, 0x05, 0x97, 0x04, 0x97, 0x03, 0x97, 0x01, 0x21, 0x48, 0x02, - 0x0B, 0x46, 0x01, 0x91, 0x44, 0x49, 0x4A, 0x68, 0x02, 0x43, 0x4A, 0x60, 0x4A, 0x68, 0x02, 0x40, - 0x12, 0x92, 0x12, 0x98, 0x58, 0x04, 0x0A, 0x68, 0x02, 0x43, 0x0A, 0x60, 0x09, 0x68, 0x01, 0x40, - 0x12, 0x91, 0x12, 0x98, 0x07, 0x97, 0x03, 0x26, 0x04, 0x96, 0x40, 0x20, 0x00, 0x90, 0x03, 0x90, - 0x09, 0x20, 0xC4, 0x06, 0x03, 0xAD, 0x20, 0x46, 0x29, 0x46, 0x00, 0xF0, 0x1F, 0xF9, 0x07, 0x97, - 0x04, 0x96, 0x08, 0x20, 0x02, 0x90, 0x03, 0x90, 0x20, 0x46, 0x29, 0x46, 0x00, 0xF0, 0x16, 0xF9, - 0x32, 0x48, 0x01, 0x68, 0x32, 0x4A, 0x0A, 0x40, 0x02, 0x60, 0x31, 0x03, 0x02, 0x68, 0x8A, 0x43, - 0x02, 0x60, 0x01, 0x68, 0x20, 0x22, 0x0A, 0x43, 0x02, 0x60, 0x01, 0x68, 0x00, 0x9C, 0xA1, 0x43, - 0x01, 0x60, 0x01, 0x68, 0x80, 0x22, 0x0A, 0x43, 0x02, 0x60, 0x31, 0x02, 0x02, 0x68, 0x8A, 0x43, - 0x01, 0x9B, 0x19, 0x02, 0x51, 0x18, 0x01, 0x60, 0xB1, 0x02, 0x02, 0x68, 0x8A, 0x43, 0x99, 0x02, - 0x1E, 0x46, 0x51, 0x18, 0x01, 0x60, 0x23, 0x4D, 0x28, 0x68, 0x02, 0x99, 0x08, 0x43, 0x28, 0x60, - 0x28, 0x68, 0x20, 0x43, 0x28, 0x60, 0x18, 0x04, 0x29, 0x68, 0x01, 0x43, 0x29, 0x60, 0x07, 0x20, - 0x80, 0x05, 0x1D, 0x49, 0x0A, 0x68, 0x82, 0x43, 0xD8, 0x05, 0x10, 0x18, 0x08, 0x60, 0xD8, 0x07, - 0x0E, 0x90, 0x11, 0x97, 0x10, 0x97, 0x0F, 0x97, 0x2C, 0x46, 0x28, 0x3C, 0x0E, 0xA9, 0x20, 0x46, - 0x00, 0xF0, 0xA4, 0xF8, 0x0D, 0x97, 0x0C, 0x96, 0x0B, 0x97, 0x0A, 0x97, 0x09, 0x97, 0x09, 0xA9, - 0x20, 0x46, 0x00, 0xF0, 0xB5, 0xF8, 0x04, 0x20, 0xC0, 0x43, 0x29, 0x46, 0x1C, 0x39, 0x0A, 0x68, - 0x02, 0x40, 0x0A, 0x60, 0x29, 0x46, 0x14, 0x39, 0x0A, 0x68, 0x06, 0x23, 0x13, 0x43, 0xB3, 0x43, - 0x0B, 0x60, 0x24, 0x3D, 0x29, 0x68, 0x01, 0x40, 0x29, 0x60, 0x28, 0x68, 0x02, 0x99, 0x88, 0x43, - 0x28, 0x60, 0x13, 0xB0, 0xF0, 0xBD, 0xC0, 0x46, 0x14, 0x10, 0x02, 0x40, 0x08, 0x00, 0x02, 0x40, - 0xEF, 0xBF, 0xFF, 0xFF, 0x28, 0x24, 0x01, 0x40, 0x08, 0x27, 0x01, 0x40, 0x10, 0xB5, 0x09, 0x4C, - 0x20, 0x68, 0x80, 0x07, 0x03, 0xD4, 0x20, 0x68, 0x00, 0x07, 0x07, 0xD4, 0x10, 0xBD, 0x01, 0x20, - 0x60, 0x60, 0xFF, 0xF7, 0x35, 0xFF, 0x20, 0x68, 0x00, 0x07, 0xF7, 0xD5, 0x08, 0x20, 0x60, 0x60, - 0x10, 0xBD, 0xC0, 0x46, 0x00, 0x00, 0x02, 0x40, 0xF0, 0xB5, 0x81, 0xB0, 0x01, 0x25, 0xEC, 0x03, - 0x21, 0x48, 0x01, 0x78, 0x21, 0x48, 0x00, 0x29, 0x11, 0xD0, 0x1E, 0x49, 0x09, 0x78, 0x00, 0x29, - 0x0D, 0xD0, 0x29, 0x03, 0x02, 0x68, 0x0A, 0x43, 0x02, 0x60, 0x00, 0x21, 0x01, 0x64, 0x1F, 0x48, - 0x00, 0x78, 0x00, 0x28, 0x24, 0xD0, 0x03, 0xF0, 0x01, 0xFB, 0x02, 0x25, 0x22, 0xE0, 0x01, 0x1F, - 0x0A, 0x68, 0x52, 0x04, 0x0C, 0xD5, 0x16, 0x4A, 0x12, 0x78, 0x00, 0x2A, 0x08, 0xD0, 0xAA, 0x03, - 0x14, 0x4B, 0x1E, 0x68, 0x02, 0x27, 0x37, 0x43, 0x08, 0x26, 0xB7, 0x43, 0x1F, 0x60, 0x02, 0x60, - 0x0A, 0x68, 0x92, 0x04, 0x04, 0xD4, 0x09, 0x68, 0x21, 0x42, 0x11, 0xD0, 0x29, 0x03, 0x0E, 0xE0, - 0x29, 0x03, 0x01, 0x60, 0x01, 0x6C, 0xA9, 0x43, 0x01, 0x64, 0x04, 0xF0, 0xE3, 0xFA, 0x03, 0xE0, - 0x03, 0xF0, 0x20, 0xFC, 0x0A, 0x48, 0x05, 0x70, 0x07, 0x48, 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, - 0x01, 0xB0, 0xF0, 0xBD, 0x10, 0x05, 0x00, 0x20, 0x17, 0x05, 0x00, 0x20, 0x04, 0x00, 0x02, 0x40, - 0x1A, 0x05, 0x00, 0x20, 0x20, 0x04, 0x00, 0x40, 0x10, 0x04, 0x01, 0x40, 0xE0, 0x04, 0x00, 0x20, - 0x01, 0x05, 0x00, 0x20, 0x80, 0xB5, 0x01, 0x20, 0xC0, 0x03, 0x02, 0x49, 0x08, 0x60, 0x03, 0xF0, - 0x71, 0xFA, 0x80, 0xBD, 0x14, 0x04, 0x01, 0x40, 0xFE, 0xE7, 0x00, 0x00, 0x82, 0x68, 0xD2, 0x07, - 0x12, 0xD1, 0xC2, 0x68, 0x09, 0x4B, 0x13, 0x40, 0x4A, 0x68, 0x1A, 0x43, 0x8B, 0x68, 0x13, 0x43, - 0xCA, 0x68, 0x1A, 0x43, 0xC2, 0x60, 0x03, 0x22, 0x92, 0x07, 0x03, 0x69, 0x93, 0x43, 0x09, 0x68, - 0x19, 0x43, 0x01, 0x61, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, 0x70, 0x47, 0xC7, 0x3F, 0xFF, 0xFF, - 0x70, 0xB5, 0x82, 0x68, 0xD2, 0x07, 0x0C, 0xD1, 0xC2, 0x68, 0x07, 0x4B, 0x13, 0x40, 0x74, 0xC9, - 0x09, 0x68, 0x1A, 0x43, 0x22, 0x43, 0x2A, 0x43, 0x32, 0x43, 0x0A, 0x43, 0xC2, 0x60, 0x00, 0x20, - 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, 0xC0, 0x46, 0x3C, 0xC2, 0xFE, 0xFF, 0xF0, 0xB5, 0x84, 0xB0, - 0x0B, 0x68, 0x00, 0x2B, 0x49, 0xD0, 0x00, 0x25, 0x01, 0x91, 0x00, 0x90, 0x10, 0xE0, 0x7F, 0x43, - 0x7F, 0x43, 0x3B, 0x46, 0x0F, 0x21, 0x59, 0x43, 0x8E, 0x43, 0x5D, 0x43, 0x35, 0x43, 0x00, 0x98, - 0x05, 0x51, 0x01, 0x99, 0x03, 0x9D, 0x6D, 0x1C, 0x0B, 0x68, 0x1C, 0x46, 0xEC, 0x40, 0x35, 0xD0, - 0x01, 0x22, 0xAA, 0x40, 0x1A, 0x40, 0xF6, 0xD0, 0x03, 0x95, 0x14, 0x46, 0x54, 0x43, 0x4B, 0x68, - 0x26, 0x46, 0x5E, 0x43, 0x03, 0x27, 0x67, 0x43, 0x05, 0x68, 0xBD, 0x43, 0x35, 0x43, 0x05, 0x60, - 0xFE, 0x43, 0x02, 0x93, 0x5D, 0x1E, 0x23, 0x46, 0x01, 0x2D, 0x05, 0xD8, 0x8D, 0x68, 0x5D, 0x43, - 0x87, 0x68, 0x37, 0x40, 0x2F, 0x43, 0x87, 0x60, 0x0D, 0x69, 0x5D, 0x43, 0xC7, 0x68, 0x37, 0x40, - 0x2F, 0x43, 0xC7, 0x60, 0x02, 0x9D, 0x02, 0x2D, 0x03, 0x9D, 0xD4, 0xD1, 0x17, 0x0A, 0x05, 0xD0, - 0x24, 0x24, 0x4D, 0x69, 0x06, 0x59, 0x00, 0x2F, 0xC1, 0xD1, 0x04, 0xE0, 0x20, 0x24, 0x4D, 0x69, - 0x06, 0x59, 0x00, 0x2F, 0xBB, 0xD1, 0x5B, 0x43, 0xBC, 0xE7, 0x00, 0x23, 0x4A, 0x68, 0x52, 0x1E, - 0x01, 0x2A, 0x05, 0xD8, 0xC9, 0x68, 0x59, 0x43, 0x42, 0x68, 0x9A, 0x43, 0x0A, 0x43, 0x42, 0x60, - 0x00, 0x20, 0x04, 0xB0, 0xF0, 0xBD, 0x00, 0x00, 0x80, 0xB5, 0x7D, 0x21, 0xC9, 0x00, 0xFF, 0xF7, - 0x89, 0xFD, 0x40, 0x1E, 0x03, 0x49, 0x48, 0x60, 0x00, 0x20, 0x88, 0x60, 0x05, 0x20, 0x08, 0x60, - 0x80, 0xBD, 0xC0, 0x46, 0x10, 0xE0, 0x00, 0xE0, 0xB0, 0xB5, 0x04, 0x46, 0x3E, 0x4D, 0x28, 0x68, - 0x0C, 0x21, 0x01, 0x40, 0x3D, 0x48, 0x08, 0x29, 0x0F, 0xD1, 0x29, 0x68, 0xC9, 0x03, 0x00, 0xD4, - 0x3B, 0x48, 0xA9, 0x6A, 0x0F, 0x22, 0x0A, 0x40, 0x51, 0x1C, 0xFF, 0xF7, 0x6B, 0xFD, 0x01, 0x46, - 0x28, 0x68, 0x80, 0x02, 0x00, 0x0F, 0x80, 0x1C, 0x48, 0x43, 0x20, 0x60, 0x29, 0x68, 0x09, 0x06, - 0x09, 0x0F, 0x35, 0x4A, 0x51, 0x5C, 0xC8, 0x40, 0x60, 0x60, 0x29, 0x68, 0x49, 0x05, 0x49, 0x0F, - 0x30, 0x4A, 0x51, 0x5C, 0xC8, 0x40, 0xA0, 0x60, 0xB0, 0xBD, 0xC0, 0x46, 0x10, 0xB5, 0x00, 0x28, - 0x01, 0xD0, 0x00, 0x20, 0x10, 0xBD, 0x28, 0x4C, 0xE1, 0x6A, 0x03, 0x20, 0x08, 0x40, 0xC0, 0x46, - 0x78, 0x44, 0x00, 0x79, 0x40, 0x00, 0x87, 0x44, 0x01, 0x30, 0x24, 0x29, 0x20, 0x68, 0x0C, 0x21, - 0x01, 0x40, 0x22, 0x48, 0x08, 0x29, 0x0F, 0xD1, 0x21, 0x68, 0xC9, 0x03, 0x00, 0xD4, 0x20, 0x48, - 0xA1, 0x6A, 0x0F, 0x22, 0x0A, 0x40, 0x51, 0x1C, 0xFF, 0xF7, 0x34, 0xFD, 0x01, 0x46, 0x20, 0x68, - 0x80, 0x02, 0x00, 0x0F, 0x80, 0x1C, 0x48, 0x43, 0x21, 0x68, 0x22, 0x68, 0x52, 0x05, 0x52, 0x0F, - 0x18, 0x4B, 0x9A, 0x5C, 0x09, 0x06, 0x09, 0x0F, 0x17, 0x4B, 0x59, 0x5C, 0xC8, 0x40, 0xD0, 0x40, - 0x10, 0xBD, 0xE0, 0x69, 0x02, 0x21, 0x01, 0x40, 0x88, 0x03, 0x10, 0xBD, 0x20, 0x1F, 0x00, 0x68, - 0x80, 0x07, 0xC1, 0x17, 0x0D, 0x48, 0x08, 0x40, 0x10, 0xBD, 0x20, 0x68, 0x0C, 0x21, 0x01, 0x40, - 0x0A, 0x48, 0x08, 0x29, 0xEC, 0xD1, 0x21, 0x68, 0xC9, 0x03, 0x00, 0xD4, 0x08, 0x48, 0xA1, 0x6A, - 0x0F, 0x22, 0x0A, 0x40, 0x51, 0x1C, 0xFF, 0xF7, 0x05, 0xFD, 0x01, 0x46, 0x20, 0x68, 0x80, 0x02, - 0x00, 0x0F, 0x80, 0x1C, 0x48, 0x43, 0x10, 0xBD, 0x04, 0x10, 0x02, 0x40, 0x00, 0x12, 0x7A, 0x00, - 0x00, 0x09, 0x3D, 0x00, 0x2E, 0x5D, 0x00, 0x08, 0x1E, 0x5D, 0x00, 0x08, 0x01, 0x49, 0x08, 0x60, - 0x70, 0x47, 0xC0, 0x46, 0x9C, 0x01, 0x00, 0x20, 0xB0, 0xB5, 0x1F, 0x22, 0xD3, 0x02, 0x34, 0xC9, - 0x9C, 0x43, 0x3F, 0x23, 0x9B, 0x02, 0x9D, 0x43, 0x0B, 0x78, 0x2B, 0x43, 0x23, 0x43, 0x0F, 0x24, - 0x24, 0x03, 0xA2, 0x43, 0x1A, 0x43, 0x03, 0x23, 0x9B, 0x03, 0x4C, 0x68, 0x8D, 0x68, 0x9C, 0x43, - 0x14, 0x43, 0x49, 0x88, 0x03, 0x4A, 0x0A, 0x40, 0x22, 0x43, 0x2A, 0x43, 0x42, 0x64, 0x00, 0x20, - 0xB0, 0xBD, 0xC0, 0x46, 0xFF, 0x1F, 0x00, 0x00, 0xB0, 0xB5, 0x05, 0x68, 0x01, 0x22, 0x94, 0x07, - 0xA0, 0x42, 0x05, 0xD0, 0x23, 0x4B, 0x98, 0x42, 0x02, 0xD0, 0x23, 0x4B, 0x98, 0x42, 0x17, 0xD1, - 0x70, 0x23, 0x9D, 0x43, 0x4B, 0x68, 0x2B, 0x43, 0x20, 0x4D, 0xA8, 0x42, 0x14, 0xDD, 0x20, 0x4C, - 0xA0, 0x42, 0x06, 0xDC, 0x1B, 0x4C, 0xA0, 0x42, 0x16, 0xD0, 0x20, 0x4C, 0xA0, 0x42, 0x13, 0xD0, - 0x2E, 0xE0, 0x1C, 0x4C, 0xA0, 0x42, 0x0F, 0xD0, 0x1B, 0x4C, 0xA0, 0x42, 0x0C, 0xD0, 0x27, 0xE0, - 0x2B, 0x46, 0x16, 0x4D, 0xA8, 0x42, 0xEA, 0xDC, 0xA0, 0x42, 0x05, 0xD0, 0x12, 0x4C, 0xA0, 0x42, - 0x02, 0xD0, 0x17, 0x4C, 0xA0, 0x42, 0x1B, 0xD1, 0x03, 0x24, 0x24, 0x02, 0xA3, 0x43, 0xCC, 0x68, - 0x1C, 0x43, 0x04, 0x60, 0x8B, 0x68, 0xC3, 0x62, 0x0B, 0x88, 0x83, 0x62, 0x97, 0x4B, 0xC3, 0x18, - 0x0A, 0x24, 0xE3, 0x41, 0x07, 0x2B, 0x06, 0xD8, 0x14, 0x46, 0x9C, 0x40, 0xE1, 0x23, 0x1C, 0x42, - 0x01, 0xD0, 0x09, 0x7C, 0x01, 0x63, 0x41, 0x69, 0x11, 0x43, 0x41, 0x61, 0x00, 0x20, 0xB0, 0xBD, - 0x1C, 0x46, 0xE6, 0xE7, 0x00, 0x2C, 0x01, 0x40, 0x00, 0x04, 0x00, 0x40, 0xFF, 0x2B, 0x01, 0x40, - 0xFF, 0x43, 0x01, 0x40, 0x00, 0x48, 0x01, 0x40, 0x00, 0x44, 0x01, 0x40, 0x00, 0x40, 0x01, 0x40, - 0x00, 0x20, 0x00, 0x40, 0xF0, 0xB5, 0x84, 0xB0, 0x01, 0x23, 0xFF, 0x29, 0x44, 0xDC, 0x01, 0x29, - 0x7F, 0xD0, 0x10, 0x29, 0x00, 0xD0, 0xFB, 0xE0, 0x01, 0x6A, 0x10, 0x23, 0x99, 0x43, 0x01, 0x62, - 0x01, 0x6A, 0x30, 0x23, 0x99, 0x43, 0x13, 0x69, 0x5B, 0x08, 0x5C, 0x01, 0x0C, 0x43, 0x17, 0x68, - 0x51, 0x68, 0x09, 0x01, 0x21, 0x43, 0x79, 0x4C, 0x06, 0x19, 0x0A, 0x24, 0xE6, 0x41, 0x45, 0x68, - 0x84, 0x69, 0x07, 0x2E, 0x1E, 0xD8, 0x03, 0x97, 0x01, 0x27, 0xB7, 0x40, 0xE1, 0x26, 0x37, 0x42, - 0x17, 0xD0, 0x03, 0x26, 0xB6, 0x02, 0xB5, 0x43, 0x53, 0x69, 0x02, 0x93, 0x97, 0x69, 0xD3, 0x69, - 0xBF, 0x00, 0x26, 0x46, 0x70, 0x4C, 0x3C, 0x40, 0x2C, 0x43, 0xDD, 0x00, 0x25, 0x43, 0x34, 0x46, - 0xC0, 0x23, 0x99, 0x43, 0x02, 0x9B, 0x5B, 0x08, 0xDB, 0x01, 0x0B, 0x43, 0x91, 0x68, 0x89, 0x01, - 0x19, 0x43, 0x03, 0x9F, 0x45, 0x60, 0x73, 0x23, 0x1B, 0x02, 0x9C, 0x43, 0x3B, 0x02, 0x23, 0x43, - 0x83, 0x61, 0xD2, 0x68, 0x82, 0x63, 0x76, 0xE0, 0x01, 0x27, 0x3D, 0x02, 0xA9, 0x42, 0x74, 0xD0, - 0x3D, 0x03, 0xA9, 0x42, 0x00, 0xD0, 0xB3, 0xE0, 0x01, 0x6A, 0xA9, 0x43, 0x01, 0x62, 0x5B, 0x49, - 0x45, 0x18, 0x0A, 0x21, 0xCD, 0x41, 0x01, 0x6A, 0x43, 0x68, 0x14, 0x68, 0x02, 0x94, 0x54, 0x68, - 0x01, 0x94, 0x14, 0x69, 0x03, 0x94, 0xC6, 0x69, 0x07, 0x2D, 0x00, 0xD9, 0xA3, 0xE0, 0x3C, 0x46, - 0xAC, 0x40, 0xE1, 0x25, 0x2C, 0x42, 0x00, 0xD1, 0x9D, 0xE0, 0xBC, 0x03, 0xA3, 0x43, 0x94, 0x69, - 0xA4, 0x01, 0x1C, 0x43, 0x01, 0x9D, 0x44, 0x60, 0x73, 0x23, 0x1B, 0x02, 0x9E, 0x43, 0x02, 0x9B, - 0x1B, 0x02, 0x33, 0x43, 0xC3, 0x61, 0xD2, 0x68, 0x02, 0x64, 0x03, 0x22, 0x12, 0x03, 0x91, 0x43, - 0x47, 0x4A, 0x10, 0x32, 0x03, 0x9B, 0x1B, 0x03, 0x13, 0x40, 0x0B, 0x43, 0x29, 0x03, 0x19, 0x43, - 0x39, 0xE0, 0x01, 0x27, 0xF9, 0x43, 0x03, 0x6A, 0x0B, 0x40, 0x03, 0x62, 0x13, 0x69, 0x0B, 0x40, - 0x04, 0x6A, 0x03, 0x21, 0x02, 0x91, 0x8C, 0x43, 0x1C, 0x43, 0x16, 0x68, 0x51, 0x68, 0x21, 0x43, - 0x3A, 0x4B, 0xC3, 0x18, 0x0A, 0x24, 0xE3, 0x41, 0x45, 0x68, 0x84, 0x69, 0x07, 0x2B, 0x1B, 0xD8, - 0x03, 0x96, 0x3E, 0x46, 0x9E, 0x40, 0xE1, 0x23, 0x1E, 0x42, 0x14, 0xD0, 0x02, 0x9B, 0x1B, 0x02, - 0x9D, 0x43, 0x7B, 0x02, 0x56, 0x69, 0x02, 0x96, 0x97, 0x69, 0xD6, 0x69, 0x9F, 0x43, 0x2F, 0x43, - 0x75, 0x00, 0x3D, 0x43, 0x0C, 0x23, 0x99, 0x43, 0x02, 0x9B, 0x5B, 0x08, 0xDB, 0x00, 0x0B, 0x43, - 0x91, 0x68, 0x89, 0x00, 0x19, 0x43, 0x03, 0x9E, 0x45, 0x60, 0x73, 0x23, 0x9C, 0x43, 0x34, 0x43, - 0x84, 0x61, 0xD2, 0x68, 0x42, 0x63, 0x01, 0x62, 0x41, 0xE0, 0x01, 0x6A, 0xA9, 0x43, 0x01, 0x62, - 0x24, 0x49, 0x49, 0x1C, 0x13, 0x69, 0x1B, 0x02, 0x0B, 0x40, 0x03, 0x21, 0x02, 0x91, 0x09, 0x02, - 0x05, 0x6A, 0x8D, 0x43, 0x1D, 0x43, 0x11, 0x68, 0x03, 0x91, 0x51, 0x68, 0x0B, 0x02, 0x2B, 0x43, - 0x1A, 0x49, 0x41, 0x18, 0x0A, 0x25, 0xE9, 0x41, 0x46, 0x68, 0xC5, 0x69, 0x07, 0x29, 0x1D, 0xD8, - 0x8F, 0x40, 0xE1, 0x21, 0x0F, 0x42, 0x19, 0xD0, 0x02, 0x9C, 0x21, 0x03, 0x8E, 0x43, 0x51, 0x69, - 0x01, 0x91, 0x91, 0x69, 0xD7, 0x69, 0x00, 0x97, 0x09, 0x01, 0x11, 0x4F, 0x0F, 0x40, 0x37, 0x43, - 0x00, 0x99, 0x4E, 0x01, 0x3E, 0x43, 0xA1, 0x02, 0x8B, 0x43, 0x0F, 0x49, 0x09, 0x1D, 0x01, 0x9C, - 0xA4, 0x02, 0x0C, 0x40, 0x1C, 0x43, 0x91, 0x68, 0x8B, 0x02, 0x23, 0x43, 0x46, 0x60, 0x73, 0x21, - 0x8D, 0x43, 0x03, 0x99, 0x0D, 0x43, 0xC5, 0x61, 0xD1, 0x68, 0xC1, 0x63, 0x03, 0x62, 0x00, 0x23, - 0x18, 0x46, 0x04, 0xB0, 0xF0, 0xBD, 0x1C, 0x46, 0x64, 0xE7, 0xC0, 0x46, 0x00, 0xD4, 0xFE, 0xBF, - 0xF0, 0xDF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFC, 0xF7, 0xFF, 0xFF, 0x70, 0xB5, 0x84, 0xB0, - 0x02, 0x46, 0x03, 0x68, 0x01, 0x20, 0xDB, 0x07, 0x28, 0xD1, 0x13, 0x68, 0x29, 0x4C, 0x1C, 0x40, - 0x4B, 0x68, 0x23, 0x43, 0xCC, 0x68, 0x1C, 0x43, 0x0B, 0x69, 0x23, 0x43, 0x8C, 0x69, 0x1C, 0x43, - 0x14, 0x60, 0x03, 0x23, 0x1C, 0x03, 0x55, 0x68, 0xA5, 0x43, 0x8C, 0x68, 0x2C, 0x43, 0x54, 0x60, - 0x1B, 0x02, 0x94, 0x68, 0x9C, 0x43, 0x4B, 0x69, 0x23, 0x43, 0x93, 0x60, 0x1E, 0x4B, 0x9A, 0x42, - 0x0E, 0xD0, 0x1E, 0x4B, 0x9A, 0x42, 0x09, 0xD1, 0x0E, 0x46, 0x05, 0x46, 0x14, 0x46, 0x00, 0x20, - 0xFF, 0xF7, 0xDC, 0xFD, 0x02, 0x46, 0x00, 0x2A, 0x28, 0x46, 0x0B, 0xD1, 0x04, 0xB0, 0x70, 0xBD, - 0x0E, 0x46, 0x05, 0x46, 0x14, 0x46, 0x01, 0xA8, 0xFF, 0xF7, 0xA6, 0xFD, 0x03, 0x9A, 0x00, 0x2A, - 0x28, 0x46, 0xF3, 0xD0, 0x31, 0x68, 0x00, 0x29, 0xF0, 0xD0, 0x01, 0x20, 0xC0, 0x03, 0xB3, 0x69, - 0x83, 0x42, 0x0D, 0xD1, 0x50, 0x00, 0x4A, 0x08, 0x10, 0x18, 0xFF, 0xF7, 0x13, 0xFB, 0x0C, 0x49, - 0x01, 0x40, 0x00, 0x07, 0x40, 0x0F, 0x40, 0x18, 0xE0, 0x60, 0x00, 0x20, 0x04, 0xB0, 0x70, 0xBD, - 0x48, 0x08, 0x80, 0x18, 0xFF, 0xF7, 0x06, 0xFB, 0x80, 0xB2, 0xE0, 0x60, 0x00, 0x20, 0x04, 0xB0, - 0x70, 0xBD, 0xC0, 0x46, 0xF3, 0x69, 0xFF, 0xFF, 0x00, 0x44, 0x00, 0x40, 0x00, 0x38, 0x01, 0x40, - 0xF0, 0xFF, 0x00, 0x00, 0x06, 0x48, 0x07, 0x49, 0x01, 0x60, 0x07, 0x49, 0x01, 0x60, 0x02, 0x21, - 0x41, 0x60, 0x7D, 0x21, 0x49, 0x01, 0x81, 0x60, 0x04, 0x49, 0x01, 0x60, 0x70, 0x47, 0xC0, 0x46, - 0x00, 0x30, 0x00, 0x40, 0xCC, 0xCC, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0xAA, 0xAA, 0x00, 0x00, - 0xF0, 0xB5, 0x9F, 0xB0, 0x00, 0x26, 0x1D, 0x96, 0x1C, 0x96, 0x1B, 0x96, 0x1A, 0x96, 0x19, 0x96, - 0x18, 0x96, 0x17, 0x96, 0x16, 0x96, 0x15, 0x96, 0x14, 0x96, 0x13, 0x96, 0x12, 0x96, 0x11, 0x96, - 0x10, 0x96, 0x0F, 0x96, 0x0E, 0x96, 0x0D, 0x96, 0x0C, 0x96, 0x0B, 0x96, 0x0A, 0x96, 0x09, 0x96, - 0x08, 0x96, 0x07, 0x96, 0x06, 0x96, 0x05, 0x96, 0x01, 0x24, 0xE2, 0x02, 0x04, 0x92, 0x6B, 0x49, - 0x48, 0x68, 0x10, 0x43, 0x48, 0x60, 0x48, 0x68, 0x10, 0x40, 0x1E, 0x90, 0x1E, 0x98, 0x19, 0xA9, - 0x0E, 0x74, 0x0E, 0x80, 0x1C, 0x96, 0x10, 0x46, 0x31, 0x38, 0x1B, 0x90, 0x1A, 0x96, 0x64, 0x4D, - 0x28, 0x46, 0xFF, 0xF7, 0xE1, 0xFD, 0x28, 0x68, 0x80, 0x21, 0x01, 0x91, 0x08, 0x43, 0x28, 0x60, - 0xA8, 0x68, 0x60, 0x49, 0x01, 0x40, 0xA9, 0x60, 0xA8, 0x69, 0x08, 0x21, 0x02, 0x91, 0x08, 0x43, - 0xA8, 0x61, 0x12, 0x96, 0x13, 0x96, 0x14, 0x96, 0x15, 0x96, 0x16, 0x96, 0x17, 0x96, 0x18, 0x96, - 0x60, 0x20, 0x11, 0x90, 0x11, 0xAF, 0x28, 0x46, 0x21, 0x46, 0x3A, 0x46, 0xFF, 0xF7, 0x22, 0xFE, - 0x04, 0x20, 0xC1, 0x43, 0x00, 0x91, 0xA8, 0x69, 0x08, 0x40, 0xA8, 0x61, 0xA8, 0x69, 0x04, 0x99, - 0x08, 0x43, 0xA8, 0x61, 0x13, 0x96, 0x12, 0x96, 0x10, 0x21, 0x28, 0x46, 0x3A, 0x46, 0xFF, 0xF7, - 0x11, 0xFE, 0xA8, 0x69, 0x4C, 0x49, 0x08, 0x40, 0xA8, 0x61, 0xE8, 0x69, 0x02, 0x99, 0x08, 0x43, - 0xE8, 0x61, 0x13, 0x96, 0x12, 0x96, 0x21, 0x02, 0x02, 0x91, 0x28, 0x46, 0x3A, 0x46, 0xFF, 0xF7, - 0x01, 0xFE, 0xE8, 0x69, 0x00, 0x99, 0x08, 0x40, 0xE8, 0x61, 0xE8, 0x69, 0x04, 0x99, 0x08, 0x43, - 0xE8, 0x61, 0x13, 0x96, 0x12, 0x96, 0x21, 0x03, 0x28, 0x46, 0x3A, 0x46, 0xFF, 0xF7, 0xF2, 0xFD, - 0xE8, 0x69, 0x3D, 0x49, 0x08, 0x40, 0xE8, 0x61, 0x68, 0x68, 0x70, 0x21, 0x88, 0x43, 0x68, 0x60, - 0x0B, 0xA9, 0xCE, 0x81, 0x18, 0x20, 0x08, 0x73, 0xA8, 0x68, 0x01, 0x9F, 0xB8, 0x43, 0xA8, 0x60, - 0x10, 0x96, 0x60, 0x03, 0x0F, 0x90, 0x0D, 0x96, 0x0C, 0x96, 0x0B, 0x96, 0x28, 0x46, 0xFF, 0xF7, - 0x5B, 0xFD, 0x25, 0x46, 0x60, 0x04, 0x2D, 0x4B, 0x19, 0x68, 0x01, 0x43, 0x19, 0x60, 0x19, 0x68, - 0x01, 0x40, 0x1E, 0x91, 0x1E, 0x98, 0xA0, 0x04, 0x03, 0x94, 0x19, 0x68, 0x01, 0x43, 0x19, 0x60, - 0x19, 0x68, 0x01, 0x40, 0x1E, 0x91, 0x02, 0x24, 0x0A, 0x94, 0x09, 0x96, 0x08, 0x96, 0x07, 0x96, - 0x06, 0x94, 0x05, 0x97, 0x1E, 0x98, 0x09, 0x20, 0xC0, 0x06, 0x04, 0x90, 0x05, 0xAF, 0x39, 0x46, - 0xFF, 0xF7, 0x3C, 0xFC, 0x0A, 0x94, 0x09, 0x96, 0x08, 0x96, 0x07, 0x96, 0x06, 0x94, 0x05, 0x95, - 0x1E, 0x4D, 0x28, 0x46, 0x39, 0x46, 0xFF, 0xF7, 0x31, 0xFC, 0x0A, 0x94, 0x09, 0x96, 0x08, 0x96, - 0x07, 0x96, 0x06, 0x94, 0x05, 0x94, 0x28, 0x46, 0x39, 0x46, 0xFF, 0xF7, 0x27, 0xFC, 0x0A, 0x94, - 0x09, 0x96, 0x08, 0x96, 0x07, 0x96, 0x06, 0x94, 0x02, 0x98, 0x05, 0x90, 0x04, 0x9D, 0x28, 0x46, - 0x39, 0x46, 0xFF, 0xF7, 0x1B, 0xFC, 0x0A, 0x94, 0x09, 0x96, 0x08, 0x96, 0x07, 0x96, 0x06, 0x94, - 0x03, 0x98, 0x40, 0x02, 0x05, 0x90, 0x28, 0x46, 0x39, 0x46, 0xFF, 0xF7, 0x0F, 0xFC, 0x0A, 0x94, - 0x09, 0x96, 0x08, 0x96, 0x07, 0x96, 0x06, 0x94, 0x03, 0x98, 0x80, 0x02, 0x05, 0x90, 0x28, 0x46, - 0x39, 0x46, 0xFF, 0xF7, 0x03, 0xFC, 0x1F, 0xB0, 0xF0, 0xBD, 0xC0, 0x46, 0x14, 0x10, 0x02, 0x40, - 0x00, 0x2C, 0x01, 0x40, 0xF8, 0xBF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0x00, 0x04, 0x00, 0x48, - 0x70, 0x47, 0x70, 0x47, 0xF0, 0xB5, 0x81, 0xB0, 0x33, 0x48, 0x01, 0x68, 0x01, 0x25, 0xA9, 0x43, - 0x01, 0x60, 0x32, 0x4C, 0xE0, 0x7C, 0xE1, 0x7B, 0x01, 0x29, 0x0A, 0xD1, 0x40, 0x1C, 0xE0, 0x74, - 0xC1, 0xB2, 0x07, 0x29, 0x03, 0xD3, 0x2E, 0x48, 0x05, 0x73, 0xE5, 0x74, 0x28, 0x46, 0x28, 0x40, - 0x09, 0xE0, 0x41, 0x1E, 0xE1, 0x74, 0x08, 0x06, 0x03, 0xD1, 0x29, 0x48, 0x05, 0x73, 0x06, 0x21, - 0xE1, 0x74, 0x28, 0x46, 0x88, 0x43, 0xA0, 0x73, 0xEF, 0xF3, 0x10, 0x80, 0x72, 0xB6, 0x24, 0x4F, - 0xF8, 0x7B, 0x00, 0x28, 0x02, 0xD1, 0xE0, 0x7C, 0x00, 0xF0, 0x20, 0xFA, 0x62, 0xB6, 0x00, 0xF0, - 0xFD, 0xF9, 0x20, 0x4E, 0x30, 0x68, 0x20, 0x49, 0x88, 0x42, 0x00, 0xD3, 0xE5, 0x72, 0x00, 0x20, - 0xF8, 0x76, 0x38, 0x77, 0xE0, 0x7C, 0x40, 0x00, 0x80, 0x19, 0x21, 0x6D, 0x41, 0x82, 0x48, 0x20, - 0x38, 0x5A, 0x4A, 0x22, 0xBA, 0x5A, 0x10, 0x18, 0x40, 0x08, 0x40, 0x18, 0x41, 0x08, 0x21, 0x65, - 0x2F, 0x22, 0xF1, 0x4B, 0x9A, 0x5C, 0x00, 0x2A, 0x03, 0xD0, 0x7A, 0x7F, 0x4A, 0x43, 0x91, 0x09, - 0x02, 0xE0, 0xA2, 0x78, 0x01, 0x09, 0x51, 0x43, 0x80, 0x08, 0x40, 0x1A, 0x42, 0x22, 0xB8, 0x52, - 0x39, 0x87, 0xE0, 0x7A, 0x00, 0x28, 0x05, 0xD0, 0xB0, 0x68, 0x0C, 0x49, 0x88, 0x42, 0x07, 0xD9, - 0x01, 0xB0, 0xF0, 0xBD, 0x01, 0xF0, 0x3E, 0xF8, 0xB0, 0x68, 0x08, 0x49, 0x88, 0x42, 0xF7, 0xD8, - 0x40, 0x1C, 0xB0, 0x60, 0x01, 0xB0, 0xF0, 0xBD, 0x0C, 0x20, 0x00, 0x40, 0xC0, 0x00, 0x00, 0x20, - 0x00, 0x05, 0x00, 0x20, 0x7C, 0x05, 0x00, 0x20, 0xC5, 0x09, 0x00, 0x00, 0x0F, 0x27, 0x00, 0x00, - 0x70, 0x47, 0x70, 0x47, 0xB0, 0xB5, 0x45, 0x49, 0x0A, 0x68, 0x01, 0x20, 0x02, 0x43, 0x0A, 0x60, - 0x09, 0x68, 0x43, 0x4D, 0x29, 0x68, 0x01, 0x43, 0x29, 0x60, 0x29, 0x68, 0x89, 0x07, 0x08, 0xD4, - 0x29, 0x68, 0x89, 0x07, 0x05, 0xD4, 0x29, 0x68, 0x89, 0x07, 0x02, 0xD4, 0x29, 0x68, 0x89, 0x07, - 0xF3, 0xD5, 0x2B, 0x68, 0x80, 0x21, 0x0B, 0x43, 0x78, 0x22, 0x93, 0x43, 0x2B, 0x60, 0x6B, 0x6B, - 0x03, 0x43, 0x6B, 0x63, 0x6B, 0x6B, 0x9B, 0x07, 0x08, 0xD4, 0x6B, 0x6B, 0x9B, 0x07, 0x05, 0xD4, - 0x6B, 0x6B, 0x9B, 0x07, 0x02, 0xD4, 0x6B, 0x6B, 0x9B, 0x07, 0xF3, 0xD5, 0x6B, 0x6B, 0x0B, 0x43, - 0x93, 0x43, 0x6B, 0x63, 0x69, 0x6A, 0x01, 0x43, 0x69, 0x62, 0x69, 0x6A, 0x89, 0x07, 0x08, 0xD4, - 0x69, 0x6A, 0x89, 0x07, 0x05, 0xD4, 0x69, 0x6A, 0x89, 0x07, 0x02, 0xD4, 0x69, 0x6A, 0x89, 0x07, - 0xF3, 0xD5, 0x3D, 0x21, 0x09, 0x04, 0x6A, 0x68, 0x8A, 0x43, 0x05, 0x21, 0xC9, 0x04, 0x51, 0x18, - 0x69, 0x60, 0xE9, 0x6A, 0x0F, 0x22, 0x91, 0x43, 0xE9, 0x62, 0x01, 0x06, 0x2A, 0x68, 0x0A, 0x43, - 0x2A, 0x60, 0x41, 0x06, 0x2A, 0x68, 0x0A, 0x42, 0x08, 0xD1, 0x2A, 0x68, 0x0A, 0x42, 0x05, 0xD1, - 0x2A, 0x68, 0x0A, 0x42, 0x02, 0xD1, 0x2A, 0x68, 0x0A, 0x42, 0xF3, 0xD0, 0x69, 0x68, 0xF0, 0x22, - 0x91, 0x43, 0x69, 0x60, 0x07, 0x21, 0x09, 0x02, 0x6A, 0x68, 0x8A, 0x43, 0x6A, 0x60, 0x69, 0x68, - 0x02, 0x22, 0x0A, 0x43, 0x82, 0x43, 0x6A, 0x60, 0x69, 0x68, 0x0C, 0x20, 0x01, 0x40, 0x08, 0x29, - 0x0B, 0xD0, 0x69, 0x68, 0x01, 0x40, 0x08, 0x29, 0x07, 0xD0, 0x69, 0x68, 0x01, 0x40, 0x08, 0x29, - 0x03, 0xD0, 0x69, 0x68, 0x01, 0x40, 0x08, 0x29, 0xEE, 0xD1, 0x0A, 0x4C, 0x20, 0x46, 0xFF, 0xF7, - 0x5B, 0xFB, 0x20, 0x46, 0xFF, 0xF7, 0xF2, 0xFB, 0x68, 0x6B, 0x04, 0x21, 0x88, 0x43, 0x68, 0x63, - 0x28, 0x6B, 0x03, 0x21, 0x88, 0x43, 0x28, 0x63, 0xB0, 0xBD, 0xC0, 0x46, 0x00, 0x20, 0x02, 0x40, - 0x00, 0x10, 0x02, 0x40, 0x00, 0x6C, 0xDC, 0x02, 0x70, 0x47, 0x00, 0x00, 0x80, 0xB5, 0x01, 0x20, - 0xC0, 0x43, 0x02, 0x49, 0x08, 0x60, 0xFF, 0xF7, 0xED, 0xFE, 0x80, 0xBD, 0x10, 0x20, 0x00, 0x40, - 0x02, 0x20, 0xC0, 0x43, 0x07, 0x49, 0x0A, 0x68, 0x92, 0x07, 0x03, 0xD4, 0x0A, 0x68, 0xD2, 0x07, - 0x04, 0xD1, 0x70, 0x47, 0x08, 0x60, 0x0A, 0x68, 0xD2, 0x07, 0xFA, 0xD0, 0x40, 0x1C, 0x08, 0x60, - 0x70, 0x47, 0xC0, 0x46, 0x10, 0x40, 0x01, 0x40, 0x70, 0x47, 0x00, 0x00, 0x02, 0x20, 0xC0, 0x43, - 0x07, 0x49, 0x0A, 0x68, 0x92, 0x07, 0x03, 0xD4, 0x0A, 0x68, 0xD2, 0x07, 0x04, 0xD1, 0x70, 0x47, - 0x08, 0x60, 0x0A, 0x68, 0xD2, 0x07, 0xFA, 0xD0, 0x40, 0x1C, 0x08, 0x60, 0x70, 0x47, 0xC0, 0x46, - 0x10, 0x2C, 0x01, 0x40, 0x02, 0x20, 0xC0, 0x43, 0x07, 0x49, 0x0A, 0x68, 0x92, 0x07, 0x03, 0xD4, - 0x0A, 0x68, 0xD2, 0x07, 0x04, 0xD1, 0x70, 0x47, 0x08, 0x60, 0x0A, 0x68, 0xD2, 0x07, 0xFA, 0xD0, - 0x40, 0x1C, 0x08, 0x60, 0x70, 0x47, 0xC0, 0x46, 0x10, 0x04, 0x00, 0x40, 0x80, 0xB5, 0x05, 0x48, - 0x01, 0x68, 0xC9, 0x07, 0x04, 0xD0, 0x01, 0x21, 0xC9, 0x43, 0x01, 0x60, 0x03, 0xF0, 0x22, 0xFB, - 0x80, 0xBD, 0xC0, 0x46, 0x10, 0x10, 0x00, 0x40, 0x70, 0x47, 0x00, 0x00, 0x81, 0xB0, 0x00, 0x20, - 0x00, 0x90, 0x22, 0x48, 0x02, 0x68, 0x22, 0x49, 0x0A, 0x40, 0x52, 0x18, 0x18, 0x32, 0x02, 0x60, - 0x02, 0x68, 0x00, 0x2A, 0x08, 0xD5, 0x02, 0x68, 0x00, 0x2A, 0x05, 0xD5, 0x02, 0x68, 0x00, 0x2A, - 0x02, 0xD5, 0x02, 0x68, 0x00, 0x2A, 0xF3, 0xD4, 0x01, 0x22, 0x00, 0x92, 0x00, 0x9B, 0x00, 0x2B, - 0x17, 0xD0, 0x00, 0x9B, 0x5B, 0x1E, 0x00, 0x93, 0x00, 0x9B, 0x00, 0x2B, 0x11, 0xD0, 0x00, 0x9B, - 0x5B, 0x1E, 0x00, 0x93, 0x00, 0x9B, 0x00, 0x2B, 0x0B, 0xD0, 0x00, 0x9B, 0x5B, 0x1E, 0x00, 0x93, - 0x00, 0x9B, 0x00, 0x2B, 0x05, 0xD0, 0x00, 0x9B, 0x5B, 0x1E, 0x00, 0x93, 0x00, 0x9B, 0x00, 0x2B, - 0xE7, 0xD1, 0x03, 0x68, 0x0B, 0x40, 0x13, 0x43, 0x03, 0x60, 0x01, 0x46, 0x08, 0x39, 0x0A, 0x68, - 0xD2, 0x07, 0x08, 0xD1, 0x0A, 0x68, 0xD2, 0x07, 0x05, 0xD1, 0x0A, 0x68, 0xD2, 0x07, 0x02, 0xD1, - 0x09, 0x68, 0xC9, 0x07, 0xF1, 0xD0, 0x01, 0xB0, 0x70, 0x47, 0xC0, 0x46, 0x08, 0x24, 0x01, 0x40, - 0xE8, 0xFF, 0xFF, 0x7F, 0x70, 0xB5, 0x03, 0x21, 0x8A, 0x03, 0x09, 0x20, 0xC0, 0x06, 0x03, 0x68, - 0x93, 0x43, 0x01, 0x22, 0x94, 0x03, 0x1B, 0x19, 0x03, 0x60, 0x80, 0x23, 0x83, 0x62, 0x0B, 0x04, - 0x04, 0x68, 0x9C, 0x43, 0x13, 0x04, 0xE3, 0x18, 0x03, 0x60, 0x13, 0x02, 0x83, 0x62, 0xEB, 0x4B, - 0x1D, 0x68, 0x15, 0x43, 0x02, 0x24, 0xA5, 0x43, 0x1D, 0x60, 0x9A, 0x62, 0x8D, 0x04, 0x06, 0x68, - 0xAE, 0x43, 0x95, 0x04, 0x75, 0x19, 0x05, 0x60, 0x55, 0x02, 0x85, 0x62, 0x1D, 0x68, 0x04, 0x26, - 0x2E, 0x43, 0x08, 0x25, 0xAE, 0x43, 0x1E, 0x60, 0x9C, 0x62, 0x09, 0x05, 0x03, 0x68, 0x8B, 0x43, - 0x11, 0x05, 0x59, 0x18, 0x01, 0x60, 0x91, 0x02, 0x81, 0x62, 0x70, 0xBD, 0x70, 0xB5, 0xDC, 0x4A, - 0x14, 0x7D, 0x03, 0x21, 0x8D, 0x03, 0x09, 0x20, 0xC0, 0x06, 0x03, 0x68, 0xAB, 0x43, 0x00, 0x2C, - 0x04, 0xD0, 0x01, 0x24, 0xE4, 0x03, 0x23, 0x43, 0x03, 0x60, 0x05, 0xE0, 0x01, 0x24, 0xA4, 0x03, - 0x23, 0x43, 0x03, 0x60, 0x80, 0x23, 0x83, 0x62, 0x0B, 0x04, 0x04, 0x68, 0x9C, 0x43, 0x01, 0x23, - 0x5D, 0x04, 0x64, 0x19, 0x04, 0x60, 0x16, 0x7D, 0xCC, 0x4C, 0x25, 0x68, 0x8D, 0x43, 0x00, 0x2E, - 0x03, 0xD0, 0x02, 0x26, 0x35, 0x43, 0x25, 0x60, 0x02, 0xE0, 0x1D, 0x43, 0x25, 0x60, 0xA3, 0x62, - 0x8D, 0x04, 0x06, 0x68, 0xAE, 0x43, 0xDD, 0x04, 0x75, 0x19, 0x05, 0x60, 0x15, 0x7D, 0x22, 0x68, - 0x0C, 0x26, 0xB2, 0x43, 0x00, 0x2D, 0x03, 0xD0, 0x08, 0x25, 0x2A, 0x43, 0x22, 0x60, 0x04, 0xE0, - 0x04, 0x25, 0x2A, 0x43, 0x22, 0x60, 0x02, 0x22, 0xA2, 0x62, 0x09, 0x05, 0x02, 0x68, 0x8A, 0x43, - 0x59, 0x05, 0x51, 0x18, 0x01, 0x60, 0x70, 0xBD, 0xDC, 0x06, 0x00, 0x20, 0x0C, 0x48, 0x00, 0x68, - 0x00, 0x09, 0x18, 0x28, 0x01, 0xD8, 0x00, 0x20, 0x00, 0xE0, 0x01, 0x20, 0x80, 0x00, 0xB8, 0x49, - 0x0A, 0x68, 0x04, 0x23, 0x9A, 0x43, 0x10, 0x18, 0x08, 0x60, 0x06, 0x48, 0x01, 0x78, 0x4A, 0x42, - 0x4A, 0x41, 0x51, 0x05, 0x04, 0x4A, 0x11, 0x60, 0x00, 0x78, 0x40, 0x05, 0x50, 0x60, 0x70, 0x47, - 0x7C, 0x05, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20, 0x08, 0x04, 0x01, 0x40, 0xF0, 0xB5, 0x41, 0x1E, - 0x05, 0x29, 0x00, 0xD9, 0x48, 0xE1, 0x09, 0x20, 0xC0, 0x06, 0xC0, 0x46, 0x79, 0x44, 0x09, 0x79, - 0x49, 0x00, 0x8F, 0x44, 0x02, 0xB9, 0x5B, 0x8A, 0x2E, 0xE8, 0xA0, 0x4B, 0x19, 0x68, 0x04, 0x22, - 0x0A, 0x43, 0x08, 0x21, 0x8A, 0x43, 0x1A, 0x60, 0x02, 0x24, 0x9C, 0x62, 0x03, 0x22, 0x11, 0x05, - 0x05, 0x68, 0x8D, 0x43, 0x01, 0x21, 0x0E, 0x05, 0xAD, 0x19, 0x05, 0x60, 0x8D, 0x02, 0x85, 0x62, - 0x1D, 0x68, 0x0D, 0x43, 0xA5, 0x43, 0x1D, 0x60, 0x99, 0x61, 0x93, 0x04, 0x04, 0x68, 0x9C, 0x43, - 0x8B, 0x04, 0xE3, 0x18, 0x03, 0x60, 0x4B, 0x02, 0x83, 0x62, 0x91, 0x4B, 0x1B, 0x7D, 0x94, 0x03, - 0x02, 0x68, 0xA2, 0x43, 0x00, 0x2B, 0x00, 0xD1, 0xFB, 0xE0, 0xCB, 0x03, 0x1A, 0x43, 0x02, 0x60, - 0xFC, 0xE0, 0x03, 0x22, 0x91, 0x03, 0x03, 0x68, 0x8B, 0x43, 0x01, 0x21, 0x8C, 0x03, 0x1B, 0x19, - 0x03, 0x60, 0x80, 0x23, 0x83, 0x62, 0x13, 0x04, 0x04, 0x68, 0x9C, 0x43, 0x0B, 0x04, 0xE3, 0x18, - 0x03, 0x60, 0x0B, 0x02, 0x83, 0x62, 0x81, 0x4B, 0x1C, 0x68, 0x04, 0x25, 0x25, 0x43, 0x08, 0x24, - 0xA5, 0x43, 0x1D, 0x60, 0x02, 0x24, 0x9C, 0x61, 0x15, 0x05, 0x06, 0x68, 0xAE, 0x43, 0x0D, 0x05, - 0x75, 0x19, 0x05, 0x60, 0x8D, 0x02, 0x85, 0x62, 0x79, 0x4D, 0x2E, 0x7D, 0x1D, 0x68, 0x95, 0x43, - 0x00, 0x2E, 0x00, 0xD1, 0xBC, 0xE0, 0x25, 0x43, 0x1D, 0x60, 0xBC, 0xE0, 0x73, 0x4B, 0x1A, 0x68, - 0x01, 0x21, 0x0A, 0x43, 0x02, 0x24, 0xA2, 0x43, 0x1A, 0x60, 0x99, 0x62, 0x03, 0x22, 0x95, 0x04, - 0x06, 0x68, 0xAE, 0x43, 0x8D, 0x04, 0x75, 0x19, 0x05, 0x60, 0x4D, 0x02, 0x85, 0x62, 0x95, 0x03, - 0x06, 0x68, 0xAE, 0x43, 0x8D, 0x03, 0x75, 0x19, 0x05, 0x60, 0x80, 0x25, 0x85, 0x61, 0x12, 0x04, - 0x05, 0x68, 0x95, 0x43, 0x0A, 0x04, 0xAA, 0x18, 0x02, 0x60, 0x0A, 0x02, 0x82, 0x62, 0x64, 0x4A, - 0x16, 0x7D, 0x1D, 0x68, 0x0C, 0x22, 0x95, 0x43, 0x49, 0x05, 0x64, 0x4A, 0x00, 0x2E, 0x00, 0xD1, - 0x95, 0xE0, 0x08, 0x24, 0x25, 0x43, 0x1D, 0x60, 0xAF, 0xE0, 0x5C, 0x4B, 0x19, 0x68, 0x04, 0x22, - 0x0A, 0x43, 0x08, 0x21, 0x8A, 0x43, 0x1A, 0x60, 0x02, 0x24, 0x9C, 0x62, 0x03, 0x21, 0x0A, 0x05, - 0x06, 0x68, 0x96, 0x43, 0x01, 0x25, 0x2A, 0x05, 0xB2, 0x18, 0x02, 0x60, 0xAA, 0x02, 0x82, 0x62, - 0x8A, 0x03, 0x06, 0x68, 0x96, 0x43, 0xAA, 0x03, 0xB2, 0x18, 0x02, 0x60, 0x80, 0x22, 0x82, 0x61, - 0x0A, 0x04, 0x06, 0x68, 0x96, 0x43, 0x2A, 0x04, 0xB2, 0x18, 0x02, 0x60, 0x2A, 0x02, 0x82, 0x62, - 0x4B, 0x4A, 0x17, 0x7D, 0x1E, 0x68, 0x8E, 0x43, 0xE9, 0x04, 0x4B, 0x4A, 0x00, 0x2F, 0x6B, 0xD0, - 0x26, 0x43, 0x1E, 0x60, 0x51, 0x23, 0x81, 0xE0, 0x03, 0x22, 0x91, 0x03, 0x03, 0x68, 0x8B, 0x43, - 0x01, 0x21, 0x8C, 0x03, 0x1B, 0x19, 0x03, 0x60, 0x80, 0x23, 0x83, 0x62, 0x13, 0x04, 0x04, 0x68, - 0x9C, 0x43, 0x0B, 0x04, 0xE3, 0x18, 0x03, 0x60, 0x0B, 0x02, 0x83, 0x62, 0x3B, 0x4B, 0x1D, 0x68, - 0x0D, 0x43, 0x02, 0x24, 0xA5, 0x43, 0x1D, 0x60, 0x99, 0x61, 0x92, 0x04, 0x05, 0x68, 0x95, 0x43, - 0x8A, 0x04, 0xAA, 0x18, 0x02, 0x60, 0x4A, 0x02, 0x82, 0x62, 0x35, 0x4A, 0x16, 0x7D, 0x1D, 0x68, - 0x0C, 0x22, 0x95, 0x43, 0x49, 0x05, 0x35, 0x4A, 0x00, 0x2E, 0x4B, 0xD0, 0x08, 0x24, 0x25, 0x43, - 0x1D, 0x60, 0x61, 0x23, 0x52, 0xE0, 0x2D, 0x4A, 0x11, 0x68, 0x01, 0x23, 0x19, 0x43, 0x02, 0x24, - 0xA1, 0x43, 0x11, 0x60, 0x93, 0x62, 0x03, 0x21, 0x8D, 0x04, 0x06, 0x68, 0xAE, 0x43, 0x9D, 0x04, - 0x75, 0x19, 0x05, 0x60, 0x5D, 0x02, 0x85, 0x62, 0x15, 0x68, 0x04, 0x26, 0x2E, 0x43, 0x08, 0x25, - 0xAE, 0x43, 0x16, 0x60, 0x94, 0x61, 0x0A, 0x05, 0x04, 0x68, 0x94, 0x43, 0x1A, 0x05, 0xA2, 0x18, - 0x02, 0x60, 0x9A, 0x02, 0x82, 0x62, 0x1E, 0x4A, 0x15, 0x7D, 0x89, 0x03, 0x04, 0x68, 0x8C, 0x43, - 0x59, 0x04, 0x1C, 0x4A, 0x00, 0x2D, 0x23, 0xD0, 0xDB, 0x03, 0x1C, 0x43, 0x04, 0x60, 0x24, 0xE0, - 0x0D, 0x43, 0x1D, 0x60, 0x99, 0x62, 0xC9, 0x04, 0x61, 0x23, 0x17, 0x4A, 0x1E, 0xE0, 0x04, 0x26, - 0x35, 0x43, 0x1D, 0x60, 0x9C, 0x62, 0x18, 0xE0, 0x2E, 0x43, 0x1E, 0x60, 0x9D, 0x62, 0x51, 0x23, - 0x14, 0xE0, 0x8B, 0x03, 0x1A, 0x43, 0x02, 0x60, 0x80, 0x22, 0x82, 0x62, 0x49, 0x04, 0x51, 0x23, - 0x0C, 0x4A, 0x0B, 0xE0, 0x04, 0x26, 0x35, 0x43, 0x1D, 0x60, 0x9C, 0x62, 0x61, 0x23, 0x05, 0xE0, - 0x9B, 0x03, 0x1C, 0x43, 0x04, 0x60, 0x80, 0x23, 0x83, 0x62, 0x41, 0x23, 0x04, 0x68, 0x14, 0x40, - 0x0C, 0x43, 0x04, 0x60, 0x06, 0x48, 0x03, 0x60, 0xF0, 0xBD, 0xC0, 0x46, 0x00, 0x04, 0x00, 0x48, - 0xDC, 0x06, 0x00, 0x20, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, - 0x1C, 0x00, 0x01, 0x40, 0xF0, 0xB5, 0x89, 0xB0, 0xE0, 0x48, 0x02, 0x68, 0xC1, 0x6F, 0x89, 0x1A, - 0xDF, 0x4D, 0xA9, 0x81, 0x8B, 0xB2, 0x59, 0x09, 0x69, 0x81, 0xDE, 0x4C, 0x24, 0x88, 0xA3, 0x42, - 0x00, 0xD8, 0x23, 0xE1, 0xDC, 0x4C, 0x24, 0x88, 0xA3, 0x42, 0x00, 0xD3, 0x1E, 0xE1, 0xDB, 0x4B, - 0x00, 0x27, 0x1F, 0x80, 0x43, 0x68, 0x9A, 0x1A, 0x92, 0xB2, 0x01, 0x26, 0x8A, 0x42, 0x32, 0x46, - 0x00, 0xD8, 0x3A, 0x46, 0x2A, 0x62, 0x82, 0x68, 0xC3, 0x68, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, - 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x6A, 0x62, 0x02, 0x69, 0x43, 0x69, 0x9A, 0x1A, 0x92, 0xB2, - 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0xAA, 0x62, 0x82, 0x69, 0xC3, 0x69, 0x9A, 0x1A, - 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x07, 0x92, 0xEA, 0x62, 0x02, 0x6A, - 0x43, 0x6A, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x06, 0x92, - 0x2A, 0x63, 0x82, 0x6A, 0xC3, 0x6A, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, - 0x3A, 0x46, 0x05, 0x92, 0x6A, 0x63, 0x02, 0x6B, 0x43, 0x6B, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, - 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x04, 0x92, 0xAA, 0x63, 0x82, 0x6B, 0xC3, 0x6B, 0x9A, 0x1A, - 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x08, 0x92, 0xEA, 0x63, 0x02, 0x6C, - 0x43, 0x6C, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x02, 0x92, - 0x2A, 0x64, 0x82, 0x6C, 0xC3, 0x6C, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, - 0x3A, 0x46, 0x01, 0x92, 0x6A, 0x64, 0x02, 0x6D, 0x43, 0x6D, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, - 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x00, 0x92, 0xAA, 0x64, 0x82, 0x6D, 0xC3, 0x6D, 0x9A, 0x1A, - 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, 0x3A, 0x46, 0x03, 0x92, 0xEA, 0x64, 0x02, 0x6E, - 0x43, 0x6E, 0x9A, 0x1A, 0x92, 0xB2, 0x8A, 0x42, 0x33, 0x46, 0x00, 0xD8, 0x3B, 0x46, 0x2B, 0x65, - 0x82, 0x6E, 0xC4, 0x6E, 0xA2, 0x1A, 0x92, 0xB2, 0x8A, 0x42, 0x34, 0x46, 0x00, 0xD8, 0x3C, 0x46, - 0x6C, 0x65, 0x02, 0x6F, 0x45, 0x6F, 0xAA, 0x1A, 0x92, 0xB2, 0x8A, 0x42, 0x32, 0x46, 0x00, 0xD8, - 0x3A, 0x46, 0xB5, 0x4D, 0xAA, 0x65, 0x85, 0x6F, 0xC0, 0x6F, 0x40, 0x1B, 0x80, 0xB2, 0x88, 0x42, - 0x00, 0xD8, 0x3E, 0x46, 0xB0, 0x48, 0xC6, 0x65, 0x01, 0x6A, 0x45, 0x6A, 0x80, 0x6A, 0x06, 0x9F, - 0x79, 0x40, 0x02, 0x9F, 0x79, 0x40, 0xC9, 0x00, 0x05, 0x9F, 0x7D, 0x40, 0x01, 0x9F, 0x7D, 0x40, - 0xAD, 0x00, 0x0D, 0x43, 0x04, 0x99, 0x48, 0x40, 0x00, 0x99, 0x48, 0x40, 0x47, 0x00, 0x08, 0x98, - 0x07, 0x99, 0x48, 0x40, 0x08, 0x90, 0xD8, 0x00, 0xA1, 0x00, 0x08, 0x18, 0x51, 0x00, 0x44, 0x18, - 0xA2, 0x48, 0x00, 0x78, 0xA2, 0x4B, 0x19, 0x78, 0xC9, 0xB2, 0x01, 0x43, 0x0D, 0xD1, 0x00, 0xF0, - 0x33, 0xFD, 0x9F, 0x4B, 0x00, 0x28, 0x08, 0xD0, 0x9B, 0x49, 0x08, 0x79, 0x40, 0x1C, 0x08, 0x71, - 0xC0, 0xB2, 0x65, 0x28, 0x01, 0xD3, 0x01, 0x20, 0x18, 0x70, 0xA0, 0x19, 0x3D, 0x43, 0x08, 0x9A, - 0x03, 0x99, 0x4A, 0x40, 0x19, 0x78, 0x00, 0x29, 0x01, 0xD0, 0x0F, 0x21, 0x48, 0x40, 0xFE, 0x21, - 0x0D, 0x40, 0xA9, 0x18, 0x88, 0x42, 0x3B, 0xD1, 0x8F, 0x4C, 0x22, 0x6A, 0x63, 0x6A, 0x27, 0x46, - 0x28, 0x37, 0xE1, 0xCF, 0xA1, 0x6B, 0x07, 0x91, 0xE1, 0x6B, 0x08, 0x91, 0x92, 0x02, 0x5B, 0x02, - 0x13, 0x43, 0x02, 0x02, 0x1A, 0x43, 0xEB, 0x01, 0x13, 0x43, 0xB2, 0x01, 0x1A, 0x43, 0x7B, 0x01, - 0x13, 0x43, 0x07, 0x98, 0x01, 0x01, 0x19, 0x43, 0x08, 0x98, 0xC0, 0x00, 0x08, 0x43, 0x21, 0x6C, - 0x63, 0x6C, 0xA2, 0x6C, 0xE6, 0x6C, 0x8D, 0x00, 0x05, 0x43, 0x59, 0x00, 0x29, 0x43, 0x25, 0x46, - 0x00, 0x20, 0xEB, 0x4B, 0x18, 0x80, 0xE3, 0x89, 0x5B, 0x1C, 0xE3, 0x81, 0x01, 0x2E, 0x02, 0xD1, - 0x7C, 0x4B, 0x01, 0x24, 0x1C, 0x70, 0x11, 0x43, 0xAA, 0x79, 0x00, 0x2A, 0x15, 0xD0, 0x02, 0x2A, - 0x0E, 0xD0, 0x01, 0x2A, 0x1E, 0xD1, 0x02, 0x20, 0xA8, 0x71, 0x29, 0x82, 0x09, 0xB0, 0xF0, 0xBD, - 0x00, 0x20, 0x71, 0x49, 0x88, 0x71, 0x48, 0x8A, 0x40, 0x1C, 0x48, 0x82, 0x09, 0xB0, 0xF0, 0xBD, - 0x03, 0x20, 0xA8, 0x71, 0xE9, 0x71, 0x09, 0xB0, 0xF0, 0xBD, 0x30, 0x29, 0x15, 0xDB, 0x6A, 0x78, - 0x00, 0x2A, 0x12, 0xD0, 0x6C, 0x48, 0x00, 0x22, 0x02, 0x70, 0xDC, 0x48, 0x01, 0x80, 0xAA, 0x70, - 0x09, 0xB0, 0xF0, 0xBD, 0x03, 0x2A, 0xE9, 0xD1, 0x25, 0x29, 0xE7, 0xD1, 0x29, 0x8A, 0xEA, 0x79, - 0xD3, 0x4B, 0x5A, 0x54, 0xA8, 0x71, 0x09, 0xB0, 0xF0, 0xBD, 0x4A, 0x1E, 0x2E, 0x2A, 0x06, 0xD8, - 0x61, 0x48, 0x01, 0x70, 0xD1, 0x48, 0x00, 0x22, 0x02, 0x80, 0xC8, 0xB2, 0x0E, 0xE0, 0x00, 0x29, - 0x02, 0xD0, 0x5D, 0x48, 0x00, 0x78, 0x09, 0xE0, 0x29, 0x78, 0x01, 0x29, 0x01, 0xD1, 0x00, 0x21, - 0x69, 0x70, 0x59, 0x49, 0x08, 0x70, 0xC9, 0x49, 0x08, 0x80, 0xA8, 0x70, 0x00, 0x28, 0xC5, 0xD0, - 0x56, 0x49, 0x09, 0x78, 0x00, 0x29, 0xC1, 0xD1, 0x50, 0x49, 0x09, 0x78, 0x00, 0x29, 0xBD, 0xD0, - 0xE9, 0x78, 0x88, 0x42, 0x02, 0xD0, 0x00, 0x21, 0xA9, 0x70, 0xE8, 0x70, 0x05, 0x28, 0x05, 0xD3, - 0xA9, 0x78, 0x49, 0x1C, 0xA9, 0x70, 0xC9, 0xB2, 0x06, 0x29, 0xAF, 0xD3, 0x29, 0x46, 0x00, 0x25, - 0x8D, 0x70, 0x41, 0x1E, 0x23, 0x29, 0x00, 0xD9, 0x80, 0xE0, 0xC0, 0x46, 0x79, 0x44, 0x09, 0x79, - 0x49, 0x00, 0x8F, 0x44, 0x11, 0x4E, 0x2F, 0x6B, 0x6F, 0x3D, 0x52, 0x59, 0x78, 0x73, 0x7B, 0x1D, - 0x42, 0x27, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x15, 0x60, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, - 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x66, 0x3E, 0x49, 0x01, 0x20, 0x08, 0x70, 0x65, 0xE0, - 0xA7, 0x48, 0x40, 0x7C, 0x01, 0x21, 0x08, 0x1A, 0x39, 0x49, 0x08, 0x70, 0x14, 0x20, 0x5D, 0xE0, - 0x02, 0xF0, 0x6E, 0xF9, 0xA2, 0x48, 0x40, 0x7C, 0x40, 0x1C, 0x36, 0x49, 0x08, 0x70, 0x32, 0x48, - 0x00, 0x78, 0x53, 0xE0, 0x2C, 0x49, 0x4D, 0x71, 0x07, 0x20, 0x40, 0x02, 0xFF, 0x30, 0x08, 0x81, - 0x0E, 0x20, 0x4B, 0xE0, 0x2F, 0x49, 0x03, 0x20, 0x08, 0x70, 0x47, 0xE0, 0xD8, 0x05, 0x00, 0x20, - 0x80, 0x04, 0x00, 0x20, 0xF0, 0x04, 0x00, 0x20, 0x40, 0x01, 0x00, 0x20, 0x44, 0x05, 0x00, 0x20, - 0x29, 0x48, 0x01, 0x21, 0x01, 0x70, 0x06, 0x20, 0x38, 0xE0, 0x01, 0x21, 0x1E, 0x4B, 0x59, 0x71, - 0x07, 0x20, 0x40, 0x02, 0x18, 0x81, 0x1A, 0x78, 0x0D, 0x20, 0x01, 0x2A, 0x2E, 0xD1, 0x59, 0x70, - 0x2C, 0xE0, 0x20, 0x49, 0x02, 0x20, 0x08, 0x70, 0x28, 0xE0, 0x1D, 0x48, 0x01, 0x21, 0x01, 0x70, - 0x87, 0x48, 0x45, 0x74, 0x07, 0x20, 0x21, 0xE0, 0x19, 0x48, 0x05, 0x70, 0x84, 0x48, 0x01, 0x21, - 0x41, 0x74, 0x08, 0x20, 0x1A, 0xE0, 0x82, 0x48, 0x40, 0x7C, 0x15, 0x49, 0x08, 0x70, 0x15, 0x20, - 0x14, 0xE0, 0x01, 0x20, 0x0C, 0x49, 0x88, 0x71, 0x24, 0x20, 0x0F, 0xE0, 0x11, 0x49, 0x04, 0x20, - 0x08, 0x70, 0x0B, 0xE0, 0x0F, 0x49, 0x05, 0x20, 0x08, 0x70, 0x07, 0xE0, 0x78, 0x48, 0x01, 0x21, - 0x81, 0x74, 0x0A, 0x20, 0x02, 0xE0, 0x76, 0x48, 0x85, 0x74, 0x09, 0x20, 0x06, 0x49, 0x0D, 0x70, - 0x0A, 0x49, 0x08, 0x70, 0x09, 0xB0, 0xF0, 0xBD, 0x80, 0x04, 0x00, 0x20, 0x17, 0x05, 0x00, 0x20, - 0x10, 0x05, 0x00, 0x20, 0x0E, 0x05, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20, 0x13, 0x05, 0x00, 0x20, - 0xCF, 0x00, 0x00, 0x20, 0x0B, 0x05, 0x00, 0x20, 0x20, 0x05, 0x00, 0x20, 0x30, 0x08, 0x00, 0x20, - 0xF0, 0xB5, 0x81, 0xB0, 0x5E, 0x4E, 0x03, 0xCE, 0x08, 0x1A, 0x5E, 0x49, 0x41, 0x18, 0x5F, 0x4A, - 0x08, 0x3E, 0x91, 0x42, 0x0E, 0xD8, 0x5E, 0x4D, 0x00, 0x24, 0x2C, 0x80, 0x5D, 0x4B, 0x59, 0x78, - 0x00, 0x29, 0x09, 0xD0, 0xD9, 0x78, 0x00, 0x29, 0x66, 0xD1, 0x99, 0x78, 0x00, 0x29, 0x2D, 0xD0, - 0x18, 0x89, 0x2C, 0xE0, 0x54, 0x4D, 0x8B, 0xE0, 0x1E, 0x46, 0x59, 0x49, 0x89, 0x7C, 0x00, 0x29, - 0x0D, 0xD0, 0x59, 0x49, 0x0A, 0x88, 0x7D, 0x21, 0xCB, 0x00, 0x90, 0x42, 0x15, 0xD9, 0x52, 0x4C, - 0x24, 0x88, 0x09, 0x01, 0x00, 0x91, 0x51, 0x1C, 0x5B, 0x1C, 0x22, 0x46, 0x11, 0xE0, 0x4E, 0x49, - 0x0A, 0x88, 0x50, 0x49, 0x09, 0x88, 0x7D, 0x23, 0x1B, 0x01, 0x2F, 0x33, 0x00, 0x93, 0x2F, 0x23, - 0x01, 0xF0, 0x9A, 0xFE, 0x31, 0x28, 0x06, 0xDA, 0x06, 0xE0, 0x4A, 0x49, 0x09, 0x88, 0x00, 0x93, - 0x00, 0x23, 0x01, 0xF0, 0x91, 0xFE, 0x04, 0x46, 0x34, 0x61, 0x61, 0xE0, 0x18, 0x81, 0x49, 0x1C, - 0x99, 0x70, 0x41, 0x4F, 0x39, 0x88, 0x80, 0xB2, 0x00, 0xF0, 0x7A, 0xFB, 0x33, 0x28, 0x03, 0xD3, - 0x3C, 0x4B, 0x5C, 0x70, 0x39, 0x46, 0x20, 0xE0, 0x38, 0x88, 0x07, 0x21, 0x41, 0x43, 0x05, 0xCE, - 0x10, 0x1A, 0x40, 0x18, 0xC0, 0x08, 0x38, 0x80, 0x36, 0x4B, 0x99, 0x78, 0x08, 0x3E, 0x33, 0x29, - 0x39, 0x46, 0x12, 0xD3, 0x19, 0x38, 0x81, 0xB2, 0x34, 0x4A, 0x89, 0x18, 0xCA, 0x0F, 0x89, 0x18, - 0x49, 0x08, 0x21, 0x22, 0x1F, 0x46, 0x32, 0x4B, 0x99, 0x54, 0x01, 0x21, 0xF9, 0x70, 0x2E, 0x49, - 0x08, 0x80, 0x01, 0xF0, 0x53, 0xFF, 0x2C, 0x49, 0x3B, 0x46, 0x08, 0x88, 0x18, 0x81, 0xD8, 0x78, - 0x00, 0x28, 0x2D, 0xD0, 0x03, 0xCE, 0x08, 0x1A, 0x19, 0x79, 0x7D, 0x22, 0xD2, 0x00, 0xFA, 0x32, - 0x90, 0x42, 0x0D, 0xD2, 0x49, 0x1C, 0x1F, 0x46, 0x19, 0x71, 0x26, 0x4A, 0x13, 0x88, 0x07, 0x26, - 0x5E, 0x43, 0x30, 0x18, 0xC0, 0x08, 0x10, 0x80, 0xC8, 0xB2, 0x4C, 0x28, 0x04, 0xD2, 0x17, 0xE0, - 0x1F, 0x46, 0xC8, 0xB2, 0x4C, 0x28, 0x13, 0xD3, 0x1E, 0x48, 0x01, 0x88, 0x19, 0x31, 0x8A, 0xB2, - 0x14, 0x4B, 0x9A, 0x18, 0x33, 0x32, 0xD3, 0x0F, 0xD2, 0x18, 0x52, 0x08, 0x20, 0x23, 0x18, 0x4E, - 0xF2, 0x54, 0x7C, 0x70, 0x01, 0x80, 0x02, 0xF0, 0x53, 0xF8, 0x3C, 0x71, 0x01, 0xF0, 0xFA, 0xFE, - 0x00, 0x20, 0x28, 0x80, 0x15, 0x48, 0x02, 0x88, 0x0E, 0x49, 0x09, 0x69, 0x8C, 0x1A, 0x14, 0x4B, - 0x1B, 0x68, 0x9C, 0x42, 0x01, 0xDD, 0xD1, 0x18, 0x06, 0xE0, 0x54, 0x1A, 0x9C, 0x42, 0x03, 0xDD, - 0xD1, 0x1A, 0x01, 0x80, 0x01, 0xB0, 0xF0, 0xBD, 0x01, 0x80, 0x01, 0xB0, 0xF0, 0xBD, 0xC0, 0x46, - 0xD8, 0x05, 0x00, 0x20, 0xDF, 0xFC, 0xFF, 0xFF, 0x3A, 0x05, 0x00, 0x20, 0x76, 0x05, 0x00, 0x00, - 0x44, 0x05, 0x00, 0x20, 0xE4, 0x04, 0x00, 0x20, 0xDC, 0x00, 0x00, 0x20, 0x2A, 0xF9, 0xFF, 0xFF, - 0xDC, 0x06, 0x00, 0x20, 0xDA, 0x00, 0x00, 0x20, 0xDE, 0x00, 0x00, 0x20, 0x3E, 0x05, 0x00, 0x20, - 0xA8, 0x01, 0x00, 0x20, 0x80, 0xB2, 0x65, 0x49, 0x0A, 0x68, 0x0B, 0x68, 0x9B, 0x1A, 0x9B, 0xB2, - 0x98, 0x42, 0x0E, 0xD9, 0x0B, 0x68, 0x9B, 0x1A, 0x9B, 0xB2, 0x98, 0x42, 0x09, 0xD9, 0x0B, 0x68, - 0x9B, 0x1A, 0x9B, 0xB2, 0x98, 0x42, 0x04, 0xD9, 0x0B, 0x68, 0x9B, 0x1A, 0x9B, 0xB2, 0x98, 0x42, - 0xEB, 0xD8, 0x70, 0x47, 0x70, 0xB5, 0x00, 0x28, 0x3D, 0xD0, 0x03, 0x25, 0x05, 0x40, 0x7D, 0x21, - 0xC9, 0x00, 0x55, 0x4A, 0x55, 0x4B, 0x00, 0x2D, 0x04, 0x46, 0x32, 0xD0, 0x1C, 0x68, 0x1E, 0x68, - 0x36, 0x1B, 0x16, 0x40, 0x8E, 0x42, 0x0E, 0xD2, 0x1E, 0x68, 0x36, 0x1B, 0x16, 0x40, 0x8E, 0x42, - 0x09, 0xD2, 0x1E, 0x68, 0x36, 0x1B, 0x16, 0x40, 0x8E, 0x42, 0x04, 0xD2, 0x1E, 0x68, 0x36, 0x1B, - 0x16, 0x40, 0x8E, 0x42, 0xEB, 0xD3, 0x01, 0x2D, 0x03, 0xD1, 0x44, 0x1E, 0x04, 0x28, 0x36, 0xD2, - 0x19, 0xE0, 0x1C, 0x68, 0x1E, 0x68, 0x36, 0x1B, 0x16, 0x40, 0x8E, 0x42, 0x0E, 0xD2, 0x1E, 0x68, - 0x36, 0x1B, 0x16, 0x40, 0x8E, 0x42, 0x09, 0xD2, 0x1E, 0x68, 0x36, 0x1B, 0x16, 0x40, 0x8E, 0x42, - 0x04, 0xD2, 0x1E, 0x68, 0x36, 0x1B, 0x16, 0x40, 0x8E, 0x42, 0xEB, 0xD3, 0x02, 0x2D, 0x03, 0xD1, - 0x84, 0x1E, 0x04, 0x28, 0x1B, 0xD2, 0x70, 0xBD, 0x1C, 0x68, 0x1D, 0x68, 0x2D, 0x1B, 0x15, 0x40, - 0x8D, 0x42, 0x0E, 0xD2, 0x1D, 0x68, 0x2D, 0x1B, 0x15, 0x40, 0x8D, 0x42, 0x09, 0xD2, 0x1D, 0x68, - 0x2D, 0x1B, 0x15, 0x40, 0x8D, 0x42, 0x04, 0xD2, 0x1D, 0x68, 0x2D, 0x1B, 0x15, 0x40, 0x8D, 0x42, - 0xEB, 0xD3, 0xC4, 0x1E, 0x04, 0x28, 0x02, 0xD2, 0xE5, 0xE7, 0x24, 0x1F, 0xE3, 0xD0, 0x18, 0x68, - 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0x0E, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, - 0x8D, 0x42, 0x09, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0x04, 0xD2, 0x1D, 0x68, - 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0xEB, 0xD3, 0x18, 0x68, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, - 0x8D, 0x42, 0x0E, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0x09, 0xD2, 0x1D, 0x68, - 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0x04, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, - 0xEB, 0xD3, 0x18, 0x68, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0x0E, 0xD2, 0x1D, 0x68, - 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0x09, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, - 0x04, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0xEB, 0xD3, 0x18, 0x68, 0x1D, 0x68, - 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0xB8, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, - 0xB3, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, 0x15, 0x40, 0x8D, 0x42, 0xAE, 0xD2, 0x1D, 0x68, 0x2D, 0x1A, - 0x15, 0x40, 0x8D, 0x42, 0xEB, 0xD3, 0xA8, 0xE7, 0xF8, 0xFF, 0x00, 0x00, 0x24, 0x48, 0x01, 0x40, - 0xF0, 0xB5, 0x7F, 0x4B, 0xC9, 0x33, 0x7F, 0x48, 0x43, 0x80, 0x7F, 0x49, 0x0F, 0x68, 0x00, 0x25, - 0x2A, 0x46, 0x03, 0xE0, 0x52, 0x18, 0x28, 0x35, 0x78, 0x2D, 0x77, 0xD0, 0x7A, 0x49, 0x4E, 0x19, - 0x71, 0x68, 0xCF, 0x1B, 0x1B, 0xD1, 0xB7, 0x68, 0x79, 0x1A, 0x21, 0xD1, 0xF1, 0x68, 0xCF, 0x1B, - 0x27, 0xD1, 0x37, 0x69, 0x79, 0x1A, 0x2D, 0xD1, 0x71, 0x69, 0xCF, 0x1B, 0x33, 0xD1, 0xB7, 0x69, - 0x79, 0x1A, 0x39, 0xD1, 0xF1, 0x69, 0xCF, 0x1B, 0x3F, 0xD1, 0x37, 0x6A, 0x79, 0x1A, 0x45, 0xD1, - 0x71, 0x6A, 0xCF, 0x1B, 0x4B, 0xD1, 0xB7, 0x6A, 0x79, 0x1A, 0xDC, 0xD0, 0x50, 0xE0, 0x9C, 0xB2, - 0xA7, 0x42, 0x01, 0xD2, 0x47, 0x80, 0x3B, 0x46, 0xD2, 0x19, 0xB7, 0x68, 0x79, 0x1A, 0xDD, 0xD0, - 0x9C, 0xB2, 0xA1, 0x42, 0x01, 0xD2, 0x41, 0x80, 0x0B, 0x46, 0x52, 0x18, 0xF1, 0x68, 0xCF, 0x1B, - 0xD7, 0xD0, 0x9C, 0xB2, 0xA7, 0x42, 0x01, 0xD2, 0x47, 0x80, 0x3B, 0x46, 0xD2, 0x19, 0x37, 0x69, - 0x79, 0x1A, 0xD1, 0xD0, 0x9C, 0xB2, 0xA1, 0x42, 0x01, 0xD2, 0x41, 0x80, 0x0B, 0x46, 0x52, 0x18, - 0x71, 0x69, 0xCF, 0x1B, 0xCB, 0xD0, 0x9C, 0xB2, 0xA7, 0x42, 0x01, 0xD2, 0x47, 0x80, 0x3B, 0x46, - 0xD2, 0x19, 0xB7, 0x69, 0x79, 0x1A, 0xC5, 0xD0, 0x9C, 0xB2, 0xA1, 0x42, 0x01, 0xD2, 0x41, 0x80, - 0x0B, 0x46, 0x52, 0x18, 0xF1, 0x69, 0xCF, 0x1B, 0xBF, 0xD0, 0x9C, 0xB2, 0xA7, 0x42, 0x01, 0xD2, - 0x47, 0x80, 0x3B, 0x46, 0xD2, 0x19, 0x37, 0x6A, 0x79, 0x1A, 0xB9, 0xD0, 0x9C, 0xB2, 0xA1, 0x42, - 0x01, 0xD2, 0x41, 0x80, 0x0B, 0x46, 0x52, 0x18, 0x71, 0x6A, 0xCF, 0x1B, 0xB3, 0xD0, 0x9C, 0xB2, - 0xA7, 0x42, 0x01, 0xD2, 0x47, 0x80, 0x3B, 0x46, 0xD2, 0x19, 0xB7, 0x6A, 0x79, 0x1A, 0x8A, 0xD0, - 0x9C, 0xB2, 0xA1, 0x42, 0x86, 0xD2, 0x41, 0x80, 0x0B, 0x46, 0x83, 0xE7, 0x51, 0x09, 0x3F, 0x4D, - 0xA9, 0x61, 0x3F, 0x4C, 0x21, 0x78, 0x01, 0x29, 0x25, 0xD1, 0x59, 0x1E, 0x89, 0xB2, 0x02, 0x29, - 0x0A, 0xD8, 0x0F, 0x21, 0xC9, 0x01, 0x8A, 0x42, 0x06, 0xD2, 0x00, 0x21, 0xA9, 0x71, 0x39, 0x4E, - 0x31, 0x70, 0x0E, 0x26, 0x36, 0x49, 0x0E, 0xE0, 0x19, 0x1F, 0x89, 0xB2, 0x04, 0x29, 0x12, 0xD8, - 0x19, 0x21, 0xC9, 0x01, 0x8A, 0x42, 0x0E, 0xD2, 0x32, 0x49, 0x01, 0x26, 0x0E, 0x70, 0x30, 0x49, - 0x0E, 0x70, 0xA9, 0x1D, 0x07, 0x26, 0x30, 0x4F, 0x3E, 0x70, 0x01, 0x26, 0x0E, 0x70, 0x20, 0x21, - 0x01, 0x70, 0x2E, 0x49, 0x0E, 0x70, 0x1F, 0x46, 0xC9, 0x3F, 0x2D, 0x49, 0x09, 0x78, 0x01, 0x29, - 0x0D, 0xD1, 0xBD, 0xB2, 0x22, 0x4E, 0xB5, 0x42, 0x09, 0xD2, 0x26, 0x4D, 0x2F, 0x26, 0x2E, 0x70, - 0x01, 0x25, 0x27, 0x4E, 0x35, 0x70, 0x02, 0x26, 0x06, 0x70, 0x24, 0x4E, 0x35, 0x70, 0x25, 0x78, - 0x0D, 0x43, 0x00, 0xD0, 0xF0, 0xBD, 0x59, 0x1E, 0x89, 0xB2, 0x02, 0x29, 0x0A, 0xD8, 0x0F, 0x21, - 0xC9, 0x01, 0x8A, 0x42, 0x06, 0xD2, 0x00, 0x21, 0x18, 0x4A, 0x91, 0x71, 0x19, 0x4A, 0x11, 0x70, - 0x0E, 0x21, 0x0E, 0xE0, 0x19, 0x1F, 0x89, 0xB2, 0x04, 0x29, 0x14, 0x4B, 0x13, 0xD8, 0x19, 0x21, - 0xC9, 0x01, 0x8A, 0x42, 0x0F, 0xD2, 0x13, 0x49, 0x01, 0x22, 0x0A, 0x70, 0x22, 0x70, 0x9C, 0x1D, - 0x07, 0x21, 0x11, 0x4A, 0x11, 0x70, 0x01, 0x21, 0x21, 0x70, 0x20, 0x21, 0x0F, 0x4A, 0x01, 0x23, - 0x13, 0x70, 0x01, 0x70, 0xF0, 0xBD, 0xB9, 0xB2, 0x05, 0x4A, 0x91, 0x42, 0xD2, 0xD2, 0x09, 0x49, - 0x2F, 0x22, 0x0A, 0x70, 0x01, 0x21, 0x0A, 0x4A, 0x11, 0x70, 0x02, 0x21, 0xEE, 0xE7, 0xC0, 0x46, - 0x57, 0x4D, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x20, 0xD8, 0x05, 0x00, 0x20, 0xE4, 0x04, 0x00, 0x20, - 0x19, 0x05, 0x00, 0x20, 0xA4, 0x01, 0x00, 0x20, 0xE1, 0x04, 0x00, 0x20, 0x18, 0x05, 0x00, 0x20, - 0x1A, 0x05, 0x00, 0x20, 0x07, 0x48, 0x08, 0x49, 0x81, 0x60, 0x08, 0x49, 0xC1, 0x60, 0x41, 0x68, - 0x07, 0x4A, 0x0A, 0x40, 0xD1, 0x1C, 0x41, 0x60, 0x01, 0x68, 0x01, 0x22, 0x0A, 0x43, 0x02, 0x60, - 0x70, 0x47, 0xC0, 0x46, 0x08, 0x00, 0x02, 0x40, 0x40, 0x24, 0x01, 0x40, 0x9C, 0x05, 0x00, 0x20, - 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x20, 0x40, 0x05, 0x59, 0x49, 0x0A, 0x68, 0x02, 0x43, 0x0A, 0x60, - 0x70, 0x47, 0xC0, 0x46, 0xB0, 0xB5, 0x82, 0xB0, 0x4C, 0x48, 0x01, 0x68, 0x01, 0x24, 0x21, 0x43, - 0x01, 0x60, 0x01, 0x68, 0x10, 0x22, 0x0A, 0x43, 0x02, 0x60, 0x21, 0x02, 0x02, 0x68, 0x0A, 0x43, - 0x02, 0x60, 0x01, 0x68, 0x04, 0x22, 0x0A, 0x43, 0x02, 0x60, 0x01, 0x68, 0x40, 0x22, 0x0A, 0x43, - 0x02, 0x60, 0xA1, 0x02, 0x02, 0x68, 0x0A, 0x43, 0x02, 0x60, 0x21, 0x03, 0x02, 0x68, 0x0A, 0x43, - 0x02, 0x60, 0x64, 0x21, 0x01, 0x62, 0x01, 0x46, 0x20, 0x39, 0x0A, 0x68, 0x22, 0x43, 0x0A, 0x60, - 0xE5, 0x03, 0x41, 0x6A, 0x29, 0x43, 0x41, 0x62, 0x0C, 0x38, 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, - 0x37, 0x48, 0x01, 0x6A, 0x21, 0x43, 0x01, 0x62, 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, 0x35, 0x48, - 0x01, 0x46, 0x0C, 0x39, 0x0A, 0x68, 0x22, 0x43, 0x0A, 0x60, 0x81, 0x68, 0x21, 0x43, 0x81, 0x60, - 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, 0x01, 0x68, 0xA1, 0x43, 0x01, 0x60, 0x2E, 0x48, 0x01, 0x68, - 0x21, 0x43, 0x01, 0x60, 0x41, 0x69, 0x21, 0x43, 0x41, 0x61, 0xA0, 0x07, 0x01, 0x68, 0x21, 0x43, - 0x01, 0x60, 0x41, 0x69, 0x21, 0x43, 0x41, 0x61, 0x28, 0x48, 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, - 0x41, 0x69, 0x21, 0x43, 0x41, 0x61, 0xC1, 0x68, 0x21, 0x43, 0xC1, 0x60, 0xFE, 0xF7, 0x9C, 0xF9, - 0xFF, 0xF7, 0x80, 0xFF, 0xFF, 0xF7, 0x5A, 0xF8, 0x00, 0x20, 0x01, 0x90, 0x51, 0x48, 0x01, 0x68, - 0x21, 0x43, 0x01, 0x60, 0x1E, 0x48, 0x00, 0x68, 0x1E, 0x49, 0xFE, 0xF7, 0xCB, 0xF8, 0x06, 0x21, - 0x41, 0x43, 0x01, 0x91, 0x01, 0x98, 0x00, 0x28, 0x17, 0xD0, 0x01, 0x98, 0x40, 0x1E, 0x01, 0x90, - 0x01, 0x98, 0x00, 0x28, 0x11, 0xD0, 0x01, 0x98, 0x40, 0x1E, 0x01, 0x90, 0x01, 0x98, 0x00, 0x28, - 0x0B, 0xD0, 0x01, 0x98, 0x40, 0x1E, 0x01, 0x90, 0x01, 0x98, 0x00, 0x28, 0x05, 0xD0, 0x01, 0x98, - 0x40, 0x1E, 0x01, 0x90, 0x01, 0x98, 0x00, 0x28, 0xE7, 0xD1, 0xFF, 0x20, 0x00, 0x06, 0x0E, 0x49, - 0x0A, 0x68, 0x82, 0x43, 0xE0, 0x07, 0x10, 0x18, 0x08, 0x60, 0x0C, 0x48, 0x80, 0x21, 0x01, 0x60, - 0x0B, 0x48, 0x01, 0x68, 0x29, 0x43, 0x01, 0x60, 0x02, 0xB0, 0xB0, 0xBD, 0x20, 0x2C, 0x01, 0x40, - 0x00, 0x04, 0x00, 0x40, 0x0C, 0x20, 0x00, 0x40, 0x00, 0x48, 0x01, 0x40, 0x00, 0x10, 0x00, 0x40, - 0x9C, 0x01, 0x00, 0x20, 0x40, 0x0D, 0x03, 0x00, 0x04, 0xE4, 0x00, 0xE0, 0x00, 0xE1, 0x00, 0xE0, - 0x00, 0x04, 0x01, 0x40, 0x70, 0xB5, 0x03, 0x21, 0x8A, 0x03, 0x09, 0x20, 0xC0, 0x06, 0x03, 0x68, - 0x93, 0x43, 0x01, 0x22, 0x94, 0x03, 0x1B, 0x19, 0x03, 0x60, 0x80, 0x23, 0x83, 0x61, 0x0B, 0x04, - 0x04, 0x68, 0x9C, 0x43, 0x13, 0x04, 0xE3, 0x18, 0x03, 0x60, 0x13, 0x02, 0x83, 0x62, 0x0F, 0x4B, - 0x1D, 0x68, 0x15, 0x43, 0x02, 0x24, 0xA5, 0x43, 0x1D, 0x60, 0x9A, 0x61, 0x8D, 0x04, 0x06, 0x68, - 0xAE, 0x43, 0x95, 0x04, 0x75, 0x19, 0x05, 0x60, 0x55, 0x02, 0x85, 0x62, 0x1D, 0x68, 0x04, 0x26, - 0x2E, 0x43, 0x08, 0x25, 0xAE, 0x43, 0x1E, 0x60, 0x9C, 0x61, 0x09, 0x05, 0x03, 0x68, 0x8B, 0x43, - 0x11, 0x05, 0x59, 0x18, 0x01, 0x60, 0x91, 0x02, 0x81, 0x62, 0x70, 0xBD, 0x00, 0x04, 0x00, 0x48, - 0x02, 0x48, 0x01, 0x68, 0x01, 0x22, 0x0A, 0x43, 0x02, 0x60, 0x70, 0x47, 0x14, 0x2C, 0x01, 0x40, - 0x40, 0x1A, 0xC1, 0x17, 0x48, 0x40, 0x40, 0x1A, 0x70, 0x47, 0x00, 0x00, 0x07, 0x48, 0x00, 0x68, - 0xC0, 0x00, 0x07, 0x49, 0x40, 0x18, 0x01, 0x21, 0x89, 0x03, 0x81, 0x40, 0x05, 0x4A, 0x12, 0x68, - 0x0A, 0x40, 0x0E, 0x30, 0xC2, 0x40, 0xD0, 0xB2, 0x70, 0x47, 0xC0, 0x46, 0xA0, 0x01, 0x00, 0x20, - 0x20, 0xFF, 0xF7, 0xFF, 0x1C, 0x00, 0x01, 0x40, 0x02, 0x48, 0x01, 0x68, 0x10, 0x20, 0x08, 0x40, - 0x70, 0x47, 0xC0, 0x46, 0x10, 0x04, 0x00, 0x48, 0xF0, 0xB5, 0x00, 0x29, 0x34, 0xD0, 0x01, 0x22, - 0x0A, 0x40, 0x01, 0x29, 0x33, 0xD1, 0x00, 0x21, 0x0D, 0x46, 0x00, 0x2A, 0x2A, 0xD0, 0x42, 0x5C, - 0x6A, 0x40, 0x51, 0x00, 0x07, 0x20, 0x52, 0xB2, 0x00, 0x2A, 0x00, 0xD5, 0xC1, 0xE0, 0x4A, 0x00, - 0x49, 0xB2, 0x00, 0x29, 0x00, 0xD5, 0xC2, 0xE0, 0x51, 0x00, 0x52, 0xB2, 0x00, 0x2A, 0x00, 0xD5, - 0xC3, 0xE0, 0x4A, 0x00, 0x49, 0xB2, 0x00, 0x29, 0x00, 0xD5, 0xC4, 0xE0, 0x51, 0x00, 0x52, 0xB2, - 0x00, 0x2A, 0x00, 0xD5, 0xC5, 0xE0, 0x4A, 0x00, 0x49, 0xB2, 0x00, 0x29, 0x00, 0xD5, 0xC6, 0xE0, - 0x51, 0x00, 0x52, 0xB2, 0x00, 0x2A, 0x00, 0xD5, 0xC7, 0xE0, 0x4D, 0x00, 0x49, 0xB2, 0x00, 0x29, - 0x00, 0xD5, 0x45, 0x40, 0xE8, 0xB2, 0xF0, 0xBD, 0x00, 0x25, 0xE8, 0xB2, 0xF0, 0xBD, 0xFE, 0x23, - 0x0B, 0x40, 0x00, 0x21, 0x0D, 0x46, 0x02, 0xE0, 0x89, 0x1C, 0x8B, 0x42, 0xC5, 0xD0, 0x46, 0x5C, - 0x6E, 0x40, 0x75, 0x00, 0x07, 0x24, 0x76, 0xB2, 0x00, 0x2E, 0x40, 0xD4, 0x6E, 0x00, 0x6D, 0xB2, - 0x00, 0x2D, 0x41, 0xD4, 0x75, 0x00, 0x76, 0xB2, 0x00, 0x2E, 0x42, 0xD4, 0x6E, 0x00, 0x6D, 0xB2, - 0x00, 0x2D, 0x43, 0xD4, 0x75, 0x00, 0x76, 0xB2, 0x00, 0x2E, 0x44, 0xD4, 0x6E, 0x00, 0x6D, 0xB2, - 0x00, 0x2D, 0x45, 0xD4, 0x77, 0x00, 0x75, 0xB2, 0x00, 0x2D, 0x46, 0xD4, 0x7D, 0x00, 0x7E, 0xB2, - 0x00, 0x2E, 0x00, 0xD5, 0x65, 0x40, 0x46, 0x18, 0x76, 0x78, 0x6E, 0x40, 0x75, 0x00, 0x76, 0xB2, - 0x00, 0x2E, 0x40, 0xD4, 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0x41, 0xD4, 0x75, 0x00, 0x76, 0xB2, - 0x00, 0x2E, 0x42, 0xD4, 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0x43, 0xD4, 0x75, 0x00, 0x76, 0xB2, - 0x00, 0x2E, 0x44, 0xD4, 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0x45, 0xD4, 0x77, 0x00, 0x75, 0xB2, - 0x00, 0x2D, 0x46, 0xD4, 0x7D, 0x00, 0x7E, 0xB2, 0x00, 0x2E, 0xB5, 0xD5, 0x47, 0xE0, 0x65, 0x40, - 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0xBD, 0xD5, 0x66, 0x40, 0x75, 0x00, 0x76, 0xB2, 0x00, 0x2E, - 0xBC, 0xD5, 0x65, 0x40, 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0xBB, 0xD5, 0x66, 0x40, 0x75, 0x00, - 0x76, 0xB2, 0x00, 0x2E, 0xBA, 0xD5, 0x65, 0x40, 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0xB9, 0xD5, - 0x66, 0x40, 0x77, 0x00, 0x75, 0xB2, 0x00, 0x2D, 0xB8, 0xD5, 0x67, 0x40, 0x7D, 0x00, 0x7E, 0xB2, - 0x00, 0x2E, 0xB7, 0xD4, 0xB7, 0xE7, 0x65, 0x40, 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0xBD, 0xD5, - 0x66, 0x40, 0x75, 0x00, 0x76, 0xB2, 0x00, 0x2E, 0xBC, 0xD5, 0x65, 0x40, 0x6E, 0x00, 0x6D, 0xB2, - 0x00, 0x2D, 0xBB, 0xD5, 0x66, 0x40, 0x75, 0x00, 0x76, 0xB2, 0x00, 0x2E, 0xBA, 0xD5, 0x65, 0x40, - 0x6E, 0x00, 0x6D, 0xB2, 0x00, 0x2D, 0xB9, 0xD5, 0x66, 0x40, 0x77, 0x00, 0x75, 0xB2, 0x00, 0x2D, - 0xB8, 0xD5, 0x67, 0x40, 0x7D, 0x00, 0x7E, 0xB2, 0x00, 0x2E, 0x00, 0xD4, 0x6C, 0xE7, 0x65, 0x40, - 0x6A, 0xE7, 0x41, 0x40, 0x4A, 0x00, 0x49, 0xB2, 0x00, 0x29, 0x00, 0xD4, 0x3C, 0xE7, 0x42, 0x40, - 0x51, 0x00, 0x52, 0xB2, 0x00, 0x2A, 0x00, 0xD4, 0x3B, 0xE7, 0x41, 0x40, 0x4A, 0x00, 0x49, 0xB2, - 0x00, 0x29, 0x00, 0xD4, 0x3A, 0xE7, 0x42, 0x40, 0x51, 0x00, 0x52, 0xB2, 0x00, 0x2A, 0x00, 0xD4, - 0x39, 0xE7, 0x41, 0x40, 0x4A, 0x00, 0x49, 0xB2, 0x00, 0x29, 0x00, 0xD4, 0x38, 0xE7, 0x42, 0x40, - 0x51, 0x00, 0x52, 0xB2, 0x00, 0x2A, 0x00, 0xD4, 0x37, 0xE7, 0x41, 0x40, 0x4D, 0x00, 0x49, 0xB2, - 0x00, 0x29, 0x00, 0xD5, 0x35, 0xE7, 0x35, 0xE7, 0x10, 0xB5, 0x82, 0xB0, 0x47, 0x49, 0x0B, 0x68, - 0x01, 0x20, 0x42, 0x07, 0x13, 0x60, 0x4B, 0x68, 0x53, 0x60, 0x8B, 0x68, 0x93, 0x60, 0xCB, 0x68, - 0xD3, 0x60, 0x0B, 0x69, 0x13, 0x61, 0x4B, 0x69, 0x53, 0x61, 0x8B, 0x69, 0x93, 0x61, 0xCB, 0x69, - 0xD3, 0x61, 0x0B, 0x6A, 0x13, 0x62, 0x4B, 0x6A, 0x53, 0x62, 0x8B, 0x6A, 0x93, 0x62, 0xCB, 0x6A, - 0xD3, 0x62, 0x0B, 0x6B, 0x13, 0x63, 0x4B, 0x6B, 0x53, 0x63, 0x8B, 0x6B, 0x93, 0x63, 0xCB, 0x6B, - 0xD3, 0x63, 0x0B, 0x6C, 0x13, 0x64, 0x4B, 0x6C, 0x53, 0x64, 0x8B, 0x6C, 0x93, 0x64, 0xCB, 0x6C, - 0xD3, 0x64, 0x0B, 0x6D, 0x13, 0x65, 0x4B, 0x6D, 0x53, 0x65, 0x8B, 0x6D, 0x93, 0x65, 0xCB, 0x6D, - 0xD3, 0x65, 0x0B, 0x6E, 0x13, 0x66, 0x4B, 0x6E, 0x53, 0x66, 0x8B, 0x6E, 0x93, 0x66, 0xCB, 0x6E, - 0xD3, 0x66, 0x0B, 0x6F, 0x13, 0x67, 0x4B, 0x6F, 0x53, 0x67, 0x8B, 0x6F, 0x93, 0x67, 0xC9, 0x6F, - 0xD1, 0x67, 0x27, 0x49, 0x0B, 0x68, 0x27, 0x4A, 0x13, 0x60, 0x4B, 0x68, 0x53, 0x60, 0x8B, 0x68, - 0x93, 0x60, 0xCB, 0x68, 0xD3, 0x60, 0x0B, 0x69, 0x13, 0x61, 0x4B, 0x69, 0x53, 0x61, 0x8B, 0x69, - 0x93, 0x61, 0xCB, 0x69, 0xD3, 0x61, 0x0B, 0x6A, 0x13, 0x62, 0x4B, 0x6A, 0x53, 0x62, 0x8B, 0x6A, - 0x93, 0x62, 0xCB, 0x6A, 0xD3, 0x62, 0x0B, 0x6B, 0x13, 0x63, 0x4B, 0x6B, 0x53, 0x63, 0x8B, 0x6B, - 0x93, 0x63, 0xC9, 0x6B, 0xD1, 0x63, 0x18, 0x49, 0x0A, 0x68, 0x02, 0x43, 0x0A, 0x60, 0x09, 0x68, - 0x01, 0x40, 0x01, 0x91, 0x01, 0x98, 0x15, 0x48, 0x01, 0x68, 0x03, 0x24, 0xA1, 0x43, 0x01, 0x60, - 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, 0x7D, 0x20, 0xC1, 0x00, 0x11, 0x48, 0x00, 0x68, 0xFD, 0xF7, - 0xA9, 0xFE, 0x40, 0x1E, 0x01, 0x0E, 0x00, 0xD0, 0xFE, 0xE7, 0x93, 0x49, 0x0D, 0x4A, 0x50, 0x60, - 0x0D, 0x48, 0x03, 0x68, 0x0B, 0x40, 0xA1, 0x07, 0x59, 0x18, 0x01, 0x60, 0x00, 0x20, 0x90, 0x60, - 0x07, 0x20, 0x10, 0x60, 0x62, 0xB6, 0x02, 0xB0, 0x10, 0xBD, 0xC0, 0x46, 0x00, 0x10, 0x00, 0x08, - 0x80, 0x10, 0x00, 0x08, 0x80, 0x00, 0x00, 0x20, 0x18, 0x10, 0x02, 0x40, 0x00, 0x00, 0x01, 0x40, - 0x9C, 0x01, 0x00, 0x20, 0x10, 0xE0, 0x00, 0xE0, 0x20, 0xED, 0x00, 0xE0, 0xF0, 0xB5, 0x93, 0xB0, - 0xFE, 0xF7, 0x18, 0xFD, 0x7C, 0x4B, 0x58, 0x68, 0x01, 0x26, 0x01, 0x96, 0x30, 0x43, 0x58, 0x60, - 0x58, 0x68, 0x30, 0x40, 0x0C, 0x90, 0x0C, 0x98, 0x30, 0x07, 0x99, 0x68, 0x01, 0x43, 0x99, 0x60, - 0x99, 0x68, 0x01, 0x40, 0x0C, 0x91, 0x0C, 0x98, 0x74, 0x48, 0x01, 0x68, 0x10, 0x22, 0x07, 0x92, - 0x11, 0x43, 0x01, 0x60, 0x75, 0x04, 0x18, 0x68, 0x28, 0x43, 0x18, 0x60, 0x18, 0x68, 0x28, 0x40, - 0x0C, 0x90, 0x0C, 0x98, 0xB1, 0x04, 0x08, 0x91, 0x18, 0x68, 0x08, 0x43, 0x18, 0x60, 0x18, 0x68, - 0x08, 0x40, 0x0C, 0x90, 0x0C, 0x98, 0x09, 0x20, 0xC1, 0x06, 0x04, 0x91, 0xF0, 0x03, 0x88, 0x62, - 0x18, 0x68, 0x30, 0x43, 0x18, 0x60, 0x18, 0x68, 0x1F, 0x46, 0x30, 0x40, 0x0C, 0x90, 0x0C, 0x98, - 0xFF, 0x20, 0x0B, 0x90, 0x00, 0x04, 0x62, 0x4A, 0x11, 0x68, 0x81, 0x43, 0xB0, 0x05, 0x08, 0x18, - 0x10, 0x60, 0xB0, 0x02, 0x5F, 0x49, 0x08, 0x60, 0x10, 0x68, 0x5F, 0x4B, 0x18, 0x40, 0xB4, 0x07, - 0x00, 0x19, 0x10, 0x60, 0xF0, 0x02, 0x0A, 0x90, 0x08, 0x60, 0xFE, 0xF7, 0x51, 0xFB, 0xB8, 0x68, - 0x30, 0x43, 0xB8, 0x60, 0xB8, 0x68, 0x30, 0x40, 0x0C, 0x90, 0x0C, 0x98, 0x17, 0x20, 0x03, 0x90, - 0x09, 0x94, 0xA0, 0x62, 0x55, 0x48, 0xE0, 0x62, 0x00, 0x24, 0x11, 0x94, 0x10, 0x94, 0x0F, 0x94, - 0x0E, 0x94, 0x0D, 0x94, 0x0C, 0x94, 0x38, 0x68, 0x05, 0x95, 0x28, 0x43, 0x38, 0x60, 0x38, 0x68, - 0x28, 0x40, 0x12, 0x90, 0x03, 0x27, 0x0D, 0x97, 0x02, 0x97, 0x02, 0x20, 0x06, 0x90, 0x0C, 0x90, - 0x12, 0x98, 0x0C, 0xAE, 0x04, 0x9D, 0x28, 0x46, 0x31, 0x46, 0xFE, 0xF7, 0x1F, 0xF8, 0x10, 0x94, - 0x0D, 0x97, 0x20, 0x20, 0x0C, 0x90, 0x28, 0x46, 0x31, 0x46, 0xFE, 0xF7, 0x17, 0xF8, 0x40, 0x49, - 0x48, 0x68, 0x0B, 0x9A, 0x90, 0x43, 0x48, 0x60, 0x0D, 0x46, 0x7D, 0x20, 0x40, 0x01, 0x01, 0x46, - 0x60, 0x31, 0x3C, 0x4B, 0x19, 0x60, 0x01, 0x9E, 0x31, 0x02, 0x37, 0x4F, 0xBA, 0x68, 0x0A, 0x43, - 0xBA, 0x60, 0xBA, 0x68, 0x0A, 0x40, 0x0C, 0x92, 0x0C, 0x99, 0x39, 0x49, 0x03, 0x9A, 0x8A, 0x62, - 0xC8, 0x62, 0xA8, 0x68, 0x34, 0x4A, 0x10, 0x40, 0xA8, 0x60, 0xF0, 0x04, 0x18, 0x60, 0x0A, 0x68, - 0x80, 0x20, 0x02, 0x43, 0x0A, 0x60, 0xB9, 0x68, 0x07, 0x9D, 0x29, 0x43, 0xB9, 0x60, 0xB9, 0x68, - 0x29, 0x40, 0x0C, 0x91, 0x0C, 0x99, 0x0B, 0x99, 0x09, 0x02, 0x29, 0x4B, 0x9A, 0x68, 0x8A, 0x43, - 0x02, 0x99, 0x89, 0x03, 0x51, 0x18, 0x99, 0x60, 0x26, 0x49, 0x05, 0x9A, 0x0A, 0x60, 0x29, 0x49, - 0x2F, 0x22, 0x0A, 0x60, 0x32, 0x23, 0x4B, 0x60, 0x79, 0x68, 0x08, 0x9B, 0x19, 0x43, 0x79, 0x60, - 0x79, 0x68, 0x19, 0x40, 0x0C, 0x91, 0x0C, 0x99, 0x23, 0x49, 0x8A, 0x62, 0x1F, 0x4A, 0xCA, 0x62, - 0x0A, 0x68, 0x82, 0x43, 0x0A, 0x60, 0x11, 0x94, 0x10, 0x94, 0x0F, 0x94, 0x0E, 0x94, 0x0D, 0x94, - 0x0C, 0x94, 0xB8, 0x68, 0x06, 0x99, 0x08, 0x43, 0xB8, 0x60, 0xB8, 0x68, 0x08, 0x40, 0x12, 0x90, - 0x12, 0x98, 0x38, 0x68, 0x18, 0x43, 0x38, 0x60, 0x38, 0x68, 0x18, 0x40, 0x12, 0x90, 0x12, 0x98, - 0x11, 0x96, 0x10, 0x94, 0x0F, 0x94, 0x0E, 0x94, 0x0D, 0x91, 0x0C, 0x95, 0x13, 0x48, 0x0C, 0xA9, - 0xFD, 0xF7, 0xAC, 0xFF, 0x0A, 0x4A, 0x10, 0x68, 0x0B, 0x49, 0x08, 0x40, 0x09, 0x99, 0x40, 0x18, - 0x10, 0x60, 0x0A, 0x98, 0x07, 0x49, 0x08, 0x60, 0x0D, 0x48, 0x04, 0x60, 0x3F, 0x21, 0x41, 0x60, - 0x01, 0xF0, 0x8A, 0xFF, 0x13, 0xB0, 0xF0, 0xBD, 0x14, 0x10, 0x02, 0x40, 0x00, 0x20, 0x02, 0x40, - 0x08, 0xE4, 0x00, 0xE0, 0x00, 0xE1, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x40, 0x28, 0x10, 0x00, 0x40, 0x00, 0x48, 0x01, 0x40, 0x00, 0x04, 0x00, 0x48, - 0x28, 0x04, 0x00, 0x40, 0xB0, 0xB5, 0x24, 0x4C, 0x20, 0x68, 0x7E, 0x28, 0x19, 0xD3, 0x23, 0x48, - 0x01, 0x68, 0xB9, 0x48, 0x7C, 0x29, 0x09, 0xD8, 0x40, 0x21, 0x41, 0x5A, 0xC9, 0x08, 0x4A, 0x29, - 0x04, 0xD8, 0xA1, 0x68, 0x89, 0x08, 0x7D, 0x29, 0x00, 0xD2, 0xB0, 0xBD, 0x41, 0x7D, 0x49, 0x1C, - 0x41, 0x75, 0xC8, 0xB2, 0x65, 0x28, 0x04, 0xD3, 0x01, 0xF0, 0x38, 0xF8, 0x00, 0x20, 0xA0, 0x60, - 0xB0, 0xBD, 0xA5, 0x4C, 0x60, 0x7B, 0x00, 0x28, 0x09, 0xD0, 0x00, 0x25, 0xFF, 0xF7, 0x06, 0xFD, - 0xA1, 0x7B, 0x88, 0x42, 0xE9, 0xD0, 0x6D, 0x1C, 0x60, 0x7B, 0x85, 0x42, 0xF6, 0xD3, 0xEF, 0xF3, - 0x10, 0x80, 0x72, 0xB6, 0x01, 0xF0, 0x22, 0xF8, 0x4A, 0x20, 0xA3, 0x49, 0x0A, 0x5A, 0x48, 0x23, - 0xCA, 0x52, 0x0A, 0x4A, 0x13, 0x68, 0x0B, 0x52, 0x00, 0x20, 0x10, 0x60, 0x08, 0x4A, 0x90, 0x61, - 0x42, 0x23, 0xC9, 0x5A, 0x49, 0x1C, 0x11, 0x62, 0x50, 0x60, 0x10, 0x68, 0x01, 0x21, 0x01, 0x43, - 0x11, 0x60, 0x62, 0xB6, 0xB0, 0xBD, 0xC0, 0x46, 0x7C, 0x05, 0x00, 0x20, 0x24, 0x00, 0x00, 0x40, - 0x0C, 0x20, 0x00, 0x40, 0xF0, 0xB5, 0x83, 0xB0, 0x87, 0x4C, 0x88, 0x49, 0xC0, 0x22, 0x20, 0x46, - 0xFD, 0xF7, 0xF4, 0xFC, 0x86, 0x48, 0x00, 0x90, 0x19, 0x20, 0x18, 0x21, 0x30, 0x22, 0x85, 0x4D, - 0x2B, 0x46, 0x00, 0xF0, 0xD1, 0xFF, 0x84, 0x4F, 0x78, 0x86, 0x20, 0x7E, 0x01, 0x46, 0x0C, 0x39, - 0x0B, 0x29, 0x07, 0xD8, 0x00, 0x95, 0x0C, 0x21, 0x18, 0x22, 0x80, 0x4B, 0x00, 0xF0, 0xC4, 0xFF, - 0x78, 0x86, 0x20, 0x7E, 0xFC, 0x21, 0x01, 0x40, 0x7D, 0x4A, 0x08, 0x29, 0x07, 0xD1, 0x00, 0x92, - 0x07, 0x21, 0x10, 0x22, 0x7B, 0x4B, 0x00, 0xF0, 0xB7, 0xFF, 0x78, 0x86, 0x00, 0xE0, 0x78, 0x8E, - 0x79, 0x49, 0x18, 0x39, 0x80, 0xB2, 0x08, 0x60, 0x60, 0x7E, 0x01, 0x46, 0x32, 0x39, 0x64, 0x29, - 0x0A, 0xD8, 0x21, 0x7D, 0x00, 0x29, 0x0C, 0xD0, 0x38, 0x85, 0x03, 0x21, 0xFD, 0xF7, 0xE2, 0xFC, - 0xB8, 0x85, 0x0A, 0x30, 0xF8, 0x85, 0x0A, 0xE0, 0x55, 0x20, 0xB8, 0x85, 0x96, 0x20, 0x38, 0x85, - 0x05, 0xE0, 0x41, 0x08, 0xB9, 0x85, 0x40, 0x00, 0x38, 0x85, 0x0A, 0x31, 0xF9, 0x85, 0xA0, 0x7E, - 0x28, 0x26, 0x70, 0x43, 0x14, 0x30, 0xB8, 0x82, 0x02, 0x20, 0x01, 0xF0, 0xC3, 0xFE, 0x60, 0x78, - 0x00, 0x28, 0x41, 0xD0, 0xA0, 0x7F, 0x0B, 0x28, 0x00, 0xD9, 0x05, 0x20, 0x01, 0xF0, 0xBA, 0xFE, - 0x27, 0x21, 0x00, 0x20, 0x02, 0x90, 0x60, 0x54, 0x23, 0x21, 0x61, 0x5C, 0x39, 0x71, 0x22, 0x21, - 0x61, 0x5C, 0x5E, 0x4A, 0x89, 0x18, 0xF9, 0x83, 0x25, 0x21, 0x61, 0x5C, 0xFA, 0x31, 0x39, 0x84, - 0x20, 0x21, 0x61, 0x5C, 0x49, 0x00, 0x5A, 0x4A, 0x89, 0x18, 0x79, 0x83, 0x21, 0x21, 0x61, 0x5C, - 0x49, 0x00, 0x58, 0x4A, 0x89, 0x18, 0xB9, 0x83, 0x24, 0x21, 0x61, 0x5C, 0x49, 0x1E, 0x4A, 0x42, - 0x4A, 0x41, 0x55, 0x49, 0xCA, 0x70, 0xA2, 0x5D, 0x1A, 0x3A, 0x15, 0x23, 0xDB, 0x43, 0x9A, 0x42, - 0x01, 0xD8, 0x05, 0x22, 0xA2, 0x55, 0x29, 0x26, 0xA5, 0x5D, 0x0B, 0x3D, 0x0A, 0x23, 0xDA, 0x43, - 0x95, 0x42, 0x00, 0xD8, 0xA3, 0x55, 0x2A, 0x25, 0x60, 0x5D, 0x06, 0x46, 0x0A, 0x3E, 0xF6, 0xB2, - 0xF6, 0x2E, 0x03, 0xD8, 0x63, 0x55, 0x17, 0xE0, 0xBD, 0x8A, 0x5B, 0xE0, 0x09, 0x23, 0xDB, 0x43, - 0x43, 0x43, 0x52, 0x3B, 0xFB, 0x70, 0xD8, 0xB2, 0x3B, 0x8D, 0x1B, 0x18, 0x3B, 0x85, 0xBB, 0x8D, - 0x1B, 0x18, 0xBB, 0x85, 0x7B, 0x8F, 0x1B, 0x18, 0x7B, 0x87, 0x7B, 0x8D, 0x1B, 0x18, 0x7B, 0x85, - 0x39, 0x4D, 0x2B, 0x68, 0x03, 0x43, 0x2B, 0x60, 0x02, 0x9E, 0x2B, 0x23, 0xE0, 0x5C, 0x8D, 0x38, - 0x47, 0x25, 0xED, 0x43, 0xA8, 0x42, 0x01, 0xD8, 0xFF, 0x20, 0xE0, 0x54, 0x2C, 0x20, 0x20, 0x5C, - 0x40, 0x1E, 0x62, 0x28, 0x01, 0xD8, 0x01, 0x20, 0x88, 0x71, 0x2D, 0x23, 0xE0, 0x5C, 0x0B, 0x38, - 0x90, 0x42, 0x01, 0xD8, 0x05, 0x20, 0xE0, 0x54, 0x2E, 0x20, 0x23, 0x5C, 0x09, 0x2B, 0x0A, 0xD8, - 0x04, 0x2B, 0x0D, 0xD8, 0x0A, 0x46, 0x1A, 0x32, 0x7B, 0x44, 0x1B, 0x79, 0x5B, 0x00, 0x9F, 0x44, - 0x02, 0x30, 0x05, 0x07, 0x33, 0x00, 0x8E, 0x76, 0x4E, 0x76, 0x28, 0x4A, 0x01, 0x20, 0x10, 0x70, - 0xBD, 0x8A, 0xA8, 0x08, 0x4A, 0x28, 0x00, 0xD8, 0xBE, 0x71, 0xE1, 0x7E, 0x20, 0x20, 0xFD, 0xF7, - 0x39, 0xFC, 0x04, 0x46, 0x0C, 0x21, 0x28, 0x46, 0xFD, 0xF7, 0x34, 0xFC, 0x21, 0x46, 0xFD, 0xF7, - 0x31, 0xFC, 0x38, 0x87, 0x64, 0x21, 0x28, 0x46, 0xFD, 0xF7, 0x2C, 0xFC, 0x21, 0x46, 0xFD, 0xF7, - 0x29, 0xFC, 0xF8, 0x86, 0x12, 0x4A, 0x4B, 0x20, 0x81, 0x00, 0x08, 0x46, 0xC8, 0x30, 0x00, 0x90, - 0xD2, 0x1C, 0x0B, 0x48, 0x43, 0x1C, 0x28, 0x46, 0x00, 0xF0, 0xDE, 0xFE, 0xB8, 0x84, 0x03, 0xB0, - 0xF0, 0xBD, 0x01, 0x20, 0x48, 0x76, 0xD0, 0xE7, 0x10, 0x48, 0x06, 0x70, 0x10, 0x48, 0x01, 0x22, - 0x02, 0x70, 0x19, 0x31, 0x0A, 0x46, 0xC9, 0xE7, 0xDC, 0x06, 0x00, 0x20, 0x38, 0x5D, 0x00, 0x08, - 0xE7, 0x03, 0x00, 0x00, 0xCF, 0x07, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x20, 0x9E, 0x0F, 0x00, 0x00, - 0xB5, 0x0B, 0x00, 0x00, 0x6D, 0x17, 0x00, 0x00, 0x44, 0x2C, 0x01, 0x40, 0x5E, 0x05, 0x00, 0x00, - 0xEE, 0x02, 0x00, 0x00, 0xD6, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, 0x81, 0x04, 0x00, 0x20, - 0x80, 0x04, 0x00, 0x20, 0x00, 0xB5, 0x8F, 0xB0, 0xFF, 0xF7, 0xC6, 0xFC, 0x7F, 0x20, 0x45, 0x01, - 0x28, 0x68, 0xE6, 0x49, 0x88, 0x42, 0x11, 0xD1, 0x68, 0x68, 0xE5, 0x49, 0x88, 0x42, 0x0D, 0xD1, - 0x28, 0x7B, 0x1F, 0x28, 0x07, 0xD0, 0x2B, 0x28, 0x03, 0xD0, 0x35, 0x28, 0x06, 0xD1, 0xE1, 0x48, - 0x02, 0xE0, 0xE1, 0x48, 0x00, 0xE0, 0xE1, 0x48, 0xE1, 0x49, 0xC8, 0x64, 0xFF, 0xF7, 0x4E, 0xFD, - 0xFF, 0xF7, 0x98, 0xFA, 0xFF, 0xF7, 0xAE, 0xFE, 0xDE, 0x4B, 0xDA, 0x78, 0x02, 0x20, 0x08, 0x90, - 0x42, 0x40, 0x18, 0x79, 0xC1, 0xB2, 0xBD, 0x20, 0x41, 0x40, 0x11, 0x43, 0x4A, 0x1E, 0x91, 0x41, - 0x5A, 0x78, 0xD2, 0xB2, 0xD6, 0x4B, 0x1B, 0x78, 0xDC, 0xB2, 0x01, 0x23, 0x94, 0x42, 0x0C, 0x93, - 0x1A, 0x46, 0x00, 0xD8, 0x00, 0x22, 0x11, 0x43, 0x01, 0x29, 0x0F, 0xD1, 0xD1, 0x4E, 0x30, 0x71, - 0x02, 0x20, 0xF0, 0x70, 0x70, 0x1D, 0xD0, 0xA1, 0x0C, 0x22, 0xFD, 0xF7, 0x87, 0xFB, 0x74, 0x70, - 0xCB, 0x48, 0xC2, 0x6C, 0xC0, 0x21, 0x30, 0x46, 0x01, 0xF0, 0x98, 0xF8, 0xC8, 0x4C, 0x60, 0x8E, - 0x20, 0x86, 0xC8, 0x4A, 0x50, 0x7C, 0x40, 0x1E, 0x41, 0x1E, 0x88, 0x41, 0xE0, 0x73, 0x10, 0x7D, - 0x00, 0x28, 0x01, 0xD1, 0x00, 0x20, 0xD0, 0x74, 0x26, 0x20, 0x02, 0x90, 0x10, 0x5C, 0xC6, 0x49, - 0x00, 0x28, 0x11, 0xD0, 0x01, 0x20, 0x50, 0x82, 0xA0, 0x71, 0x00, 0x20, 0x90, 0x75, 0x90, 0x82, - 0x08, 0x46, 0xBE, 0x30, 0x60, 0x87, 0xA0, 0x8D, 0x32, 0x30, 0xA0, 0x85, 0xE0, 0x8D, 0x32, 0x30, - 0xE0, 0x85, 0x20, 0x8D, 0x32, 0x30, 0x20, 0x85, 0x00, 0xF0, 0x88, 0xFF, 0xBB, 0x4F, 0x00, 0x26, - 0x7E, 0x87, 0xFE, 0xF7, 0x7F, 0xF8, 0xBA, 0x48, 0xBA, 0x49, 0x01, 0x60, 0x01, 0xF0, 0x0E, 0xF8, - 0x38, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x01, 0x20, 0x78, 0x71, 0x1F, 0x35, 0xB6, 0x48, 0x01, 0x68, - 0x29, 0x40, 0x01, 0x60, 0xF9, 0x66, 0x00, 0x68, 0x01, 0x0C, 0x39, 0x67, 0x00, 0x0F, 0x39, 0x46, - 0x01, 0xD1, 0x19, 0x20, 0xC6, 0x43, 0x8E, 0x77, 0x01, 0xF0, 0x36, 0xFD, 0xFF, 0x20, 0x07, 0x90, - 0x68, 0x30, 0x0E, 0x90, 0x7D, 0x20, 0x81, 0x00, 0x06, 0x91, 0xC1, 0x00, 0x0B, 0x91, 0x00, 0x01, - 0x0D, 0x90, 0xA6, 0x4D, 0x07, 0xE0, 0xA9, 0x48, 0x07, 0x60, 0x47, 0x60, 0x87, 0x60, 0xFE, 0xF7, - 0x19, 0xFB, 0x5A, 0x20, 0x2F, 0x52, 0xA6, 0x48, 0x80, 0x68, 0x05, 0x28, 0x01, 0xD3, 0x08, 0x98, - 0x00, 0xE0, 0x04, 0x20, 0xA0, 0x74, 0x60, 0x74, 0xA1, 0x4A, 0xD0, 0x8A, 0x91, 0x8A, 0x08, 0x18, - 0x11, 0x8B, 0x40, 0x18, 0x51, 0x8B, 0x40, 0x18, 0x91, 0x8B, 0x40, 0x18, 0xD1, 0x8B, 0x40, 0x18, - 0x00, 0x1D, 0x40, 0x08, 0xD0, 0x60, 0x96, 0x48, 0x96, 0x49, 0x01, 0x60, 0x8D, 0x4F, 0x78, 0x7D, - 0x01, 0x28, 0x09, 0xD1, 0x20, 0x6D, 0x61, 0x8E, 0x00, 0x91, 0x4B, 0x08, 0x60, 0x21, 0xC8, 0x22, - 0x00, 0xF0, 0xE2, 0xFD, 0x20, 0x86, 0x78, 0x7D, 0x02, 0x28, 0x15, 0xD1, 0x90, 0x48, 0x00, 0x68, - 0x01, 0x46, 0x65, 0x39, 0x94, 0x29, 0x02, 0xD8, 0x05, 0x21, 0x41, 0x43, 0x0B, 0xE0, 0x41, 0x1E, - 0x63, 0x29, 0x02, 0xD2, 0x0E, 0x99, 0x8D, 0x31, 0x05, 0xE0, 0xF9, 0x21, 0xC9, 0x43, 0xFA, 0x38, - 0x88, 0x42, 0x88, 0x49, 0x00, 0xD8, 0x21, 0x86, 0x44, 0x20, 0x28, 0x5A, 0x86, 0x49, 0x88, 0x42, - 0x04, 0xD3, 0xE8, 0x7D, 0x00, 0x28, 0x01, 0xD0, 0x00, 0xF0, 0x9C, 0xFC, 0xA8, 0x8D, 0x83, 0x49, - 0x88, 0x42, 0x32, 0xD3, 0x2D, 0x20, 0xC1, 0x00, 0xEC, 0x8D, 0x20, 0x04, 0xFD, 0xF7, 0x12, 0xFB, - 0x69, 0x6F, 0x40, 0x18, 0x68, 0x67, 0x7E, 0x48, 0x00, 0x78, 0x03, 0x28, 0x11, 0xD0, 0x02, 0x28, - 0x07, 0xD0, 0x01, 0x28, 0x1F, 0xD1, 0xE8, 0x7F, 0x0E, 0x99, 0x08, 0x18, 0x99, 0x30, 0x02, 0x21, - 0x15, 0xE0, 0xE0, 0xB2, 0x29, 0x21, 0x41, 0x43, 0xC8, 0x0A, 0x03, 0x21, 0x4A, 0x02, 0x80, 0x18, - 0x0D, 0xE0, 0x54, 0x20, 0x28, 0x5A, 0x73, 0x49, 0x41, 0x43, 0x09, 0x0C, 0x40, 0x1A, 0x40, 0x08, - 0x40, 0x18, 0x00, 0x09, 0xC0, 0xB2, 0x64, 0x49, 0x08, 0x18, 0xD6, 0x30, 0x01, 0x21, 0x6C, 0x4A, - 0x11, 0x70, 0x6D, 0x49, 0x08, 0x80, 0x00, 0x20, 0xA8, 0x85, 0x65, 0x48, 0x86, 0x68, 0x01, 0x21, - 0x00, 0x23, 0x64, 0x2E, 0x0A, 0x46, 0x00, 0xD8, 0x1A, 0x46, 0xA8, 0x8E, 0x80, 0xB2, 0xC8, 0x28, - 0x00, 0xD3, 0x19, 0x46, 0x43, 0x42, 0x43, 0x41, 0x0B, 0x9C, 0xA6, 0x42, 0x0C, 0x9C, 0x00, 0xD8, - 0x1C, 0x46, 0x00, 0x2C, 0x01, 0xD1, 0x0A, 0x40, 0x01, 0xD0, 0x00, 0x21, 0xA9, 0x74, 0xF9, 0x7C, - 0x00, 0x29, 0x4B, 0x4C, 0x03, 0xD0, 0x9F, 0x28, 0x01, 0xD8, 0x00, 0x21, 0xA9, 0x74, 0x29, 0x7A, - 0x00, 0x29, 0x06, 0xD0, 0x0E, 0x99, 0x29, 0x31, 0x88, 0x42, 0x08, 0xD2, 0x00, 0x20, 0xA8, 0x74, - 0x05, 0xE0, 0x95, 0x28, 0x01, 0xD8, 0x64, 0x20, 0x00, 0xE0, 0x0A, 0x20, 0xE0, 0x71, 0x4C, 0x4F, - 0xF8, 0x68, 0x03, 0x21, 0x0A, 0x91, 0xFD, 0xF7, 0xAD, 0xFA, 0x38, 0x60, 0x0B, 0x2E, 0x3D, 0x4F, - 0x30, 0xD3, 0x01, 0x46, 0x28, 0x7B, 0x00, 0x28, 0x2C, 0xD0, 0x45, 0x4E, 0x30, 0x69, 0xFF, 0xF7, - 0x37, 0xFA, 0x31, 0x68, 0x0D, 0x9A, 0x91, 0x42, 0x20, 0xD2, 0x4A, 0x08, 0x90, 0x42, 0x1D, 0xD9, - 0x00, 0x20, 0x3F, 0x4A, 0x90, 0x60, 0xEA, 0x79, 0x52, 0x1C, 0xEA, 0x71, 0x22, 0x6D, 0x0B, 0x9B, - 0x9A, 0x42, 0x00, 0xD9, 0x01, 0x20, 0xAA, 0x8F, 0x92, 0xB2, 0x2F, 0x2A, 0x0C, 0x9A, 0x00, 0xD8, - 0xC4, 0xE2, 0xBB, 0x7C, 0x00, 0x2B, 0x00, 0xD0, 0xC5, 0xE2, 0x01, 0x2A, 0x01, 0xD1, 0x00, 0x20, - 0xE8, 0x74, 0x01, 0x20, 0xE0, 0x72, 0x20, 0x8D, 0x40, 0x08, 0x28, 0x86, 0x30, 0x48, 0x01, 0x61, - 0x00, 0x20, 0x28, 0x73, 0x22, 0x6D, 0x2B, 0x7C, 0x34, 0x48, 0x08, 0x38, 0x01, 0x68, 0x34, 0x4E, - 0x31, 0x40, 0x00, 0x2B, 0x10, 0xD0, 0x79, 0x2A, 0x0E, 0xD3, 0x01, 0x60, 0x2F, 0x4A, 0x10, 0x68, - 0x2F, 0x49, 0x08, 0x40, 0x2F, 0x49, 0x08, 0x18, 0x24, 0x38, 0x10, 0x60, 0x10, 0x1F, 0x01, 0x68, - 0x40, 0x20, 0x08, 0x43, 0xBF, 0x21, 0x0B, 0xE0, 0x2A, 0x4A, 0x51, 0x18, 0x24, 0x39, 0x01, 0x60, - 0x26, 0x4A, 0x10, 0x68, 0x26, 0x49, 0x08, 0x40, 0x10, 0x60, 0x10, 0x1F, 0x00, 0x68, 0x07, 0x99, - 0x88, 0x43, 0x11, 0x1F, 0x08, 0x60, 0xA8, 0x7B, 0x00, 0x28, 0x45, 0xD0, 0x76, 0x20, 0x2B, 0x5A, - 0x54, 0x20, 0x29, 0x5A, 0x5A, 0x20, 0x2C, 0x5A, 0xEA, 0x8D, 0xE8, 0x7F, 0x00, 0x94, 0x00, 0xF0, - 0x09, 0xFC, 0x2C, 0x46, 0x0E, 0x34, 0x0A, 0x20, 0x3F, 0xE0, 0xC0, 0x46, 0xDA, 0xE3, 0x25, 0x59, - 0xD9, 0x63, 0xB8, 0x4E, 0x00, 0xF8, 0x00, 0x08, 0x00, 0xF8, 0x01, 0x08, 0x00, 0x7C, 0x00, 0x08, - 0xC0, 0x00, 0x00, 0x20, 0xDC, 0x06, 0x00, 0x20, 0x46, 0x69, 0x72, 0x65, 0x66, 0x6C, 0x79, 0x20, - 0x47, 0x32, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, - 0x00, 0x30, 0x00, 0x40, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x58, 0x01, 0x40, 0x34, 0x2C, 0x01, 0x40, - 0x7C, 0x05, 0x00, 0x20, 0xE2, 0x04, 0x00, 0x00, 0x11, 0x27, 0x00, 0x00, 0x21, 0x4E, 0x00, 0x00, - 0x85, 0x04, 0x00, 0x20, 0xAF, 0x47, 0x00, 0x00, 0x88, 0x04, 0x00, 0x20, 0x10, 0xE4, 0x00, 0xE0, - 0xFF, 0xFF, 0xFF, 0x00, 0x24, 0x00, 0x00, 0x40, 0x20, 0x20, 0x28, 0x5C, 0x00, 0x28, 0x09, 0xD0, - 0x00, 0xF0, 0xB8, 0xFB, 0x2C, 0x46, 0x20, 0x34, 0x31, 0x20, 0x01, 0xF0, 0x03, 0xF8, 0x00, 0x20, - 0x20, 0x70, 0xC8, 0x4C, 0x68, 0x7B, 0x40, 0x1C, 0x68, 0x73, 0xC0, 0xB2, 0xC9, 0x28, 0x76, 0xD3, - 0xFD, 0xF7, 0x7E, 0xFA, 0xC4, 0x49, 0x08, 0x88, 0x49, 0x89, 0x09, 0x1A, 0x5C, 0x22, 0xAA, 0x5A, - 0x12, 0x1A, 0x50, 0x20, 0x50, 0x43, 0xFD, 0xF7, 0xE5, 0xF9, 0x06, 0x46, 0x1E, 0x36, 0x5E, 0x20, - 0x2E, 0x52, 0x54, 0x24, 0x28, 0x5B, 0x07, 0x27, 0x47, 0x43, 0x60, 0x20, 0x28, 0x5A, 0xBB, 0x49, - 0x48, 0x43, 0xBB, 0x49, 0xFD, 0xF7, 0xB6, 0xF9, 0xB6, 0x49, 0xC9, 0x8C, 0x48, 0x43, 0x64, 0x21, - 0x09, 0x91, 0xFD, 0xF7, 0xAF, 0xF9, 0xC0, 0x19, 0xC7, 0x08, 0x2F, 0x53, 0xB5, 0x49, 0x08, 0x68, - 0xB5, 0x4A, 0x10, 0x40, 0x00, 0x1D, 0x08, 0x60, 0x6A, 0x7C, 0x53, 0x00, 0xB3, 0x4C, 0xE0, 0x5A, - 0x46, 0x21, 0x69, 0x5A, 0xE1, 0x52, 0xEE, 0x77, 0x52, 0x1C, 0xD3, 0xB2, 0x00, 0x26, 0x63, 0x2B, - 0x33, 0x46, 0x00, 0xD8, 0x13, 0x46, 0xAE, 0x4C, 0x63, 0x74, 0xAE, 0x4B, 0x5A, 0x68, 0x10, 0x1A, - 0x40, 0x18, 0x58, 0x60, 0x09, 0x99, 0xFD, 0xF7, 0x8D, 0xF9, 0xE0, 0x86, 0x80, 0xB2, 0xA0, 0x67, - 0xA2, 0x49, 0x48, 0x43, 0xCD, 0x21, 0x89, 0x00, 0xFD, 0xF7, 0x84, 0xF9, 0x01, 0x04, 0x00, 0xD5, - 0x30, 0x46, 0xA3, 0x4D, 0xE8, 0x85, 0xE8, 0x78, 0x00, 0x28, 0x9A, 0x4C, 0x1D, 0xD0, 0x68, 0x7A, - 0x21, 0x8C, 0x41, 0x43, 0xB8, 0xB2, 0x81, 0x42, 0x15, 0xD9, 0x68, 0x8D, 0x40, 0x1C, 0x68, 0x85, - 0x29, 0x7D, 0x9D, 0x4A, 0x51, 0x43, 0x9D, 0x4A, 0x89, 0x18, 0x80, 0xB2, 0x81, 0x42, 0x0C, 0xDA, - 0x00, 0x24, 0xAC, 0x87, 0xFE, 0xF7, 0x36, 0xF9, 0x00, 0xF0, 0x48, 0xFC, 0x6C, 0x87, 0xEC, 0x74, - 0xEC, 0x75, 0x8C, 0x4C, 0x01, 0xE0, 0x00, 0x20, 0x68, 0x85, 0x00, 0x20, 0x68, 0x73, 0x00, 0x27, - 0x6F, 0x75, 0x28, 0x7D, 0x00, 0x28, 0x3C, 0xD0, 0xA8, 0x8F, 0x31, 0x28, 0x74, 0xD3, 0xE9, 0x7D, - 0x00, 0x29, 0x71, 0xD0, 0x88, 0x28, 0x00, 0xD9, 0x2C, 0xE1, 0xA8, 0x78, 0x00, 0x28, 0x0F, 0xD0, - 0x8B, 0x4A, 0x10, 0x68, 0x01, 0x21, 0x88, 0x43, 0x10, 0x60, 0x00, 0xF0, 0x27, 0xFC, 0x00, 0x24, - 0x88, 0x48, 0x04, 0x60, 0x44, 0x60, 0x84, 0x60, 0xFE, 0xF7, 0x40, 0xF9, 0xAC, 0x70, 0x79, 0x4C, - 0x50, 0x27, 0xE9, 0x5B, 0x4E, 0x26, 0x2A, 0x46, 0xAD, 0x5B, 0x4C, 0x20, 0x13, 0x5A, 0xE2, 0x7B, - 0x00, 0x2A, 0x0A, 0x96, 0x00, 0xD1, 0x08, 0xE2, 0x4A, 0x1E, 0x11, 0xB2, 0x00, 0x29, 0x0E, 0x99, - 0x00, 0xD4, 0x11, 0x46, 0x6A, 0x1E, 0x14, 0xB2, 0x00, 0x2C, 0x0E, 0x9D, 0x00, 0xD4, 0x15, 0x46, - 0x5B, 0x1E, 0x1A, 0xB2, 0x00, 0x2A, 0x0E, 0x9A, 0x6A, 0x4C, 0x00, 0xD5, 0x0C, 0xE2, 0x1A, 0x46, - 0x0A, 0xE2, 0x70, 0x48, 0xC1, 0x68, 0x74, 0x48, 0xFD, 0xF7, 0x34, 0xF9, 0xE9, 0x7C, 0x41, 0x43, - 0x5A, 0x20, 0x29, 0x52, 0x88, 0xB2, 0x71, 0x49, 0x48, 0x43, 0xC0, 0x0C, 0x62, 0x21, 0x68, 0x52, - 0xA1, 0x79, 0x00, 0x29, 0x0D, 0x99, 0x09, 0xD0, 0x63, 0x8F, 0x22, 0x8F, 0xE1, 0x8E, 0x5D, 0x4C, - 0xA4, 0x8F, 0x00, 0x94, 0x5B, 0x4C, 0x00, 0xF0, 0xBF, 0xFB, 0x01, 0x46, 0xA1, 0x86, 0x2B, 0x20, - 0x67, 0x4F, 0x3A, 0x5C, 0xE8, 0x7F, 0x90, 0x42, 0x66, 0x4E, 0x09, 0xD9, 0xA3, 0x8F, 0x01, 0x21, - 0x00, 0x91, 0x11, 0x46, 0x0A, 0x39, 0x0A, 0x32, 0x5B, 0x08, 0x00, 0xF0, 0xAD, 0xFB, 0xA0, 0x86, - 0x58, 0x48, 0x80, 0x68, 0x63, 0x28, 0x2C, 0xD8, 0x21, 0x6D, 0x06, 0x98, 0x81, 0x42, 0x28, 0xD9, - 0x0C, 0x20, 0x32, 0x29, 0x31, 0xD3, 0x31, 0xE0, 0x01, 0x26, 0xAE, 0x70, 0x58, 0x49, 0x08, 0x7F, - 0x00, 0x28, 0x00, 0xD1, 0x87, 0xE5, 0x29, 0x20, 0x08, 0x5C, 0x40, 0x00, 0x65, 0x8E, 0x28, 0x18, - 0x13, 0x38, 0x40, 0x21, 0x4A, 0x4A, 0x50, 0x52, 0x81, 0xB2, 0x20, 0x8E, 0x48, 0x43, 0x29, 0x46, - 0xFD, 0xF7, 0xC8, 0xF8, 0x28, 0x1A, 0x46, 0x4D, 0x40, 0x1C, 0x58, 0x24, 0x28, 0x53, 0x00, 0xF0, - 0x2B, 0xFD, 0x28, 0x5B, 0x3B, 0x4C, 0x47, 0x49, 0x08, 0x60, 0x48, 0x60, 0x88, 0x60, 0xEE, 0x73, - 0x6F, 0xE5, 0x40, 0x48, 0x00, 0x68, 0x0C, 0x21, 0x00, 0x91, 0x64, 0x21, 0x03, 0x23, 0x06, 0x9A, - 0x00, 0xF0, 0x72, 0xFB, 0x21, 0x6D, 0x32, 0x29, 0x00, 0xD2, 0x02, 0x20, 0x60, 0x73, 0x2F, 0x20, - 0x38, 0x5C, 0x00, 0x28, 0x09, 0xD0, 0x40, 0x20, 0x28, 0x5A, 0x17, 0x21, 0x00, 0x91, 0x64, 0x21, - 0x0D, 0x23, 0x0D, 0x9A, 0x00, 0xF0, 0x60, 0xFB, 0x68, 0x77, 0x30, 0x68, 0x3A, 0x49, 0x88, 0x42, - 0x00, 0xD2, 0x50, 0xE5, 0xE8, 0x7C, 0x01, 0x28, 0x00, 0xD0, 0x4C, 0xE5, 0xA8, 0x7C, 0x40, 0x1C, - 0xA8, 0x74, 0x00, 0xF0, 0x73, 0xFB, 0x01, 0x20, 0x09, 0x90, 0xE0, 0x72, 0xA8, 0x8F, 0x2F, 0x28, - 0x03, 0xD8, 0x32, 0x48, 0x20, 0x65, 0x00, 0x20, 0xE8, 0x74, 0x00, 0x26, 0x25, 0x48, 0x86, 0x60, - 0x2C, 0x4F, 0x38, 0x68, 0x4A, 0x21, 0x68, 0x52, 0x3E, 0x60, 0x21, 0x6D, 0x0A, 0x9A, 0x4A, 0x43, - 0x80, 0xB2, 0x10, 0x18, 0x01, 0x09, 0x2A, 0x4A, 0x11, 0x40, 0xC2, 0x08, 0x52, 0x1A, 0x42, 0x23, - 0xEA, 0x52, 0x80, 0x08, 0x20, 0x65, 0x29, 0x87, 0x90, 0xB2, 0x1A, 0x49, 0x89, 0x68, 0x05, 0x29, - 0x13, 0xD2, 0x39, 0x68, 0x1A, 0x49, 0x08, 0x62, 0xE0, 0x7C, 0xE1, 0x7B, 0x01, 0x29, 0x00, 0xD0, - 0xC6, 0xE0, 0x40, 0x1C, 0xE0, 0x74, 0xC1, 0xB2, 0x07, 0x29, 0x19, 0x4F, 0x02, 0xD3, 0x09, 0x98, - 0x28, 0x73, 0xE0, 0x74, 0x09, 0x99, 0x08, 0x40, 0xC5, 0xE0, 0x39, 0x68, 0x81, 0x42, 0xE9, 0xD2, - 0x39, 0x68, 0x81, 0x42, 0xE6, 0xD2, 0x39, 0x68, 0x81, 0x42, 0xE3, 0xD2, 0x39, 0x68, 0x81, 0x42, - 0xF3, 0xD3, 0xDF, 0xE7, 0xC0, 0x00, 0x00, 0x20, 0xB8, 0xF7, 0xFF, 0x1F, 0xE4, 0x0C, 0x00, 0x00, - 0xFF, 0x0F, 0x00, 0x00, 0x08, 0x24, 0x01, 0x40, 0xE8, 0xFF, 0xFF, 0x7F, 0x32, 0x08, 0x00, 0x20, - 0x00, 0x05, 0x00, 0x20, 0x7C, 0x05, 0x00, 0x20, 0x7C, 0xFC, 0xFF, 0xFF, 0x20, 0x4E, 0x00, 0x00, - 0x0C, 0x20, 0x00, 0x40, 0x34, 0x2C, 0x01, 0x40, 0xC0, 0x27, 0x09, 0x00, 0xCD, 0xCC, 0x00, 0x00, - 0xDC, 0x06, 0x00, 0x20, 0x24, 0x00, 0x00, 0x40, 0xC9, 0xAF, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, - 0xFE, 0xFF, 0x00, 0x00, 0x01, 0x26, 0xAE, 0x70, 0x50, 0x20, 0x09, 0x90, 0x28, 0x5A, 0x4E, 0x21, - 0x05, 0x91, 0x6A, 0x5A, 0x4C, 0x21, 0x0A, 0x91, 0x69, 0x5A, 0xE3, 0x7B, 0x00, 0x2B, 0x01, 0x96, - 0x03, 0x97, 0x00, 0xD1, 0x70, 0xE1, 0x43, 0x1E, 0x18, 0xB2, 0x00, 0x28, 0x0E, 0x98, 0x00, 0xD4, - 0x18, 0x46, 0x53, 0x1E, 0x1A, 0xB2, 0x00, 0x2A, 0x0E, 0x9A, 0x00, 0xD4, 0x1A, 0x46, 0x4B, 0x1E, - 0x19, 0xB2, 0x00, 0x29, 0x0E, 0x99, 0x00, 0xD4, 0x19, 0x46, 0x0A, 0x9B, 0x09, 0x9E, 0xA8, 0x53, - 0x05, 0x98, 0x2A, 0x52, 0xE9, 0x52, 0x40, 0x20, 0x04, 0x90, 0x20, 0x5A, 0x09, 0xB2, 0x49, 0x00, - 0xDF, 0x4A, 0x51, 0x5E, 0x41, 0x18, 0x60, 0x8E, 0x48, 0x43, 0x0D, 0x99, 0xFD, 0xF7, 0x0A, 0xF8, - 0x2D, 0x26, 0x27, 0x46, 0xE6, 0x4C, 0xA1, 0x5D, 0x48, 0x43, 0x0A, 0x24, 0x21, 0x46, 0xFD, 0xF7, - 0x01, 0xF8, 0x80, 0xB2, 0x01, 0xF0, 0xC4, 0xF9, 0x04, 0x98, 0x38, 0x5A, 0x05, 0x99, 0x69, 0x5E, - 0x49, 0x00, 0xD3, 0x4A, 0x51, 0x5E, 0x41, 0x18, 0x78, 0x8E, 0x48, 0x43, 0x0D, 0x99, 0xFC, 0xF7, - 0xF1, 0xFF, 0xDB, 0x49, 0x89, 0x5D, 0x48, 0x43, 0x21, 0x46, 0x25, 0x46, 0xFC, 0xF7, 0xEA, 0xFF, - 0x80, 0xB2, 0x01, 0xF0, 0xB3, 0xF9, 0x04, 0x98, 0x38, 0x5A, 0x09, 0x99, 0xC2, 0x4C, 0x61, 0x5E, - 0x49, 0x00, 0xC7, 0x4A, 0x51, 0x5E, 0x41, 0x18, 0x78, 0x8E, 0x48, 0x43, 0x0D, 0x99, 0xFC, 0xF7, - 0xD9, 0xFF, 0xCF, 0x49, 0x89, 0x5D, 0x26, 0x46, 0x48, 0x43, 0x29, 0x46, 0xFC, 0xF7, 0xD2, 0xFF, - 0x80, 0xB2, 0x01, 0xF0, 0xA1, 0xF9, 0xA0, 0x8F, 0xC8, 0x28, 0x57, 0xD9, 0x0A, 0x98, 0x03, 0x99, - 0x31, 0x52, 0x09, 0x98, 0xF8, 0x87, 0x3C, 0x46, 0x0F, 0x46, 0x52, 0xE0, 0x02, 0x46, 0xBB, 0x7C, - 0x00, 0x2B, 0x00, 0xD1, 0x39, 0xE5, 0x02, 0x46, 0x01, 0x2A, 0x00, 0xD1, 0x37, 0xE5, 0x38, 0xE5, - 0x41, 0x1E, 0xE1, 0x74, 0x08, 0x06, 0xBE, 0x4F, 0x03, 0xD1, 0x09, 0x98, 0x28, 0x73, 0x06, 0x21, - 0xE1, 0x74, 0x09, 0x98, 0x88, 0x43, 0xA0, 0x73, 0xEF, 0xF3, 0x10, 0x80, 0x72, 0xB6, 0xE8, 0x7B, - 0x00, 0x28, 0x02, 0xD1, 0xE0, 0x7C, 0xFD, 0xF7, 0xF1, 0xFF, 0x62, 0xB6, 0xFD, 0xF7, 0xCE, 0xFF, - 0xA6, 0x48, 0x00, 0x68, 0xA1, 0x49, 0x88, 0x42, 0x01, 0xD9, 0x09, 0x98, 0xE0, 0x72, 0xEE, 0x76, - 0x2E, 0x77, 0xAE, 0x75, 0xA1, 0x4A, 0x90, 0x68, 0x41, 0x1C, 0x91, 0x60, 0xE0, 0x7C, 0x40, 0x00, - 0x82, 0x18, 0x20, 0x6D, 0x50, 0x82, 0x02, 0x9A, 0xBA, 0x5C, 0x53, 0x1E, 0x9A, 0x41, 0x7B, 0x7F, - 0x00, 0x2B, 0x0C, 0x9B, 0x00, 0xD1, 0x13, 0x46, 0x01, 0x2B, 0x07, 0xD1, 0x14, 0x29, 0x00, 0xD2, - 0x19, 0xE4, 0x0D, 0x99, 0x49, 0x1C, 0x88, 0x42, 0x04, 0xD3, 0x14, 0xE4, 0x0D, 0x99, 0x88, 0x42, - 0x00, 0xD3, 0x10, 0xE4, 0xE6, 0x72, 0xFE, 0xF7, 0x15, 0xFE, 0x0C, 0xE4, 0xF8, 0x8F, 0x3C, 0x46, - 0x03, 0x9F, 0xFE, 0xF7, 0x17, 0xFC, 0x0A, 0x98, 0x30, 0x5A, 0x00, 0x28, 0x35, 0x46, 0x01, 0xD0, - 0xFF, 0xF7, 0x01, 0xFC, 0x01, 0x9E, 0xEE, 0x74, 0x2F, 0x75, 0x85, 0x48, 0x20, 0x65, 0xE6, 0x72, - 0x86, 0x4A, 0x10, 0x61, 0x10, 0x60, 0x83, 0x49, 0x08, 0x60, 0xEF, 0x73, 0x14, 0x20, 0x90, 0x60, - 0x8F, 0x48, 0x40, 0x7F, 0x00, 0x28, 0x01, 0xD0, 0xE0, 0x8D, 0x28, 0x86, 0x20, 0x7B, 0xE1, 0x7B, - 0x01, 0x29, 0x00, 0xD0, 0xA6, 0xE0, 0x40, 0x1C, 0xE0, 0x74, 0xC1, 0xB2, 0x07, 0x29, 0x02, 0xD3, - 0x2E, 0x73, 0xE6, 0x74, 0x30, 0x46, 0x30, 0x40, 0xA5, 0xE0, 0x4C, 0x1C, 0x21, 0xB2, 0x00, 0x22, - 0x0E, 0x9E, 0xB1, 0x42, 0x11, 0x46, 0x00, 0xDC, 0x21, 0x46, 0x6C, 0x1C, 0x25, 0xB2, 0x0E, 0x9E, - 0xB5, 0x42, 0x15, 0x46, 0x00, 0xDC, 0x25, 0x46, 0x5B, 0x1C, 0x1C, 0xB2, 0x0E, 0x9E, 0xB4, 0x42, - 0x00, 0xDC, 0x1A, 0x46, 0x66, 0x4C, 0x0A, 0x9E, 0x67, 0x4B, 0xD9, 0x53, 0x9D, 0x53, 0x1A, 0x52, - 0x40, 0x20, 0x05, 0x90, 0x20, 0x5A, 0x11, 0xB2, 0x49, 0x00, 0x69, 0x4A, 0x51, 0x5E, 0x41, 0x18, - 0x60, 0x8E, 0x48, 0x43, 0x0D, 0x9E, 0x31, 0x46, 0xFC, 0xF7, 0x1C, 0xFF, 0x09, 0x97, 0x2D, 0x27, - 0x6F, 0x4C, 0xE1, 0x5D, 0x48, 0x43, 0x0A, 0x25, 0x29, 0x46, 0xFC, 0xF7, 0x13, 0xFF, 0x80, 0xB2, - 0x01, 0xF0, 0xD6, 0xF8, 0x56, 0x48, 0x05, 0x9C, 0x00, 0x5B, 0x57, 0x49, 0x0A, 0x9A, 0x89, 0x5E, - 0x49, 0x00, 0x5B, 0x4A, 0x51, 0x5E, 0x41, 0x18, 0x51, 0x48, 0x40, 0x8E, 0x48, 0x43, 0x31, 0x46, - 0xFC, 0xF7, 0x00, 0xFF, 0x62, 0x49, 0xC9, 0x5D, 0x48, 0x43, 0x29, 0x46, 0xFC, 0xF7, 0xFA, 0xFE, - 0x80, 0xB2, 0x01, 0xF0, 0xC3, 0xF8, 0x4A, 0x48, 0x00, 0x5B, 0x09, 0x99, 0x4A, 0x4A, 0x51, 0x5E, - 0x49, 0x00, 0x4F, 0x4A, 0x51, 0x5E, 0x41, 0x18, 0x45, 0x48, 0x40, 0x8E, 0x48, 0x43, 0x31, 0x46, - 0xFC, 0xF7, 0xE8, 0xFE, 0x56, 0x4A, 0xD1, 0x5D, 0x14, 0x46, 0x48, 0x43, 0x29, 0x46, 0xFC, 0xF7, - 0xE1, 0xFE, 0x80, 0xB2, 0x01, 0xF0, 0xB0, 0xF8, 0xE5, 0x7E, 0x3E, 0x48, 0x29, 0x46, 0xFC, 0xF7, - 0xB9, 0xFE, 0x06, 0x46, 0x43, 0x48, 0x29, 0x46, 0x3B, 0x4D, 0x39, 0x4C, 0xFC, 0xF7, 0xB2, 0xFE, - 0x03, 0x46, 0xA8, 0x8F, 0x00, 0x96, 0x30, 0x21, 0x78, 0x22, 0x00, 0xF0, 0x6D, 0xF9, 0xE0, 0x87, - 0x80, 0xB2, 0xFE, 0xF7, 0x6F, 0xFB, 0x0E, 0x98, 0xF1, 0x30, 0xE1, 0x8F, 0xFC, 0xF7, 0xA2, 0xFE, - 0x5A, 0x21, 0x68, 0x52, 0xFF, 0xF7, 0x57, 0xFB, 0x43, 0x1C, 0x18, 0xB2, 0x0E, 0x9E, 0xB0, 0x42, - 0x38, 0x46, 0x00, 0xDC, 0x18, 0x46, 0x53, 0x1C, 0x1A, 0xB2, 0x0E, 0x9E, 0xB2, 0x42, 0x3A, 0x46, - 0x00, 0xDC, 0x1A, 0x46, 0x4B, 0x1C, 0x19, 0xB2, 0x0E, 0x9E, 0xB1, 0x42, 0x39, 0x46, 0x00, 0xDC, - 0x8A, 0xE6, 0x8A, 0xE6, 0x41, 0x1E, 0xE1, 0x74, 0x08, 0x06, 0x02, 0xD1, 0x2E, 0x73, 0x06, 0x21, - 0xE1, 0x74, 0x30, 0x46, 0x88, 0x43, 0xA0, 0x73, 0xEF, 0xF3, 0x10, 0x80, 0x72, 0xB6, 0xE8, 0x7B, - 0x00, 0x28, 0x02, 0xD1, 0xE0, 0x7C, 0xFD, 0xF7, 0xE1, 0xFE, 0x62, 0xB6, 0xFD, 0xF7, 0xBE, 0xFE, - 0x1E, 0x48, 0x00, 0x68, 0x19, 0x49, 0x88, 0x42, 0x00, 0xD9, 0xE6, 0x72, 0xEF, 0x76, 0x2F, 0x77, - 0xE0, 0x7C, 0x40, 0x00, 0x19, 0x49, 0x40, 0x18, 0x21, 0x6D, 0x41, 0x82, 0xFE, 0xF7, 0x10, 0xFE, - 0xFF, 0xF7, 0x19, 0xFB, 0xFD, 0xF7, 0x2E, 0xFE, 0x00, 0x20, 0xE8, 0x75, 0x28, 0x76, 0x12, 0x49, - 0x08, 0x60, 0x48, 0x60, 0xA8, 0x87, 0x68, 0x87, 0x88, 0x60, 0x00, 0xF0, 0x31, 0xFB, 0x01, 0x20, - 0x01, 0x02, 0x11, 0x48, 0xFC, 0xF7, 0x43, 0xFE, 0xBF, 0xF3, 0x4F, 0x8F, 0x0F, 0x48, 0x10, 0x49, - 0x01, 0x60, 0xBF, 0xF3, 0x4F, 0x8F, 0x00, 0xBF, 0x00, 0xBF, 0x00, 0xBF, 0x00, 0xBF, 0xFA, 0xE7, - 0xC0, 0x00, 0x00, 0x20, 0x2A, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, 0xC4, 0x09, 0x00, 0x00, - 0x28, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x40, 0x34, 0x2C, 0x01, 0x40, 0x7C, 0x05, 0x00, 0x20, - 0xAC, 0x01, 0x00, 0x20, 0x58, 0x1B, 0x00, 0x00, 0xD8, 0x05, 0x00, 0x20, 0x0C, 0xED, 0x00, 0xE0, - 0x04, 0x00, 0xFA, 0x05, 0xB0, 0xB5, 0x12, 0x4C, 0xD6, 0x49, 0x30, 0x25, 0x20, 0x46, 0x2A, 0x46, - 0xFC, 0xF7, 0xFC, 0xFD, 0x20, 0x46, 0x29, 0x46, 0xFE, 0xF7, 0xF6, 0xFD, 0x60, 0x55, 0xB0, 0xBD, - 0xDC, 0x06, 0x00, 0x20, 0xB0, 0xB5, 0x0A, 0x4C, 0x04, 0x9D, 0x25, 0x72, 0xA3, 0x71, 0x22, 0x71, - 0xA1, 0x70, 0x20, 0x70, 0x28, 0x0A, 0xE0, 0x71, 0x18, 0x0A, 0x60, 0x71, 0x10, 0x0A, 0xE0, 0x70, - 0x08, 0x0A, 0x60, 0x70, 0x09, 0x21, 0x20, 0x46, 0xFE, 0xF7, 0xDE, 0xFD, 0x60, 0x72, 0xB0, 0xBD, - 0xA4, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x83, 0xB0, 0x59, 0x49, 0x0E, 0x89, 0x00, 0x2E, 0x02, 0xD0, - 0x00, 0x20, 0x08, 0x81, 0x24, 0xE0, 0x57, 0x49, 0x09, 0x78, 0x00, 0x29, 0x00, 0xD1, 0x56, 0x48, - 0xC1, 0x0B, 0x0D, 0xD1, 0x81, 0x0B, 0x0D, 0xD1, 0x41, 0x0B, 0x0D, 0xD1, 0x01, 0x0B, 0x0D, 0xD1, - 0xC1, 0x0A, 0x0D, 0xD1, 0x81, 0x0A, 0x0D, 0xD1, 0x41, 0x0A, 0x4A, 0x1E, 0x91, 0x41, 0x0A, 0xE0, - 0x07, 0x21, 0x08, 0xE0, 0x06, 0x21, 0x06, 0xE0, 0x05, 0x21, 0x04, 0xE0, 0x04, 0x21, 0x02, 0xE0, - 0x03, 0x21, 0x00, 0xE0, 0x02, 0x21, 0x46, 0x4A, 0x51, 0x61, 0xC8, 0x40, 0x4E, 0x02, 0x06, 0x43, - 0x46, 0x48, 0x00, 0x78, 0x80, 0x00, 0x01, 0x90, 0x45, 0x49, 0x0B, 0x18, 0x40, 0x27, 0x5F, 0x60, - 0x31, 0x0A, 0x44, 0x48, 0x40, 0x5C, 0x00, 0x90, 0x84, 0x00, 0x38, 0x46, 0xA0, 0x43, 0x98, 0x60, - 0x35, 0x09, 0x69, 0x40, 0x71, 0x40, 0x0F, 0x24, 0x02, 0x94, 0x8C, 0x43, 0x31, 0x01, 0x09, 0x19, - 0x37, 0x4A, 0x91, 0x61, 0x00, 0x99, 0xCA, 0x00, 0x3A, 0x40, 0x42, 0x40, 0xDA, 0x60, 0xC8, 0x03, - 0x02, 0x99, 0x0D, 0x40, 0x37, 0x49, 0x4D, 0x5D, 0xA9, 0x02, 0x01, 0x43, 0x35, 0x48, 0x00, 0x5D, - 0x00, 0x90, 0x02, 0x9C, 0x26, 0x40, 0x33, 0x4C, 0xA4, 0x5D, 0x02, 0x94, 0x66, 0x01, 0x06, 0x43, - 0x34, 0x46, 0x0C, 0x43, 0x2A, 0x48, 0xC4, 0x61, 0xC8, 0x0A, 0x38, 0x40, 0x50, 0x40, 0x18, 0x61, - 0x8A, 0x0A, 0x3A, 0x40, 0x42, 0x40, 0x5A, 0x61, 0x48, 0x0A, 0x38, 0x40, 0x50, 0x40, 0x98, 0x61, - 0xA9, 0x00, 0x39, 0x40, 0x41, 0x40, 0xD9, 0x61, 0xE8, 0x00, 0x38, 0x40, 0x48, 0x40, 0x18, 0x62, - 0xA1, 0x09, 0x39, 0x40, 0x41, 0x40, 0x59, 0x62, 0x60, 0x09, 0x38, 0x40, 0x48, 0x40, 0x98, 0x62, - 0x21, 0x09, 0x39, 0x40, 0x41, 0x40, 0xD9, 0x62, 0x02, 0x9A, 0x90, 0x00, 0x38, 0x40, 0x48, 0x40, - 0x18, 0x63, 0xD1, 0x00, 0x39, 0x40, 0x41, 0x40, 0x59, 0x63, 0x3F, 0x20, 0xC0, 0x43, 0x01, 0x40, - 0x72, 0x08, 0x3A, 0x40, 0x4A, 0x40, 0x9A, 0x63, 0x71, 0x00, 0x3E, 0x40, 0x56, 0x40, 0xDE, 0x63, - 0x39, 0x40, 0x71, 0x40, 0x19, 0x64, 0x00, 0x22, 0x11, 0x4C, 0x01, 0x9D, 0x62, 0x51, 0x00, 0x9C, - 0xA2, 0x00, 0x3A, 0x40, 0x4A, 0x40, 0x5A, 0x64, 0xE1, 0x00, 0x39, 0x40, 0x51, 0x40, 0x99, 0x64, - 0x22, 0x01, 0x3A, 0x40, 0x4A, 0x40, 0xDA, 0x64, 0x02, 0x40, 0x60, 0x01, 0x38, 0x40, 0x50, 0x40, - 0x18, 0x65, 0xE1, 0x07, 0x49, 0x0E, 0x41, 0x40, 0x59, 0x65, 0x03, 0xB0, 0xF0, 0xBD, 0xC0, 0x46, - 0x80, 0x04, 0x00, 0x20, 0x13, 0x05, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0x00, 0xE1, 0x04, 0x00, 0x20, - 0x9C, 0x07, 0x00, 0x20, 0xF8, 0x5D, 0x00, 0x08, 0x70, 0xB5, 0x04, 0x9D, 0x88, 0x42, 0x14, 0xDD, - 0x2C, 0x46, 0x25, 0x46, 0x90, 0x42, 0x14, 0xDA, 0xAB, 0x42, 0x15, 0xD0, 0x16, 0x46, 0xEA, 0x18, - 0x54, 0x10, 0x72, 0x18, 0x52, 0x10, 0x91, 0x42, 0x0C, 0xD0, 0x90, 0x42, 0xF1, 0xDD, 0x51, 0x1C, - 0x88, 0x42, 0x23, 0x46, 0x32, 0x46, 0xEB, 0xDC, 0x01, 0xE0, 0x16, 0x46, 0x1C, 0x46, 0xB0, 0x42, - 0x00, 0xDB, 0x2C, 0x46, 0x20, 0x46, 0x70, 0xBD, 0x18, 0x46, 0x70, 0xBD, 0x01, 0x20, 0x40, 0x05, - 0x02, 0x49, 0x0A, 0x68, 0x82, 0x43, 0x0A, 0x60, 0x48, 0x61, 0x70, 0x47, 0x00, 0x04, 0x01, 0x40, - 0xF0, 0xB5, 0x81, 0xB0, 0xAA, 0x4D, 0xAB, 0x48, 0x68, 0x60, 0xEF, 0xF3, 0x10, 0x80, 0x72, 0xB6, - 0xA9, 0x48, 0x00, 0x78, 0xE8, 0x60, 0x28, 0x61, 0x68, 0x61, 0x79, 0x24, 0x85, 0x4E, 0x86, 0x4F, - 0x37, 0x60, 0xA4, 0x1E, 0x14, 0x21, 0x20, 0x46, 0xFC, 0xF7, 0xFC, 0xFC, 0xC0, 0xB2, 0xFD, 0xF7, - 0x65, 0xFD, 0x60, 0x08, 0x0A, 0x30, 0x28, 0x60, 0x0A, 0x20, 0xFE, 0xF7, 0xD3, 0xF9, 0x02, 0x2C, - 0xEE, 0xD8, 0xFD, 0xF7, 0xBF, 0xFC, 0x00, 0x20, 0x28, 0x60, 0x9C, 0x49, 0x08, 0x80, 0x9C, 0x48, - 0x00, 0x88, 0x68, 0x60, 0x62, 0xB6, 0x01, 0xB0, 0xF0, 0xBD, 0xC0, 0x46, 0xF0, 0xB5, 0x83, 0xB0, - 0x03, 0x20, 0xFD, 0xF7, 0x4B, 0xFD, 0x00, 0x21, 0x35, 0x20, 0x94, 0x4E, 0x8F, 0x4A, 0x02, 0x91, - 0x0B, 0x46, 0x04, 0xE0, 0x5B, 0x1C, 0xA8, 0x1C, 0x31, 0x3D, 0x7E, 0x2D, 0x4C, 0xD2, 0x05, 0x46, - 0x6D, 0x48, 0x6C, 0x49, 0x08, 0x60, 0x02, 0x98, 0x30, 0x80, 0x50, 0x19, 0x40, 0x1E, 0x07, 0x78, - 0xFF, 0x2F, 0xEF, 0xD0, 0x50, 0x5D, 0x00, 0x28, 0x2D, 0xD0, 0x0A, 0x21, 0x84, 0x4E, 0x31, 0x60, - 0xF7, 0x21, 0x41, 0x43, 0x7D, 0x20, 0x40, 0x01, 0x09, 0x18, 0x23, 0x48, 0x01, 0x93, 0xFC, 0xF7, - 0xB9, 0xFC, 0x04, 0x46, 0x21, 0x48, 0x21, 0x46, 0xFC, 0xF7, 0xB4, 0xFC, 0x70, 0x60, 0x7E, 0x49, - 0x09, 0x78, 0x48, 0x43, 0x7B, 0x49, 0xFC, 0xF7, 0xAD, 0xFC, 0xF0, 0x60, 0x30, 0x61, 0x70, 0x61, - 0x01, 0x98, 0xC0, 0xB2, 0xFE, 0x21, 0x41, 0x43, 0xCE, 0x19, 0x19, 0x48, 0x21, 0x46, 0xFC, 0xF7, - 0xA1, 0xFC, 0x70, 0x43, 0x75, 0x4E, 0x64, 0x21, 0xFC, 0xF7, 0x9C, 0xFC, 0x80, 0xB2, 0xFE, 0xF7, - 0x79, 0xF9, 0x00, 0x23, 0x0B, 0xE0, 0x00, 0x24, 0x6D, 0x48, 0xC4, 0x60, 0x04, 0x61, 0x44, 0x61, - 0xD8, 0xB2, 0xFE, 0x21, 0x41, 0x43, 0xC8, 0x19, 0xFE, 0xF7, 0x6C, 0xF9, 0x23, 0x46, 0xBE, 0x4A, - 0xA8, 0x1C, 0x31, 0x3D, 0x7E, 0x2D, 0xB2, 0xD3, 0xFD, 0xF7, 0x54, 0xFC, 0x00, 0x20, 0x64, 0x4A, - 0x10, 0x60, 0x67, 0x49, 0x09, 0x88, 0x51, 0x60, 0x30, 0x80, 0x43, 0x48, 0x41, 0x49, 0x08, 0x60, - 0x03, 0xB0, 0xF0, 0xBD, 0xDC, 0x06, 0x00, 0x20, 0xC0, 0xD8, 0xA7, 0x00, 0x00, 0x3E, 0x49, 0x00, - 0xA0, 0x86, 0x01, 0x00, 0xB0, 0xB5, 0x5A, 0x4D, 0x5A, 0x48, 0x68, 0x60, 0x28, 0x20, 0x28, 0x60, - 0x59, 0x48, 0x00, 0x78, 0xE8, 0x60, 0x28, 0x61, 0x68, 0x61, 0x02, 0x20, 0xFD, 0xF7, 0xCE, 0xFC, - 0x96, 0x24, 0x20, 0x46, 0xFE, 0xF7, 0x3E, 0xF9, 0x32, 0x48, 0x33, 0x49, 0x01, 0x60, 0x50, 0x20, - 0x28, 0x60, 0x20, 0x46, 0xFE, 0xF7, 0x36, 0xF9, 0xFD, 0xF7, 0x24, 0xFC, 0x00, 0x20, 0x28, 0x60, - 0x4E, 0x49, 0x08, 0x80, 0x4E, 0x48, 0x00, 0x88, 0x68, 0x60, 0xB0, 0xBD, 0xB0, 0xB5, 0x48, 0x4D, - 0x48, 0x48, 0x68, 0x60, 0x32, 0x20, 0x28, 0x60, 0x47, 0x48, 0x00, 0x78, 0xE8, 0x60, 0x28, 0x61, - 0x68, 0x61, 0x02, 0x20, 0xFD, 0xF7, 0xAA, 0xFC, 0x96, 0x24, 0x20, 0x46, 0xFE, 0xF7, 0x1A, 0xF9, - 0x20, 0x48, 0x21, 0x49, 0x01, 0x60, 0x1E, 0x20, 0x28, 0x60, 0x20, 0x46, 0xFE, 0xF7, 0x12, 0xF9, - 0xFD, 0xF7, 0x00, 0xFC, 0x00, 0x20, 0x28, 0x60, 0x3C, 0x49, 0x08, 0x80, 0x3C, 0x48, 0x00, 0x88, - 0x68, 0x60, 0xB0, 0xBD, 0xF0, 0xB5, 0x81, 0xB0, 0x35, 0x4D, 0x36, 0x48, 0x68, 0x60, 0xEF, 0xF3, - 0x10, 0x80, 0x72, 0xB6, 0x13, 0x4E, 0x14, 0x4F, 0x37, 0x60, 0x3C, 0x20, 0x28, 0x60, 0x32, 0x48, - 0x00, 0x78, 0xE8, 0x60, 0x28, 0x61, 0x68, 0x61, 0x01, 0x20, 0xFD, 0xF7, 0x7F, 0xFC, 0x4B, 0x24, - 0x20, 0x46, 0xFE, 0xF7, 0xEF, 0xF8, 0x50, 0x20, 0x28, 0x60, 0x20, 0x46, 0xFE, 0xF7, 0xEA, 0xF8, - 0x5A, 0x20, 0x28, 0x60, 0x37, 0x60, 0x20, 0x46, 0xFE, 0xF7, 0xE4, 0xF8, 0xFD, 0xF7, 0xD2, 0xFB, - 0x00, 0x20, 0x28, 0x60, 0x25, 0x49, 0x08, 0x80, 0x25, 0x48, 0x00, 0x88, 0x68, 0x60, 0x62, 0xB6, - 0x01, 0xB0, 0xF0, 0xBD, 0x00, 0x30, 0x00, 0x40, 0xAA, 0xAA, 0x00, 0x00, 0xB0, 0xB5, 0xEF, 0xF3, - 0x10, 0x80, 0x72, 0xB6, 0x30, 0x20, 0x70, 0x49, 0x08, 0x5C, 0x19, 0x4D, 0xFF, 0x28, 0x02, 0xD0, - 0xFF, 0xF7, 0x04, 0xFF, 0x25, 0xE0, 0x17, 0x48, 0x68, 0x60, 0x17, 0x48, 0x00, 0x78, 0xE8, 0x60, - 0x28, 0x61, 0x68, 0x61, 0x03, 0x20, 0xFD, 0xF7, 0x49, 0xFC, 0x37, 0x20, 0x28, 0x60, 0xC8, 0x24, - 0x20, 0x46, 0xFE, 0xF7, 0xB7, 0xF8, 0x05, 0x20, 0xFD, 0xF7, 0x40, 0xFC, 0x28, 0x20, 0x28, 0x60, - 0x20, 0x46, 0xFE, 0xF7, 0xAF, 0xF8, 0x06, 0x20, 0xFD, 0xF7, 0x38, 0xFC, 0x19, 0x20, 0x28, 0x60, - 0x20, 0x46, 0xFE, 0xF7, 0xA7, 0xF8, 0xFD, 0xF7, 0x95, 0xFB, 0x08, 0x48, 0x00, 0x21, 0x01, 0x80, - 0x29, 0x60, 0x07, 0x48, 0x00, 0x88, 0x68, 0x60, 0x62, 0xB6, 0xB0, 0xBD, 0xDC, 0x06, 0x00, 0x20, - 0x28, 0x2C, 0x01, 0x40, 0xCF, 0x07, 0x00, 0x00, 0xD5, 0x05, 0x00, 0x20, 0x44, 0x05, 0x00, 0x20, - 0xF2, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x0A, 0x4C, 0x60, 0x78, 0x02, 0x28, 0x08, 0xD0, 0x01, 0x28, - 0x03, 0xD1, 0xFD, 0xF7, 0x6F, 0xFD, 0x00, 0x20, 0x60, 0x70, 0x00, 0xF0, 0xEB, 0xF9, 0x10, 0xBD, - 0x04, 0x48, 0x80, 0x89, 0xFF, 0xF7, 0x9E, 0xFD, 0x00, 0x20, 0x60, 0x70, 0x10, 0xBD, 0xC0, 0x46, - 0x00, 0x05, 0x00, 0x20, 0x7C, 0x05, 0x00, 0x20, 0x10, 0xB5, 0x03, 0x22, 0x10, 0x04, 0x09, 0x21, - 0xC9, 0x06, 0x0B, 0x68, 0x83, 0x43, 0x01, 0x20, 0x04, 0x04, 0x1B, 0x19, 0x0B, 0x60, 0x03, 0x02, - 0x8B, 0x62, 0x93, 0x04, 0x0C, 0x68, 0x9C, 0x43, 0x83, 0x04, 0xE3, 0x18, 0x0B, 0x60, 0x43, 0x02, - 0x8B, 0x62, 0x13, 0x05, 0x0C, 0x68, 0x9C, 0x43, 0x03, 0x05, 0xE3, 0x18, 0x0B, 0x60, 0x83, 0x02, - 0x8B, 0x62, 0x92, 0x03, 0x0B, 0x68, 0x93, 0x43, 0xC2, 0x03, 0x9A, 0x18, 0x0A, 0x60, 0x06, 0x49, - 0x0A, 0x68, 0x02, 0x23, 0x13, 0x43, 0x83, 0x43, 0x0B, 0x60, 0x08, 0x68, 0x08, 0x22, 0x02, 0x43, - 0x04, 0x20, 0x82, 0x43, 0x0A, 0x60, 0x10, 0xBD, 0x00, 0x04, 0x00, 0x48, 0xB0, 0xB5, 0xBE, 0x48, - 0x01, 0x68, 0x02, 0x22, 0x11, 0x43, 0x01, 0x60, 0x01, 0x68, 0x91, 0x43, 0x01, 0x60, 0xBB, 0x48, - 0x01, 0x46, 0x08, 0x39, 0x41, 0x22, 0x0A, 0x60, 0x0A, 0x21, 0x01, 0x60, 0x13, 0x49, 0x09, 0x78, - 0x81, 0x60, 0x13, 0x49, 0xC1, 0x60, 0x02, 0x46, 0x0C, 0x3A, 0x13, 0x68, 0x01, 0x21, 0x0B, 0x43, - 0x13, 0x60, 0x00, 0x22, 0x42, 0x60, 0xB3, 0x4B, 0x0E, 0x4C, 0xDC, 0x60, 0x04, 0x46, 0x14, 0x34, - 0x9C, 0x60, 0x0D, 0x4C, 0x24, 0x78, 0x5C, 0x60, 0x0C, 0x4C, 0x1C, 0x60, 0x4B, 0x02, 0x04, 0x46, - 0x14, 0x3C, 0x25, 0x68, 0x1D, 0x43, 0x25, 0x60, 0x03, 0x68, 0x0B, 0x43, 0x03, 0x60, 0xAB, 0x4B, - 0x1A, 0x70, 0x20, 0x38, 0x02, 0x68, 0x0A, 0x43, 0x02, 0x60, 0xB0, 0xBD, 0xA4, 0x01, 0x00, 0x20, - 0xFF, 0xFF, 0x00, 0x00, 0xD8, 0x05, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20, 0x8B, 0x09, 0x00, 0x00, - 0x02, 0x48, 0x00, 0x21, 0x41, 0x60, 0x01, 0x60, 0x70, 0x47, 0xC0, 0x46, 0x24, 0x04, 0x00, 0x40, - 0x80, 0xB5, 0x04, 0x49, 0x0A, 0x78, 0x04, 0x48, 0x42, 0x70, 0xCA, 0x6C, 0xC0, 0x21, 0x00, 0xF0, - 0x05, 0xF8, 0x80, 0xBD, 0xC0, 0x00, 0x00, 0x20, 0xDC, 0x06, 0x00, 0x20, 0xF0, 0xB5, 0x03, 0xAF, - 0x8B, 0xB0, 0x6E, 0x46, 0xF4, 0x1D, 0x19, 0x34, 0x15, 0x46, 0x71, 0x61, 0xB0, 0x61, 0xC8, 0x0F, - 0x0B, 0x18, 0x01, 0x22, 0x19, 0x46, 0xF2, 0x60, 0x91, 0x43, 0xC9, 0x1D, 0x07, 0x22, 0x91, 0x43, - 0x6A, 0x46, 0x50, 0x1A, 0x59, 0x10, 0x85, 0x46, 0xF0, 0x61, 0x31, 0x61, 0xFC, 0xF7, 0xE7, 0xFA, - 0x70, 0x69, 0x02, 0x28, 0x5A, 0xDB, 0xB2, 0x69, 0x35, 0x60, 0x74, 0x60, 0x03, 0x20, 0x31, 0x69, - 0x08, 0x40, 0xB0, 0x60, 0x48, 0x1E, 0x03, 0x28, 0x01, 0xD2, 0x00, 0x21, 0x23, 0xE0, 0x48, 0x00, - 0x5D, 0x49, 0x01, 0x40, 0x71, 0x61, 0x00, 0x23, 0x19, 0x46, 0xD2, 0x5C, 0xB0, 0x69, 0xC0, 0x18, - 0x44, 0x78, 0x24, 0x02, 0xA2, 0x18, 0xF5, 0x69, 0xEA, 0x52, 0x82, 0x78, 0xC4, 0x78, 0x24, 0x02, - 0xA2, 0x18, 0xEC, 0x18, 0x62, 0x80, 0x02, 0x79, 0x45, 0x79, 0x2D, 0x02, 0xAA, 0x18, 0xA2, 0x80, - 0x82, 0x79, 0xC0, 0x79, 0x00, 0x02, 0x80, 0x18, 0xE0, 0x80, 0xB2, 0x69, 0x08, 0x33, 0x09, 0x1D, - 0x70, 0x69, 0x98, 0x42, 0xE1, 0xD1, 0xB3, 0x68, 0x00, 0x2B, 0x74, 0x68, 0x35, 0x68, 0x25, 0xD0, - 0x49, 0x00, 0xB0, 0x69, 0x40, 0x5C, 0xB2, 0x69, 0x52, 0x18, 0x52, 0x78, 0x12, 0x02, 0x10, 0x18, - 0xF2, 0x69, 0x71, 0x61, 0x50, 0x52, 0x01, 0x2B, 0x18, 0xD0, 0x70, 0x69, 0x80, 0x1C, 0xB1, 0x69, - 0x0A, 0x5C, 0xB1, 0x69, 0x0B, 0x18, 0x5B, 0x78, 0x1B, 0x02, 0x9A, 0x18, 0xF3, 0x69, 0x1A, 0x52, - 0xB1, 0x69, 0xB0, 0x68, 0x02, 0x28, 0x09, 0xD0, 0x0A, 0x46, 0x70, 0x69, 0x00, 0x1D, 0x09, 0x5C, - 0x12, 0x18, 0x52, 0x78, 0x12, 0x02, 0x51, 0x18, 0xF2, 0x69, 0x11, 0x52, 0x30, 0x69, 0xA0, 0x60, - 0x36, 0x48, 0x01, 0x68, 0xC9, 0x07, 0x08, 0xD0, 0x01, 0x68, 0xC9, 0x07, 0x05, 0xD0, 0x01, 0x68, - 0xC9, 0x07, 0x02, 0xD0, 0x01, 0x68, 0xC9, 0x07, 0xF3, 0xD1, 0x41, 0x68, 0x09, 0x06, 0x06, 0xD5, - 0x2F, 0x49, 0x0A, 0x68, 0x03, 0x46, 0x08, 0x3B, 0x1A, 0x60, 0x49, 0x68, 0x19, 0x60, 0xA9, 0x05, - 0xF3, 0x68, 0x1D, 0xD1, 0x42, 0x68, 0x02, 0x21, 0x0A, 0x43, 0x42, 0x60, 0x85, 0x60, 0x42, 0x68, - 0x40, 0x23, 0x13, 0x43, 0x43, 0x60, 0x02, 0x68, 0xD2, 0x07, 0x08, 0xD0, 0x02, 0x68, 0xD2, 0x07, - 0x05, 0xD0, 0x02, 0x68, 0xD2, 0x07, 0x02, 0xD0, 0x02, 0x68, 0xD2, 0x07, 0xF3, 0xD1, 0x02, 0x68, - 0x92, 0x06, 0x01, 0xD5, 0x20, 0x22, 0x02, 0x60, 0x42, 0x68, 0x8A, 0x43, 0x42, 0x60, 0xF3, 0x68, - 0x00, 0x21, 0x61, 0x60, 0x07, 0xE0, 0x41, 0x68, 0x99, 0x43, 0x41, 0x60, 0x61, 0x68, 0x89, 0x1C, - 0x61, 0x60, 0x21, 0x68, 0x49, 0x1C, 0x21, 0x60, 0x21, 0x68, 0xA2, 0x68, 0x91, 0x42, 0x1A, 0xD2, - 0x41, 0x68, 0x19, 0x43, 0x41, 0x60, 0x21, 0x68, 0x49, 0x00, 0xF2, 0x69, 0x51, 0x5A, 0x62, 0x68, - 0x51, 0x53, 0x01, 0x68, 0xC9, 0x07, 0x08, 0xD0, 0x01, 0x68, 0xC9, 0x07, 0x05, 0xD0, 0x01, 0x68, - 0xC9, 0x07, 0x02, 0xD0, 0x01, 0x68, 0xC9, 0x07, 0xF3, 0xD1, 0x01, 0x68, 0x89, 0x06, 0xDA, 0xD5, - 0x20, 0x21, 0x01, 0x60, 0xD7, 0xE7, 0x41, 0x68, 0x80, 0x22, 0x0A, 0x43, 0x42, 0x60, 0xFE, 0x1F, - 0x05, 0x3E, 0xB5, 0x46, 0xF0, 0xBD, 0xC0, 0x46, 0xF8, 0xFF, 0xFF, 0x7F, 0x0C, 0x20, 0x02, 0x40, - 0x44, 0x01, 0x00, 0x20, 0xB0, 0xB5, 0x1C, 0x48, 0x01, 0x68, 0x02, 0x22, 0x11, 0x43, 0x01, 0x60, - 0x01, 0x68, 0x91, 0x43, 0x01, 0x60, 0x19, 0x48, 0x01, 0x46, 0x08, 0x39, 0x60, 0x22, 0x0A, 0x60, - 0x03, 0x21, 0x01, 0x60, 0x16, 0x49, 0x09, 0x78, 0x81, 0x60, 0x3D, 0x21, 0xC1, 0x60, 0x02, 0x46, - 0x0C, 0x3A, 0x13, 0x68, 0x01, 0x21, 0x0B, 0x43, 0x13, 0x60, 0x12, 0x4B, 0x12, 0x4A, 0xDA, 0x60, - 0x02, 0x46, 0x14, 0x32, 0x9A, 0x60, 0x11, 0x4A, 0x54, 0x78, 0x17, 0x34, 0x5C, 0x60, 0x10, 0x4C, - 0x1C, 0x60, 0x4B, 0x02, 0x04, 0x46, 0x14, 0x3C, 0x25, 0x68, 0x1D, 0x43, 0x25, 0x60, 0x03, 0x68, - 0x0B, 0x43, 0x03, 0x60, 0xCB, 0x03, 0x44, 0x6A, 0x1C, 0x43, 0x44, 0x62, 0x11, 0x70, 0x20, 0x38, - 0x02, 0x68, 0x0A, 0x43, 0x02, 0x60, 0xB0, 0xBD, 0x10, 0x10, 0x02, 0x40, 0x20, 0x04, 0x00, 0x40, - 0xEA, 0x04, 0x00, 0x20, 0x44, 0x00, 0x02, 0x40, 0x9C, 0x07, 0x00, 0x20, 0xE0, 0x04, 0x00, 0x20, - 0x9B, 0x09, 0x00, 0x00, 0xF0, 0xB5, 0x0C, 0x49, 0x0C, 0x68, 0x08, 0x22, 0x14, 0x43, 0x04, 0x23, - 0x9C, 0x43, 0x0C, 0x60, 0x09, 0x4C, 0x25, 0x68, 0x01, 0x26, 0xB5, 0x43, 0x25, 0x60, 0x65, 0x68, - 0x07, 0x4F, 0x2F, 0x40, 0x38, 0x18, 0x60, 0x60, 0x20, 0x68, 0x30, 0x43, 0x20, 0x60, 0x08, 0x68, - 0x18, 0x43, 0x90, 0x43, 0x08, 0x60, 0xF0, 0xBD, 0x00, 0x38, 0x01, 0x40, 0x1C, 0x00, 0x02, 0x40, - 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0xB5, 0x85, 0xB0, 0xF5, 0x4D, 0xA8, 0x7C, 0x00, 0x28, 0x05, 0xD0, - 0xF4, 0x4E, 0x70, 0x7E, 0x00, 0x28, 0x05, 0xD0, 0xF0, 0x8F, 0x2F, 0xE1, 0xF1, 0x48, 0xC1, 0x8F, - 0x81, 0x86, 0x9B, 0xE1, 0x7D, 0x22, 0xD4, 0x00, 0xEF, 0x48, 0x00, 0x79, 0x43, 0x00, 0x19, 0x19, - 0x26, 0x20, 0x2D, 0x5C, 0xF0, 0x8F, 0x01, 0x27, 0x00, 0x2D, 0x04, 0x94, 0x18, 0xD0, 0x81, 0x42, - 0x48, 0xD2, 0xE9, 0x4A, 0xD2, 0x7B, 0xE6, 0x4C, 0x65, 0x7C, 0xAA, 0x42, 0x5A, 0xD1, 0x3C, 0x46, - 0x01, 0x25, 0x03, 0x95, 0xF5, 0x73, 0x00, 0x25, 0xB5, 0x86, 0x37, 0x79, 0x00, 0x2F, 0x00, 0xD1, - 0xA0, 0xE0, 0xF5, 0x73, 0x03, 0x9B, 0x9A, 0x1A, 0xDF, 0x4B, 0xDA, 0x73, 0x27, 0x46, 0x4C, 0xE0, - 0x81, 0x42, 0x3A, 0xD2, 0x03, 0x97, 0xDC, 0x4B, 0xDF, 0x7B, 0xD9, 0x4D, 0x6B, 0x7C, 0x02, 0x97, - 0x9F, 0x42, 0x00, 0xD0, 0x8C, 0xE0, 0x30, 0x7D, 0x43, 0x1E, 0x98, 0x41, 0x40, 0x23, 0xF3, 0x5A, - 0x9B, 0xB2, 0xC8, 0x2B, 0x03, 0x9F, 0x3B, 0x46, 0x00, 0xD3, 0x03, 0x46, 0xD2, 0x4D, 0xAC, 0x8C, - 0x2D, 0x6D, 0xA5, 0x42, 0x00, 0xD8, 0x03, 0x46, 0x01, 0x2B, 0x00, 0xD0, 0x88, 0xE0, 0xCF, 0x48, - 0x00, 0x24, 0x84, 0x60, 0x01, 0x20, 0xCC, 0x4D, 0xE8, 0x72, 0x02, 0x99, 0x40, 0x1A, 0xE8, 0x73, - 0xFF, 0xF7, 0x3C, 0xFC, 0xB4, 0x72, 0x28, 0x79, 0x40, 0x00, 0x04, 0x9C, 0x01, 0x19, 0xF0, 0x8F, - 0xC3, 0x4D, 0x79, 0xE0, 0xC2, 0x4D, 0xE2, 0x1A, 0x82, 0x42, 0x28, 0xD8, 0x19, 0x19, 0x81, 0x42, - 0x4F, 0xD3, 0x00, 0x21, 0x82, 0x42, 0x4F, 0xD9, 0x4F, 0xE0, 0xBD, 0x4D, 0xE2, 0x1A, 0x82, 0x42, - 0x78, 0xD8, 0x19, 0x19, 0x81, 0x42, 0x00, 0xD2, 0xB4, 0xE0, 0x00, 0x21, 0x82, 0x42, 0x00, 0xD8, - 0xB3, 0xE0, 0xB3, 0xE0, 0xF2, 0x7B, 0x00, 0x2A, 0x0C, 0xD1, 0x00, 0x22, 0x32, 0x71, 0xB8, 0x4A, - 0x00, 0x92, 0x2F, 0x3A, 0x2F, 0x23, 0xFF, 0xF7, 0xEF, 0xFB, 0xB0, 0x86, 0xF0, 0x8F, 0xB2, 0x49, - 0x09, 0x79, 0x4B, 0x00, 0xAE, 0x4D, 0x04, 0x9C, 0xE2, 0x1A, 0x82, 0x42, 0xD6, 0xD9, 0x03, 0x97, - 0x69, 0x7C, 0x01, 0x27, 0x7D, 0x1A, 0xAC, 0x4C, 0xE4, 0x7B, 0xA5, 0x42, 0x0B, 0xD1, 0xF7, 0x73, - 0x00, 0x25, 0xB5, 0x86, 0x34, 0x79, 0x00, 0x2C, 0x33, 0xD0, 0xF5, 0x73, 0xA6, 0x4B, 0xD9, 0x73, - 0x03, 0x9F, 0x04, 0x9C, 0x04, 0xE0, 0xF1, 0x7B, 0x00, 0x29, 0x03, 0x9F, 0x04, 0x9C, 0x0C, 0xD1, - 0x00, 0x21, 0x31, 0x71, 0x2F, 0x23, 0x00, 0x93, 0xA1, 0x4B, 0xFF, 0xF7, 0xC5, 0xFB, 0xB0, 0x86, - 0x9D, 0x48, 0x00, 0x79, 0x43, 0x00, 0xE2, 0x1A, 0xF0, 0x8F, 0x99, 0x4D, 0x19, 0x19, 0x81, 0x42, - 0xAF, 0xD2, 0x01, 0x21, 0x82, 0x42, 0x00, 0xD8, 0x0F, 0x46, 0x00, 0x2F, 0x73, 0xD1, 0x00, 0x21, - 0xB1, 0x86, 0xF2, 0x7B, 0x00, 0x2A, 0x6E, 0xD0, 0x01, 0x22, 0x32, 0x71, 0xF1, 0x73, 0x6A, 0xE0, - 0x03, 0x9F, 0x11, 0xE0, 0x8E, 0x4D, 0x27, 0x46, 0x04, 0x9C, 0xE2, 0x1A, 0x82, 0x42, 0x95, 0xD9, - 0xBD, 0xE7, 0x8B, 0x4D, 0x03, 0x9F, 0x04, 0x9C, 0x19, 0x19, 0x81, 0x42, 0x91, 0xD2, 0xE0, 0xE7, - 0xD0, 0x00, 0xF0, 0x87, 0x86, 0x4D, 0x04, 0x9C, 0x89, 0x4A, 0x00, 0x92, 0x2F, 0x3A, 0x2F, 0x23, - 0xFF, 0xF7, 0x92, 0xFB, 0xB0, 0x86, 0xF0, 0x8F, 0x83, 0x49, 0x09, 0x79, 0x4B, 0x00, 0xE2, 0x1A, - 0x82, 0x42, 0x86, 0xD9, 0x69, 0x7C, 0x01, 0x23, 0x5C, 0x1A, 0x7F, 0x4D, 0xED, 0x7B, 0xAC, 0x42, - 0x21, 0xD1, 0x30, 0x7D, 0x44, 0x1E, 0xA0, 0x41, 0x40, 0x24, 0x34, 0x5B, 0xA4, 0xB2, 0xC8, 0x2C, - 0x03, 0x97, 0x00, 0xD3, 0x07, 0x46, 0x78, 0x4D, 0xAC, 0x8C, 0x2D, 0x6D, 0xA5, 0x42, 0x00, 0xD8, - 0x07, 0x46, 0x01, 0x2F, 0x72, 0x4D, 0x11, 0xD1, 0x73, 0x4F, 0xF9, 0x73, 0xFB, 0x72, 0x73, 0x48, - 0x00, 0x24, 0x84, 0x60, 0xFF, 0xF7, 0x8A, 0xFB, 0xB4, 0x72, 0x38, 0x79, 0x40, 0x00, 0x04, 0x9C, - 0x22, 0x1A, 0xF0, 0x8F, 0x05, 0xE0, 0x6A, 0x4D, 0x04, 0x9C, 0x03, 0xE0, 0x04, 0x9C, 0xF4, 0x87, - 0x20, 0x46, 0x03, 0x9F, 0x2F, 0x21, 0x00, 0x91, 0x00, 0x21, 0x69, 0x4B, 0xFF, 0xF7, 0x54, 0xFB, - 0xB0, 0x86, 0x65, 0x48, 0x00, 0x79, 0x43, 0x00, 0xE2, 0x1A, 0xF0, 0x8F, 0x19, 0x19, 0x81, 0x42, - 0x00, 0xD3, 0x4A, 0xE7, 0x01, 0x21, 0x82, 0x42, 0x00, 0xD8, 0x0F, 0x46, 0x00, 0x2F, 0x02, 0xD1, - 0x00, 0x21, 0xB1, 0x72, 0xB1, 0x86, 0x71, 0x7E, 0x00, 0x29, 0x6F, 0xD0, 0x7D, 0x21, 0xCC, 0x00, - 0x21, 0x46, 0x2F, 0x31, 0x88, 0x42, 0x2F, 0xD9, 0x57, 0x4F, 0xF9, 0x7B, 0x6A, 0x7C, 0x91, 0x42, - 0x24, 0xD1, 0xB8, 0x8C, 0x3D, 0x6D, 0x01, 0x22, 0x00, 0x23, 0x85, 0x42, 0x10, 0x46, 0x00, 0xD8, - 0x18, 0x46, 0x40, 0x25, 0x75, 0x5B, 0xAD, 0xB2, 0xC8, 0x2D, 0x00, 0xD3, 0x1A, 0x46, 0x10, 0x40, - 0x32, 0x7D, 0xD2, 0xB2, 0x53, 0x1E, 0x9A, 0x41, 0x02, 0x43, 0x01, 0x2A, 0x0B, 0xD1, 0x4B, 0x48, - 0x00, 0x25, 0x85, 0x60, 0x01, 0x20, 0xF8, 0x72, 0x40, 0x1A, 0xF8, 0x73, 0xFF, 0xF7, 0x36, 0xFB, - 0xB5, 0x72, 0xF0, 0x8F, 0x01, 0xE0, 0x00, 0x20, 0xF0, 0x87, 0x41, 0x4D, 0x39, 0x7A, 0x42, 0x00, - 0x51, 0x1A, 0xA7, 0x4A, 0x89, 0x18, 0xB1, 0x86, 0x01, 0x46, 0x30, 0x39, 0x89, 0xB2, 0xA1, 0x42, - 0x2F, 0xD2, 0x2F, 0x46, 0x69, 0x7C, 0x01, 0x22, 0x53, 0x1A, 0x3B, 0x4C, 0xE5, 0x7B, 0xAB, 0x42, - 0x21, 0xD1, 0xA0, 0x8C, 0x25, 0x6D, 0x00, 0x23, 0x85, 0x42, 0x10, 0x46, 0x00, 0xD8, 0x18, 0x46, - 0x40, 0x25, 0x75, 0x5B, 0xAD, 0xB2, 0xC8, 0x2D, 0x00, 0xD3, 0x1A, 0x46, 0x10, 0x40, 0x32, 0x7D, - 0xD2, 0xB2, 0x53, 0x1E, 0x9A, 0x41, 0x02, 0x43, 0x01, 0x2A, 0x0A, 0xD1, 0xE1, 0x73, 0x01, 0x20, - 0xE0, 0x72, 0xF6, 0x48, 0x00, 0x25, 0x85, 0x60, 0xFF, 0xF7, 0x00, 0xFB, 0xB5, 0x72, 0xF0, 0x8F, - 0x01, 0xE0, 0x00, 0x20, 0xF0, 0x87, 0x21, 0x7A, 0x42, 0x00, 0x51, 0x1A, 0x31, 0x39, 0xB1, 0x86, - 0x3D, 0x46, 0x2F, 0x28, 0x02, 0xD8, 0x00, 0x20, 0xB0, 0x72, 0xB0, 0x86, 0xE9, 0x4F, 0xF8, 0x79, - 0xE7, 0x4E, 0xB1, 0x7C, 0x81, 0x42, 0x0A, 0xD9, 0xA8, 0x7D, 0x00, 0x28, 0x07, 0xD0, 0xFC, 0xF7, - 0xD1, 0xFF, 0xFF, 0xF7, 0xE3, 0xFA, 0x66, 0x20, 0xB0, 0x74, 0x00, 0x20, 0x55, 0xE0, 0xE8, 0x7C, - 0x00, 0x28, 0x05, 0xD0, 0xB0, 0x8E, 0x1D, 0x28, 0x25, 0xD8, 0x00, 0x21, 0xB1, 0x87, 0x3F, 0xE0, - 0x70, 0x79, 0x00, 0x28, 0x48, 0xD0, 0x30, 0x78, 0x00, 0x28, 0x72, 0xD0, 0x79, 0x6C, 0xBB, 0x6C, - 0xB0, 0x8E, 0x00, 0x91, 0x2F, 0x21, 0xD8, 0x4C, 0x22, 0x46, 0xFF, 0xF7, 0xA5, 0xFA, 0x01, 0x46, - 0xD7, 0x48, 0xFB, 0xF7, 0xFF, 0xFF, 0xE9, 0x7E, 0x49, 0x08, 0xFB, 0xF7, 0xFB, 0xFF, 0x30, 0x85, - 0xB0, 0x8E, 0x2E, 0x28, 0x6A, 0xD8, 0x00, 0x20, 0x78, 0x65, 0xB0, 0x87, 0x70, 0x66, 0x30, 0x7D, - 0x00, 0x28, 0x2F, 0xD0, 0x24, 0xE1, 0x1E, 0x28, 0x0C, 0xD1, 0x1E, 0x20, 0x18, 0xE0, 0xC0, 0x46, - 0xDC, 0x06, 0x00, 0x20, 0x00, 0x05, 0x00, 0x20, 0xC0, 0x00, 0x00, 0x20, 0x7C, 0x05, 0x00, 0x20, - 0xFF, 0x07, 0x00, 0x00, 0x28, 0x21, 0x69, 0x5C, 0x14, 0x22, 0x4A, 0x43, 0x82, 0x42, 0x07, 0xD9, - 0xA0, 0x21, 0x00, 0x91, 0x1E, 0x21, 0x2F, 0x23, 0xFF, 0xF7, 0x76, 0xFA, 0xB0, 0x87, 0xB0, 0x8E, - 0x28, 0x21, 0x6A, 0x5C, 0x14, 0x21, 0x51, 0x43, 0x81, 0x42, 0x07, 0xD8, 0xBA, 0x4A, 0x00, 0x92, - 0xA0, 0x23, 0xFF, 0xF7, 0x69, 0xFA, 0x00, 0xE0, 0xB0, 0x8E, 0xB0, 0x87, 0x30, 0x7D, 0x00, 0x28, - 0x00, 0xD0, 0xF5, 0xE0, 0xF0, 0x7D, 0x00, 0x28, 0x00, 0xD1, 0xF1, 0xE0, 0xE9, 0x7C, 0x50, 0x24, - 0x20, 0x46, 0x48, 0x43, 0x2F, 0x30, 0xB2, 0x8F, 0x90, 0x42, 0x00, 0xD9, 0x86, 0xE0, 0xF0, 0x7C, - 0x00, 0x28, 0x6B, 0xD1, 0xFC, 0xF7, 0x5E, 0xFF, 0xF8, 0x7A, 0x00, 0x28, 0x61, 0xD1, 0xF0, 0x7C, - 0x00, 0x28, 0x5C, 0xD1, 0xF8, 0x7C, 0xF9, 0x7B, 0x01, 0x29, 0x28, 0xD1, 0x40, 0x1C, 0xF8, 0x74, - 0xC1, 0xB2, 0x07, 0x29, 0x02, 0xD3, 0x01, 0x20, 0x30, 0x73, 0xF8, 0x74, 0x01, 0x21, 0x01, 0x40, - 0x27, 0xE0, 0x70, 0x6E, 0xA3, 0x49, 0xFB, 0xF7, 0x95, 0xFF, 0x81, 0xB2, 0x30, 0x29, 0x00, 0xD8, - 0x30, 0x21, 0x00, 0x04, 0xC0, 0x0E, 0x0D, 0xD0, 0x9B, 0x49, 0x0B, 0xE0, 0x70, 0x6E, 0x9D, 0x49, - 0xFB, 0xF7, 0x88, 0xFF, 0x81, 0xB2, 0x30, 0x29, 0x00, 0xD8, 0x30, 0x21, 0x00, 0x04, 0xC0, 0x0E, - 0x00, 0xD0, 0x21, 0x46, 0xB1, 0x87, 0x30, 0x7D, 0x00, 0x28, 0xBB, 0xD0, 0xB0, 0xE0, 0x40, 0x1E, - 0xF8, 0x74, 0x01, 0x06, 0x03, 0xD1, 0x01, 0x20, 0x30, 0x73, 0x06, 0x20, 0xF8, 0x74, 0x01, 0x21, - 0x81, 0x43, 0xB9, 0x73, 0xEF, 0xF3, 0x10, 0x80, 0x72, 0xB6, 0xF0, 0x7B, 0x00, 0x28, 0x02, 0xD1, - 0xF8, 0x7C, 0xFC, 0xF7, 0xB3, 0xFF, 0x62, 0xB6, 0xFC, 0xF7, 0x90, 0xFF, 0x87, 0x48, 0x01, 0x68, - 0x89, 0x4A, 0x91, 0x42, 0x02, 0xD3, 0x01, 0x21, 0x82, 0x4A, 0xD1, 0x72, 0x00, 0x21, 0xF1, 0x76, - 0x31, 0x77, 0x80, 0x4F, 0x39, 0x6D, 0x83, 0x4A, 0x3A, 0x65, 0xFA, 0x7C, 0x83, 0x4B, 0x84, 0x4D, - 0x1D, 0x60, 0x52, 0x00, 0x10, 0x18, 0x41, 0x82, 0x01, 0x20, 0xF0, 0x74, 0x77, 0x4D, 0xFD, 0xF7, - 0xE9, 0xFD, 0x01, 0x20, 0xF0, 0x74, 0x38, 0x8D, 0x30, 0x86, 0xE9, 0x7C, 0xBB, 0x8D, 0xB0, 0x8F, - 0x75, 0x4A, 0x00, 0x29, 0x05, 0xD0, 0x11, 0x46, 0x2F, 0x39, 0x00, 0x91, 0x28, 0x33, 0x89, 0x21, - 0x03, 0xE0, 0x11, 0x46, 0x2F, 0x39, 0x00, 0x91, 0x2F, 0x21, 0xFF, 0xF7, 0xD5, 0xF9, 0x70, 0x86, - 0x26, 0x20, 0x28, 0x5C, 0x00, 0x28, 0x01, 0xD1, 0x00, 0x20, 0xF0, 0x73, 0xE8, 0x7C, 0x60, 0x43, - 0x2F, 0x30, 0xB1, 0x8F, 0x88, 0x42, 0x58, 0xD9, 0xF0, 0x7A, 0x05, 0x28, 0x15, 0xD8, 0xC0, 0x46, - 0x78, 0x44, 0x00, 0x79, 0x40, 0x00, 0x87, 0x44, 0x12, 0x05, 0x0E, 0x08, 0x0B, 0x05, 0xC0, 0x46, - 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xF7, 0xAA, 0xFA, 0x07, 0xE0, 0xFF, 0xF7, 0xE1, 0xF9, 0x04, 0xE0, - 0xFF, 0xF7, 0xC8, 0xFA, 0x01, 0xE0, 0xFF, 0xF7, 0x7D, 0xFA, 0x00, 0x20, 0xF0, 0x72, 0x28, 0x7D, - 0x00, 0x28, 0x21, 0xD0, 0xF0, 0x7C, 0x00, 0x28, 0x6D, 0xD0, 0xF8, 0x7C, 0x3C, 0x21, 0x41, 0x43, - 0x38, 0x8B, 0x42, 0x18, 0x10, 0x46, 0x3C, 0x38, 0x03, 0xB2, 0xFF, 0x21, 0x68, 0x31, 0x8B, 0x42, - 0x01, 0xDD, 0x58, 0x48, 0x10, 0x18, 0x4C, 0x22, 0xB0, 0x52, 0x02, 0x46, 0xEF, 0x32, 0x13, 0xB2, - 0x8B, 0x42, 0x01, 0xDD, 0x02, 0x46, 0x79, 0x3A, 0x32, 0x53, 0x02, 0x46, 0x77, 0x32, 0x13, 0xB2, - 0x8B, 0x42, 0x10, 0xDC, 0x10, 0x46, 0x0F, 0xE0, 0x00, 0x20, 0x70, 0x86, 0xF1, 0x7C, 0x00, 0x29, - 0x6A, 0xD1, 0x46, 0x49, 0x88, 0x60, 0x01, 0x20, 0xF8, 0x72, 0x28, 0x7F, 0x00, 0x28, 0x5E, 0xD0, - 0xFD, 0xF7, 0x30, 0xFE, 0x60, 0xE0, 0xF1, 0x38, 0x4E, 0x21, 0x70, 0x52, 0xE8, 0x7C, 0x01, 0x28, - 0x01, 0xD1, 0x01, 0x20, 0x30, 0x75, 0x00, 0x20, 0x70, 0x86, 0xF0, 0x7B, 0x00, 0x28, 0x01, 0xD0, - 0x05, 0xB0, 0xF0, 0xBD, 0xB0, 0x8F, 0x2E, 0x28, 0x12, 0xD9, 0x69, 0x7F, 0x1E, 0x22, 0xCA, 0x40, - 0x74, 0x8E, 0x36, 0x49, 0x89, 0x68, 0x91, 0x42, 0x0B, 0xD2, 0x39, 0x8D, 0x8C, 0x42, 0x01, 0xD2, - 0x71, 0x86, 0x0C, 0x46, 0x79, 0x8D, 0x8C, 0x42, 0x03, 0xD9, 0x71, 0x86, 0x0C, 0x46, 0x00, 0xE0, - 0x74, 0x8E, 0xB9, 0x8E, 0x8C, 0x42, 0x01, 0xD9, 0x71, 0x86, 0x0C, 0x46, 0xB1, 0x79, 0x00, 0x29, - 0x05, 0xD0, 0x22, 0x21, 0x79, 0x5E, 0x8C, 0x42, 0x01, 0xDD, 0x8C, 0xB2, 0x74, 0x86, 0x30, 0x28, - 0xD6, 0xD3, 0xB0, 0x6E, 0x01, 0x28, 0xD3, 0xDB, 0x26, 0x49, 0xFB, 0xF7, 0x7B, 0xFE, 0x20, 0x18, - 0x70, 0x86, 0x05, 0xB0, 0xF0, 0xBD, 0x21, 0x48, 0x00, 0x21, 0x81, 0x60, 0x01, 0x22, 0xFA, 0x72, - 0xB1, 0x75, 0x28, 0x7F, 0x00, 0x28, 0x0F, 0xD0, 0xE8, 0x7C, 0x00, 0x28, 0x85, 0xD1, 0x29, 0x20, - 0x28, 0x5C, 0x40, 0x00, 0x18, 0x49, 0x40, 0x18, 0x43, 0x38, 0x70, 0x84, 0x04, 0x92, 0xFF, 0xF7, - 0xCB, 0xFA, 0x04, 0x98, 0xF0, 0x73, 0x78, 0xE7, 0xFC, 0xF7, 0x2C, 0xFE, 0x75, 0xE7, 0xF0, 0x7B, - 0x00, 0x28, 0x01, 0xD1, 0xFC, 0xF7, 0x26, 0xFE, 0x26, 0x20, 0x29, 0x5C, 0xF0, 0x7B, 0x00, 0x29, - 0xA4, 0xD0, 0x00, 0x28, 0xA2, 0xD0, 0xF1, 0x8F, 0x7D, 0x20, 0xC4, 0x00, 0x20, 0x46, 0xFD, 0xF7, - 0xFF, 0xFD, 0x00, 0x19, 0x70, 0x84, 0x80, 0xB2, 0x79, 0x8E, 0x49, 0x1E, 0x81, 0x42, 0x02, 0xDD, - 0xFF, 0xF7, 0xAA, 0xFA, 0x91, 0xE7, 0xFD, 0xF7, 0xB5, 0xFD, 0x8E, 0xE7, 0xDC, 0x06, 0x00, 0x20, - 0x00, 0x05, 0x00, 0x20, 0xC0, 0x00, 0x00, 0x20, 0xFF, 0x07, 0x00, 0x00, 0x7C, 0x05, 0x00, 0x20, - 0x00, 0x87, 0x93, 0x03, 0x10, 0x27, 0x00, 0x00, 0xC5, 0x09, 0x00, 0x00, 0x24, 0x00, 0x00, 0x40, - 0x88, 0x13, 0x00, 0x00, 0x5C, 0xFE, 0xFF, 0xFF, 0x03, 0x20, 0x00, 0x02, 0x03, 0x49, 0x0A, 0x68, - 0x82, 0x43, 0x01, 0x20, 0x00, 0x02, 0x10, 0x18, 0x08, 0x60, 0x70, 0x47, 0x0C, 0x04, 0x00, 0x48, - 0x01, 0x49, 0x08, 0x60, 0x70, 0x47, 0xC0, 0x46, 0x34, 0x2C, 0x01, 0x40, 0x01, 0x49, 0x08, 0x60, - 0x70, 0x47, 0xC0, 0x46, 0x38, 0x2C, 0x01, 0x40, 0x01, 0x49, 0x08, 0x60, 0x70, 0x47, 0xC0, 0x46, - 0x3C, 0x2C, 0x01, 0x40, 0x0B, 0x28, 0x00, 0xD3, 0x0B, 0x20, 0x40, 0x00, 0x01, 0x49, 0x08, 0x70, - 0x70, 0x47, 0xC0, 0x46, 0xD5, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x91, 0xB0, 0x00, 0x24, 0x0F, 0x94, - 0x0E, 0x94, 0x0D, 0x94, 0x0C, 0x94, 0x0B, 0x94, 0x0A, 0x94, 0x09, 0x94, 0x08, 0x94, 0x07, 0x94, - 0x06, 0x94, 0x05, 0x94, 0x04, 0x94, 0x03, 0x94, 0x01, 0x20, 0x82, 0x03, 0x02, 0x92, 0x03, 0x46, - 0x00, 0x90, 0x3D, 0x48, 0x41, 0x68, 0x11, 0x43, 0x41, 0x60, 0x41, 0x68, 0x11, 0x40, 0x10, 0x91, - 0x10, 0x99, 0x99, 0x04, 0x02, 0x68, 0x0A, 0x43, 0x02, 0x60, 0x00, 0x68, 0x08, 0x40, 0x10, 0x90, - 0x10, 0x98, 0x08, 0x94, 0x07, 0x93, 0x06, 0x94, 0x02, 0x20, 0x04, 0x90, 0x40, 0x26, 0x03, 0x96, - 0x03, 0x25, 0x05, 0x95, 0x31, 0x48, 0x03, 0xA9, 0xFB, 0xF7, 0xE0, 0xFF, 0x30, 0x4F, 0x38, 0x68, - 0x30, 0x49, 0x01, 0x40, 0x10, 0x31, 0x39, 0x60, 0x28, 0x03, 0x39, 0x68, 0x81, 0x43, 0x39, 0x60, - 0x38, 0x68, 0x20, 0x21, 0x88, 0x43, 0x38, 0x60, 0x38, 0x68, 0xB0, 0x43, 0x38, 0x60, 0x38, 0x68, - 0x80, 0x21, 0x01, 0x91, 0x08, 0x43, 0x38, 0x60, 0x28, 0x02, 0x39, 0x68, 0x81, 0x43, 0x39, 0x60, - 0xA8, 0x02, 0x39, 0x68, 0x81, 0x43, 0x39, 0x60, 0x0F, 0x94, 0x0E, 0x94, 0x0C, 0x94, 0x0B, 0x94, - 0x0A, 0x94, 0xE1, 0x20, 0x40, 0x02, 0x09, 0x90, 0x08, 0x26, 0x0D, 0x96, 0x1E, 0x4D, 0x2C, 0x46, - 0x08, 0x3C, 0x09, 0xA9, 0x20, 0x46, 0xFC, 0xF7, 0x41, 0xFA, 0x00, 0x9B, 0x98, 0x02, 0x29, 0x68, - 0x81, 0x43, 0x29, 0x60, 0x09, 0x20, 0xC0, 0x02, 0x29, 0x1F, 0x0A, 0x68, 0x82, 0x43, 0x0A, 0x60, - 0x28, 0x68, 0x2A, 0x21, 0x88, 0x43, 0x28, 0x60, 0x20, 0x68, 0x18, 0x43, 0x20, 0x60, 0x02, 0x98, - 0x10, 0x30, 0x39, 0x68, 0x01, 0x40, 0x0C, 0x20, 0x10, 0x29, 0x02, 0x46, 0x00, 0xD0, 0x32, 0x46, - 0x0E, 0x4B, 0xD3, 0x51, 0x10, 0x29, 0x00, 0xD0, 0x06, 0x46, 0x28, 0x46, 0x20, 0x30, 0xF0, 0x51, - 0x78, 0x68, 0x0B, 0x49, 0x01, 0x40, 0x31, 0x31, 0x79, 0x60, 0x28, 0x68, 0x01, 0x99, 0x08, 0x43, - 0x28, 0x60, 0x11, 0xB0, 0xF0, 0xBD, 0xC0, 0x46, 0x14, 0x10, 0x02, 0x40, 0x00, 0x04, 0x00, 0x48, - 0x1C, 0x00, 0x02, 0x40, 0xEF, 0xBF, 0xFF, 0xFF, 0x08, 0x38, 0x01, 0x40, 0xA4, 0x05, 0x00, 0x20, - 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0xB5, 0x83, 0xB0, 0xF2, 0x4D, 0x68, 0x8E, 0x40, 0x27, 0xE8, 0x53, - 0xA8, 0x8D, 0x40, 0x1C, 0xA8, 0x85, 0xA8, 0x8C, 0x40, 0x1C, 0xA8, 0x84, 0xE8, 0x8C, 0x40, 0x1C, - 0xE8, 0x84, 0xE8, 0x7D, 0x00, 0x28, 0x44, 0xD1, 0x68, 0x7A, 0x00, 0x28, 0x41, 0xD1, 0x28, 0x7E, - 0x00, 0x28, 0x3E, 0xD0, 0xA8, 0x8E, 0x00, 0x28, 0x03, 0xD0, 0x52, 0x20, 0x00, 0x21, 0x29, 0x52, - 0x37, 0xE0, 0x52, 0x20, 0x29, 0x5A, 0x49, 0x1C, 0x29, 0x52, 0x89, 0xB2, 0xE2, 0x4A, 0x91, 0x42, - 0x2F, 0xD3, 0x69, 0x8F, 0x1F, 0x29, 0x1E, 0xD3, 0x02, 0x97, 0x01, 0x20, 0xE8, 0x75, 0xE8, 0x78, - 0x00, 0x28, 0x1C, 0xD0, 0x54, 0x20, 0x2C, 0x5A, 0xB9, 0x20, 0x41, 0x00, 0x20, 0x46, 0xFB, 0xF7, - 0x29, 0xFD, 0x68, 0x72, 0x60, 0x08, 0xB9, 0x28, 0x13, 0xD3, 0x00, 0x24, 0xD7, 0x4F, 0xD8, 0x4E, - 0xFF, 0xF7, 0x2C, 0xF9, 0x64, 0x20, 0xFD, 0xF7, 0xFD, 0xF9, 0x3E, 0x60, 0x64, 0x1C, 0x68, 0x7A, - 0x84, 0x42, 0xF5, 0xD3, 0x05, 0xE0, 0x00, 0x21, 0x29, 0x52, 0x29, 0x76, 0x09, 0xE0, 0xFF, 0xF7, - 0x1D, 0xF9, 0xA8, 0x7E, 0x00, 0x28, 0x02, 0x9F, 0x03, 0xD1, 0x26, 0x20, 0xCD, 0x49, 0x00, 0x22, - 0x0A, 0x54, 0xCC, 0x4E, 0xF1, 0x7F, 0x00, 0x29, 0x10, 0xD0, 0x56, 0x20, 0x2A, 0x5A, 0x52, 0x1C, - 0x2A, 0x52, 0x92, 0xB2, 0xC8, 0x4B, 0x5B, 0x78, 0xC9, 0x18, 0x14, 0x23, 0x4B, 0x43, 0x14, 0x3B, - 0x93, 0x42, 0x03, 0xD2, 0x00, 0x21, 0x29, 0x52, 0x01, 0x20, 0xA8, 0x73, 0x28, 0x7D, 0x00, 0x28, - 0x00, 0xD0, 0x71, 0xE1, 0xC0, 0x4C, 0xE0, 0x7A, 0x00, 0x28, 0x3F, 0xD0, 0xE8, 0x7C, 0x00, 0x28, - 0x3C, 0xD0, 0xFE, 0xF7, 0xCB, 0xFF, 0xFD, 0xF7, 0xA1, 0xFC, 0xA1, 0x7B, 0x00, 0x29, 0x0D, 0xD0, - 0x00, 0x28, 0x0D, 0xD0, 0xA8, 0x7D, 0x40, 0x1C, 0xA8, 0x75, 0x22, 0x7C, 0xC0, 0xB2, 0x90, 0x42, - 0x14, 0xD8, 0xE8, 0x7E, 0x00, 0x28, 0x29, 0xD1, 0x28, 0x7F, 0x16, 0xE0, 0x00, 0x28, 0x06, 0xD0, - 0x28, 0x7F, 0x40, 0x1C, 0x28, 0x77, 0xEA, 0x7E, 0x00, 0x2A, 0x1F, 0xD1, 0x0B, 0xE0, 0xA8, 0x7D, - 0x40, 0x1C, 0xA8, 0x75, 0x22, 0x7C, 0xC0, 0xB2, 0x90, 0x42, 0x0B, 0xD9, 0x00, 0x20, 0x28, 0x77, - 0xEA, 0x7E, 0x00, 0x2A, 0x12, 0xD1, 0x00, 0x29, 0x08, 0xD0, 0x61, 0x7C, 0xC0, 0xB2, 0x88, 0x42, - 0x08, 0xD8, 0x0B, 0xE0, 0xE8, 0x7E, 0x00, 0x28, 0x08, 0xD1, 0x28, 0x7F, 0xA1, 0x7C, 0xC0, 0xB2, - 0x88, 0x42, 0x03, 0xD9, 0x01, 0x20, 0xE8, 0x76, 0x00, 0xF0, 0x06, 0xFA, 0xE8, 0x8C, 0x15, 0x28, - 0x00, 0xD2, 0xB8, 0xE0, 0x00, 0x20, 0x00, 0x90, 0xE8, 0x84, 0xE9, 0x7C, 0xA8, 0x79, 0x00, 0x28, - 0x02, 0x97, 0x01, 0x91, 0x3F, 0xD0, 0x00, 0x29, 0x3D, 0xD0, 0x2E, 0x20, 0x28, 0x5E, 0x2C, 0x21, - 0x71, 0x5C, 0xC7, 0x22, 0xD2, 0x43, 0x4A, 0x43, 0x10, 0x18, 0x94, 0x49, 0x08, 0x60, 0x8A, 0x69, - 0x83, 0x1A, 0x8E, 0x68, 0xCA, 0x68, 0x0F, 0x69, 0x5A, 0x43, 0x4A, 0x61, 0x88, 0x61, 0x46, 0x43, - 0xF3, 0x19, 0x0E, 0x6A, 0xB3, 0x42, 0x00, 0xDB, 0x33, 0x46, 0x76, 0x42, 0xB3, 0x42, 0x00, 0xDC, - 0x33, 0x46, 0x0B, 0x61, 0xD2, 0x18, 0x4B, 0x68, 0x58, 0x43, 0x10, 0x18, 0x4A, 0x6A, 0x90, 0x42, - 0x02, 0x9F, 0x00, 0xDB, 0x10, 0x46, 0x52, 0x42, 0x90, 0x42, 0x82, 0x4E, 0x00, 0xDC, 0x10, 0x46, - 0xC8, 0x61, 0x83, 0x49, 0xFB, 0xF7, 0x8E, 0xFC, 0x61, 0x8C, 0x08, 0x18, 0x02, 0xB2, 0xA1, 0x8D, - 0x8A, 0x42, 0x00, 0xDB, 0x01, 0x46, 0x08, 0xB2, 0x7D, 0x21, 0x09, 0x01, 0x88, 0x42, 0x00, 0xDB, - 0x08, 0x46, 0x60, 0x84, 0x01, 0x99, 0x70, 0x7F, 0x00, 0x28, 0x31, 0xD0, 0x00, 0x29, 0x2F, 0xD0, - 0xE0, 0x8A, 0x21, 0x6D, 0x09, 0x1A, 0x75, 0x48, 0x81, 0x62, 0x02, 0x6C, 0x8B, 0x1A, 0x06, 0x6B, - 0x42, 0x6B, 0x87, 0x6B, 0x5A, 0x43, 0xC2, 0x63, 0x01, 0x64, 0x4E, 0x43, 0xF3, 0x19, 0x86, 0x6C, - 0xB3, 0x42, 0x00, 0xDB, 0x33, 0x46, 0x76, 0x42, 0xB3, 0x42, 0x00, 0xDC, 0x33, 0x46, 0x83, 0x63, - 0xD2, 0x18, 0xC3, 0x6A, 0x59, 0x43, 0x51, 0x18, 0xC2, 0x6C, 0x91, 0x42, 0x02, 0x9F, 0x00, 0xDB, - 0x11, 0x46, 0x52, 0x42, 0x91, 0x42, 0x00, 0xDC, 0x11, 0x46, 0x41, 0x64, 0xA8, 0x6E, 0x08, 0x18, - 0x64, 0x49, 0x88, 0x42, 0x00, 0xDB, 0x08, 0x46, 0xC1, 0x17, 0x88, 0x43, 0xA8, 0x66, 0x01, 0x99, - 0x68, 0x79, 0x00, 0x28, 0x37, 0xD0, 0x00, 0x29, 0x35, 0xD0, 0x38, 0x46, 0x29, 0x8D, 0x5E, 0x4A, - 0xD2, 0x68, 0x51, 0x1A, 0x61, 0x65, 0xE2, 0x6E, 0x8B, 0x1A, 0xE6, 0x6D, 0x22, 0x6E, 0x67, 0x6E, - 0x5A, 0x43, 0xA2, 0x66, 0xE1, 0x66, 0x4E, 0x43, 0xF3, 0x19, 0x66, 0x6F, 0xB3, 0x42, 0x00, 0xDB, - 0x33, 0x46, 0x76, 0x42, 0xB3, 0x42, 0x00, 0xDC, 0x33, 0x46, 0x63, 0x66, 0xD2, 0x18, 0xA3, 0x6D, - 0x59, 0x43, 0x51, 0x18, 0xA2, 0x6F, 0x91, 0x42, 0x07, 0x46, 0x00, 0xDB, 0x11, 0x46, 0x52, 0x42, - 0x91, 0x42, 0x00, 0xDC, 0x11, 0x46, 0x21, 0x67, 0x6A, 0x6E, 0x89, 0x18, 0x4B, 0x4A, 0x91, 0x42, - 0x00, 0xDB, 0x11, 0x46, 0xCA, 0x17, 0x91, 0x43, 0x69, 0x66, 0xE9, 0x48, 0x80, 0x68, 0x63, 0x28, - 0x01, 0xD8, 0x00, 0x98, 0x60, 0x66, 0x60, 0x79, 0x00, 0x28, 0x31, 0xD0, 0x7D, 0x20, 0x80, 0x00, - 0xE3, 0x4B, 0x19, 0x68, 0x01, 0x90, 0x81, 0x42, 0x02, 0xD8, 0x38, 0x46, 0x10, 0x26, 0x01, 0xE0, - 0x38, 0x46, 0x06, 0x26, 0x29, 0x8E, 0x02, 0x22, 0x96, 0x29, 0x17, 0x46, 0x00, 0xD3, 0x37, 0x46, - 0x9B, 0x68, 0x96, 0x2B, 0x00, 0xD3, 0x3A, 0x46, 0xD7, 0x4C, 0x62, 0x72, 0x07, 0x46, 0x2B, 0x5A, - 0x5E, 0x1A, 0x96, 0x42, 0x0A, 0xDD, 0x89, 0x18, 0xE9, 0x53, 0x21, 0x6D, 0x01, 0x98, 0x81, 0x42, - 0xD6, 0x49, 0x20, 0xD9, 0x01, 0x20, 0xA0, 0x72, 0xC8, 0x7D, 0x08, 0xE0, 0xC8, 0x1A, 0x90, 0x42, - 0xD2, 0x4B, 0x01, 0xDD, 0x88, 0x1A, 0xE8, 0x53, 0x00, 0x20, 0xA0, 0x72, 0xD8, 0x7D, 0xA0, 0x70, - 0xE8, 0x7D, 0x00, 0x28, 0x14, 0xD0, 0xE8, 0x7C, 0x00, 0x28, 0x11, 0xD0, 0xA8, 0x8F, 0x30, 0x28, - 0x0E, 0xD3, 0xEE, 0x5B, 0x27, 0x8E, 0x30, 0x46, 0x78, 0x43, 0x7D, 0x21, 0x09, 0x01, 0xFB, 0xF7, - 0xA9, 0xFB, 0x40, 0x1C, 0x1F, 0xE0, 0x00, 0x20, 0xA0, 0x72, 0xE8, 0x7D, 0x00, 0x28, 0xEA, 0xD1, - 0xE8, 0x7B, 0x00, 0x28, 0x0F, 0xD0, 0x68, 0x8C, 0x26, 0x46, 0x3C, 0x46, 0x37, 0x8E, 0x78, 0x43, - 0x7D, 0x21, 0x09, 0x01, 0xFB, 0xF7, 0x96, 0xFB, 0x71, 0x8E, 0x08, 0x1A, 0x40, 0x1C, 0x58, 0x21, - 0x68, 0x52, 0x2E, 0x5B, 0x09, 0xE0, 0xEE, 0x5B, 0x27, 0x8E, 0x30, 0x46, 0x78, 0x43, 0x7D, 0x21, - 0x09, 0x01, 0xFB, 0xF7, 0x87, 0xFB, 0x58, 0x21, 0x68, 0x52, 0x11, 0x49, 0x0F, 0x60, 0x80, 0xB2, - 0x88, 0x60, 0xC8, 0x60, 0x2E, 0x86, 0x08, 0x61, 0x44, 0x20, 0x29, 0x5A, 0x49, 0x1C, 0x29, 0x52, - 0x03, 0xB0, 0xF0, 0xBD, 0x00, 0x05, 0x00, 0x20, 0x21, 0x4E, 0x00, 0x00, 0x00, 0x30, 0x00, 0x40, - 0xAA, 0xAA, 0x00, 0x00, 0xDC, 0x06, 0x00, 0x20, 0xC0, 0x00, 0x00, 0x20, 0x4C, 0x01, 0x00, 0x20, - 0xF0, 0xD8, 0xFF, 0xFF, 0x60, 0xE3, 0x16, 0x00, 0x7C, 0x05, 0x00, 0x20, 0xF0, 0x58, 0x38, 0x01, - 0x2C, 0x2C, 0x01, 0x40, 0x70, 0xB5, 0x4E, 0x48, 0x00, 0x78, 0x4E, 0x4C, 0x21, 0x78, 0x00, 0x29, - 0x0B, 0xD0, 0x00, 0x28, 0x09, 0xD0, 0x4D, 0x48, 0x00, 0x78, 0x00, 0x28, 0x10, 0xD0, 0xFE, 0xF7, - 0xED, 0xFF, 0x54, 0x48, 0x02, 0x21, 0x01, 0x70, 0x70, 0xBD, 0x47, 0x49, 0x09, 0x78, 0x01, 0x29, - 0x0C, 0xD0, 0x00, 0x29, 0x24, 0xD1, 0xFD, 0xF7, 0xDB, 0xF8, 0xFE, 0xF7, 0xDF, 0xFF, 0x70, 0xBD, - 0xFF, 0xF7, 0x20, 0xF9, 0x4B, 0x48, 0x01, 0x21, 0x01, 0x70, 0x70, 0xBD, 0x00, 0x28, 0x0C, 0xD0, - 0x3E, 0x48, 0x00, 0x78, 0x00, 0x28, 0x14, 0xD0, 0x3D, 0x48, 0x00, 0x88, 0xFE, 0xF7, 0x32, 0xFD, - 0xFC, 0xF7, 0xF8, 0xFC, 0xFE, 0xF7, 0xCA, 0xFF, 0x70, 0xBD, 0x3A, 0x4D, 0x28, 0x78, 0x01, 0x28, - 0x0A, 0xD0, 0x39, 0x48, 0x00, 0x78, 0x01, 0x28, 0x0E, 0xD0, 0x20, 0x78, 0x00, 0x28, 0x1D, 0xD0, - 0x70, 0xBD, 0xFF, 0xF7, 0xFF, 0xF8, 0x70, 0xBD, 0xFC, 0xF7, 0xE4, 0xFC, 0xFE, 0xF7, 0xB6, 0xFF, - 0x31, 0x48, 0x00, 0x78, 0x01, 0x28, 0xF0, 0xD1, 0xFD, 0xF7, 0xE6, 0xFA, 0x00, 0x28, 0x03, 0xD0, - 0x2E, 0x48, 0x03, 0x21, 0x01, 0x70, 0x04, 0xE0, 0x2C, 0x48, 0x02, 0x21, 0x01, 0x70, 0xFC, 0xF7, - 0xF7, 0xFE, 0xFE, 0xF7, 0xA3, 0xFF, 0x20, 0x78, 0x00, 0x28, 0xE1, 0xD1, 0x28, 0x49, 0x0A, 0x88, - 0x28, 0x4C, 0x28, 0x78, 0x00, 0x28, 0x16, 0xD0, 0x60, 0x79, 0x07, 0x28, 0x13, 0xD8, 0x06, 0x2A, - 0x11, 0xD3, 0x43, 0x1C, 0x63, 0x71, 0x24, 0x48, 0x05, 0x68, 0xC0, 0x6F, 0x40, 0x1B, 0x65, 0x69, - 0x40, 0x19, 0x60, 0x61, 0x08, 0x2B, 0x06, 0xD1, 0xC3, 0x09, 0xC0, 0x08, 0xC5, 0x1A, 0xA5, 0x81, - 0xC0, 0x18, 0x1A, 0x4B, 0x98, 0x80, 0x1D, 0x4D, 0x28, 0x88, 0x00, 0x28, 0x05, 0xD1, 0x63, 0x78, - 0x00, 0x2B, 0x02, 0xD1, 0x50, 0x1C, 0x08, 0x80, 0x70, 0xBD, 0x00, 0x26, 0x0E, 0x80, 0x18, 0x49, - 0x88, 0x42, 0xB5, 0xD3, 0x61, 0x89, 0xFD, 0xF7, 0x8B, 0xFA, 0x32, 0x28, 0x01, 0xD8, 0x20, 0x78, - 0x46, 0x1C, 0x26, 0x70, 0xF0, 0xB2, 0x33, 0x28, 0x07, 0xD3, 0xE0, 0x78, 0x00, 0x28, 0x04, 0xD1, - 0xFE, 0xF7, 0xB6, 0xFD, 0x01, 0x20, 0x00, 0x02, 0x20, 0x80, 0x28, 0x88, 0x60, 0x81, 0x70, 0xBD, - 0x10, 0x05, 0x00, 0x20, 0x17, 0x05, 0x00, 0x20, 0x18, 0x05, 0x00, 0x20, 0xE0, 0x04, 0x00, 0x20, - 0x88, 0x05, 0x00, 0x20, 0x19, 0x05, 0x00, 0x20, 0x1A, 0x05, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20, - 0x3A, 0x05, 0x00, 0x20, 0xE4, 0x04, 0x00, 0x20, 0xD8, 0x05, 0x00, 0x20, 0x34, 0x05, 0x00, 0x20, - 0xDD, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x81, 0xB0, 0x3C, 0x48, 0x01, 0x68, - 0x4A, 0x22, 0x3C, 0x4E, 0xB1, 0x52, 0x00, 0x24, 0x04, 0x60, 0x3B, 0x4D, 0x2A, 0x6D, 0x03, 0x23, - 0x53, 0x43, 0x89, 0xB2, 0x59, 0x18, 0x0A, 0x09, 0x38, 0x4B, 0x13, 0x40, 0xCA, 0x08, 0xD2, 0x1A, - 0x42, 0x27, 0xF2, 0x53, 0x89, 0x08, 0x29, 0x65, 0x33, 0x87, 0x91, 0xB2, 0x34, 0x4F, 0xBA, 0x68, - 0x05, 0x2A, 0x11, 0xD2, 0x00, 0x68, 0x33, 0x48, 0x01, 0x60, 0xE8, 0x7C, 0xE9, 0x7B, 0x01, 0x29, - 0x17, 0xD1, 0x40, 0x1C, 0xE8, 0x74, 0xC1, 0xB2, 0x07, 0x29, 0x02, 0xD3, 0x01, 0x20, 0x30, 0x73, - 0xE8, 0x74, 0x01, 0x21, 0x01, 0x40, 0x16, 0xE0, 0x02, 0x68, 0x8A, 0x42, 0xEB, 0xD2, 0x02, 0x68, - 0x8A, 0x42, 0xE8, 0xD2, 0x02, 0x68, 0x8A, 0x42, 0xE5, 0xD2, 0x02, 0x68, 0x8A, 0x42, 0xF3, 0xD3, - 0xE1, 0xE7, 0x40, 0x1E, 0xE8, 0x74, 0x01, 0x06, 0x03, 0xD1, 0x01, 0x20, 0x30, 0x73, 0x06, 0x20, - 0xE8, 0x74, 0x01, 0x21, 0x81, 0x43, 0xA9, 0x73, 0xEF, 0xF3, 0x10, 0x80, 0x72, 0xB6, 0xF0, 0x7B, - 0x00, 0x28, 0x02, 0xD1, 0xE8, 0x7C, 0xFC, 0xF7, 0xC1, 0xFA, 0x62, 0xB6, 0xFC, 0xF7, 0x9E, 0xFA, - 0x38, 0x68, 0x19, 0x49, 0x88, 0x42, 0x01, 0xD3, 0x01, 0x20, 0xE8, 0x72, 0xF4, 0x76, 0x34, 0x77, - 0xB4, 0x75, 0xB8, 0x68, 0x41, 0x1C, 0xB9, 0x60, 0xE8, 0x7C, 0x40, 0x00, 0xC2, 0x19, 0x28, 0x6D, - 0x50, 0x82, 0x26, 0x22, 0x11, 0x4B, 0x9A, 0x5C, 0xD2, 0xB2, 0x5B, 0x7F, 0x13, 0x43, 0x7D, 0x22, - 0x12, 0x01, 0x00, 0x2B, 0x05, 0xD0, 0x14, 0x29, 0x08, 0xD3, 0x51, 0x1C, 0x88, 0x42, 0x02, 0xD3, - 0x04, 0xE0, 0x90, 0x42, 0x02, 0xD2, 0xEC, 0x72, 0xFD, 0xF7, 0xEC, 0xF8, 0x01, 0xB0, 0xF0, 0xBD, - 0x24, 0x00, 0x00, 0x40, 0x00, 0x05, 0x00, 0x20, 0xC0, 0x00, 0x00, 0x20, 0xFE, 0xFF, 0x00, 0x00, - 0x7C, 0x05, 0x00, 0x20, 0x2C, 0x20, 0x00, 0x40, 0xC5, 0x09, 0x00, 0x00, 0xDC, 0x06, 0x00, 0x20, - 0x02, 0xE0, 0x08, 0xC8, 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, 0x70, 0x47, 0x70, 0x47, - 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x01, 0x02, 0x0B, 0x02, 0xBD, 0x46, 0x69, 0x72, - 0x65, 0x66, 0x6C, 0x79, 0x20, 0x47, 0x32, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, - 0x19, 0x64, 0x18, 0x0E, 0x00, 0x00, 0x0A, 0x00, 0x91, 0x69, 0x7E, 0x32, 0x00, 0x32, 0x00, 0x00, - 0x0F, 0x0A, 0x0A, 0xFF, 0x66, 0x06, 0x00, 0x01, 0x00, 0x78, 0x04, 0x04, 0x19, 0xBE, 0x7D, 0x00, - 0x83, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x1B, 0x12, 0x13, 0x1D, 0x15, 0x16, 0x17, - 0x1A, 0x09, 0x0A, 0x0B, 0x1E, 0x0D, 0x0E, 0x0F, 0x28, 0x5E, 0x00, 0x08, 0xC0, 0x00, 0x00, 0x20, - 0xC0, 0x03, 0x00, 0x00, 0xF4, 0x11, 0x00, 0x08, 0x10, 0x61, 0x00, 0x08, 0x80, 0x04, 0x00, 0x20, - 0x80, 0x08, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x08, 0x10, 0x1A, 0x02, 0x1E, 0x01, 0x18, 0x64, 0x01, - 0x01, 0x0A, 0x01, 0x02, 0x01, 0x01, 0x05, 0x05, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0xD4, 0x03, - 0x64, 0x19, 0xB4, 0x10, 0x08, 0x4C, 0x04, 0x6C, 0x07, 0xDC, 0x05, 0xC8, 0x1A, 0xD0, 0x06, 0x12, - 0x6E, 0x12, 0x78, 0x12, 0xC8, 0x1A, 0x18, 0x02, 0x10, 0x08, 0xCF, 0x07, 0xCF, 0x07, 0xD0, 0x07, - 0x14, 0x12, 0x46, 0x1E, 0x90, 0x01, 0xD0, 0x07, 0x64, 0x12, 0x21, 0x23, 0x10, 0x27, 0x33, 0xE8, - 0x03, 0x12, 0x7C, 0x64, 0x08, 0xD4, 0x30, 0x72, 0x0A, 0x02, 0x13, 0x64, 0x29, 0x30, 0x23, 0x50, - 0xC3, 0x12, 0x20, 0x2B, 0x20, 0x4E, 0x08, 0x40, 0x09, 0x23, 0x01, 0x67, 0x45, 0xAB, 0x89, 0xEF, - 0xCD, 0x63, 0x90, 0x01, 0x39, 0x50, 0xF1, 0x23, 0x20, 0x4E, 0x4B, 0xA0, 0x86, 0x21, 0x59, 0x27, - 0x11, 0x0A, 0x19, 0x32, 0x60, 0x11, 0x13, 0x12, 0x7A, 0x12, 0x1C, 0x34, 0x01, 0x40, 0x08, 0x32, - 0xFA, 0x12, 0xB4, 0x12, 0xB7, 0x12, 0xBA, 0x12, 0xBD, 0x12, 0xC1, 0x12, 0xC4, 0x12, 0xC7, 0x12, - 0xCA, 0x12, 0xCD, 0x12, 0xD0, 0x12, 0xD3, 0x12, 0xD6, 0x12, 0xD9, 0x12, 0xDC, 0x12, 0xE0, 0x12, - 0xE3, 0x12, 0xE6, 0x12, 0xE9, 0x12, 0xEC, 0x12, 0xEF, 0x12, 0xF2, 0x12, 0xF5, 0x1A, 0xF7, 0x32, - 0x1A, 0xFD, 0x67, 0x18, 0x66, 0x03, 0x01, 0x06, 0x01, 0x09, 0x01, 0x0B, 0x01, 0x0E, 0x01, 0x11, - 0x01, 0x13, 0x01, 0x16, 0x01, 0x19, 0x01, 0x1B, 0x01, 0x1E, 0x01, 0x20, 0x01, 0x23, 0x01, 0x25, - 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2F, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x38, - 0x01, 0x3A, 0x01, 0x3C, 0x01, 0x3E, 0x01, 0x40, 0x01, 0x42, 0x01, 0x44, 0x01, 0x46, 0x01, 0x47, - 0x01, 0x49, 0x01, 0x4B, 0x01, 0x4D, 0x01, 0x4E, 0x01, 0x50, 0x01, 0x51, 0x01, 0x53, 0x01, 0x54, - 0x01, 0x56, 0x01, 0x57, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5B, 0x01, 0x5C, 0x01, 0x5D, 0x01, 0x5E, - 0x01, 0x5F, 0x01, 0x60, 0x01, 0x61, 0x01, 0x62, 0x01, 0x63, 0x02, 0x1C, 0x64, 0x01, 0x65, 0x02, - 0x1A, 0x66, 0x02, 0x3A, 0x67, 0x02, 0xFA, 0x68, 0x02, 0x49, 0x18, 0x29, 0x22, 0x29, 0x2A, 0x3A, - 0x64, 0x36, 0x1A, 0x62, 0x40, 0x1A, 0x60, 0x48, 0x1A, 0x5E, 0x50, 0x1A, 0x5C, 0x58, 0x1A, 0x5A, - 0x60, 0x1A, 0x57, 0x68, 0x1A, 0x54, 0x70, 0x1A, 0x51, 0x78, 0x1A, 0x4E, 0x80, 0x1A, 0x4B, 0x88, - 0x1A, 0x47, 0x90, 0x1A, 0x44, 0x98, 0x1A, 0x40, 0xA0, 0x1A, 0x3C, 0xA8, 0x1A, 0x38, 0xB0, 0x1A, - 0x33, 0xB8, 0x1A, 0x2F, 0xC0, 0x1A, 0x2A, 0xC8, 0x1A, 0x25, 0xD0, 0x1A, 0x20, 0xD8, 0x1A, 0x1B, - 0xE0, 0x1A, 0x16, 0xE8, 0x1A, 0x11, 0xF0, 0x1A, 0x0B, 0xF8, 0x15, 0x06, 0x01, 0x03, 0x01, 0x13, - 0x01, 0xFD, 0x12, 0xFA, 0x12, 0xF7, 0x12, 0xF5, 0x12, 0xF2, 0x12, 0xEF, 0x12, 0xEC, 0x12, 0xE9, - 0x12, 0xE6, 0x12, 0xE3, 0x12, 0xE0, 0x12, 0xDC, 0x12, 0xD9, 0x12, 0xD6, 0x12, 0xD3, 0x12, 0xD0, - 0x12, 0xCD, 0x12, 0xCA, 0x12, 0xC7, 0x12, 0xC4, 0x12, 0xC1, 0x12, 0xBD, 0x12, 0xBA, 0x12, 0xB7, - 0x12, 0xB4, 0x12, 0xB1, 0x12, 0xAE, 0x12, 0xAB, 0x12, 0xA7, 0x12, 0xA4, 0x12, 0xA1, 0x12, 0x9E, - 0x12, 0x9B, 0x12, 0x98, 0x12, 0x95, 0x12, 0x92, 0x12, 0x8F, 0x12, 0x8C, 0x12, 0x88, 0x12, 0x85, - 0x12, 0x82, 0x12, 0x7F, 0x12, 0x7C, 0x12, 0x79, 0x12, 0x76, 0x12, 0x73, 0x12, 0x71, 0x12, 0x6E, - 0x12, 0x6B, 0x12, 0x68, 0x12, 0x65, 0x12, 0x62, 0x12, 0x5F, 0x12, 0x5D, 0x12, 0x5A, 0x12, 0x57, - 0x12, 0x55, 0x12, 0x52, 0x12, 0x4F, 0x12, 0x4D, 0x12, 0x4A, 0x12, 0x48, 0x12, 0x45, 0x12, 0x43, - 0x12, 0x40, 0x12, 0x3E, 0x12, 0x3C, 0x12, 0x39, 0x12, 0x37, 0x12, 0x35, 0x12, 0x33, 0x12, 0x30, - 0x12, 0x2E, 0x12, 0x2C, 0x12, 0x2A, 0x12, 0x28, 0x12, 0x26, 0x12, 0x24, 0x12, 0x22, 0x12, 0x21, - 0x12, 0x1F, 0x12, 0x1D, 0x12, 0x1B, 0x12, 0x1A, 0x12, 0x18, 0x12, 0x17, 0x12, 0x15, 0x12, 0x14, - 0x12, 0x12, 0x12, 0x11, 0x12, 0x10, 0x12, 0x0E, 0x12, 0x0D, 0x12, 0x0C, 0x12, 0x0B, 0x12, 0x0A, - 0x12, 0x09, 0x12, 0x08, 0x12, 0x07, 0x12, 0x06, 0x1A, 0x05, 0x02, 0x12, 0x04, 0x1A, 0x03, 0x02, - 0x1A, 0x02, 0x02, 0x19, 0xD9, 0x19, 0x02, 0x01, 0x12, 0x49, 0x18, 0x29, 0x22, 0x29, 0x2A, 0x3A, - 0x04, 0x36, 0x1A, 0x06, 0x40, 0x1A, 0x08, 0x48, 0x1A, 0x0A, 0x50, 0x1A, 0x0C, 0x58, 0x1A, 0x0E, - 0x60, 0x1A, 0x11, 0x68, 0x1A, 0x14, 0x70, 0x1A, 0x17, 0x78, 0x1A, 0x1A, 0x80, 0x1A, 0x1D, 0x88, - 0x1A, 0x21, 0x90, 0x1A, 0x24, 0x98, 0x1A, 0x28, 0xA0, 0x1A, 0x2C, 0xA8, 0x1A, 0x30, 0xB0, 0x1A, - 0x35, 0xB8, 0x1A, 0x39, 0xC0, 0x1A, 0x3E, 0xC8, 0x1A, 0x43, 0xD0, 0x1A, 0x48, 0xD8, 0x1A, 0x4D, - 0xE0, 0x1A, 0x52, 0xE8, 0x1A, 0x57, 0xF0, 0x1A, 0x5D, 0xF8, 0x12, 0x62, 0x12, 0x65, 0x12, 0x68, - 0x12, 0x6B, 0x12, 0x6E, 0x12, 0x71, 0x12, 0x73, 0x12, 0x76, 0x12, 0x79, 0x12, 0x7C, 0x12, 0x7F, - 0x12, 0x82, 0x12, 0x85, 0x12, 0x88, 0x12, 0x8C, 0x12, 0x8F, 0x12, 0x92, 0x12, 0x95, 0x12, 0x98, - 0x12, 0x9B, 0x12, 0x9E, 0x12, 0xA1, 0x12, 0xA4, 0x12, 0xA7, 0x12, 0xAB, 0x12, 0xAE, 0x52, 0xB1 -}; - -/* D:\Firefly\AM32\FIRMWARE BYTES\0x08007BE0.bin (5/2/2025 1:59:24 PM) - StartOffset(h): 00000000, EndOffset(h): 0000000F, Length(h): 00000010 */ - -const unsigned char am32_firmware_tag[16] = { - 0x46, 0x4F, 0x52, 0x45, 0x46, 0x4C, 0x49, 0x5F, 0x47, 0x32, 0x30, 0x5F, 0x46, 0x30, 0x35, 0x31 -}; - -#endif // INCLUDE_AM32_FIRMWARE diff --git a/src/modules/esc_flasher/AM32Firmware.h b/src/modules/esc_flasher/AM32Firmware.h deleted file mode 100644 index b6b0c5cf7d..0000000000 --- a/src/modules/esc_flasher/AM32Firmware.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -#define INCLUDE_AM32_FIRMWARE - -#define AM32_FIRMWARE_ADDR (0x08001000) -#define AM32_FIRMWARE_TAG_ADDR (0x08007BE0) - -__EXPORT extern const uint8_t am32_fw_version_major; -__EXPORT extern const uint8_t am32_fw_version_minor; - -__EXPORT extern const uint8_t am32_firmware[20752]; -__EXPORT extern const uint8_t am32_firmware_tag[16]; diff --git a/src/modules/esc_flasher/CMakeLists.txt b/src/modules/esc_flasher/CMakeLists.txt index 78ea60508a..2e34e817c2 100644 --- a/src/modules/esc_flasher/CMakeLists.txt +++ b/src/modules/esc_flasher/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2020 PX4 Development Team. All rights reserved. +# Copyright (c) 2024 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -34,9 +34,9 @@ px4_add_module( MODULE modules__esc_flasher MAIN esc_flasher - COMPILE_FLAGS SRCS EscFlasher.cpp - EscFlasher.hpp - AM32Firmware.c - ) + am32_protocol.cpp + DEPENDS + px4_work_queue +) diff --git a/src/modules/esc_flasher/EscFlasher.cpp b/src/modules/esc_flasher/EscFlasher.cpp index 45bb5a6139..9c51a8782f 100644 --- a/src/modules/esc_flasher/EscFlasher.cpp +++ b/src/modules/esc_flasher/EscFlasher.cpp @@ -1,1323 +1,76 @@ +/**************************************************************************** + * + * Copyright (c) 2024 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file EscFlasher.cpp + * + * AM32 ESC firmware update module. + */ + #include "EscFlasher.hpp" -/* - * The ESC Flasher module will allow configuring of some ESC features, over DSHOT. - * It will enable ESC reflashing for AM32 using bit-banging UART 19200 on the signal line. -*/ +#include +#include +#include +#include +#include -#include "AM32Firmware.h" -#include +#ifdef __PX4_NUTTX +#include +#endif using namespace time_literals; -#define ESC_FLASHER_RET_OK (0) -#define ESC_FLASHER_RET_NOK (-1) - -static const uint32_t SECONDS_TO_MICROSECONDS = 1000000; - -ESC_Flasher::ESC_Flasher() : ModuleParams(nullptr), _loop_perf(perf_alloc(PC_ELAPSED, "esc_flasher")) { - // New instance created, set all initial values - subscribe_orb_messages(); - - run_calls = 0; - print_flag = false; - time_now = 0; - time_1s = 0; - time_flash_start = 0; - time_delayed_start = 0; - - start_flash = 0; - motor_flags = 0; - cancel_flash = 0; - - // Get ESC type, needs to be set via param ESC_TYPE - _esc_type = (ESCType)_param_esc_type.get(); - set_esc_type(true); - - // Get ESC update param at boot, check if it is pending - _esc_update = (ESCUpdate)_param_esc_update.get(); - if (_esc_update != ESCUpdate::Disabled) { - // Don't allow update at boot, must be set some time after startup - _param_esc_update.reset(); - _esc_update = ESCUpdate::Disabled; - PX4_ERR("Cannot update ESCs at boot, try again after startup"); - } - - esc_update_state = 0; - prev_esc_update_state = 0; - flashing_result = 0; - memset(esc_update_motors, 0, sizeof(esc_update_motors)); - memset(&esc_flashing_state, 0, sizeof(esc_flashing_state)); - - memset(&esc_versions, 0, sizeof(esc_versions)); - memset(&esc_gpios, 0, sizeof(esc_gpios)); - gpio_high = 0; - time_gpio_set = 0; - - update_params(); - - msg_id = 0; - - load_firmware_source(); - - // Set to run at 1 Hz - run_delay = run_delay_max; - wake = false; -} - -ESC_Flasher::~ESC_Flasher() { - // Instance deleted, clean up - unsubscribe_orb_messages(); - perf_free(_loop_perf); -} - -void ESC_Flasher::load_firmware_source(void) { - esc_flasher_status_s status{0}; - // Default to mem_array - _am32_fw_source = AM32_FIRMWARE_SOURCE::MEM_ARRAY; - _am32_fw_ver_major = 0; - _am32_fw_ver_minor = 0; - - // TODO option for changing source via parameter, looking for firmware from that source - - switch (_am32_fw_source) { - case AM32_FIRMWARE_SOURCE::MEM_ARRAY: -#ifdef INCLUDE_AM32_FIRMWARE - _am32_fw_ver_major = am32_fw_version_major; - _am32_fw_ver_minor = am32_fw_version_minor; - - // This is temporary so compile includes the array - if (sizeof(am32_firmware) > 1234) { - PX4_DEBUG("Sample byte from am32_firmware[1234] %d", (int)am32_firmware[1234]); - } - - // Publish status with included firmware version - status.esc_flashing_in_progress = false; - status.version_major = _am32_fw_ver_major; - status.version_minor = _am32_fw_ver_minor; - status.timestamp = hrt_absolute_time(); - _esc_flasher_status_pub.publish(status); -#endif - break; - case AM32_FIRMWARE_SOURCE::SD_CARD: - _am32_fw_ver_major = 0; - _am32_fw_ver_minor = 0; - break; - case AM32_FIRMWARE_SOURCE::STREAM_HEAP: - _am32_fw_ver_major = 0; - _am32_fw_ver_minor = 0; - break; - } -} - -void ESC_Flasher::set_esc_type(bool print) { - if (_esc_type == ESCType::Unknown) { - if (print) PX4_INFO("ESC Flasher starting, ESC Type is DEFAULT"); - } - else if (_esc_type == ESCType::AM32) { - if (print) PX4_INFO("ESC Flasher starting, ESC Type is AM32"); - } - else if (_esc_type == ESCType::BLHELI32) { - if (print) PX4_INFO("ESC Flasher starting, ESC Type is BLHeli32"); - } - else if (_esc_type == ESCType::AM32) { - if (print) PX4_INFO("ESC Flasher starting, ESC Type is AM32_Old"); - } - else if (_esc_type == ESCType::BlueJay) { - if (print) PX4_INFO("ESC Flasher starting, ESC Type is BlueJay"); - } - else { - _esc_type = ESCType::Unknown; - _param_esc_type.reset(); - PX4_ERR("Invalid ESC Type, parameter ESC_TYPE has been reset"); - } -} - -void ESC_Flasher::update_params(void) { - // check for parameter updates - if (_parameter_update_sub.updated()) { - // clear update - parameter_update_s pupdate; - _parameter_update_sub.copy(&pupdate); - - // update parameters from storage - updateParams(); - - // Get ESC type, needs to be set via param ESC_TYPE - _esc_type = (ESCType)_param_esc_type.get(); - set_esc_type(false); - - ESCUpdate esc_update = (ESCUpdate)_param_esc_update.get(); - if (esc_update != ESCUpdate::Disabled && esc_update != ESCUpdate::Recover2) { - if (_esc_type == ESCType::AM32) { - // Start the update process - _esc_update = esc_update; - // Flag all motors for update - memset(esc_update_motors, 1, sizeof(esc_update_motors)); - // Clear param ESC_UPDATE - _param_esc_update.reset(); - - // Start flashing state machine - esc_update_state = 1; - prev_esc_update_state = 0; - - // Set a 2 second delay before bit-banging - time_delayed_start = hrt_absolute_time() + 2_s; - - if (esc_update == ESCUpdate::Recover) { - PX4_INFO("Starting recovery on motors 1 2 3 4"); - } - else { - PX4_INFO("Starting flash on motors 1 2 3 4"); - } - } - else { - // ESC Type not supported - _esc_update = ESCUpdate::Disabled; - _param_esc_update.reset(); - - PX4_ERR("ESC type %d not supported by esc_flasher", (int)_esc_type); - } - } - else if (esc_update == ESCUpdate::Recover2) { - if (_esc_type == ESCType::AM32) { - if (_esc_update == ESCUpdate::Recover) { - // Proceed with the recovery, battery should be in by now - _esc_update = esc_update; - esc_update_state = 31; - PX4_INFO("Proceeding with recovery step 2"); - } - } - else { - // ESC Type not supported - _esc_update = ESCUpdate::Disabled; - _param_esc_update.reset(); - - PX4_ERR("ESC type %d not supported by esc_flasher", (int)_esc_type); - } - } - } -} - -void ESC_Flasher::play_tune(int tune) { - tune_control_s tune_control{0}; - if (tune == 1) { - tune_control.tune_id = tune_control_s::TUNE_ID_PROG_PX4IO; // PROG_PX4IO - } - else if (tune == 2) { - tune_control.tune_id = tune_control_s::TUNE_ID_PROG_PX4IO_OK; // PROG_PX4IO_OK - } - else if (tune == 3) { - tune_control.tune_id = tune_control_s::TUNE_ID_PROG_PX4IO_ERR; // PROG_PX4IO_ERR - } - tune_control.volume = tune_control_s::VOLUME_LEVEL_MAX; - tune_control.tune_override = true; - tune_control.timestamp = hrt_absolute_time(); - _tune_control_pub.publish(tune_control); -} - -void ESC_Flasher::stop_tune(void) { - tune_control_s tune_control{0}; - tune_control.tune_override = true; - tune_control.timestamp = hrt_absolute_time(); - _tune_control_pub.publish(tune_control); -} - -void ESC_Flasher::subscribe_orb_messages(void) { - _sensor_gps_sub.subscribe(); - _vehicle_status_sub.subscribe(); -} - -void ESC_Flasher::unsubscribe_orb_messages(void) { - _sensor_gps_sub.unsubscribe(); - _vehicle_status_sub.unsubscribe(); -} - -void ESC_Flasher::run() { - while (!should_exit()) { - perf_begin(_loop_perf); - - update_params(); - RunLoop(); - - perf_end(_loop_perf); - - // Sleep so our task can yield to other tasks - //px4_usleep(run_delay); - delay_loops = run_delay / run_delay_min; - while (!wake && delay_loops--) { - px4_usleep(run_delay_min); - } - wake = false; - } -} - -// ESC Flasher work loop -void ESC_Flasher::RunLoop() { - static esc_flasher_request_s request; - static esc_flasher_request_ack_s update; - static esc_flasher_status_s status; - static uint32_t gpio; - int retval = 0; - - run_calls++; - - time_now = hrt_absolute_time(); - - // Get vehicle armed state - vehicle_armed = get_current_arming_state(); - - // Check update flags from command 'esc_flasher flash' - if (start_flash) { - if (_esc_type == ESCType::AM32) { - if (_esc_update == ESCUpdate::Disabled) { - if (motor_flags) { - _esc_update = ESCUpdate::ForceUpdate; - esc_update_state = 1; - prev_esc_update_state = 0; - // Start immediately, no need to wait for param response - time_delayed_start = time_now; - // Set motor update flags - memset(esc_update_motors, 0, sizeof(esc_update_motors)); - char motors_string[16] = "1 2 3 4"; - int motors_index = 0; - for (uint32_t i = 0; i < ESC_COUNT; i++) { - esc_update_motors[i] = (motor_flags & (1 << i)); - if (esc_update_motors[i]) { - motors_string[motors_index * 2] = (0x30 + i + 1); - motors_index++; - } - } - // Terminate string - motors_string[(motors_index * 2) - 1] = 0; - - if (motors_index == 1) PX4_INFO("Starting flash on motor %s", motors_string); - else PX4_INFO("Starting flash on motors %s", motors_string); - } - else { - PX4_ERR("Invalid motor flags for flashing"); - } - } - else { - PX4_ERR("ESC flashing already in progress"); - } - } - else { - // ESC Type not supported - _esc_update = ESCUpdate::Disabled; - - PX4_ERR("ESC type %d not supported by esc_flasher", (int)_esc_type); - } - - start_flash = 0; - motor_flags = 0; - } - - if (cancel_flash) { - // Send request to DShot to cancel any pending requests - _esc_update = ESCUpdate::Cancel; - esc_update_state = 96; - prev_esc_update_state = 0; - - cancel_flash = 0; - } - - // Check update parameter - if (_esc_update != ESCUpdate::Disabled) { - if (_esc_update != ESCUpdate::Cancel && vehicle_armed != vehicle_status_s::ARMING_STATE_DISARMED) { - PX4_ERR("Cannot update ESCs while vehicle is armed!"); - _esc_update = ESCUpdate::Disabled; - } - else { - switch (esc_update_state) { - case 0: - // Idle state - - break; - case 1: - // Set loop to run fast 200 Hz - run_delay = run_delay_min; - - if (time_now >= time_delayed_start) { - PX4_INFO("ESC Flasher starting update"); - esc_update_state = 2; - time_flash_start = time_now; - flashing_result = 0; - - // Play start flashing tune - esc_flasher_tune = 1; - - // RECOVERY CASE - if (_esc_update == ESCUpdate::Recover) { - // Skip to state 4 disable DShot - esc_update_state = 4; - } - } - - break; - case 2: - // Send ESC_INFO request to DShot - // First, read any pending ack - _esc_flasher_request_ack_sub.copy(&update); - - // Send ESC_INFO request to DShot driver - memset(&request, 0, sizeof(request)); - request.timestamp = hrt_absolute_time(); - request.msg_id = ++msg_id; - request.request = esc_flasher_request_s::REQUEST_ESC_INFO; - for (uint32_t i = 0; i < ESC_COUNT; i++) { - if (esc_update_motors[i]) request.motor_flags |= (1 << i); - } - _esc_flasher_request_pub.publish(request); - - esc_info_timeout = 0; - esc_update_state = 3; - - break; - case 3: - // Wait for response from DShot driver to REQUEST_ESC_INFO request - if (_esc_flasher_request_ack_sub.updated()) { - esc_flasher_request_ack_s response; - _esc_flasher_request_ack_sub.copy(&response); - - // Save/print response - if (response.msg_id == msg_id && response.request == esc_flasher_request_ack_s::REQUEST_ESC_INFO && - response.result == esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_ACCEPTED) { - for (uint32_t i = 0; i < ESC_COUNT; i++) { - if (response.fw_flags & (1 << i)) { - esc_versions[i].major = response.fw_major[i]; - esc_versions[i].minor = response.fw_minor[i]; - PX4_INFO("ESC_INFO request filled, ESC %d firmware version: %d.%d", - (int)i, (int)esc_versions[i].major, (int)esc_versions[i].minor); - } - } - } - else if (response.msg_id == msg_id && response.request == esc_flasher_request_ack_s::REQUEST_ESC_INFO && - response.result == esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_UNSUPPORTED) { - // Unsupported ESC detected - PX4_ERR("Unsupported ESC detected, cancelling ESC flasher"); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 9; - } - else { - // Mismatch somewhere - PX4_ERR("Response from DShot mismatch! Response: %d, msg_id: %d, response msg_id: %d", - (int)response.result, (int)msg_id, (int)response.msg_id); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 10; - - break; - } - - // If we are in mode ESCUpdate::Update, only flash motors that aren't on the correct fw version - // Mode ESCUpdate:ForceUpdate should flash the new firmware always - if (_esc_update == ESCUpdate::Update) { - // If firmware versions match, clear the update flag for that motor - for (uint32_t i = 0; i < ESC_COUNT; i++) { - if (esc_update_motors[i] && (esc_versions[i].major == _am32_fw_ver_major) && - (esc_versions[i].minor == _am32_fw_ver_minor)) { - esc_update_motors[i] = 0; - } - } - } - - // Check that we still have ESCs to flash, if not then cancel state machine - bool flash_required = false; - for (uint32_t i = 0; i < ESC_COUNT; i++) { - if (esc_update_motors[i]) { - flash_required = true; - break; - } - } - if (!flash_required) { - // No ESCs need flashing, go to completion state - prev_esc_update_state = esc_update_state; - esc_update_state = 20; - flashing_result = 1; - - break; - } - - esc_update_state = 4; - } - else { - esc_info_timeout++; - if (esc_info_timeout > (1_s / run_delay) * 5) { - // Wait 5 seconds for response, then timeout - PX4_ERR("Timeout waiting for response from DShot driver"); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 11; - - break; - } - } - - break; - case 4: - // Received fw versions, next ask DShot for flashing - // DShot should disable its outputs until we send another request - // First, read any pending ack - _esc_flasher_request_ack_sub.copy(&update); - - // Send REQUEST_FLASHING request to DShot driver - memset(&request, 0, sizeof(request)); - request.timestamp = hrt_absolute_time(); - request.msg_id = ++msg_id; - request.request = esc_flasher_request_s::REQUEST_FLASHING; - for (uint32_t i = 0; i < ESC_COUNT; i++) { - if (esc_update_motors[i]) request.motor_flags |= (1 << i); - } - _esc_flasher_request_pub.publish(request); - - esc_info_timeout = 0; - esc_update_state = 5; - - break; - case 5: - // Wait for response from DShot driver to REQUEST_FLASHING request - if (_esc_flasher_request_ack_sub.updated()) { - esc_flasher_request_ack_s response; - _esc_flasher_request_ack_sub.copy(&response); - - // Save/print response - if (response.msg_id == msg_id && response.request == esc_flasher_request_ack_s::REQUEST_FLASHING && - response.result == esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_ACCEPTED) { - for (uint32_t i = 0; i < ESC_COUNT; i++) { - if (response.gpio_flags & (1 << i)) { - esc_gpios[i] = response.gpio_pins[i]; - PX4_INFO("ESC_INFO request filled, ESC %d GPIO %d", - (int)i, (int)esc_gpios[i]); - } - } - PX4_INFO("REQUEST_FLASHING response received"); - } - else { - // Mismatch somewhere - PX4_ERR("Response from DShot mismatch! Response: %d, msg_id: %d, response msg_id: %d", - (int)response.result, (int)msg_id, (int)response.msg_id); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 12; - - break; - } - - esc_update_state = 6; - } - else { - esc_info_timeout++; - if (esc_info_timeout > (1_s / run_delay) * 5) { - // Wait 5 seconds for response, then timeout - PX4_ERR("Timeout waiting for response from DShot driver"); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 13; - - break; - } - } - - break; - case 6: - // Start bit-banging! - // First set GPIOs as outputs and set high '1' - for (uint32_t i = 0; i < ESC_COUNT; i++) { - gpio = esc_gpios[i]; - if (gpio) { - gpio &= ~(GPIO_MODE_MASK); - gpio |= GPIO_OUTPUT; - gpio |= GPIO_OUTPUT_SET; - px4_arch_configgpio(gpio); - - // If we have set any GPIO to high, set a flag that can be checked during Cancel request - gpio_high = 1; - } - } - - PX4_INFO("Set motor GPIOs to output '1'"); - - time_gpio_set = time_now; - esc_update_state = 7; - - if (_esc_update == ESCUpdate::Recover) { - // Special state for holding until Recover2 is sent - esc_update_state = 30; - PX4_INFO("Insert battery, then set param ESC_UPDATE to 4 to continue"); - } - - break; - case 30: - // Wait for ESC_UPDATE 4 - - break; - case 31: - // Recovery2 has been sent, battery should be in - esc_update_motor_index = 0; - time_gpio_set = time_now; - esc_update_state = 7; - - break; - case 7: - // Wait > 0.5 seconds for ESCs to reboot to bootloader - if (time_now - time_gpio_set > 600_ms) { - esc_update_motor_index = 0; - esc_update_state = 8; - } - - break; - case 8: - // Send AM32 bootloader init packet - if (esc_update_motor_index >= ESC_COUNT) { - // How did we get here? - // We are done, for now - esc_update_state = 20; - flashing_result = 2; - - break; - } - if (esc_update_motors[esc_update_motor_index]) { - //int bitbang_send_packet(uint32_t gpio, uint8_t* packet, uint16_t length, uint8_t* response, uint8_t response_length); - retval = bitbang_send_packet(esc_gpios[esc_update_motor_index], (uint8_t*)am32_boot_init, sizeof(am32_boot_init), - response_data, am32_boot_init_resp_len); - if (retval == am32_boot_init_resp_len) { - // Yay! - PX4_INFO("Response received from AM32 Bootloader %d: %d %d %d %d %d %d %d %d %d", (int)(esc_update_motor_index + 1), - (int)response_data[0], (int)response_data[1], (int)response_data[2], (int)response_data[3], (int)response_data[4], - (int)response_data[5], (int)response_data[6], (int)response_data[7], (int)response_data[8]); - - // Move on to next motor - esc_update_motor_index++; - if (esc_update_motor_index >= ESC_COUNT) { - // Begin the flashing process - memset(&esc_flashing_state, 0, sizeof(esc_flashing_state)); - esc_update_state = 10; - } - } - else { - // Oh no - PX4_ERR("Wrong or no response received from AM32 Bootloader"); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 14; - } - } - else { - esc_update_motor_index++; - } - - break; - case 9: - // Unused state - - break; - case 10: - // Flashing order is: - // Setup next write locally - Get address, size of next chunk - // Set address of the next write chunk (CMD 0xFF), check ACK - // Set buffer size of the next chunk (CMD 0xFE) - // Send buffer (no CMD, just data), check ACK - // Send write command (CMD 0x01), check ACK - // Go back to Set address and loop until write all firmware bytes - // Send run app command (CMD 0x00 0x00 0x00 0x00) - // Success! - - // Use esc_flashing_state struct to flash 1 ESC at a time - // Get first motor - for (uint32_t i = esc_flashing_state.current_motor; i < ESC_COUNT; i++) { - if (esc_update_motors[i]) { - esc_flashing_state.current_motor = i; - esc_flashing_state.current_gpio = esc_gpios[i]; - break; - } - } - - if (esc_flashing_state.current_motor >= ESC_COUNT) { - // All motors complete, move on to finishing state - PX4_INFO("All motors flashed successfully!"); - esc_update_state = 16; - break; - } - - esc_update_state = 11; - - break; - case 11: - // Setup firmware flash next part (am32_firmware or am32_firmware_tag) - if (esc_flashing_state.fw_part == 0) { - esc_flashing_state.fw_length = sizeof(am32_firmware); - esc_flashing_state.fw_bytes_sent = 0; - esc_flashing_state.fw_bytes_left = esc_flashing_state.fw_length; - esc_flashing_state.base_address = AM32_FIRMWARE_ADDR & (0xFFFF); - } - else if (esc_flashing_state.fw_part == 1) { - esc_flashing_state.fw_length = sizeof(am32_firmware_tag); - esc_flashing_state.fw_bytes_sent = 0; - esc_flashing_state.fw_bytes_left = esc_flashing_state.fw_length; - esc_flashing_state.base_address = AM32_FIRMWARE_TAG_ADDR & (0xFFFF); - } - - esc_update_state = 12; - - break; - case 12: - // Copy next chunk to tx_buffer - esc_flashing_state.tx_length = esc_flashing_state.fw_bytes_left; - if (esc_flashing_state.tx_length > max_chunk_length) { - esc_flashing_state.tx_length = max_chunk_length; - } - - // Copy data and set next address - if (esc_flashing_state.fw_part == 0) { - memcpy(esc_flashing_state.tx_buffer, &am32_firmware[esc_flashing_state.fw_bytes_sent], esc_flashing_state.tx_length); - esc_flashing_state.next_address = esc_flashing_state.base_address + esc_flashing_state.fw_bytes_sent; - } - else { - memcpy(esc_flashing_state.tx_buffer, &am32_firmware_tag[esc_flashing_state.fw_bytes_sent], esc_flashing_state.tx_length); - esc_flashing_state.next_address = esc_flashing_state.base_address + esc_flashing_state.fw_bytes_sent; - } - - // Make CRC, append it on to the tx packet - esc_flashing_state.crc = make_crc(esc_flashing_state.tx_buffer, esc_flashing_state.tx_length); - esc_flashing_state.tx_buffer[esc_flashing_state.tx_length] = (uint8_t)(esc_flashing_state.crc & 0x00FF); - esc_flashing_state.tx_buffer[esc_flashing_state.tx_length + 1] = (uint8_t)(((esc_flashing_state.crc & 0xFF00) >> 8)); - - // Send set address command - am32_set_addr[0] = 0xFF; - am32_set_addr[1] = 0x00; - am32_set_addr[2] = (esc_flashing_state.next_address & 0xFF00) >> 8; - am32_set_addr[3] = (esc_flashing_state.next_address & 0x00FF); - esc_flashing_state.crc = make_crc(am32_set_addr, 4); - am32_set_addr[4] = (uint8_t)(esc_flashing_state.crc & 0x00FF); - am32_set_addr[5] = (uint8_t)((esc_flashing_state.crc & 0xFF00) >> 8); - - retval = bitbang_send_packet(esc_flashing_state.current_gpio, am32_set_addr, sizeof(am32_set_addr), - response_data, am32_set_addr_resp_len); - if (retval == am32_set_addr_resp_len && response_data[0] == 0x30) { - // Received ACK, continue to next step - esc_update_state = 13; - } - else { - // Something went wrong - PX4_ERR("Error in Set Address command for motor %d", (int)esc_flashing_state.current_motor + 1); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 15; - - break; - } - - break; - case 13: - // Sent set buffer size command - am32_set_buff_size[0] = 0xFE; - am32_set_buff_size[1] = 0x00; - if (esc_flashing_state.tx_length == max_chunk_length) { - am32_set_buff_size[2] = 1; - am32_set_buff_size[3] = 0; - } - else { - am32_set_buff_size[2] = 0; - am32_set_buff_size[3] = esc_flashing_state.tx_length; - } - esc_flashing_state.crc = make_crc(am32_set_buff_size, 4); - am32_set_buff_size[4] = (uint8_t)(esc_flashing_state.crc & 0x00FF); - am32_set_buff_size[5] = (uint8_t)((esc_flashing_state.crc & 0xFF00) >> 8); - - retval = bitbang_send_packet(esc_flashing_state.current_gpio, am32_set_buff_size, sizeof(am32_set_buff_size), - response_data, am32_set_buff_size_resp_len); - - // There is no expected response to this command, so proceed to send the buffer - retval = bitbang_send_packet(esc_flashing_state.current_gpio, esc_flashing_state.tx_buffer, esc_flashing_state.tx_length + 2, - response_data, am32_send_buff_resp_len); - if (retval == am32_send_buff_resp_len && response_data[0] == 0x30) { - // Received ACK, continue to next step - esc_update_state = 14; - } - else { - // Something went wrong - PX4_ERR("Error in Send Buffer command for motor %d", (int)esc_flashing_state.current_motor + 1); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 16; - - break; - } - - break; - case 14: - // Send Write command - am32_write[0] = 0x01; - am32_write[1] = 0; - esc_flashing_state.crc = make_crc(am32_write, 2); - am32_write[2] = (uint8_t)(esc_flashing_state.crc & 0x00FF); - am32_write[3] = (uint8_t)((esc_flashing_state.crc & 0xFF00) >> 8); - - retval = bitbang_send_packet(esc_flashing_state.current_gpio, am32_write, sizeof(am32_write), - response_data, am32_write_resp_len); - if (retval == am32_write_resp_len && response_data[0] == 0x30) { - // Received ACK, continue to next step - if (esc_flashing_state.fw_part == 0) { - PX4_INFO("Sent firmware packet %d of %d to motor %d", (int)(esc_flashing_state.fw_bytes_sent / max_chunk_length) + 1, - (int)(esc_flashing_state.fw_length / max_chunk_length) + 1, (int)esc_flashing_state.current_motor + 1); - } - else { - PX4_INFO("Sent firmware tag section to motor %d", (int)esc_flashing_state.current_motor + 1); - } - // Set byte counts after latest write - esc_flashing_state.fw_bytes_sent += esc_flashing_state.tx_length; - esc_flashing_state.fw_bytes_left -= esc_flashing_state.tx_length; - - if (esc_flashing_state.fw_bytes_left) { - // Go back to setup next chunk - esc_update_state = 12; - } - else { - // Done with firmware part, check states - if (esc_flashing_state.fw_part == 0) { - esc_flashing_state.fw_part = 1; - esc_update_state = 11; - } - else { - // Flashed all parts, run app and move to next motor - PX4_INFO("Firmware update complete on motor %d", (int)esc_flashing_state.current_motor + 1); - esc_update_state = 15; - } - } - } - else { - // Something went wrong - PX4_ERR("Error in Write command for motor %d", (int)esc_flashing_state.current_motor); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 17; - - break; - } - - break; - case 15: - // Send Run App command and move on to next motor - retval = bitbang_send_packet(esc_flashing_state.current_gpio, (uint8_t*)am32_run_app, sizeof(am32_run_app), - response_data, am32_run_app_resp_len); - - esc_flashing_state.current_motor++; - esc_flashing_state.fw_part = 0; - esc_update_state = 10; - - break; - case 16: - // All motors should be flashed by this state, notify DShot so it can re-enable - memset(&request, 0, sizeof(request)); - request.timestamp = hrt_absolute_time(); - request.msg_id = ++msg_id; - request.request = esc_flasher_request_s::REQUEST_FLASHING_COMPLETE; - _esc_flasher_request_pub.publish(request); - - esc_info_timeout = 0; - esc_update_state = 17; - - break; - case 17: - // Wait for response from DShot driver to REQUEST_FLASHING_COMPLETE request - if (_esc_flasher_request_ack_sub.updated()) { - esc_flasher_request_ack_s response; - _esc_flasher_request_ack_sub.copy(&response); - - // Save/print response - if (response.msg_id == msg_id && response.request == esc_flasher_request_ack_s::REQUEST_FLASHING_COMPLETE && - response.result == esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_ACCEPTED) { - PX4_INFO("REQUEST_FLASHING_COMPLETE response received"); - } - else { - // Mismatch somewhere - PX4_ERR("Response from DShot mismatch! Response: %d, msg_id: %d, response msg_id: %d", - (int)response.result, (int)msg_id, (int)response.msg_id); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 18; - - break; - } - - esc_update_state = 20; - flashing_result = 2; - } - else { - esc_info_timeout++; - if (esc_info_timeout > (1_s / run_delay) * 5) { - // Wait 5 seconds for response, then timeout - PX4_ERR("Timeout waiting for response from DShot driver"); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 19; - - break; - } - } - - break; - case 20: - // Complete - if (flashing_result == 1) { - //PX4_ERR("No ESCs need flashing, returning to idle state"); - mavlink_log_critical(&_mavlink_log_pub, "ESCs are already up-to-date, %d.%d\t", - (int)am32_fw_version_major, (int)am32_fw_version_minor); - } - else { - //PX4_INFO("ESC Flasher finished in %d seconds! Returning to idle state", (int)((time_now - time_flash_start) / 1_s)); - mavlink_log_critical(&_mavlink_log_pub, "ESC flash finished in %d seconds, %d.%d\t", - (int)((time_now - time_flash_start) / 1_s), (int)am32_fw_version_major, (int)am32_fw_version_minor); - } - - _esc_update = ESCUpdate::Disabled; - esc_update_state = 0; - time_flash_start = 0; - // Clear motor update flags - memset(esc_update_motors, 0, sizeof(esc_update_motors)); - - // Play success tune - esc_flasher_tune = 2; - - // Set loop to run slow 1 Hz - run_delay = run_delay_max; - - break; - case 96: - // CANCEL command, reset any GPIOs, wait > 20 ms, send Cancel request to DShot - if (gpio_high) { - for (uint32_t i = 0; i < ESC_COUNT; i++) { - gpio = esc_gpios[i]; - if (gpio) { - gpio &= ~(GPIO_MODE_MASK); - gpio |= GPIO_OUTPUT; - //gpio |= GPIO_OUTPUT_SET; - px4_arch_configgpio(gpio); - } - } - gpio_high = 0; - time_gpio_set = time_now; - } - else { - time_gpio_set = 0; - } - esc_update_state = 97; - - // Set loop to run fast 200 Hz - run_delay = run_delay_min; - - break; - case 97: - // Send REQUEST_CANCEL request to DShot driver - // All state variables will be reset in error state - if (time_now - time_gpio_set > 30_ms) { - memset(&request, 0, sizeof(request)); - request.timestamp = hrt_absolute_time(); - request.msg_id = ++msg_id; - request.request = esc_flasher_request_s::REQUEST_CANCEL; - _esc_flasher_request_pub.publish(request); - - esc_info_timeout = 0; - esc_update_state = 98; - } - - break; - case 98: - // Wait for response from DShot driver to REQUEST_CANCEL request - if (_esc_flasher_request_ack_sub.updated()) { - esc_flasher_request_ack_s response; - _esc_flasher_request_ack_sub.copy(&response); - - // Save/print response - if (response.msg_id == msg_id && response.request == esc_flasher_request_ack_s::REQUEST_CANCEL && - response.result == esc_flasher_request_ack_s::ESC_FLASHER_CMD_RESULT_ACCEPTED) { - PX4_INFO("REQUEST_CANCEL response received"); - } - else { - // Mismatch somewhere - PX4_ERR("Response from DShot mismatch! Response: %d, msg_id: %d, response msg_id: %d", - (int)response.result, (int)msg_id, (int)response.msg_id); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 20; - - break; - } - - esc_update_state = 99; - } - else { - esc_info_timeout++; - if (esc_info_timeout > (1_s / run_delay) * 5) { - // Wait 5 seconds for response, then timeout - PX4_ERR("Timeout waiting for response from DShot driver"); - prev_esc_update_state = esc_update_state; - esc_update_state = 99; - flashing_result = 21; - - break; - } - } - - break; - case 99: - // Error/reset case - //PX4_ERR("ESC Flasher error state, returning to idle state"); - mavlink_log_critical(&_mavlink_log_pub, "ESC Flash failed, state %d, result %d\t", - (int)prev_esc_update_state, (int)flashing_result); - - // Play error tune - esc_flasher_tune = 3; - - // SHOULD WE SEND CANCEL COMMAND TO DSHOT HERE??? - - _esc_update = ESCUpdate::Disabled; - esc_update_state = 0; - prev_esc_update_state = 0; - flashing_result = 0; - time_flash_start = 0; - // Clear motor update flags - memset(esc_update_motors, 0, sizeof(esc_update_motors)); - - // Set loop to run slow 1 Hz - run_delay = run_delay_max; - - break; - } - } - } - - // Always publish esc_flasher_status while flashing - { - flashing_in_progress = (_esc_update == ESCUpdate::Disabled) ? false : true; - if (!flashing_in_progress) { - if (flashing_in_progress_last) { - // Send ESC Flasher status uORB message, try to send it only once - memset(&status, 0, sizeof(status)); - status.esc_flashing_in_progress = false; - status.version_major = _am32_fw_ver_major; - status.version_minor = _am32_fw_ver_minor; - status.timestamp = hrt_absolute_time(); - _esc_flasher_status_pub.publish(status); - // Send vehicle command for LEDs, so Commander will update LEDs with correct blink color - send_vehicle_command_leds(); - // Reset parameter ESC_UPDATE again, this might update Commander also - _param_esc_update.reset(); - } - flashing_in_progress_last = flashing_in_progress; - } - else { - // Send in progress message and LED color at 4 Hz during flashing - if (flashing_in_progress_last != flashing_in_progress) { - // Force to send message right now - time_250ms = 0; - last_color = 0; - } - if (time_now - time_250ms >= 250_ms) { - time_250ms = time_now; - memset(&status, 0, sizeof(status)); - - if (last_color != led_control_s::COLOR_GREEN) { - last_color = led_control_s::COLOR_GREEN; - } - else { - last_color = led_control_s::COLOR_BLUE; - } - - status.esc_flashing_in_progress = true; - status.timestamp = hrt_absolute_time(); - _esc_flasher_status_pub.publish(status); - - set_physical_led_outputs(last_color); - } - flashing_in_progress_last = flashing_in_progress; - } - } - - if (time_now - time_1s > 1_s) { - // 1 second loop - time_1s = time_now; - - if (print_flag) { - PX4_INFO("ESC Flasher status:"); - PX4_INFO(" Time: %llu, run calls: %lu", time_now, run_calls); - PX4_INFO(" Loop rate: %d", (int)(1_s / run_delay)); - PX4_INFO(" ESC Type: %s", esc_types_strings[(uint8_t)_esc_type]); - PX4_INFO(" ESC Update: %d", (int)_esc_update); - } - - if (esc_flasher_stop_tune) { - stop_tune(); - esc_flasher_stop_tune = 0; - } - if (esc_flasher_tune) { - if (esc_flasher_tune == 1) { - // This will STOP tune from playing during flashing - //esc_flasher_tune = 0; - } - play_tune(esc_flasher_tune); - //if (esc_flasher_tune == 2 || esc_flasher_tune == 3) { - if (esc_flasher_tune) { - // Stop any tune in the next second loop - esc_flasher_stop_tune = 1; - } - esc_flasher_tune = 0; - } - } -} - -int ESC_Flasher::bitbang_send_packet(uint32_t gpio, uint8_t* packet, uint16_t length, uint8_t* response, uint8_t response_length) { - static const uint32_t bit_time = 52; - static const uint32_t half_bit_time = bit_time / 2; - static uint8_t read_data[16]; - - uint16_t write_index = 0; - uint8_t read_index = 0; - uint8_t bit_count = 0; - uint8_t read_bits = 0; - - // Configure GPIO pin as output, set high '1' - gpio &= ~(GPIO_MODE_MASK); - gpio |= GPIO_OUTPUT; - gpio |= GPIO_OUTPUT_SET; - gpio &= ~(GPIO_PUPD_MASK); - gpio |= GPIO_PULLUP; - px4_arch_configgpio(gpio); - - // Enter critical section, disable interrupts - static irqstate_t irq_state; - irq_state = px4_enter_critical_section(); - - uint8_t nextb; - uint64_t start; - uint64_t now; - - for (uint16_t k = 0; k < length; k++) { - nextb = packet[write_index++]; - - px4_arch_gpiowrite(gpio, 0); - start = hrt_absolute_time(); - while (true) { - now = hrt_absolute_time(); - if (now - start >= bit_time) { - break; - } - } - - for (uint32_t i = 0; i < 8; i++) { - if (nextb & 1) px4_arch_gpiowrite(gpio, 1); - else px4_arch_gpiowrite(gpio, 0); - nextb >>= 1; - - start = hrt_absolute_time(); - while (true) { - now = hrt_absolute_time(); - if (now - start >= bit_time) { - break; - } - } - } - - px4_arch_gpiowrite(gpio, 1); - start = hrt_absolute_time(); - while (true) { - now = hrt_absolute_time(); - if (now - start >= bit_time) { - break; - } - } - } - - // Set GPIO to input - gpio &= ~(GPIO_MODE_MASK); - gpio |= GPIO_INPUT; - gpio &= ~(GPIO_PUPD_MASK); - gpio |= GPIO_PULLUP; - px4_arch_configgpio(gpio); - - // Read response bytes, if any - read_index = 0; - if (response_length && response) { - memset(read_data, 0, sizeof(read_data)); - - for (uint32_t i = 0; i < response_length; i++) { - start = hrt_absolute_time(); - while (px4_arch_gpioread(gpio)) { - // Wait for pin to go low - now = hrt_absolute_time(); - if (now - start > 100000) { - // Timeout 100ms waiting for start bit - - // Leave critical section, re-enable interrupts - px4_leave_critical_section(irq_state); - return -2; - } - } - - // Wait 1/2 bit-time for start bit - start = hrt_absolute_time(); - while (true) { - now = hrt_absolute_time(); - if (now - start >= half_bit_time) { - break; - } - } - - // Read 8 bits - bit_count = 0; - read_bits = 0; - while (bit_count < 8) { - // Wait 1 bit-time to read next bit - start = hrt_absolute_time(); - while (true) { - now = hrt_absolute_time(); - if (now - start >= bit_time) { - break; - } - } - read_bits |= (px4_arch_gpioread(gpio) << (bit_count++)); - } - - read_data[read_index++] = read_bits; - - // Now wait 1 more bit-time until we get to stop bit - start = hrt_absolute_time(); - while (true) { - now = hrt_absolute_time(); - if (now - start >= bit_time) { - break; - } - } - } - - // Copy data to out buffer - memcpy(response, read_data, read_index); - } - - // Put pin back into output mode, set high - gpio &= ~(GPIO_MODE_MASK); - gpio |= GPIO_OUTPUT; - gpio |= GPIO_OUTPUT_SET; - px4_arch_configgpio(gpio); - - // Delay here to give some time before next command - start = hrt_absolute_time(); - while (true) { - now = hrt_absolute_time(); - // Delay 1 bit time, for 8 bits - if (now - start >= bit_time * 8) { - break; - } - } - - // Leave critical section, re-enable interrupts - px4_leave_critical_section(irq_state); - - return read_index; -} - -// CRC struct -typedef union { - uint8_t bytes[2]; - uint16_t word; -} uint8_16_u; - -uint16_t ESC_Flasher::make_crc(uint8_t* buffer, uint16_t length) { - static uint8_16_u CRC_16; - CRC_16.word=0; - - for(int i = 0; i < length; i++) { - uint8_t xb = buffer[i]; - for (uint8_t j = 0; j < 8; j++) { - if (((xb & 0x01) ^ (CRC_16.word & 0x0001)) !=0 ) { - CRC_16.word = CRC_16.word >> 1; - CRC_16.word = CRC_16.word ^ 0xA001; - } - else { - CRC_16.word = CRC_16.word >> 1; - } - xb = xb >> 1; - } - } - - return CRC_16.word; -} - -uint8_t ESC_Flasher::get_current_arming_state(void) { - if (_vehicle_status_sub.updated()) { - _vehicle_status_sub.copy(&_vehicle_status); - } - - return _vehicle_status.arming_state; -} - -void ESC_Flasher::set_physical_led_outputs(uint8_t color) +EscFlasher::EscFlasher() : + ModuleParams(nullptr) { -#ifdef FIREFLY_ESC - // Control I2C LEDs - led_control_s led_control_msg{0}; - led_control_msg.mode = led_control_s::MODE_MANUAL_RGB; - led_control_msg.color = led_control_s::COLOR_MANUAL; - led_control_msg.priority = led_control_s::MAX_PRIORITY; - if (color == 2) { - led_control_msg.rgb_manual[0] = 0; - led_control_msg.rgb_manual[1] = 255; - led_control_msg.rgb_manual[2] = 0; - } - else if (color == 3) { - led_control_msg.rgb_manual[0] = 0; - led_control_msg.rgb_manual[1] = 0; - led_control_msg.rgb_manual[2] = 255; - } - led_control_msg.timestamp = hrt_absolute_time(); - _led_control_pub.publish(led_control_msg); -#else - // Control I2C LEDs - led_control_s led_control_msg{0}; - led_control_msg.mode = led_control_s::MODE_BREATHE; - led_control_msg.color = led_control_s::COLOR_CYAN; - led_control_msg.priority = led_control_s::MAX_PRIORITY; - led_control_msg.timestamp = hrt_absolute_time(); - _led_control_pub.publish(led_control_msg); -#endif } -void ESC_Flasher::send_vehicle_command_leds(void) { - vehicle_command_s vcmd{0}; - - // Turn LEDs off when done flashing (-1.f) - vcmd.param1 = -1.f; - vcmd.param2 = -1.f; - vcmd.param3 = -1.f; - vcmd.target_system = 1; - vcmd.target_component = 1; - vcmd.source_system = 1; - vcmd.source_component = 1; - vcmd.confirmation = 0; - vcmd.from_external = false; - vcmd.timestamp = hrt_absolute_time(); - _vehicle_command_pub.publish(vcmd); +EscFlasher::~EscFlasher() +{ + cleanup(); } -ESC_Flasher *ESC_Flasher::instantiate(int argc, char *argv[]) { - ESC_Flasher *instance = new ESC_Flasher(); +// ───────────────────────────── Module lifecycle ───────────────────────────── - if (instance == nullptr) { - PX4_ERR("alloc failed"); - } - - return instance; -} - -int ESC_Flasher::task_spawn(int argc, char *argv[]) { +int EscFlasher::task_spawn(int argc, char *argv[]) +{ _task_id = px4_task_spawn_cmd("esc_flasher", - SCHED_DEFAULT, - SCHED_PRIORITY_FAST_DRIVER, - PX4_STACK_ADJUSTED(2048), - (px4_main_t)&run_trampoline, - (char *const *)argv); + SCHED_DEFAULT, + SCHED_PRIORITY_DEFAULT, + PX4_STACK_ADJUSTED(4096), + (px4_main_t)&run_trampoline, + (char *const *)argv); if (_task_id < 0) { _task_id = -1; @@ -1327,130 +80,516 @@ int ESC_Flasher::task_spawn(int argc, char *argv[]) { return 0; } -int ESC_Flasher::print_status() +EscFlasher *EscFlasher::instantiate(int argc, char *argv[]) { - PX4_INFO("ESC Flasher status:"); - PX4_INFO(" Time: %llu, run calls: %lu", time_now, run_calls); - PX4_INFO(" Loop rate: %d", (int)(1_s / run_delay)); - PX4_INFO(" ESC Type: %s", esc_types_strings[(uint8_t)_esc_type]); - PX4_INFO(" ESC Update: %d", (int)_esc_update); + EscFlasher *instance = new EscFlasher(); - perf_print_counter(_loop_perf); + if (instance == nullptr) { + PX4_ERR("alloc failed"); + } + + return instance; +} + +void EscFlasher::run() +{ + while (!should_exit()) { + + switch (_state) { + case State::Idle: + handle_vehicle_command(); + break; + + case State::StoppingDShot: + state_stopping_dshot(); + break; + + case State::InitGPIOs: + state_init_gpios(); + break; + + case State::ConnectESC: + state_connect_esc(); + break; + + case State::ReadInfo: + state_read_info(); + break; + + case State::LoadFirmware: + state_load_firmware(); + break; + + case State::Flashing: + state_flash_chunk(); + break; + + case State::RunApp: + state_run_app(); + break; + + case State::Complete: + state_complete(); + break; + + case State::Failed: + state_failed(); + break; + } + + // Idle → slow poll; active → no extra sleep (protocol + // bit-bang already takes wall-clock time per chunk) + if (_state == State::Idle) { + px4_usleep(IDLE_INTERVAL_US); + } + } +} + +// ───────────────────────────── Vehicle command ────────────────────────────── + +void EscFlasher::handle_vehicle_command() +{ + vehicle_command_s cmd; + + while (_vehicle_cmd_sub.update(&cmd)) { + if (cmd.command != vehicle_command_s::VEHICLE_CMD_ESC_FIRMWARE_UPDATE) { + continue; + } + + // Must be disarmed + vehicle_status_s vs{}; + _vehicle_status_sub.copy(&vs); + + if (vs.arming_state != vehicle_status_s::ARMING_STATE_DISARMED) { + PX4_ERR("cannot flash ESCs while armed"); + ack_vehicle_command(cmd, vehicle_command_ack_s::VEHICLE_CMD_RESULT_DENIED); + _status.error = esc_firmware_update_status_s::ERROR_NOT_DISARMED; + publish_status(); + continue; + } + + ack_vehicle_command(cmd, vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED); + + uint8_t esc_index = (uint8_t)cmd.param1; // 255 = all + begin_update(esc_index); + return; // exit the poll loop — we are now active + } +} + +void EscFlasher::ack_vehicle_command(const vehicle_command_s &cmd, uint8_t result) +{ + vehicle_command_ack_s ack{}; + ack.command = cmd.command; + ack.result = result; + ack.target_system = cmd.source_system; + ack.target_component = cmd.source_component; + ack.timestamp = hrt_absolute_time(); + _cmd_ack_pub.publish(ack); +} + +void EscFlasher::begin_update(uint8_t esc_index) +{ + _target_esc = esc_index; + _current_esc = 0; + _esc_count = 0; + _flash_started = hrt_absolute_time(); + + memset(&_status, 0, sizeof(_status)); + _status.status = esc_firmware_update_status_s::STATUS_CONNECTING; + publish_status(); + + mavlink_log_info(&_mavlink_log_pub, "ESC firmware update starting"); + + transition(State::StoppingDShot); +} + +// ───────────────────────────── State: StoppingDShot ───────────────────────── + +void EscFlasher::state_stopping_dshot() +{ + PX4_INFO("stopping dshot driver"); + + int ret = system("dshot stop"); + + if (ret != 0) { + PX4_WARN("dshot stop returned %d (may already be stopped)", ret); + } + + // Give DShot destructor time to release DMA/timers + px4_usleep(200000); // 200 ms + + transition(State::InitGPIOs); +} + +// ───────────────────────────── State: InitGPIOs ───────────────────────────── + +void EscFlasher::state_init_gpios() +{ +#ifdef __PX4_NUTTX + + // Discover motor GPIO pins from the io_timer subsystem + _esc_count = 0; + + for (unsigned ch = 0; ch < MAX_ESCS; ch++) { + uint32_t gpio = io_timer_channel_get_gpio_output(ch); + + if (gpio != 0) { + _gpio_pins[_esc_count] = gpio & (GPIO_PORT_MASK | GPIO_PIN_MASK); + _esc_count++; + } + } + + if (_esc_count == 0) { + PX4_ERR("no motor GPIOs found"); + fail(esc_firmware_update_status_s::ERROR_DSHOT_STOP_FAILED); + return; + } + + PX4_INFO("found %u motor GPIO(s)", _esc_count); + + // If targeting a specific ESC, validate index + if (_target_esc != 255 && _target_esc >= _esc_count) { + PX4_ERR("ESC index %u out of range (have %u)", _target_esc, _esc_count); + fail(esc_firmware_update_status_s::ERROR_UNSUPPORTED_HARDWARE); + return; + } + + // Drive all target pins HIGH to force ESCs into bootloader on next reset + for (uint8_t i = 0; i < _esc_count; i++) { + bool target = (_target_esc == 255) || (i == _target_esc); + + if (target) { + uint32_t gpio_out = _gpio_pins[i] + | GPIO_OUTPUT | GPIO_OUTPUT_SET | GPIO_PULLUP; + px4_arch_configgpio(gpio_out); + } + } + + PX4_INFO("GPIOs HIGH — waiting %u ms for ESC bootloader entry", + (unsigned)(GPIO_SETTLE_TIME_US / 1000)); + + _status.esc_count = (_target_esc == 255) ? _esc_count : 1; + publish_status(); + + // Wait for ESCs to detect HIGH and enter bootloader + px4_usleep(GPIO_SETTLE_TIME_US); + + // Set starting ESC + if (_target_esc != 255) { + _current_esc = _target_esc; + + } else { + _current_esc = 0; + } + + transition(State::ConnectESC); + +#else + PX4_ERR("GPIO operations not supported on this platform"); + fail(esc_firmware_update_status_s::ERROR_DSHOT_STOP_FAILED); +#endif +} + +// ───────────────────────────── State: ConnectESC ──────────────────────────── + +void EscFlasher::state_connect_esc() +{ + PX4_INFO("connecting to ESC %u", _current_esc); + + _status.esc_index = _current_esc; + _status.status = esc_firmware_update_status_s::STATUS_CONNECTING; + _status.error = esc_firmware_update_status_s::ERROR_NONE; + publish_status(); + + int ret = am32::handshake(_gpio_pins[_current_esc], _device_info); + + if (ret != am32::OK) { + PX4_ERR("bootloader handshake failed on ESC %u (err %d)", _current_esc, ret); + fail(esc_firmware_update_status_s::ERROR_COMMS_TIMEOUT); + return; + } + + PX4_INFO("ESC %u bootloader: %02x %02x %02x %02x %02x %02x %02x %02x %02x", + _current_esc, + _device_info[0], _device_info[1], _device_info[2], + _device_info[3], _device_info[4], _device_info[5], + _device_info[6], _device_info[7], _device_info[8]); + + transition(State::ReadInfo); +} + +// ───────────────────────────── State: ReadInfo ────────────────────────────── + +void EscFlasher::state_read_info() +{ + _status.status = esc_firmware_update_status_s::STATUS_READING_INFO; + publish_status(); + + // Device info already obtained during handshake. + // Future: read firmware tag to auto-select firmware file. + + transition(State::LoadFirmware); +} + +// ───────────────────────────── State: LoadFirmware ────────────────────────── + +void EscFlasher::state_load_firmware() +{ + // Close any previously open file (when looping over ESCs) + if (_fw_fd >= 0) { + close(_fw_fd); + _fw_fd = -1; + } + + _fw_fd = open(FIRMWARE_PATH, O_RDONLY); + + if (_fw_fd < 0) { + PX4_ERR("cannot open %s", FIRMWARE_PATH); + mavlink_log_critical(&_mavlink_log_pub, "No firmware file on SD card"); + fail(esc_firmware_update_status_s::ERROR_NO_FIRMWARE_FILE); + return; + } + + struct stat st; + + if (fstat(_fw_fd, &st) != 0 || st.st_size == 0) { + PX4_ERR("cannot stat firmware file"); + close(_fw_fd); + _fw_fd = -1; + fail(esc_firmware_update_status_s::ERROR_NO_FIRMWARE_FILE); + return; + } + + _fw_size = (uint32_t)st.st_size; + _fw_written = 0; + + PX4_INFO("firmware %s: %lu bytes, %lu chunks", + FIRMWARE_PATH, (unsigned long)_fw_size, + (unsigned long)((_fw_size + am32::MAX_CHUNK_SIZE - 1) / am32::MAX_CHUNK_SIZE)); + + transition(State::Flashing); +} + +// ───────────────────────────── State: Flashing ────────────────────────────── + +void EscFlasher::state_flash_chunk() +{ + _status.status = esc_firmware_update_status_s::STATUS_WRITING; + + uint32_t remaining = _fw_size - _fw_written; + uint16_t chunk_len = (remaining > am32::MAX_CHUNK_SIZE) + ? am32::MAX_CHUNK_SIZE : (uint16_t)remaining; + + uint8_t buf[am32::MAX_CHUNK_SIZE]; + ssize_t nread = read(_fw_fd, buf, chunk_len); + + if (nread != (ssize_t)chunk_len) { + PX4_ERR("firmware read error at offset %lu", (unsigned long)_fw_written); + fail(esc_firmware_update_status_s::ERROR_NO_FIRMWARE_FILE); + return; + } + + // Set flash address (16-bit, relative to STM32 flash base) + uint16_t address = (uint16_t)((am32::FIRMWARE_ADDR & 0xFFFF) + _fw_written); + + int ret = am32::set_address(_gpio_pins[_current_esc], address); + + if (ret != am32::OK) { + PX4_ERR("set_address failed on ESC %u (err %d)", _current_esc, ret); + fail(esc_firmware_update_status_s::ERROR_COMMS_TIMEOUT); + return; + } + + ret = am32::write_chunk(_gpio_pins[_current_esc], buf, chunk_len); + + if (ret != am32::OK) { + PX4_ERR("write_chunk failed on ESC %u at 0x%04x (err %d)", + _current_esc, address, ret); + fail(esc_firmware_update_status_s::ERROR_CRC_MISMATCH); + return; + } + + _fw_written += chunk_len; + + // Progress for current ESC + _status.progress_pct = (uint8_t)((_fw_written * 100) / _fw_size); + + // Overall progress across all ESCs + uint8_t esc_total = (_target_esc == 255) ? _esc_count : 1; + uint8_t esc_done; + + if (_target_esc == 255) { + esc_done = _current_esc; + + } else { + esc_done = 0; + } + + _status.overall_progress_pct = (uint8_t)( + ((esc_done * 100) + _status.progress_pct) / esc_total); + + publish_status(); + + uint32_t total_chunks = (_fw_size + am32::MAX_CHUNK_SIZE - 1) / am32::MAX_CHUNK_SIZE; + uint32_t current_chunk = (_fw_written + am32::MAX_CHUNK_SIZE - 1) / am32::MAX_CHUNK_SIZE; + PX4_INFO("ESC %u: chunk %lu/%lu (%u%%)", + _current_esc, (unsigned long)current_chunk, + (unsigned long)total_chunks, _status.progress_pct); + + if (_fw_written >= _fw_size) { + transition(State::RunApp); + } +} + +// ───────────────────────────── State: RunApp ──────────────────────────────── + +void EscFlasher::state_run_app() +{ + PX4_INFO("sending RUN_APP to ESC %u", _current_esc); + + am32::run_app(_gpio_pins[_current_esc]); + + // Close firmware file so it can be re-opened for the next ESC + if (_fw_fd >= 0) { + close(_fw_fd); + _fw_fd = -1; + } + + advance_to_next_esc(); +} + +void EscFlasher::advance_to_next_esc() +{ + if (_target_esc != 255) { + // Single-ESC mode — done + transition(State::Complete); + return; + } + + _current_esc++; + + if (_current_esc >= _esc_count) { + transition(State::Complete); + + } else { + // Next ESC + transition(State::ConnectESC); + } +} + +// ───────────────────────────── State: Complete ────────────────────────────── + +void EscFlasher::state_complete() +{ + hrt_abstime elapsed = hrt_absolute_time() - _flash_started; + unsigned seconds = (unsigned)(elapsed / 1000000); + + PX4_INFO("ESC firmware update complete in %u seconds", seconds); + mavlink_log_info(&_mavlink_log_pub, "ESC flash done in %us — reboot required", seconds); + + _status.status = esc_firmware_update_status_s::STATUS_REBOOT_REQUIRED; + _status.progress_pct = 100; + _status.overall_progress_pct = 100; + _status.error = esc_firmware_update_status_s::ERROR_NONE; + publish_status(); + + cleanup(); + transition(State::Idle); +} + +// ───────────────────────────── State: Failed ──────────────────────────────── + +void EscFlasher::state_failed() +{ + PX4_ERR("ESC firmware update failed (error %u)", _status.error); + mavlink_log_critical(&_mavlink_log_pub, "ESC flash failed (error %u)", _status.error); + + _status.status = esc_firmware_update_status_s::STATUS_FAILED; + publish_status(); + + cleanup(); + transition(State::Idle); +} + +// ───────────────────────────── Helpers ────────────────────────────────────── + +void EscFlasher::transition(State s) +{ + _state = s; + _state_entered = hrt_absolute_time(); +} + +void EscFlasher::fail(uint8_t error_code) +{ + _status.error = error_code; + transition(State::Failed); +} + +void EscFlasher::cleanup() +{ + if (_fw_fd >= 0) { + close(_fw_fd); + _fw_fd = -1; + } + + _fw_size = 0; + _fw_written = 0; +} + +void EscFlasher::publish_status() +{ + _status.timestamp = hrt_absolute_time(); + _status_pub.publish(_status); +} + +// ───────────────────────────── ModuleBase boilerplate ──────────────────────── + +int EscFlasher::print_status() +{ + static const char *state_names[] = { + "Idle", "StoppingDShot", "InitGPIOs", "ConnectESC", + "ReadInfo", "LoadFirmware", "Flashing", "RunApp", + "Complete", "Failed" + }; + + PX4_INFO("state: %s", state_names[(int)_state]); + PX4_INFO("ESC %u / %u, firmware %lu / %lu bytes", + _current_esc, _esc_count, + (unsigned long)_fw_written, (unsigned long)_fw_size); return 0; } -int ESC_Flasher::print_usage(const char *reason) { +int EscFlasher::print_usage(const char *reason) +{ if (reason) { - PX4_ERR("%s\n", reason); + PX4_ERR("%s", reason); } PRINT_MODULE_DESCRIPTION( R"DESCR_STR( ### Description -ESC Flasher module allows configuration and reflashing -of supported ESCs +AM32 ESC firmware update module. + +Reads firmware from SD card, stops DShot, and flashes ESCs +via bit-banged bootloader protocol. Triggered by the +VEHICLE_CMD_ESC_FIRMWARE_UPDATE vehicle command. +A reboot is required after flashing to restore normal operation. )DESCR_STR"); PRINT_MODULE_USAGE_NAME("esc_flasher", "system"); - PRINT_MODULE_USAGE_COMMAND_DESCR("start", "Start the background task"); + PRINT_MODULE_USAGE_COMMAND_DESCR("start", "Start the module (auto-started on supported boards)"); PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); + return 0; } -int ESC_Flasher::custom_command(int argc, char *argv[]) { - if (argc < 1) { - PX4_INFO("Usage: esc_flasher "); - return ESC_FLASHER_RET_NOK; - } - - // Get our current object - ESC_Flasher* mESC_Flasher = NULL; - (void)mESC_Flasher; - if (is_running()) { - mESC_Flasher = (ESC_Flasher*)_object.load(); - } - else { - return ESC_FLASHER_RET_NOK; - } - - int ret = ESC_FLASHER_RET_OK; - - int myoptind = 1; - int ch; - const char *myoptarg = nullptr; - - // Get motor index - int motor_index = -1; - uint16_t motor_flags = 0; - bool found_m = false; - bool found_a = false; - while ((ch = px4_getopt(argc, argv, "am:", &myoptind, &myoptarg)) != EOF) { - switch (ch) { - case 'a': - if (found_m) { - return print_usage("Conflicting flags, use only -m <1 2 3 4> or -a"); - } - // Flash all 4 motors - motor_flags = 0b0000000000001111; - found_a = true; - break; - case 'm': - if (found_a) { - return print_usage("Conflicting flags, use only -m <1 2 3 4> or -a"); - } - motor_index = strtol(myoptarg, nullptr, 10); - if (motor_index < 1 || motor_index > 4) { - return print_usage("Motor index out of range. Acceptable values are 1-4"); - } - motor_flags |= (1 << (motor_index - 1)); - found_m = true; - break; - default: - return print_usage("unrecognized flag"); - } - } - - const char *command = argv[0]; - - // Handle various commands issued via the command line - if (strcmp(command, "print") == 0) { - // Print status every second - if (argc < 2) { - PX4_ERR("Usage: esc_flasher print <0 for off, 1 for on>"); - ret = ESC_FLASHER_RET_NOK; - } - - int enable = strtol(argv[1], NULL, 10); - mESC_Flasher->print_flag = (bool)enable; - - return ret; - } - else if (strcmp(command, "flash") == 0) { - // Flash a specific motor - if (motor_flags == 0) { - return print_usage("Usage: esc_flasher flash -m <1 2 3 4>"); - } - - mESC_Flasher->start_flash = 1; - mESC_Flasher->motor_flags = motor_flags; - // Wake it from the sleep-loop, this helps if we are running at 1 Hz - mESC_Flasher->wake = true; - - return ret; - } - else if (strcmp(command, "cancel") == 0) { - // Cancel any current request, return control to DShot - mESC_Flasher->cancel_flash = 1; - // Wake it from the sleep-loop, this helps if we are running at 1 Hz - mESC_Flasher->wake = true; - - return ret; - } - - return print_usage("Unknown command"); +int EscFlasher::custom_command(int argc, char *argv[]) +{ + return print_usage("unrecognised command"); } -extern "C" __EXPORT int esc_flasher_main(int argc, char *argv[]) { - return ESC_Flasher::main(argc, argv); +extern "C" __EXPORT int esc_flasher_main(int argc, char *argv[]) +{ + return EscFlasher::main(argc, argv); } diff --git a/src/modules/esc_flasher/EscFlasher.hpp b/src/modules/esc_flasher/EscFlasher.hpp index e1ad59025e..d0563ec822 100644 --- a/src/modules/esc_flasher/EscFlasher.hpp +++ b/src/modules/esc_flasher/EscFlasher.hpp @@ -1,47 +1,78 @@ +/**************************************************************************** + * + * Copyright (c) 2024 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file EscFlasher.hpp + * + * AM32 ESC firmware update module. + * + * Triggered by MAV_CMD_ESC_FIRMWARE_UPDATE via vehicle_command. + * Reads firmware from SD card, stops DShot, takes over motor GPIOs, + * and flashes each ESC via bit-banged AM32 bootloader protocol. + * Requires reboot to restore normal DShot operation. + */ + #pragma once -#include -#include -#include #include #include -#include -#include -#include +#include #include #include #include #include - -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include +#include -// Firefly drones have 4 ESCs -#define ESC_COUNT (4) +#include "am32_protocol.hpp" -class ESC_Flasher : public ModuleBase, public ModuleParams +class EscFlasher : public ModuleBase, public ModuleParams { public: + EscFlasher(); + ~EscFlasher() override; - ESC_Flasher(); - ~ESC_Flasher() override; - - ESC_Flasher(const ESC_Flasher &) = delete; - ESC_Flasher operator=(const ESC_Flasher &) = delete; + EscFlasher(const EscFlasher &) = delete; + EscFlasher &operator=(const EscFlasher &) = delete; /** @see ModuleBase */ static int task_spawn(int argc, char *argv[]); /** @see ModuleBase */ - static ESC_Flasher *instantiate(int argc, char *argv[]); + static EscFlasher *instantiate(int argc, char *argv[]); /** @see ModuleBase::run() */ void run() override; @@ -56,174 +87,74 @@ public: int print_status() override; private: + static constexpr uint8_t MAX_ESCS = 8; + static constexpr const char *FIRMWARE_PATH = "/fs/microsd/am32/firmware.bin"; + static constexpr uint32_t DSHOT_STOP_TIMEOUT_US = 2000000; // 2 s + static constexpr uint32_t GPIO_SETTLE_TIME_US = 600000; // 600 ms + static constexpr uint32_t IDLE_INTERVAL_US = 1000000; // 1 s - void RunLoop(); - - void subscribe_orb_messages(void); - void unsubscribe_orb_messages(void); - - void update_params(void); - uint8_t get_current_arming_state(void); - - void set_esc_type(bool print); - - void load_firmware_source(void); - - int bitbang_send_packet(uint32_t gpio, uint8_t* packet, uint16_t length, uint8_t* response, uint8_t response_length); - - uint16_t make_crc(uint8_t* buffer, uint16_t length); - - void play_tune(int tune); - void stop_tune(void); - - void set_physical_led_outputs(uint8_t color); - - void send_vehicle_command_leds(void); - - perf_counter_t _loop_perf; /**< loop performance counter */ - - uint32_t run_delay{10000}; - const uint32_t run_delay_min = 5000; //wait 0.005 sec between running the work loop (200 Hz) - const uint32_t run_delay_max = 1000000; //wait 1 sec between running the work loop (1 Hz) - volatile bool wake{false}; - uint32_t delay_loops; - - hrt_abstime time_now; - hrt_abstime time_1s; - hrt_abstime time_250ms; - hrt_abstime time_flash_start; - hrt_abstime time_delayed_start; - - uint32_t run_calls; - - bool print_flag; // Turn this on to enable continuous status printing - - bool flashing_in_progress{false}; - bool flashing_in_progress_last{false}; - - uint8_t vehicle_armed; // Indicates if the vehicle is armed - - uint8_t last_color{0}; - - uint8_t _am32_fw_ver_major; - uint8_t _am32_fw_ver_minor; - enum class AM32_FIRMWARE_SOURCE { - MEM_ARRAY, - SD_CARD, - STREAM_HEAP + enum class State : uint8_t { + Idle, + StoppingDShot, + InitGPIOs, + ConnectESC, + ReadInfo, + LoadFirmware, + Flashing, + RunApp, + Complete, + Failed }; - AM32_FIRMWARE_SOURCE _am32_fw_source{AM32_FIRMWARE_SOURCE::MEM_ARRAY}; - enum class ESCType { - Unknown = 0, - AM32 = 1, - BLHELI32 = 2, - AM32_Old = 3, - BlueJay = 4 - }; - ESCType _esc_type{ESCType::Unknown}; + // --- state machine helpers --- + void handle_vehicle_command(); + void ack_vehicle_command(const vehicle_command_s &cmd, uint8_t result); + void begin_update(uint8_t esc_index); + void advance_to_next_esc(); + void transition(State s); + void fail(uint8_t error_code); + void cleanup(); + void publish_status(); - const char *esc_type_unknown = "Unknown"; - const char *esc_type_am32 = "AM32"; - const char *esc_type_blheli32 = "BLHeli32"; - const char *esc_type_am32_old = "AM32_Old"; - const char *esc_type_bluejay = "BlueJay"; + // --- state handlers (called from run loop) --- + void state_stopping_dshot(); + void state_init_gpios(); + void state_connect_esc(); + void state_read_info(); + void state_load_firmware(); + void state_flash_chunk(); + void state_run_app(); + void state_complete(); + void state_failed(); - const char* esc_types_strings[5] = {esc_type_unknown, esc_type_am32, esc_type_blheli32, esc_type_am32_old, esc_type_bluejay}; + // --- current state --- + State _state{State::Idle}; + hrt_abstime _state_entered{0}; + hrt_abstime _flash_started{0}; - enum class ESCUpdate { - Disabled, - Update, - ForceUpdate, - Recover, - Recover2, - Cancel - }; - ESCUpdate _esc_update{ESCUpdate::Disabled}; - uint32_t esc_update_state{0}; - uint32_t prev_esc_update_state{0}; - uint8_t esc_update_motors[ESC_COUNT]; - uint32_t esc_update_motor_index; - uint32_t flashing_result; + // --- ESC tracking --- + uint8_t _target_esc{255}; // from vehicle_command param1 + uint8_t _current_esc{0}; // index into _gpio_pins[] + uint8_t _esc_count{0}; + uint32_t _gpio_pins[MAX_ESCS]{}; + uint8_t _device_info[am32::DEVICE_INFO_SIZE]{}; - // Struct to track flashing progress for a single ESC - typedef struct { - uint8_t current_motor; - uint32_t current_gpio; - uint32_t fw_length; - uint32_t fw_bytes_sent; - uint32_t fw_bytes_left; - uint16_t base_address; - uint16_t next_address; - uint8_t fw_part; // We have 2 parts to write for AM32 firmware, am32_firmware and am32_firmware_tag - uint16_t crc; - uint32_t tx_length; - uint8_t tx_buffer[258]; - } ESC_FLASHING_STATE; - ESC_FLASHING_STATE esc_flashing_state; + // --- firmware file --- + int _fw_fd{-1}; + uint32_t _fw_size{0}; + uint32_t _fw_written{0}; - const uint16_t max_chunk_length = 256; + // --- published status --- + esc_firmware_update_status_s _status{}; - // Variables to save responses from DShot - typedef struct { - uint8_t major; - uint8_t minor; - } ESC_FW_VERSIONS; - ESC_FW_VERSIONS esc_versions[ESC_COUNT]; + // --- publications --- + uORB::Publication _status_pub{ORB_ID(esc_firmware_update_status)}; + uORB::Publication _cmd_ack_pub{ORB_ID(vehicle_command_ack)}; - uint32_t esc_gpios[ESC_COUNT]; - uint32_t gpio_high; - hrt_abstime time_gpio_set; + // --- subscriptions --- + uORB::Subscription _vehicle_cmd_sub{ORB_ID(vehicle_command)}; + uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)}; - // AM32 Bootloader commands - uint8_t response_data[16]; - const uint8_t am32_boot_init[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0x0D, 'B', 'L', 'H', 'e', 'l', 'i', 0xF4, 0x7D}; - const uint8_t am32_boot_init_resp_len = 9; - uint8_t am32_set_addr[6] = {0xFF, 0, 0x10, 0x00, 0, 0}; - const uint8_t am32_set_addr_resp_len = 1; - uint8_t am32_set_buff_size[6] = {0xFE, 0, 1, 0, 0, 0}; - const uint8_t am32_set_buff_size_resp_len = 0; - const uint8_t am32_send_buff_resp_len = 1; - uint8_t am32_write[4] = {0x01, 0, 0, 0}; - const uint8_t am32_write_resp_len = 1; - const uint8_t am32_run_app[4] = {0x00, 0, 0, 0}; - const uint8_t am32_run_app_resp_len = 0; - - // These variables are useful for starting a flash from command-line - volatile uint32_t start_flash; - volatile uint16_t motor_flags; - volatile uint32_t cancel_flash; - - // Request/Ack variables - uint32_t msg_id; - uint32_t esc_info_timeout; - - // Tune control - uint32_t esc_flasher_tune{0}; // Play some different tunes during flashing - uint32_t esc_flasher_stop_tune{0}; // Stop tune 1 second after starting - - // Publication variables - uORB::Publication _esc_flasher_request_pub{ORB_ID(esc_flasher_request)}; - uORB::Publication _esc_flasher_status_pub{ORB_ID(esc_flasher_status)}; - uORB::Publication _tune_control_pub{ORB_ID(tune_control)}; - uORB::PublicationData _led_control_pub{ORB_ID(led_control)}; - uORB::Publication _vehicle_command_pub{ORB_ID(vehicle_command)}; - - // Subscription variables - uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)}; - uORB::Subscription _esc_flasher_request_ack_sub{ORB_ID(esc_flasher_request_ack)}; - uORB::Subscription _sensor_gps_sub {ORB_ID(sensor_gps)}; - uORB::Subscription _vehicle_status_sub {ORB_ID(vehicle_status)}; - - // MAVLink + // --- mavlink log --- orb_advert_t _mavlink_log_pub{nullptr}; - - sensor_gps_s _sensor_gps {}; /**< gps sensor */ - vehicle_status_s _vehicle_status {}; /**< vehicle status */ - - DEFINE_PARAMETERS( - (ParamInt) _param_esc_type, - (ParamInt) _param_esc_update - ) - }; diff --git a/src/modules/esc_flasher/am32_protocol.cpp b/src/modules/esc_flasher/am32_protocol.cpp new file mode 100644 index 0000000000..3626ca7b26 --- /dev/null +++ b/src/modules/esc_flasher/am32_protocol.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** + * + * Copyright (c) 2024 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file am32_protocol.cpp + * + * AM32 ESC bootloader protocol — bit-banged UART implementation. + */ + +#include "am32_protocol.hpp" + +#include +#include +#include +#include + +namespace am32 { + +// Bootloader handshake packet (8 zero bytes + 0x0D + "BLHeli" + CRC) +static const uint8_t boot_init[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0D, 'B', 'L', 'H', 'e', 'l', 'i', 0xF4, 0x7D +}; + +uint16_t crc16(const uint8_t *buf, uint16_t len) +{ + uint16_t crc = 0; + + for (uint16_t i = 0; i < len; i++) { + uint8_t xb = buf[i]; + + for (uint8_t j = 0; j < 8; j++) { + if (((xb & 0x01) ^ (crc & 0x0001)) != 0) { + crc = (crc >> 1) ^ 0xA001; + + } else { + crc >>= 1; + } + + xb >>= 1; + } + } + + return crc; +} + +int send_packet(uint32_t gpio, const uint8_t *tx_data, uint16_t tx_len, + uint8_t *rx_data, uint8_t rx_len) +{ + // Build GPIO configurations from the port+pin value + uint32_t gpio_out = (gpio & (GPIO_PORT_MASK | GPIO_PIN_MASK)) + | GPIO_OUTPUT | GPIO_OUTPUT_SET | GPIO_PULLUP; + uint32_t gpio_in = (gpio & (GPIO_PORT_MASK | GPIO_PIN_MASK)) + | GPIO_INPUT | GPIO_PULLUP; + + // Start as output HIGH + px4_arch_configgpio(gpio_out); + + // --- critical section: interrupts off for timing accuracy --- + irqstate_t irq_state = px4_enter_critical_section(); + + // Transmit bytes (19200 8N1, LSB-first) + for (uint16_t k = 0; k < tx_len; k++) { + uint8_t byte = tx_data[k]; + + // Start bit (LOW) + px4_arch_gpiowrite(gpio_out, 0); + hrt_abstime t = hrt_absolute_time(); + + while (hrt_absolute_time() - t < BIT_TIME_US) {} + + // 8 data bits, LSB first + for (uint8_t i = 0; i < 8; i++) { + px4_arch_gpiowrite(gpio_out, (byte >> i) & 1); + t = hrt_absolute_time(); + + while (hrt_absolute_time() - t < BIT_TIME_US) {} + } + + // Stop bit (HIGH) + px4_arch_gpiowrite(gpio_out, 1); + t = hrt_absolute_time(); + + while (hrt_absolute_time() - t < BIT_TIME_US) {} + } + + // Switch to input for receiving + px4_arch_configgpio(gpio_in); + + // Receive response bytes + uint8_t read_count = 0; + + if (rx_len > 0 && rx_data != nullptr) { + for (uint8_t i = 0; i < rx_len; i++) { + + // Wait for start bit (line goes LOW) + hrt_abstime t = hrt_absolute_time(); + + while (px4_arch_gpioread(gpio_in)) { + if (hrt_absolute_time() - t > RX_TIMEOUT_US) { + // Timeout — restore output HIGH and bail + px4_arch_configgpio(gpio_out); + px4_leave_critical_section(irq_state); + return ERR_TIMEOUT; + } + } + + // Half-bit delay to centre sampling window + t = hrt_absolute_time(); + + while (hrt_absolute_time() - t < HALF_BIT_TIME_US) {} + + // Read 8 data bits + uint8_t byte = 0; + + for (uint8_t bit = 0; bit < 8; bit++) { + t = hrt_absolute_time(); + + while (hrt_absolute_time() - t < BIT_TIME_US) {} + + if (px4_arch_gpioread(gpio_in)) { + byte |= (1 << bit); + } + } + + rx_data[read_count++] = byte; + + // Wait through stop bit + t = hrt_absolute_time(); + + while (hrt_absolute_time() - t < BIT_TIME_US) {} + } + } + + // Restore pin to output HIGH + px4_arch_configgpio(gpio_out); + + // Inter-packet gap (8 bit-times) + hrt_abstime t = hrt_absolute_time(); + + while (hrt_absolute_time() - t < BIT_TIME_US * 8) {} + + px4_leave_critical_section(irq_state); + + return read_count; +} + +int handshake(uint32_t gpio, uint8_t device_info[DEVICE_INFO_SIZE]) +{ + int ret = send_packet(gpio, boot_init, sizeof(boot_init), + device_info, DEVICE_INFO_SIZE); + + if (ret == DEVICE_INFO_SIZE) { + return OK; + } + + return (ret < 0) ? ret : ERR_NACK; +} + +int set_address(uint32_t gpio, uint16_t address) +{ + uint8_t cmd[6]; + cmd[0] = CMD_SET_ADDRESS; + cmd[1] = 0x00; + cmd[2] = (address >> 8) & 0xFF; // high byte + cmd[3] = address & 0xFF; // low byte + + uint16_t crc = crc16(cmd, 4); + cmd[4] = crc & 0xFF; + cmd[5] = (crc >> 8) & 0xFF; + + uint8_t response; + int ret = send_packet(gpio, cmd, sizeof(cmd), &response, 1); + + if (ret == 1 && response == ACK) { + return OK; + } + + return (ret < 0) ? ret : ERR_NACK; +} + +int write_chunk(uint32_t gpio, const uint8_t *data, uint16_t len) +{ + if (len == 0 || len > MAX_CHUNK_SIZE || data == nullptr) { + return ERR_INVALID; + } + + // 1) CMD_SET_BUFFER — tell bootloader how many bytes follow + uint8_t size_cmd[6]; + size_cmd[0] = CMD_SET_BUFFER; + size_cmd[1] = 0x00; + + if (len == MAX_CHUNK_SIZE) { + size_cmd[2] = 1; // 256 encoded as (1, 0) + size_cmd[3] = 0; + + } else { + size_cmd[2] = 0; + size_cmd[3] = (uint8_t)len; + } + + uint16_t crc = crc16(size_cmd, 4); + size_cmd[4] = crc & 0xFF; + size_cmd[5] = (crc >> 8) & 0xFF; + + // SET_BUFFER has no response + send_packet(gpio, size_cmd, sizeof(size_cmd), nullptr, 0); + + // 2) Send data + CRC — bootloader ACKs after receiving + uint8_t buf[MAX_CHUNK_SIZE + 2]; + memcpy(buf, data, len); + crc = crc16(data, len); + buf[len] = crc & 0xFF; + buf[len + 1] = (crc >> 8) & 0xFF; + + uint8_t response; + int ret = send_packet(gpio, buf, len + 2, &response, 1); + + if (ret != 1 || response != ACK) { + return (ret < 0) ? ret : ERR_NACK; + } + + // 3) CMD_PROG_FLASH — commit buffer to flash + uint8_t prog_cmd[4]; + prog_cmd[0] = CMD_PROG_FLASH; + prog_cmd[1] = 0x00; + crc = crc16(prog_cmd, 2); + prog_cmd[2] = crc & 0xFF; + prog_cmd[3] = (crc >> 8) & 0xFF; + + ret = send_packet(gpio, prog_cmd, sizeof(prog_cmd), &response, 1); + + if (ret == 1 && response == ACK) { + return OK; + } + + return (ret < 0) ? ret : ERR_NACK; +} + +int read_flash(uint32_t gpio, uint8_t *buf, uint16_t len) +{ + if (len == 0 || buf == nullptr) { + return ERR_INVALID; + } + + // Address must already be set via set_address() + uint8_t cmd[4]; + cmd[0] = CMD_READ_FLASH; + cmd[1] = (uint8_t)len; + + uint16_t crc = crc16(cmd, 2); + cmd[2] = crc & 0xFF; + cmd[3] = (crc >> 8) & 0xFF; + + int ret = send_packet(gpio, cmd, sizeof(cmd), buf, (uint8_t)len); + + if (ret == (int)len) { + return OK; + } + + return (ret < 0) ? ret : ERR_NACK; +} + +int run_app(uint32_t gpio) +{ + static const uint8_t cmd[] = {0x00, 0x00, 0x00, 0x00}; + + // No response expected + send_packet(gpio, cmd, sizeof(cmd), nullptr, 0); + + return OK; +} + +} // namespace am32 diff --git a/src/modules/esc_flasher/am32_protocol.hpp b/src/modules/esc_flasher/am32_protocol.hpp new file mode 100644 index 0000000000..2dc1727191 --- /dev/null +++ b/src/modules/esc_flasher/am32_protocol.hpp @@ -0,0 +1,156 @@ +/**************************************************************************** + * + * Copyright (c) 2024 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file am32_protocol.hpp + * + * AM32 ESC bootloader protocol — bit-banged UART at 19200 baud. + * + * Extracted from Matt's proof-of-concept and cleaned up into a + * function-based API for reuse by the esc_flasher module. + */ + +#pragma once + +#include + +namespace am32 { + +// AM32 bootloader command bytes +static constexpr uint8_t CMD_RUN = 0x00; +static constexpr uint8_t CMD_PROG_FLASH = 0x01; +static constexpr uint8_t CMD_READ_FLASH = 0x03; +static constexpr uint8_t CMD_SET_BUFFER = 0xFE; +static constexpr uint8_t CMD_SET_ADDRESS = 0xFF; + +// Bootloader ACK byte +static constexpr uint8_t ACK = 0x30; + +// AM32 firmware memory layout (STM32 flash addresses) +static constexpr uint32_t FIRMWARE_ADDR = 0x08001000; +static constexpr uint32_t FIRMWARE_TAG_ADDR = 0x08007BE0; +static constexpr uint16_t FIRMWARE_TAG_SIZE = 16; + +// Protocol constants +static constexpr uint16_t MAX_CHUNK_SIZE = 256; +static constexpr uint32_t BIT_TIME_US = 52; // ~19200 baud +static constexpr uint32_t HALF_BIT_TIME_US = 26; +static constexpr uint32_t RX_TIMEOUT_US = 100000; // 100 ms + +// Device info from handshake response +static constexpr uint8_t DEVICE_INFO_SIZE = 9; + +// Return codes +static constexpr int OK = 0; +static constexpr int ERR_TIMEOUT = -1; +static constexpr int ERR_NACK = -2; +static constexpr int ERR_CRC = -3; +static constexpr int ERR_INVALID = -4; + +/** + * CRC-16 with polynomial 0xA001 (AM32 bootloader). + */ +uint16_t crc16(const uint8_t *buf, uint16_t len); + +/** + * Bit-banged UART send/receive on a GPIO pin. + * + * Enters a critical section (interrupts disabled) for the + * duration of the transfer to maintain 19200-baud timing. + * + * @param gpio GPIO configuration value (port + pin bits) + * @param tx_data Bytes to transmit + * @param tx_len Number of bytes to transmit + * @param rx_data Buffer for response (may be nullptr if rx_len == 0) + * @param rx_len Expected number of response bytes + * @return Number of bytes received, or negative error code + */ +int send_packet(uint32_t gpio, const uint8_t *tx_data, uint16_t tx_len, + uint8_t *rx_data, uint8_t rx_len); + +/** + * Perform the bootloader handshake. + * + * Sends the AM32 boot-init sequence and reads back 9 bytes + * of device information. + * + * @param gpio Target ESC GPIO + * @param device_info Output buffer for 9-byte device info + * @return OK or negative error code + */ +int handshake(uint32_t gpio, uint8_t device_info[DEVICE_INFO_SIZE]); + +/** + * Set the flash write/read address. + * + * @param gpio Target ESC GPIO + * @param address 16-bit flash address + * @return OK or negative error code + */ +int set_address(uint32_t gpio, uint16_t address); + +/** + * Write a chunk to flash. + * + * Sends CMD_SET_BUFFER + size, then data + CRC, then + * CMD_PROG_FLASH and waits for ACK. Caller must call + * set_address() first. + * + * @param gpio Target ESC GPIO + * @param data Data to write (max MAX_CHUNK_SIZE bytes) + * @param len Byte count + * @return OK or negative error code + */ +int write_chunk(uint32_t gpio, const uint8_t *data, uint16_t len); + +/** + * Read flash memory. + * + * Caller must call set_address() first. + * + * @param gpio Target ESC GPIO + * @param buf Output buffer + * @param len Number of bytes to read + * @return OK or negative error code + */ +int read_flash(uint32_t gpio, uint8_t *buf, uint16_t len); + +/** + * Send the RUN_APP command so the ESC boots its application. + * + * @param gpio Target ESC GPIO + * @return OK (always; no response expected) + */ +int run_app(uint32_t gpio); + +} // namespace am32 diff --git a/src/modules/esc_flasher/esc_params.c b/src/modules/esc_flasher/esc_params.c deleted file mode 100644 index 97a66f52aa..0000000000 --- a/src/modules/esc_flasher/esc_params.c +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2012-2021 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/** - * @file esc_params.c - * - * Parameters defined for ESCs. - * - */ - -/** - * ESC type - * - * @reboot_required true - * - * @group ESC Flasher - * @value 0 - Default - * @value 1 - AM32 - * @value 2 - BLHeli32 - * @value 3 - AM32_Old - * @value 4 - BlueJay - * - * @min 0 - * @max 4 - */ -PARAM_DEFINE_INT32(ESC_TYPE, 0); - -/** - * ESC update - * - * @reboot_required false - * - * @group ESC Flasher - * @value 0 - Disabled - * @value 1 - Update if version is outdated - * @value 2 - Force update all ESCs - * @value 3 - Start recovery process - * @value 4 - Continue recovery process - * - * @min 0 - * @max 4 - */ -PARAM_DEFINE_INT32(ESC_UPDATE, 0);