diff options
Diffstat (limited to 'drivers/sensors')
-rw-r--r-- | drivers/sensors/adns5050.c | 193 | ||||
-rw-r--r-- | drivers/sensors/adns5050.h | 79 | ||||
-rw-r--r-- | drivers/sensors/adns9800.c | 219 | ||||
-rw-r--r-- | drivers/sensors/adns9800.h | 35 | ||||
-rw-r--r-- | drivers/sensors/adns9800_srom_A6.h | 3078 | ||||
-rw-r--r-- | drivers/sensors/pimoroni_trackball.c | 140 | ||||
-rw-r--r-- | drivers/sensors/pimoroni_trackball.h | 35 | ||||
-rw-r--r-- | drivers/sensors/pmw3360.c | 237 | ||||
-rw-r--r-- | drivers/sensors/pmw3360.h | 104 | ||||
-rw-r--r-- | drivers/sensors/pmw3360_firmware.h | 300 |
10 files changed, 4420 insertions, 0 deletions
diff --git a/drivers/sensors/adns5050.c b/drivers/sensors/adns5050.c new file mode 100644 index 0000000000..e7273977d5 --- /dev/null +++ b/drivers/sensors/adns5050.c @@ -0,0 +1,193 @@ +/* Copyright 2021 Colin Lam (Ploopy Corporation) + * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> + * Copyright 2019 Sunjun Kim + * Copyright 2019 Hiroyuki Okada + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "adns5050.h" +#include "wait.h" +#include "debug.h" +#include "print.h" +#include "gpio.h" + +#ifndef OPTIC_ROTATED +# define OPTIC_ROTATED false +#endif + +// Definitions for the ADNS serial line. +#ifndef ADNS_SCLK_PIN +# define ADNS_SCLK_PIN B7 +#endif + +#ifndef ADNS_SDIO_PIN +# define ADNS_SDIO_PIN C6 +#endif + +#ifndef ADNS_CS_PIN +# define ADNS_CS_PIN B4 +#endif + +#ifdef CONSOLE_ENABLE +void print_byte(uint8_t byte) { dprintf("%c%c%c%c%c%c%c%c|", (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0')); } +#endif + +// Initialize the ADNS serial pins. +void adns_init(void) { + setPinOutput(ADNS_SCLK_PIN); + setPinOutput(ADNS_SDIO_PIN); + setPinOutput(ADNS_CS_PIN); +} + +// Perform a synchronization with the ADNS. +// Just as with the serial protocol, this is used by the slave to send a +// synchronization signal to the master. +void adns_sync(void) { + writePinLow(ADNS_CS_PIN); + wait_us(1); + writePinHigh(ADNS_CS_PIN); +} + +void adns_cs_select(void) { + writePinLow(ADNS_CS_PIN); +} + +void adns_cs_deselect(void) { + writePinHigh(ADNS_CS_PIN); +} + +uint8_t adns_serial_read(void) { + setPinInput(ADNS_SDIO_PIN); + uint8_t byte = 0; + + for (uint8_t i = 0; i < 8; ++i) { + writePinLow(ADNS_SCLK_PIN); + wait_us(1); + + byte = (byte << 1) | readPin(ADNS_SDIO_PIN); + + writePinHigh(ADNS_SCLK_PIN); + wait_us(1); + } + + return byte; +} + +void adns_serial_write(uint8_t data) { + setPinOutput(ADNS_SDIO_PIN); + + for (int8_t b = 7; b >= 0; b--) { + writePinLow(ADNS_SCLK_PIN); + + if (data & (1 << b)) + writePinHigh(ADNS_SDIO_PIN); + else + writePinLow(ADNS_SDIO_PIN); + + wait_us(2); + + writePinHigh(ADNS_SCLK_PIN); + } + + // tSWR. See page 15 of the ADNS spec sheet. + // Technically, this is only necessary if the next operation is an SDIO + // read. This is not guaranteed to be the case, but we're being lazy. + wait_us(4); + + // Note that tSWW is never necessary. All write operations require at + // least 32us, which exceeds tSWW, so there's never a need to wait for it. +} + +// Read a byte of data from a register on the ADNS. +// Don't forget to use the register map (as defined in the header file). +uint8_t adns_read_reg(uint8_t reg_addr) { + adns_cs_select(); + + adns_serial_write(reg_addr); + + // We don't need a minimum tSRAD here. That's because a 4ms wait time is + // already included in adns_serial_write(), so we're good. + // See page 10 and 15 of the ADNS spec sheet. + //wait_us(4); + + uint8_t byte = adns_serial_read(); + + // tSRW & tSRR. See page 15 of the ADNS spec sheet. + // Technically, this is only necessary if the next operation is an SDIO + // read or write. This is not guaranteed to be the case. + // Honestly, this wait could probably be removed. + wait_us(1); + + adns_cs_deselect(); + + return byte; +} + +void adns_write_reg(uint8_t reg_addr, uint8_t data) { + adns_cs_select(); + adns_serial_write( 0b10000000 | reg_addr ); + adns_serial_write(data); + adns_cs_deselect(); +} + +report_adns_t adns_read_burst(void) { + adns_cs_select(); + + report_adns_t data; + data.dx = 0; + data.dy = 0; + + adns_serial_write(REG_MOTION_BURST); + + // We don't need a minimum tSRAD here. That's because a 4ms wait time is + // already included in adns_serial_write(), so we're good. + // See page 10 and 15 of the ADNS spec sheet. + //wait_us(4); + + uint8_t x = adns_serial_read(); + uint8_t y = adns_serial_read(); + + // Burst mode returns a bunch of other shit that we don't really need. + // Setting CS to high ends burst mode early. + adns_cs_deselect(); + + data.dx = convert_twoscomp(x); + data.dy = convert_twoscomp(y); + + return data; +} + +// Convert a two's complement byte from an unsigned data type into a signed +// data type. +int8_t convert_twoscomp(uint8_t data) { + if ((data & 0x80) == 0x80) + return -128 + (data & 0x7F); + else + return data; +} + +// Don't forget to use the definitions for CPI in the header file. +void adns_set_cpi(uint8_t cpi) { + adns_write_reg(REG_MOUSE_CONTROL2, cpi); +} + +bool adns_check_signature(void) { + uint8_t pid = adns_read_reg(REG_PRODUCT_ID); + uint8_t rid = adns_read_reg(REG_REVISION_ID); + uint8_t pid2 = adns_read_reg(REG_PRODUCT_ID2); + + return (pid == 0x12 && rid == 0x01 && pid2 == 0x26); +} diff --git a/drivers/sensors/adns5050.h b/drivers/sensors/adns5050.h new file mode 100644 index 0000000000..ff8e8f78e9 --- /dev/null +++ b/drivers/sensors/adns5050.h @@ -0,0 +1,79 @@ +/* Copyright 2021 Colin Lam (Ploopy Corporation) + * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> + * Copyright 2019 Sunjun Kim + * Copyright 2019 Hiroyuki Okada + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> + +// Registers +#define REG_PRODUCT_ID 0x00 +#define REG_REVISION_ID 0x01 +#define REG_MOTION 0x02 +#define REG_DELTA_X 0x03 +#define REG_DELTA_Y 0x04 +#define REG_SQUAL 0x05 +#define REG_SHUTTER_UPPER 0x06 +#define REG_SHUTTER_LOWER 0x07 +#define REG_MAXIMUM_PIXEL 0x08 +#define REG_PIXEL_SUM 0x09 +#define REG_MINIMUM_PIXEL 0x0a +#define REG_PIXEL_GRAB 0x0b +#define REG_MOUSE_CONTROL 0x0d +#define REG_MOUSE_CONTROL2 0x19 +#define REG_LED_DC_MODE 0x22 +#define REG_CHIP_RESET 0x3a +#define REG_PRODUCT_ID2 0x3e +#define REG_INV_REV_ID 0x3f +#define REG_MOTION_BURST 0x63 + +// CPI values +#define CPI125 0x11 +#define CPI250 0x12 +#define CPI375 0x13 +#define CPI500 0x14 +#define CPI625 0x15 +#define CPI750 0x16 +#define CPI875 0x17 +#define CPI1000 0x18 +#define CPI1125 0x19 +#define CPI1250 0x1a +#define CPI1375 0x1b + +#ifdef CONSOLE_ENABLE +void print_byte(uint8_t byte); +#endif + +typedef struct { + int8_t dx; + int8_t dy; +} report_adns_t; + +// A bunch of functions to implement the ADNS5050-specific serial protocol. +// Note that the "serial.h" driver is insufficient, because it does not +// manually manipulate a serial clock signal. +void adns_init(void); +void adns_sync(void); +uint8_t adns_serial_read(void); +void adns_serial_write(uint8_t data); +uint8_t adns_read_reg(uint8_t reg_addr); +void adns_write_reg(uint8_t reg_addr, uint8_t data); +report_adns_t adns_read_burst(void); +int8_t convert_twoscomp(uint8_t data); +void adns_set_cpi(uint8_t cpi); +bool adns_check_signature(void); diff --git a/drivers/sensors/adns9800.c b/drivers/sensors/adns9800.c new file mode 100644 index 0000000000..36213179f7 --- /dev/null +++ b/drivers/sensors/adns9800.c @@ -0,0 +1,219 @@ +/* Copyright 2020 Alexander Tulloh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "spi_master.h" +#include "quantum.h" +#include "adns9800_srom_A6.h" +#include "adns9800.h" + +// registers +#define REG_Product_ID 0x00 +#define REG_Revision_ID 0x01 +#define REG_Motion 0x02 +#define REG_Delta_X_L 0x03 +#define REG_Delta_X_H 0x04 +#define REG_Delta_Y_L 0x05 +#define REG_Delta_Y_H 0x06 +#define REG_SQUAL 0x07 +#define REG_Pixel_Sum 0x08 +#define REG_Maximum_Pixel 0x09 +#define REG_Minimum_Pixel 0x0a +#define REG_Shutter_Lower 0x0b +#define REG_Shutter_Upper 0x0c +#define REG_Frame_Period_Lower 0x0d +#define REG_Frame_Period_Upper 0x0e +#define REG_Configuration_I 0x0f +#define REG_Configuration_II 0x10 +#define REG_Frame_Capture 0x12 +#define REG_SROM_Enable 0x13 +#define REG_Run_Downshift 0x14 +#define REG_Rest1_Rate 0x15 +#define REG_Rest1_Downshift 0x16 +#define REG_Rest2_Rate 0x17 +#define REG_Rest2_Downshift 0x18 +#define REG_Rest3_Rate 0x19 +#define REG_Frame_Period_Max_Bound_Lower 0x1a +#define REG_Frame_Period_Max_Bound_Upper 0x1b +#define REG_Frame_Period_Min_Bound_Lower 0x1c +#define REG_Frame_Period_Min_Bound_Upper 0x1d +#define REG_Shutter_Max_Bound_Lower 0x1e +#define REG_Shutter_Max_Bound_Upper 0x1f +#define REG_LASER_CTRL0 0x20 +#define REG_Observation 0x24 +#define REG_Data_Out_Lower 0x25 +#define REG_Data_Out_Upper 0x26 +#define REG_SROM_ID 0x2a +#define REG_Lift_Detection_Thr 0x2e +#define REG_Configuration_V 0x2f +#define REG_Configuration_IV 0x39 +#define REG_Power_Up_Reset 0x3a +#define REG_Shutdown 0x3b +#define REG_Inverse_Product_ID 0x3f +#define REG_Motion_Burst 0x50 +#define REG_SROM_Load_Burst 0x62 +#define REG_Pixel_Burst 0x64 + +#define ADNS_CLOCK_SPEED 2000000 +#define MIN_CPI 200 +#define MAX_CPI 8200 +#define CPI_STEP 200 +#define CLAMP_CPI(value) value < MIN_CPI ? MIN_CPI : value > MAX_CPI ? MAX_CPI : value +#define SPI_MODE 3 +#define SPI_DIVISOR (F_CPU / ADNS_CLOCK_SPEED) +#define US_BETWEEN_WRITES 120 +#define US_BETWEEN_READS 20 +#define US_BEFORE_MOTION 100 +#define MSB1 0x80 + +extern const uint16_t adns_firmware_length; +extern const uint8_t adns_firmware_data[]; + +void adns_spi_start(void){ + spi_start(SPI_SS_PIN, false, SPI_MODE, SPI_DIVISOR); +} + +void adns_write(uint8_t reg_addr, uint8_t data){ + + adns_spi_start(); + spi_write(reg_addr | MSB1); + spi_write(data); + spi_stop(); + wait_us(US_BETWEEN_WRITES); +} + +uint8_t adns_read(uint8_t reg_addr){ + + adns_spi_start(); + spi_write(reg_addr & 0x7f ); + uint8_t data = spi_read(); + spi_stop(); + wait_us(US_BETWEEN_READS); + + return data; +} + +void adns_init() { + + setPinOutput(SPI_SS_PIN); + + spi_init(); + + // reboot + adns_write(REG_Power_Up_Reset, 0x5a); + wait_ms(50); + + // read registers and discard + adns_read(REG_Motion); + adns_read(REG_Delta_X_L); + adns_read(REG_Delta_X_H); + adns_read(REG_Delta_Y_L); + adns_read(REG_Delta_Y_H); + + // upload firmware + + // 3k firmware mode + adns_write(REG_Configuration_IV, 0x02); + + // enable initialisation + adns_write(REG_SROM_Enable, 0x1d); + + // wait a frame + wait_ms(10); + + // start SROM download + adns_write(REG_SROM_Enable, 0x18); + + // write the SROM file + + adns_spi_start(); + + spi_write(REG_SROM_Load_Burst | 0x80); + wait_us(15); + + // send all bytes of the firmware + unsigned char c; + for(int i = 0; i < adns_firmware_length; i++){ + c = (unsigned char)pgm_read_byte(adns_firmware_data + i); + spi_write(c); + wait_us(15); + } + + spi_stop(); + + wait_ms(10); + + // enable laser + uint8_t laser_ctrl0 = adns_read(REG_LASER_CTRL0); + adns_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0); +} + +config_adns_t adns_get_config(void) { + uint8_t config_1 = adns_read(REG_Configuration_I); + return (config_adns_t){ (config_1 & 0xFF) * CPI_STEP }; +} + +void adns_set_config(config_adns_t config) { + uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF; + adns_write(REG_Configuration_I, config_1); +} + +static int16_t convertDeltaToInt(uint8_t high, uint8_t low){ + + // join bytes into twos compliment + uint16_t twos_comp = (high << 8) | low; + + // convert twos comp to int + if (twos_comp & 0x8000) + return -1 * (~twos_comp + 1); + + return twos_comp; +} + +report_adns_t adns_get_report(void) { + + report_adns_t report = {0, 0}; + + adns_spi_start(); + + // start burst mode + spi_write(REG_Motion_Burst & 0x7f); + + wait_us(US_BEFORE_MOTION); + + uint8_t motion = spi_read(); + + if(motion & 0x80) { + + // clear observation register + spi_read(); + + // delta registers + uint8_t delta_x_l = spi_read(); + uint8_t delta_x_h = spi_read(); + uint8_t delta_y_l = spi_read(); + uint8_t delta_y_h = spi_read(); + + report.x = convertDeltaToInt(delta_x_h, delta_x_l); + report.y = convertDeltaToInt(delta_y_h, delta_y_l); + } + + // clear residual motion + spi_write(REG_Motion & 0x7f); + + spi_stop(); + + return report; +} diff --git a/drivers/sensors/adns9800.h b/drivers/sensors/adns9800.h new file mode 100644 index 0000000000..2f50b8f1be --- /dev/null +++ b/drivers/sensors/adns9800.h @@ -0,0 +1,35 @@ +/* Copyright 2020 Alexander Tulloh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> + +typedef struct { + /* 200 - 8200 CPI supported */ + uint16_t cpi; +} config_adns_t; + +typedef struct { + int16_t x; + int16_t y; +} report_adns_t; + +void adns_init(void); +config_adns_t adns_get_config(void); +void adns_set_config(config_adns_t); +/* Reads and clears the current delta values on the ADNS sensor */ +report_adns_t adns_get_report(void); diff --git a/drivers/sensors/adns9800_srom_A6.h b/drivers/sensors/adns9800_srom_A6.h new file mode 100644 index 0000000000..f5b3abeb62 --- /dev/null +++ b/drivers/sensors/adns9800_srom_A6.h @@ -0,0 +1,3078 @@ +#pragma once + +#include "progmem.h" + +const uint16_t adns_firmware_length = 3070; + +const uint8_t adns_firmware_data[] PROGMEM = { +0x03, +0xa6, +0x68, +0x1e, +0x7d, +0x10, +0x7e, +0x7e, +0x5f, +0x1c, +0xb8, +0xf2, +0x47, +0x0c, +0x7b, +0x74, +0x4b, +0x14, +0x8b, +0x75, +0x66, +0x51, +0x0b, +0x8c, +0x76, +0x74, +0x4b, +0x14, +0xaa, +0xd6, +0x0f, +0x9c, +0xba, +0xf6, +0x6e, +0x3f, +0xdd, +0x38, +0xd5, +0x02, +0x80, +0x9b, +0x82, +0x6d, +0x58, +0x13, +0xa4, +0xab, +0xb5, +0xc9, +0x10, +0xa2, +0xc6, +0x0a, +0x7f, +0x5d, +0x19, +0x91, +0xa0, +0xa3, +0xce, +0xeb, +0x3e, +0xc9, +0xf1, +0x60, +0x42, +0xe7, +0x4c, +0xfb, +0x74, +0x6a, +0x56, +0x2e, +0xbf, +0xdd, +0x38, +0xd3, +0x05, +0x88, +0x92, +0xa6, +0xce, +0xff, +0x5d, +0x38, +0xd1, +0xcf, +0xef, +0x58, +0xcb, +0x65, +0x48, +0xf0, +0x35, +0x85, +0xa9, +0xb2, +0x8f, +0x5e, +0xf3, +0x80, +0x94, +0x97, +0x7e, +0x75, +0x97, +0x87, +0x73, +0x13, +0xb0, +0x8a, +0x69, +0xd4, +0x0a, +0xde, +0xc1, +0x79, +0x59, +0x36, +0xdb, +0x9d, +0xd6, +0xb8, +0x15, +0x6f, +0xce, +0x3c, +0x72, +0x32, +0x45, +0x88, +0xdf, +0x6c, +0xa5, +0x6d, +0xe8, +0x76, +0x96, +0x14, +0x74, +0x20, +0xdc, +0xf4, +0xfa, +0x37, +0x6a, +0x27, +0x32, +0xe3, +0x29, +0xbf, +0xc4, +0xc7, +0x06, +0x9d, +0x58, +0xe7, +0x87, +0x7c, +0x2e, +0x9f, +0x6e, +0x49, +0x07, +0x5d, +0x23, +0x64, +0x54, +0x83, +0x6e, +0xcb, +0xb7, +0x77, +0xf7, +0x2b, +0x6e, +0x0f, +0x2e, +0x66, +0x12, +0x60, +0x55, +0x65, +0xfc, +0x43, +0xb3, +0x58, +0x73, +0x5b, +0xe8, +0x67, +0x04, +0x43, +0x02, +0xde, +0xb3, +0x89, +0xa0, +0x6d, +0x3a, +0x27, +0x79, +0x64, +0x5b, +0x0c, +0x16, +0x9e, +0x66, +0xb1, +0x8b, +0x87, +0x0c, +0x5d, +0xf2, +0xb6, +0x3d, +0x71, +0xdf, +0x42, +0x03, +0x8a, +0x06, +0x8d, +0xef, +0x1d, +0xa8, +0x96, +0x5c, +0xed, +0x31, +0x61, +0x5c, +0xa1, +0x34, +0xf6, +0x8c, +0x08, +0x60, +0x33, +0x07, +0x00, +0x3e, +0x79, +0x95, +0x1b, +0x43, +0x7f, +0xfe, +0xb6, +0xa6, +0xd4, +0x9d, +0x76, +0x72, +0xbf, +0xad, +0xc0, +0x15, +0xe8, +0x37, +0x31, +0xa3, +0x72, +0x63, +0x52, +0x1d, +0x1c, +0x5d, +0x51, +0x1b, +0xe1, +0xa9, +0xed, +0x60, +0x32, +0x3e, +0xa9, +0x50, +0x28, +0x53, +0x06, +0x59, +0xe2, +0xfc, +0xe7, +0x02, +0x64, +0x39, +0x21, +0x56, +0x4a, +0xa5, +0x40, +0x80, +0x81, +0xd5, +0x5a, +0x60, +0x7b, +0x68, +0x84, +0xf1, +0xe0, +0xb1, +0xb6, +0x5b, +0xdf, +0xa8, +0x1d, +0x6d, +0x65, +0x20, +0xc0, +0xa2, +0xb9, +0xd9, +0xbb, +0x00, +0xa6, +0xdb, +0x8b, +0x01, +0x53, +0x91, +0xfe, +0xc4, +0x51, +0x85, +0xb0, +0x96, +0x7f, +0xfd, +0x51, +0xdd, +0x14, +0x03, +0x67, +0x2e, +0x75, +0x1c, +0x76, +0xd3, +0x6e, +0xdd, +0x99, +0x55, +0x76, +0xe5, +0xab, +0x23, +0xfc, +0x4a, +0xd5, +0xc6, +0xe8, +0x2e, +0xca, +0x8a, +0xb3, +0xf6, +0x8c, +0x6c, +0xb0, +0xe9, +0xf2, +0xe7, +0x9e, +0x69, +0x41, +0xed, +0xf1, +0x6d, +0xd2, +0x86, +0xd8, +0x7e, +0xcb, +0x5d, +0x47, +0x6c, +0x85, +0x6a, +0x23, +0xed, +0x20, +0x40, +0x93, +0xb4, +0x20, +0xc7, +0xa5, +0xc9, +0xaf, +0x03, +0x15, +0xac, +0x19, +0xe5, +0x2a, +0x36, +0xdf, +0x6d, +0xc5, +0x8c, +0x80, +0x07, +0xce, +0x92, +0x0c, +0xd8, +0x06, +0x62, +0x0f, +0xdd, +0x48, +0x46, +0x1a, +0x53, +0xc7, +0x8a, +0x8c, +0x5d, +0x5d, +0xb4, +0xa1, +0x02, +0xd3, +0xa9, +0xb8, +0xf3, +0x94, +0x8f, +0x3f, +0xe5, +0x54, +0xd4, +0x11, +0x65, +0xb2, +0x5e, +0x09, +0x0b, +0x81, +0xe3, +0x75, +0xa7, +0x89, +0x81, +0x39, +0x6c, +0x46, +0xf6, +0x06, +0x9f, +0x27, +0x3b, +0xb6, +0x2d, +0x5f, +0x1d, +0x4b, +0xd4, +0x7b, +0x1d, +0x61, +0x74, +0x89, +0xe4, +0xe3, +0xbd, +0x98, +0x1b, +0xc4, +0x51, +0x3b, +0xa4, +0xfa, +0xe0, +0x92, +0xf7, +0xbe, +0xf2, +0x4d, +0xbb, +0xff, +0xad, +0x4f, +0x6d, +0x68, +0xc2, +0x79, +0x40, +0xaa, +0x9b, +0x8f, +0x0c, +0x32, +0x4b, +0x5f, +0x3e, +0xab, +0x59, +0x98, +0xb3, +0xf5, +0x1d, +0xac, +0x5e, +0xbc, +0x78, +0xd3, +0x01, +0x6c, +0x64, +0x15, +0x2f, +0xd8, +0x71, +0xa6, +0x2d, +0x45, +0xe1, +0x22, +0x42, +0xe4, +0x4e, +0x04, +0x3c, +0x7d, +0xf4, +0x40, +0x21, +0xb4, +0x67, +0x05, +0xa8, +0xe2, +0xf3, +0x72, +0x87, +0x4c, +0x7d, +0xd9, +0x1b, +0x65, +0x97, +0xf3, +0xc2, +0xe3, +0xe4, +0xc8, +0xd2, +0xde, +0xf6, +0xef, +0xdc, +0xbb, +0x44, +0x08, +0x5e, +0xe2, +0x45, +0x27, +0x01, +0xb0, +0xf6, +0x43, +0xe7, +0x3a, +0xf6, +0xdc, +0x9d, +0xed, +0xf3, +0xc5, +0x0c, +0xb8, +0x9c, +0x98, +0x3a, +0xd8, +0x36, +0xee, +0x96, +0x72, +0x67, +0xe7, +0x81, +0x91, +0xd5, +0x05, +0x0a, +0xe0, +0x82, +0xd5, +0x8f, +0xe8, +0xf9, +0xb0, +0xc9, +0xcf, +0x93, +0xe7, +0x04, +0xc5, +0xbc, +0x2b, +0x43, +0x56, +0x7e, +0xe8, +0x67, +0x7c, +0xe5, +0xfb, +0x49, +0xad, +0x5e, +0x9f, +0x25, +0x13, +0xde, +0x6e, +0x6e, +0xe9, +0xf1, +0xec, +0x87, +0x0b, +0x59, +0x81, +0x76, +0x84, +0x76, +0xb3, +0x24, +0xaf, +0x30, +0xfd, +0x27, +0x8b, +0xab, +0xd8, +0x00, +0x8b, +0x9b, +0x0c, +0xd2, +0xb2, +0x4e, +0x5e, +0x9d, +0x1d, +0x96, +0x01, +0x00, +0x67, +0xc1, +0x5f, +0x02, +0x20, +0xfd, +0x45, +0x6a, +0x01, +0x60, +0x58, +0x45, +0xca, +0x47, +0x21, +0x90, +0x5a, +0xc4, +0x43, +0x26, +0x1a, +0xd7, +0xa5, +0x4a, +0xb2, +0x5d, +0x2b, +0x35, +0x49, +0xfb, +0xa5, +0x17, +0x92, +0x21, +0x1e, +0x93, +0x96, +0x67, +0xa2, +0x7e, +0x36, +0x7a, +0xde, +0x5f, +0xbe, +0x7a, +0x58, +0x9d, +0xf8, +0x78, +0xa3, +0xfa, +0xc8, +0xd5, +0x17, +0xf0, +0x21, +0x97, +0x8c, +0x80, +0xb5, +0x4b, +0x3b, +0xbd, +0xbb, +0x41, +0x21, +0xa8, +0x50, +0x67, +0xf7, +0xe7, +0x19, +0x80, +0x10, +0x8e, +0xce, +0x04, +0x18, +0x3f, +0x51, +0x6b, +0x77, +0xd8, +0x9e, +0x16, +0xaf, +0xec, +0xef, +0x48, +0x16, +0x4d, +0x9e, +0x85, +0x38, +0x18, +0x3e, +0xd4, +0x28, +0x87, +0x60, +0x2a, +0xf6, +0x7f, +0x09, +0x86, +0x6f, +0x9c, +0x3c, +0x3a, +0xff, +0xab, +0xd0, +0x61, +0xa2, +0x97, +0x0d, +0x71, +0x94, +0x7e, +0xfd, +0xb9, +0x80, +0x02, +0x89, +0x6a, +0xb3, +0x84, +0x6c, +0x2a, +0x77, +0x62, +0xbe, +0x0b, +0xf4, +0xaf, +0xac, +0x7b, +0x7c, +0x8e, +0xca, +0x01, +0xba, +0x71, +0x78, +0x94, +0xfd, +0xb5, +0x39, +0xa4, +0x4d, +0x2f, +0x78, +0xcf, +0xca, +0x92, +0x0c, +0x1a, +0x99, +0x48, +0x4c, +0x11, +0x96, +0xb5, +0x4e, +0x41, +0x28, +0xe4, +0xa6, +0xfe, +0x4b, +0x72, +0x91, +0xe7, +0xd4, +0xdd, +0x9f, +0x12, +0xe6, +0x29, +0x38, +0xce, +0x45, +0xae, +0x02, +0xb8, +0x24, +0xae, +0xbd, +0xe9, +0x66, +0x08, +0x62, +0xa2, +0x2c, +0x2b, +0x00, +0xe2, +0x23, +0xd9, +0xc4, +0x48, +0xe4, +0xd3, +0xac, +0xbb, +0x34, +0xc7, +0xf0, +0xe3, +0x4f, +0xb9, +0x30, +0xea, +0xa2, +0x12, +0xf1, +0x30, +0x2c, +0x36, +0xde, +0x48, +0xf2, +0xb0, +0x4c, +0x43, +0x3f, +0x2e, +0x58, +0xe4, +0x20, +0xe3, +0x58, |