├── .gitignore ├── CMakeLists.txt ├── LICENSE.TXT ├── README.md ├── cmake ├── FindSDL2.cmake └── FindSDL2_image.cmake ├── include └── pico │ ├── audio_i2s.h │ ├── audio_pwm.h │ ├── pico_host_sdl.h │ └── scanvideo.h ├── pico_extras_import.cmake ├── pico_sdk_import.cmake ├── post_init.cmake ├── sd_card.c ├── sdl_audio.c ├── sdl_timer.c └── sdl_video.c /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | cmake-* 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | # Pull in Raspberry Pi Pico SDK (must be before project) 4 | include(pico_sdk_import.cmake) 5 | if (PICO_SDK_VERSION_STRING VERSION_LESS "2.0.0") 6 | message(FATAL_ERROR "Require at least Raspberry Pi Pico SDK version 2.0.0") 7 | endif() 8 | 9 | include(pico_extras_import.cmake) 10 | 11 | project(pico_host_sdl) 12 | 13 | if (PICO_PLATFORM STREQUAL "host") 14 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 15 | add_library(pico_host_sdl INTERFACE) 16 | 17 | # need SDL2 18 | find_package(SDL2 REQUIRED) 19 | find_package(SDL2_image REQUIRED) 20 | 21 | # need math 22 | find_library(M_LIBRARY m REQUIRED) 23 | 24 | # optionally ALSA 25 | find_package(ALSA) 26 | 27 | target_include_directories(pico_host_sdl INTERFACE 28 | ${CMAKE_CURRENT_LIST_DIR} 29 | # have both forms of these because windows doesn't have SDL2 prefix 30 | ${SDL2_INCLUDE_DIR} 31 | ${SDL2_IMAGE_INCLUDE_DIRS} 32 | ${SDL2_INCLUDE_DIR}/SDL2 33 | ${SDL2_IMAGE_INCLUDE_DIRS}/SDL2 34 | ) 35 | 36 | target_link_libraries(pico_host_sdl INTERFACE ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES}) 37 | 38 | IF (ALSA_FOUND) 39 | message("ALSA found") 40 | target_link_libraries(pico_host_sdl INTERFACE ${ALSA_LIBRARY}) 41 | target_compile_definitions(pico_host_sdl INTERFACE -DNATIVE_AUDIO_ALSA) 42 | else() 43 | # todo we should use SDL by default, but because it is asynchronous callbacks\ 44 | # it should really be integrated under our mu driver at the DMA level, which 45 | # would be fine, except that currently we allow sending DMA transfers of differing lengths 46 | # which SDL doesn't. 47 | message("ALSA not found, using SDL") 48 | target_compile_definitions(pico_host_sdl INTERFACE -DNATIVE_AUDIO_SDL2) 49 | endif() 50 | 51 | if (CMAKE_C_COMPILER_ID STREQUAL "MSVC") 52 | target_compile_definitions(pico_host_sdl INTERFACE _USE_MATH_DEFINES) 53 | endif() 54 | 55 | add_library(pico_host_video INTERFACE) # todo currently contains other stuff 56 | add_library(pico_host_audio INTERFACE) 57 | add_library(pico_host_timer INTERFACE) 58 | 59 | target_sources(pico_host_video INTERFACE 60 | ${CMAKE_CURRENT_LIST_DIR}/sdl_video.c) 61 | 62 | target_sources(pico_host_audio INTERFACE 63 | ${CMAKE_CURRENT_LIST_DIR}/sdl_audio.c) 64 | 65 | target_sources(pico_host_timer INTERFACE 66 | ${CMAKE_CURRENT_LIST_DIR}/sdl_timer.c 67 | ) 68 | 69 | target_include_directories(pico_host_sdl INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 70 | 71 | target_link_libraries(pico_host_video INTERFACE 72 | pico_sync 73 | pico_scanvideo 74 | pico_multicore 75 | #pico_sd_card 76 | #pthread 77 | pico_host_sdl) 78 | 79 | target_link_libraries(pico_host_audio INTERFACE 80 | pico_audio 81 | pico_host_sdl 82 | ) 83 | 84 | target_link_libraries(pico_host_timer INTERFACE 85 | pico_time 86 | pico_time_adapter 87 | ) 88 | 89 | add_library(pico_sd_card INTERFACE) 90 | target_sources(pico_sd_card INTERFACE 91 | ${CMAKE_CURRENT_LIST_DIR}/sd_card.c) 92 | 93 | target_link_libraries(pico_sd_card INTERFACE 94 | pico_sd_card_headers) 95 | 96 | # todo for now everything depends on pico_host_video as that has startup etc. 97 | add_library(pico_scanvideo_dpi INTERFACE) 98 | target_link_libraries(pico_scanvideo_dpi INTERFACE pico_host_video pico_scanvideo) 99 | 100 | add_library(pico_audio_i2s INTERFACE) 101 | target_link_libraries(pico_audio_i2s INTERFACE pico_host_audio pico_host_video) 102 | 103 | add_library(pico_audio_pwm INTERFACE) 104 | target_link_libraries(pico_audio_pwm INTERFACE pico_host_audio pico_host_video) 105 | 106 | add_library(hardware_timer INTERFACE) 107 | 108 | target_sources(hardware_timer INTERFACE 109 | ${PICO_SDK_PATH}/src/host/hardware_timer/timer.c) 110 | target_include_directories(hardware_timer INTERFACE ${PICO_SDK_PATH}/src/host/hardware_timer/include) 111 | target_link_libraries(hardware_timer INTERFACE pico_host_timer pico_host_video) 112 | 113 | # we support alarms 114 | set(PICO_TIME_NO_ALARM_SUPPORT "0" CACHE INTERNAL "") 115 | endif() 116 | 117 | pico_is_top_level_project(PICO_HOST_SDL_TOP_LEVEL_PROJECT) 118 | 119 | # The real work gets done in post_init which is called at the end of pico_sdk_init 120 | list(APPEND PICO_SDK_POST_LIST_FILES ${CMAKE_CURRENT_LIST_DIR}/post_init.cmake) 121 | list(APPEND PICO_CONFIG_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/include/pico/pico_host_sdl.h) 122 | if (PICO_HOST_SDL_TOP_LEVEL_PROJECT) 123 | message("pico_HOST_SDL: initialize SDK since we're the top-level") 124 | # Initialize the SDK 125 | pico_sdk_init() 126 | else() 127 | pico_promote_common_scope_vars() 128 | endif() 129 | 130 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 4 | following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 7 | disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This repository adds additional functionality to `PICO_PLATOFRM=host` to allow testing of `pico_audio_` and `pico_scanvideo` related applications in the host environment. It is not intended to be a complete simulator for the RP2040!!!! 4 | 5 | It additionally provides support for `pico_multicore_` and support for `pico_time` timers. 6 | 7 | # Using 8 | To use, configure your _host_ mode CMake build with: 9 | 10 | `-DPICO_PLATFORM=host -DPICO_SDK_PRE_LIST_DIRS=/pico_host_sdl` 11 | 12 | You will get audio and video and two core support with semaphores/spinlocks etc via SDL. 13 | 14 | # Notes 15 | 16 | This has only been tested on macOS and Linux operating systems. It will _NOT_ work with the MSVC compiler, however it might work on Windows if you build with gcc or WSL2 17 | 18 | The setup/build is currently a little convoluted because `pico-extras` is needed by this repository even if your application isn't using it (i.e. just uses the bare `pico-sdk`) -------------------------------------------------------------------------------- /cmake/FindSDL2.cmake: -------------------------------------------------------------------------------- 1 | # FindSDL2.cmake 2 | # 3 | # Copyright (c) 2018, Alex Mayfield 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of the nor the 14 | # names of its contributors may be used to endorse or promote products 15 | # derived from this software without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 21 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | # 28 | # Currently works with the following generators: 29 | # - Unix Makefiles (Linux, MSYS2) 30 | # - Ninja (Linux, MSYS2) 31 | # - Visual Studio 32 | 33 | # Cache variable that allows you to point CMake at a directory containing 34 | # an extracted development library. 35 | set(SDL2_DIR "${SDL2_DIR}" CACHE PATH "Location of SDL2 library directory") 36 | 37 | # Use pkg-config to find library locations in *NIX environments. 38 | find_package(PkgConfig QUIET) 39 | if(PKG_CONFIG_FOUND) 40 | pkg_search_module(PC_SDL2 QUIET sdl2) 41 | endif() 42 | 43 | # Find the include directory. 44 | find_path(SDL2_INCLUDE_DIR "SDL_version.h" 45 | HINTS "${SDL2_DIR}/include" ${PC_SDL2_INCLUDE_DIRS}) 46 | 47 | # Find the version. Taken and modified from CMake's FindSDL.cmake. 48 | if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL_version.h") 49 | file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") 50 | file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") 51 | file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") 52 | string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") 53 | string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") 54 | string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") 55 | set(SDL2_VERSION "${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}") 56 | unset(SDL2_VERSION_MAJOR_LINE) 57 | unset(SDL2_VERSION_MINOR_LINE) 58 | unset(SDL2_VERSION_PATCH_LINE) 59 | unset(SDL2_VERSION_MAJOR) 60 | unset(SDL2_VERSION_MINOR) 61 | unset(SDL2_VERSION_PATCH) 62 | endif() 63 | 64 | # Find the SDL2 and SDL2main libraries 65 | if(CMAKE_SIZEOF_VOID_P STREQUAL 8) 66 | find_library(SDL2_LIBRARY "SDL2" 67 | HINTS "${SDL2_DIR}/lib/x64" ${PC_SDL2_LIBRARY_DIRS}) 68 | find_library(SDL2_MAIN_LIBRARY "SDL2main" 69 | HINTS "${SDL2_DIR}/lib/x64" ${PC_SDL2_LIBRARY_DIRS}) 70 | else() 71 | find_library(SDL2_LIBRARY "SDL2" 72 | HINTS "${SDL2_DIR}/lib/x86" ${PC_SDL2_LIBRARY_DIRS}) 73 | find_library(SDL2_MAIN_LIBRARY "SDL2main" 74 | HINTS "${SDL2_DIR}/lib/x86" ${PC_SDL2_LIBRARY_DIRS}) 75 | endif() 76 | set(SDL2_LIBRARIES "${SDL2_MAIN_LIBRARY}" "${SDL2_LIBRARY}") 77 | 78 | include(FindPackageHandleStandardArgs) 79 | find_package_handle_standard_args(SDL2 80 | FOUND_VAR SDL2_FOUND 81 | REQUIRED_VARS SDL2_INCLUDE_DIR SDL2_LIBRARIES 82 | VERSION_VAR SDL2_VERSION 83 | ) 84 | 85 | if(SDL2_FOUND) 86 | # SDL2 imported target. 87 | add_library(SDL2::SDL2 UNKNOWN IMPORTED) 88 | set_target_properties(SDL2::SDL2 PROPERTIES 89 | INTERFACE_COMPILE_OPTIONS "${PC_SDL2_CFLAGS_OTHER}" 90 | INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" 91 | IMPORTED_LOCATION "${SDL2_LIBRARY}") 92 | 93 | # SDL2main imported target. 94 | if(MINGW) 95 | # Gross hack to get mingw32 first in the linker order. 96 | add_library(SDL2::_SDL2main_detail UNKNOWN IMPORTED) 97 | set_target_properties(SDL2::_SDL2main_detail PROPERTIES 98 | IMPORTED_LOCATION "${SDL2_MAIN_LIBRARY}") 99 | 100 | # Ensure that SDL2main comes before SDL2 in the linker order. CMake 101 | # isn't smart enough to keep proper ordering for indirect dependencies 102 | # so we have to spell it out here. 103 | target_link_libraries(SDL2::_SDL2main_detail INTERFACE SDL2::SDL2) 104 | 105 | add_library(SDL2::SDL2main INTERFACE IMPORTED) 106 | set_target_properties(SDL2::SDL2main PROPERTIES 107 | IMPORTED_LIBNAME mingw32) 108 | target_link_libraries(SDL2::SDL2main INTERFACE SDL2::_SDL2main_detail) 109 | else() 110 | add_library(SDL2::SDL2main UNKNOWN IMPORTED) 111 | set_target_properties(SDL2::SDL2main PROPERTIES 112 | IMPORTED_LOCATION "${SDL2_MAIN_LIBRARY}") 113 | endif() 114 | endif() 115 | -------------------------------------------------------------------------------- /cmake/FindSDL2_image.cmake: -------------------------------------------------------------------------------- 1 | # Locate SDL_image library 2 | # 3 | # This module defines: 4 | # 5 | # :: 6 | # 7 | # SDL2_IMAGE_LIBRARIES, the name of the library to link against 8 | # SDL2_IMAGE_INCLUDE_DIRS, where to find the headers 9 | # SDL2_IMAGE_FOUND, if false, do not try to link against 10 | # SDL2_IMAGE_VERSION_STRING - human-readable string containing the version of SDL_image 11 | # 12 | # 13 | # 14 | # For backward compatibility the following variables are also set: 15 | # 16 | # :: 17 | # 18 | # SDLIMAGE_LIBRARY (same value as SDL2_IMAGE_LIBRARIES) 19 | # SDLIMAGE_INCLUDE_DIR (same value as SDL2_IMAGE_INCLUDE_DIRS) 20 | # SDLIMAGE_FOUND (same value as SDL2_IMAGE_FOUND) 21 | # 22 | # 23 | # 24 | # $SDLDIR is an environment variable that would correspond to the 25 | # ./configure --prefix=$SDLDIR used in building SDL. 26 | # 27 | # Created by Eric Wing. This was influenced by the FindSDL.cmake 28 | # module, but with modifications to recognize OS X frameworks and 29 | # additional Unix paths (FreeBSD, etc). 30 | 31 | #============================================================================= 32 | # Copyright 2005-2009 Kitware, Inc. 33 | # Copyright 2012 Benjamin Eikel 34 | # 35 | # Distributed under the OSI-approved BSD License (the "License"); 36 | # see accompanying file Copyright.txt for details. 37 | # 38 | # This software is distributed WITHOUT ANY WARRANTY; without even the 39 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 40 | # See the License for more information. 41 | #============================================================================= 42 | # (To distribute this file outside of CMake, substitute the full 43 | # License text for the above reference.) 44 | 45 | find_path(SDL2_IMAGE_INCLUDE_DIR SDL_image.h 46 | HINTS 47 | ENV SDL2IMAGEDIR 48 | ENV SDL2DIR 49 | PATH_SUFFIXES SDL2 50 | # path suffixes to search inside ENV{SDLDIR} 51 | include/SDL2 include 52 | PATHS ${SDL2_IMAGE_PATH} 53 | ) 54 | 55 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 56 | set(VC_LIB_PATH_SUFFIX lib/x64) 57 | else() 58 | set(VC_LIB_PATH_SUFFIX lib/x86) 59 | endif() 60 | 61 | find_library(SDL2_IMAGE_LIBRARY 62 | NAMES SDL2_image 63 | HINTS 64 | ENV SDL2IMAGEDIR 65 | ENV SDL2DIR 66 | PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} 67 | PATHS ${SDL2_IMAGE_PATH} 68 | ) 69 | 70 | if(SDL2_IMAGE_INCLUDE_DIR AND EXISTS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h") 71 | file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h" SDL2_IMAGE_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_IMAGE_MAJOR_VERSION[ \t]+[0-9]+$") 72 | file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h" SDL2_IMAGE_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_IMAGE_MINOR_VERSION[ \t]+[0-9]+$") 73 | file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL_image.h" SDL2_IMAGE_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_IMAGE_PATCHLEVEL[ \t]+[0-9]+$") 74 | string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MAJOR "${SDL2_IMAGE_VERSION_MAJOR_LINE}") 75 | string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MINOR "${SDL2_IMAGE_VERSION_MINOR_LINE}") 76 | string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_PATCH "${SDL2_IMAGE_VERSION_PATCH_LINE}") 77 | set(SDL2_IMAGE_VERSION_STRING ${SDL2_IMAGE_VERSION_MAJOR}.${SDL2_IMAGE_VERSION_MINOR}.${SDL2_IMAGE_VERSION_PATCH}) 78 | unset(SDL2_IMAGE_VERSION_MAJOR_LINE) 79 | unset(SDL2_IMAGE_VERSION_MINOR_LINE) 80 | unset(SDL2_IMAGE_VERSION_PATCH_LINE) 81 | unset(SDL2_IMAGE_VERSION_MAJOR) 82 | unset(SDL2_IMAGE_VERSION_MINOR) 83 | unset(SDL2_IMAGE_VERSION_PATCH) 84 | endif() 85 | 86 | set(SDL2_IMAGE_LIBRARIES ${SDL2_IMAGE_LIBRARY}) 87 | set(SDL2_IMAGE_INCLUDE_DIRS ${SDL2_IMAGE_INCLUDE_DIR}) 88 | 89 | include(FindPackageHandleStandardArgs) 90 | 91 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_image 92 | REQUIRED_VARS SDL2_IMAGE_LIBRARIES SDL2_IMAGE_INCLUDE_DIRS 93 | VERSION_VAR SDL2_IMAGE_VERSION_STRING) 94 | 95 | # for backward compatibility 96 | set(SDLIMAGE_LIBRARY ${SDL2_IMAGE_LIBRARIES}) 97 | set(SDLIMAGE_INCLUDE_DIR ${SDL2_IMAGE_INCLUDE_DIRS}) 98 | set(SDLIMAGE_FOUND ${SDL2_IMAGE_FOUND}) 99 | 100 | mark_as_advanced(SDL2_IMAGE_LIBRARY SDL2_IMAGE_INCLUDE_DIR) 101 | -------------------------------------------------------------------------------- /include/pico/audio_i2s.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_AUDIO_I2S_H 8 | #define _PICO_AUDIO_I2S_H 9 | 10 | #include "pico/audio.h" 11 | #include "pico/pico_host_sdl.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #ifndef PICO_AUDIO_I2S_DATA_PIN 18 | #define PICO_AUDIO_I2S_DATA_PIN 0 19 | #endif 20 | 21 | #ifndef PICO_AUDIO_I2S_CLOCK_PIN_BASE 22 | #define PICO_AUDIO_I2S_CLOCK_PIN_BASE 0 23 | #endif 24 | 25 | typedef struct audio_i2s_config { 26 | uint8_t data_pin; 27 | uint8_t clock_pin_base; 28 | uint8_t dma_channel; 29 | uint8_t pio_sm; 30 | } audio_i2s_config_t; 31 | 32 | // todo i2s used here, some of it is common 33 | const struct audio_format *audio_i2s_setup(const struct audio_format *intended_audio_format, 34 | const struct audio_i2s_config *config); 35 | 36 | // todo make a common version (or a macro) .. we don't want to pull in unnecessary code by default 37 | bool audio_i2s_connect(struct audio_buffer_pool *producer); 38 | bool audio_i2s_connect_s8(struct audio_buffer_pool *producer); 39 | bool audio_i2s_connect_extra(struct audio_buffer_pool *producer, bool buffer_on_give, uint buffer_count, uint samples_per_buffer, audio_connection_t *connection); 40 | 41 | void audio_i2s_set_enabled(bool enabled); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif //_AUDIO_I2S_H 48 | -------------------------------------------------------------------------------- /include/pico/audio_pwm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | #ifndef _PICO_AUDIO_PWM_H 7 | #define _PICO_AUDIO_PWM_H 8 | 9 | #include "pico/audio.h" 10 | #include "pico/pico_host_sdl.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #define PICO_AUDIO_PWM_L_PIN 0 17 | #define PICO_AUDIO_PWM_R_PIN 1 18 | 19 | // todo we need a place to register these or just allow them to overlap, or base them on a FOURCC - this is just made up 20 | #define AUDIO_BUFFER_FORMAT_PIO_PWM_FIRST 1000 21 | #define AUDIO_BUFFER_FORMAT_PIO_PWM_CMD1 (AUDIO_BUFFER_FORMAT_PIO_PWM_FIRST) 22 | #define AUDIO_BUFFER_FORMAT_PIO_PWM_CMD3 (AUDIO_BUFFER_FORMAT_PIO_PWM_FIRST+1) 23 | 24 | struct __packed audio_pwm_channel_config { 25 | struct pio_audio_channel_config core; 26 | uint8_t pattern; 27 | }; 28 | 29 | // can copy this to modify just the pin 30 | extern const struct audio_pwm_channel_config default_left_channel_config; 31 | extern const struct audio_pwm_channel_config default_right_channel_config; 32 | extern const struct audio_pwm_channel_config default_mono_channel_config; 33 | 34 | // max_latency_ms may be -1 (for don't care) 35 | extern const struct audio_format * 36 | audio_pwm_setup(const struct audio_format *intended_audio_format, int32_t max_latency_ms, 37 | const struct audio_pwm_channel_config *channel_config0, ...); 38 | 39 | // attempt a default mapping of producer buffers to pio pwm pcio_audio output 40 | // dedicate_core_1 to have core 1 set aside entirely to do work offloading as much stuff from the producer side as possible 41 | // todo also allow IRQ hander to do it i guess 42 | extern bool audio_pwm_default_connect(struct audio_buffer_pool *producer_pool, bool dedicate_core_1); 43 | 44 | extern void audio_pwm_set_enabled(bool enabled); 45 | 46 | extern bool audio_pwm_set_correction_mode(enum audio_correction_mode mode); 47 | 48 | extern enum audio_correction_mode audio_pwm_get_correction_mode(); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif //_PIO_AUDIO_PWM_H 55 | -------------------------------------------------------------------------------- /include/pico/pico_host_sdl.h: -------------------------------------------------------------------------------- 1 | #ifndef PICO_HOST_SDL_PICO_HOST_SDL_H 2 | #define PICO_HOST_SDL_PICO_HOST_SDL_H 3 | 4 | #ifndef main 5 | #define main __real_main 6 | #endif 7 | 8 | #endif //PICO_HOST_SDL_PICO_HOST_SDL_H 9 | -------------------------------------------------------------------------------- /include/pico/scanvideo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_SCANVIDEO_H_ 8 | #define _PICO_SCANVIDEO_H_ 9 | 10 | // note that defining to false will force non-inclusion also 11 | #if !defined(PICO_SCANVIDEO_DPI) 12 | #define PICO_SCANVIDEO_DPI 1 13 | 14 | #ifndef PARAM_ASSERTIONS_ENABLED_SCANVIDEO_DPI 15 | #define PARAM_ASSERTIONS_ENABLED_SCANVIDEO_DPI 0 16 | #endif 17 | 18 | #include "pico/scanvideo/scanvideo_base.h" 19 | #include "pico/pico_host_sdl.h" 20 | 21 | typedef void (*simulate_scanvideo_pio_fn)(const uint32_t *dma_data, uint32_t dma_data_size, 22 | uint16_t *pixel_buffer, int32_t max_pixels, int32_t expected_width, bool overlay); 23 | 24 | extern void scanvideo_set_simulate_scanvideo_pio_fn(const char *id, simulate_scanvideo_pio_fn fn); 25 | 26 | typedef void (*simulate_composable_cmd_fn)(const uint16_t **dma_data, uint16_t **pixels, int32_t max_pixels, bool overlay); 27 | 28 | extern void scanvideo_set_simulate_composable_cmd(uint cmd, simulate_composable_cmd_fn fn); 29 | 30 | 31 | // todo move these to a host specific header 32 | // todo until we have an abstraction 33 | // These are or'ed with SDL_SCANCODE_* constants in last_key_scancode. 34 | enum key_modifiers { 35 | WITH_SHIFT = 0x8000, 36 | WITH_CTRL = 0x4000, 37 | WITH_ALT = 0x2000, 38 | }; 39 | extern void (*platform_key_down)(int scancode, int keysym, int modifiers); 40 | extern void (*platform_key_up)(int scancode, int keysym, int modifiers); 41 | extern void (*platform_mouse_move)(int dx, int dy); 42 | extern void (*platform_mouse_button_down)(int button); 43 | extern void (*platform_mouse_button_up)(int button); 44 | extern void (*platform_quit)(); 45 | 46 | #define PICO_SCANVIDEO_ALPHA_PIN 5u 47 | #define PICO_SCANVIDEO_PIXEL_RSHIFT 0u 48 | #define PICO_SCANVIDEO_PIXEL_GSHIFT 6u 49 | #define PICO_SCANVIDEO_PIXEL_BSHIFT 11u 50 | 51 | #endif 52 | #endif 53 | -------------------------------------------------------------------------------- /pico_extras_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_extras_import.cmake 2 | 3 | # This can be dropped into an external project to help locate pico-extras 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) 7 | set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) 8 | message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) 12 | set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) 13 | message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) 17 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (NOT PICO_EXTRAS_PATH) 22 | if (PICO_EXTRAS_FETCH_FROM_GIT) 23 | include(FetchContent) 24 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 25 | if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) 26 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 27 | endif () 28 | FetchContent_Declare( 29 | PICO_EXTRAS 30 | GIT_REPOSITORY https://github.com/raspberrypi/pico-extras 31 | GIT_TAG master 32 | ) 33 | if (NOT PICO_EXTRAS) 34 | message("Downloading PICO EXTRAS") 35 | FetchContent_Populate(PICO_EXTRAS) 36 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_SOURCE_DIR}) 37 | endif () 38 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 39 | else () 40 | if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") 41 | set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) 42 | message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") 43 | else() 44 | message(FATAL_ERROR 45 | "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif() 48 | endif () 49 | endif () 50 | 51 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") 52 | set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") 53 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") 54 | 55 | get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 56 | if (NOT EXISTS ${PICO_EXTRAS_PATH}) 57 | message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") 58 | endif () 59 | 60 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) 61 | 62 | add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of Raspberry Pi Pico SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download Raspberry Pi Pico SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY git@asic-git.pitowers.org:projectmu/pico_sdk.git 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "Raspberry Pi Pico SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /post_init.cmake: -------------------------------------------------------------------------------- 1 | pico_add_platform_library(pico_host_timer) 2 | -------------------------------------------------------------------------------- /sd_card.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #if PICO_EXTRAS 8 | #include 9 | #include "pico/sd_card.h" 10 | #include "SDL_timer.h" 11 | 12 | static FILE *sd_file_in; 13 | #if 1 14 | static double seek_time_ms = 1; 15 | static double sector_ms = 1000 * (512 + 8 + 20)/12000000.0; 16 | #else 17 | static double seek_time_ms = 0; 18 | static double sector_ms = 0; 19 | #endif 20 | static uint32_t *sd_next_control_word; 21 | static uint sd_sector; 22 | static uint sd_read_sector_count; 23 | static uint32_t sd_sector_tick_base; 24 | static double sector_next_tick_ms; 25 | 26 | int sd_init(bool allow_four_data_pins) { 27 | if (!sd_file_in) 28 | { 29 | const char *filename = "sd.card"; 30 | sd_file_in = fopen(filename, "rb"); 31 | if (!sd_file_in) 32 | { 33 | printf("Can't open file '%s'\n", filename); 34 | } 35 | } 36 | return sd_file_in ? 0 : -1; 37 | } 38 | 39 | int sd_readblocks_scatter_async(uint32_t *control_words, uint32_t block, uint block_count) { 40 | sd_next_control_word = control_words; 41 | sd_sector = block; 42 | sd_read_sector_count = block_count; 43 | sd_sector_tick_base = SDL_GetTicks(); 44 | sector_next_tick_ms = seek_time_ms + sector_ms; 45 | return SD_OK; 46 | } 47 | 48 | #define MAX_BLOCK_COUNT 32 49 | static uint32_t crcs[MAX_BLOCK_COUNT * 2]; 50 | uint32_t ctrl_words[(MAX_BLOCK_COUNT + 1) * 4]; 51 | 52 | int sd_readblocks_async(uint32_t *buf, uint32_t block, uint block_count) { 53 | assert(block_count <= MAX_BLOCK_COUNT); 54 | 55 | uint32_t *p = ctrl_words; 56 | for(int i = 0; i < block_count; i++) 57 | { 58 | *p++ = host_safe_hw_ptr (buf + i * 128); 59 | *p++ = 128; 60 | // for now we read the CRCs also 61 | *p++ = host_safe_hw_ptr (crcs + i * 2); 62 | *p++ = 2; 63 | } 64 | *p++ = 0; 65 | *p++ = 0; 66 | return sd_readblocks_scatter_async(ctrl_words, block, block_count); 67 | } 68 | 69 | bool sd_scatter_read_complete(int *status) { 70 | while (sd_read_sector_count && sector_next_tick_ms < (int32_t)((SDL_GetTicks() - sd_sector_tick_base))) { 71 | uint8_t buf[512]; 72 | fseek(sd_file_in, sd_sector * 512, SEEK_SET); 73 | uint read = fread(buf, 1, 512, sd_file_in); 74 | assert(read == 512); 75 | uint pos = 0; 76 | while (pos < 512) { 77 | void *ptr = decode_host_safe_hw_ptr(sd_next_control_word[0]); 78 | assert(ptr); 79 | assert(sd_next_control_word[1]); 80 | assert(sd_next_control_word[1] <= 128); 81 | memcpy(ptr, buf + pos, sd_next_control_word[1] * 4); 82 | pos += sd_next_control_word[1] * 4; 83 | sd_next_control_word += 2; 84 | } 85 | void *ptr = decode_host_safe_hw_ptr(sd_next_control_word[0]); 86 | assert(ptr); 87 | assert(sd_next_control_word[1] == 2); 88 | sd_next_control_word += 2; // skip CRC 89 | sd_sector++; 90 | sd_read_sector_count--; 91 | sector_next_tick_ms += sector_ms; 92 | } 93 | if (!sd_read_sector_count) { 94 | assert(!sd_next_control_word || !*sd_next_control_word); 95 | } 96 | return !sd_read_sector_count; 97 | } 98 | #endif -------------------------------------------------------------------------------- /sdl_audio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "pico.h" 8 | #include "pico/audio.h" 9 | #include "pico/multicore.h" 10 | #include "pico/mutex.h" 11 | #include "pico/sem.h" 12 | #include "hardware/sync.h" 13 | #include "pico/audio_i2s.h" 14 | #include "pico/audio_pwm.h" 15 | 16 | #ifdef NATIVE_AUDIO_ALSA 17 | # include 18 | #endif 19 | #ifdef NATIVE_AUDIO_SDL2 20 | #include "SDL_image.h" 21 | #endif 22 | 23 | const struct audio_pwm_channel_config default_left_channel_config; 24 | const struct audio_pwm_channel_config default_right_channel_config; 25 | const struct audio_pwm_channel_config default_mono_channel_config; 26 | 27 | const struct audio_format *native_audio_setup(const struct audio_format *intended_audio_format); 28 | void native_audio_enable(bool enable); 29 | bool native_audio_connect(struct audio_buffer_pool *producer_pool); 30 | 31 | static struct audio_format consumer_format; 32 | static struct audio_buffer_format consumer_buffer_format = { 33 | .format = &consumer_format 34 | }; 35 | 36 | #ifdef NATIVE_AUDIO_ALSA 37 | static int alsa_first_time = 1; 38 | static snd_pcm_t *pcm = NULL; 39 | static char pcmname[64]; 40 | 41 | static void close_alsa_output(void) { 42 | if (!pcm) return; 43 | // printf("Shutting down sound output\n"); 44 | snd_pcm_drain(pcm); 45 | snd_pcm_close(pcm); 46 | pcm = NULL; 47 | } 48 | 49 | void native_audio_enable(bool enable) { 50 | snd_pcm_pause(pcm, !enable); 51 | } 52 | 53 | const struct audio_format *native_audio_setup(const struct audio_format *intended_audio_format) 54 | { 55 | snd_pcm_hw_params_t *hw; 56 | snd_pcm_sw_params_t *sw; 57 | int err; 58 | unsigned int alsa_buffer_time; 59 | unsigned int alsa_period_time; 60 | unsigned int r; 61 | 62 | if (!pcmname[0]) { 63 | strcpy(pcmname, "default"); 64 | } 65 | 66 | if ((err = snd_pcm_open(&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { 67 | printf("Error: audio open error: %s\n", snd_strerror(err)); 68 | return NULL; 69 | } 70 | 71 | snd_pcm_hw_params_alloca(&hw); 72 | 73 | if ((err = snd_pcm_hw_params_any(pcm, hw)) < 0) { 74 | printf("ERROR: No configuration available for playback: %s\n", 75 | snd_strerror(err)); 76 | goto fail; 77 | } 78 | 79 | if ((err = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { 80 | printf("Cannot set access mode: %s.\n", snd_strerror(err)); 81 | goto fail; 82 | } 83 | 84 | // todo fix 85 | assert(intended_audio_format->format == AUDIO_BUFFER_FORMAT_PCM_S16 || intended_audio_format->format == AUDIO_BUFFER_FORMAT_PCM_S8); 86 | if (snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16) < 0) { 87 | printf("ALSA does not support 16bit signed audio for your soundcard\n"); 88 | goto fail; 89 | } 90 | 91 | if (snd_pcm_hw_params_set_channels(pcm, hw, intended_audio_format->channel_count) < 0) { 92 | printf("ALSA does not support %d channels for your soundcard\n", intended_audio_format->channel_count); 93 | goto fail; 94 | } 95 | 96 | unsigned int rate = intended_audio_format->sample_freq; 97 | 98 | r = rate; 99 | if (snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, 0) < 0) { 100 | printf("ALSA does not support %uHz for your soundcard\n", rate); 101 | goto fail; 102 | } 103 | if (r != rate) { 104 | printf("ALSA: sample rate set to %uHz instead of %u\n", rate, r); 105 | } 106 | 107 | alsa_buffer_time = 500000; 108 | alsa_period_time = 50000; 109 | 110 | if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, hw, &alsa_buffer_time, 0)) < 0) { 111 | printf("Set buffer time failed: %s.\n", snd_strerror(err)); 112 | goto fail; 113 | } 114 | 115 | if ((err = snd_pcm_hw_params_set_period_time_near(pcm, hw, &alsa_period_time, 0)) < 0) { 116 | printf("Set period time failed: %s.\n", snd_strerror(err)); 117 | goto fail; 118 | } 119 | 120 | if (snd_pcm_hw_params(pcm, hw) < 0) { 121 | printf("Unable to install hw params\n"); 122 | goto fail; 123 | } 124 | 125 | snd_pcm_sw_params_alloca(&sw); 126 | snd_pcm_sw_params_current(pcm, sw); 127 | if (snd_pcm_sw_params(pcm, sw) < 0) { 128 | printf("Unable to install sw params\n"); 129 | goto fail; 130 | } 131 | 132 | consumer_format = *intended_audio_format; 133 | return intended_audio_format; 134 | 135 | fail: close_alsa_output(); 136 | return NULL; 137 | } 138 | 139 | void alsa_producer_pool_blocking_give_s16(struct audio_connection *connection, struct audio_buffer *buffer) 140 | { 141 | // todo this is wrong for setting a single channel of stereo via non interleave 142 | uint8_t *output_data = buffer->buffer->bytes; 143 | int sample_count = buffer->sample_count; 144 | int err; 145 | while (sample_count > 0) { 146 | if ((err = snd_pcm_writei(pcm, output_data, sample_count)) < 0) { 147 | if (snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) { 148 | if ((err = snd_pcm_prepare(pcm)) < 0) 149 | printf("\nsnd_pcm_prepare() failed.\n"); 150 | alsa_first_time = 1; 151 | continue; 152 | } 153 | panic("failed to send sound data"); 154 | } 155 | sample_count -= err; 156 | output_data += snd_pcm_frames_to_bytes(pcm, err); 157 | if (alsa_first_time) { 158 | alsa_first_time = 0; 159 | snd_pcm_start(pcm); 160 | } 161 | } 162 | queue_free_audio_buffer(connection->producer_pool, buffer); 163 | } 164 | 165 | void alsa_producer_pool_blocking_give_upsample_s16(struct audio_connection *connection, struct audio_buffer *buffer) 166 | { 167 | static int16_t sample_buffer[16384]; 168 | // todo this is wrong for setting a single channel of stereo via non interleave 169 | uint8_t *output_data = buffer->buffer->bytes; 170 | assert(connection->producer_pool->format->sample_freq < connection->consumer_pool->format->sample_freq); 171 | int sample_count = (buffer->sample_count * connection->consumer_pool->format->sample_freq)/connection->producer_pool->format->sample_freq; 172 | int channel_sample_count = sample_count * buffer->format->format->channel_count; 173 | assert(channel_sample_count < count_of(sample_buffer)); 174 | uint j = 0; 175 | uint frac = (65536 * connection->producer_pool->format->sample_freq) / connection->consumer_pool->format->sample_freq; 176 | for(uint i=0;ibuffer->bytes)[j>>16]; 178 | j += frac; 179 | } 180 | int err; 181 | while (sample_count > 0) { 182 | if ((err = snd_pcm_writei(pcm, sample_buffer, sample_count)) < 0) { 183 | if (snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) { 184 | if ((err = snd_pcm_prepare(pcm)) < 0) 185 | printf("\nsnd_pcm_prepare() failed.\n"); 186 | alsa_first_time = 1; 187 | continue; 188 | } 189 | panic("failed to send sound data"); 190 | } 191 | sample_count -= err; 192 | output_data += snd_pcm_frames_to_bytes(pcm, err); 193 | if (alsa_first_time) { 194 | alsa_first_time = 0; 195 | snd_pcm_start(pcm); 196 | } 197 | } 198 | queue_free_audio_buffer(connection->producer_pool, buffer); 199 | } 200 | 201 | void alsa_producer_pool_blocking_give_s8(struct audio_connection *connection, struct audio_buffer *buffer) 202 | { 203 | static int16_t sample_buffer[16384]; 204 | // todo this is wrong for setting a single channel of stereo via non interleave 205 | uint8_t *output_data = buffer->buffer->bytes; 206 | int sample_count = buffer->sample_count; 207 | int channel_sample_count = sample_count * buffer->format->format->channel_count; 208 | assert(channel_sample_count < count_of(sample_buffer)); 209 | for(uint i=0;ibuffer->bytes[i] << 8u; 211 | } 212 | int err; 213 | while (sample_count > 0) { 214 | if ((err = snd_pcm_writei(pcm, sample_buffer, sample_count)) < 0) { 215 | if (snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) { 216 | if ((err = snd_pcm_prepare(pcm)) < 0) 217 | printf("\nsnd_pcm_prepare() failed.\n"); 218 | alsa_first_time = 1; 219 | continue; 220 | } 221 | panic("failed to send sound data"); 222 | } 223 | sample_count -= err; 224 | output_data += snd_pcm_frames_to_bytes(pcm, err); 225 | if (alsa_first_time) { 226 | alsa_first_time = 0; 227 | snd_pcm_start(pcm); 228 | } 229 | } 230 | queue_free_audio_buffer(connection->producer_pool, buffer); 231 | } 232 | 233 | void alsa_producer_pool_blocking_give_upsample_s8(struct audio_connection *connection, struct audio_buffer *buffer) 234 | { 235 | static int16_t sample_buffer[16384]; 236 | // todo this is wrong for setting a single channel of stereo via non interleave 237 | uint8_t *output_data = buffer->buffer->bytes; 238 | assert(connection->producer_pool->format->sample_freq < connection->consumer_pool->format->sample_freq); 239 | int sample_count = (buffer->sample_count * connection->consumer_pool->format->sample_freq)/connection->producer_pool->format->sample_freq; 240 | int channel_sample_count = sample_count * buffer->format->format->channel_count; 241 | assert(channel_sample_count < count_of(sample_buffer)); 242 | uint j = 0; 243 | uint frac = (65536 * connection->producer_pool->format->sample_freq) / connection->consumer_pool->format->sample_freq; 244 | for(uint i=0;ibuffer->bytes[j>>16] << 8u; 246 | j += frac; 247 | } 248 | int err; 249 | while (sample_count > 0) { 250 | if ((err = snd_pcm_writei(pcm, sample_buffer, sample_count)) < 0) { 251 | if (snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) { 252 | if ((err = snd_pcm_prepare(pcm)) < 0) 253 | printf("\nsnd_pcm_prepare() failed.\n"); 254 | alsa_first_time = 1; 255 | continue; 256 | } 257 | panic("failed to send sound data"); 258 | } 259 | sample_count -= err; 260 | output_data += snd_pcm_frames_to_bytes(pcm, err); 261 | if (alsa_first_time) { 262 | alsa_first_time = 0; 263 | snd_pcm_start(pcm); 264 | } 265 | } 266 | queue_free_audio_buffer(connection->producer_pool, buffer); 267 | } 268 | 269 | 270 | 271 | static struct audio_connection alsa_blocking_give_audio_connection_s16 = { 272 | .consumer_pool_take = consumer_pool_take_buffer_default, 273 | .consumer_pool_give = consumer_pool_give_buffer_default, 274 | .producer_pool_take = producer_pool_take_buffer_default, 275 | .producer_pool_give = alsa_producer_pool_blocking_give_s16 276 | }; 277 | 278 | static struct audio_connection alsa_blocking_give_upsample_audio_connection_s16 = { 279 | .consumer_pool_take = consumer_pool_take_buffer_default, 280 | .consumer_pool_give = consumer_pool_give_buffer_default, 281 | .producer_pool_take = producer_pool_take_buffer_default, 282 | .producer_pool_give = alsa_producer_pool_blocking_give_upsample_s16 283 | }; 284 | 285 | 286 | static struct audio_connection alsa_blocking_give_audio_connection_s8 = { 287 | .consumer_pool_take = consumer_pool_take_buffer_default, 288 | .consumer_pool_give = consumer_pool_give_buffer_default, 289 | .producer_pool_take = producer_pool_take_buffer_default, 290 | .producer_pool_give = alsa_producer_pool_blocking_give_s8 291 | }; 292 | 293 | static struct audio_connection alsa_blocking_give_upsample_audio_connection_s8 = { 294 | .consumer_pool_take = consumer_pool_take_buffer_default, 295 | .consumer_pool_give = consumer_pool_give_buffer_default, 296 | .producer_pool_take = producer_pool_take_buffer_default, 297 | .producer_pool_give = alsa_producer_pool_blocking_give_upsample_s8 298 | }; 299 | 300 | 301 | bool native_audio_connect(struct audio_buffer_pool *producer) 302 | { 303 | printf("Connecting ALSA audio\n"); 304 | 305 | // todo we need to pick a connection based on the frequency - e.g. 22050 can be more simply upsampled to 44100 306 | 307 | 308 | consumer_buffer_format.sample_stride = consumer_format.channel_count * 2; 309 | 310 | // todo don't need a consumer pool, but have to specify one in current api 311 | struct audio_buffer_pool *consumer = audio_new_consumer_pool(&consumer_buffer_format, 0, 0); 312 | 313 | if (producer->format->format == AUDIO_BUFFER_FORMAT_PCM_S16) { 314 | if (consumer->format->sample_freq == producer->format->sample_freq) { 315 | audio_complete_connection(&alsa_blocking_give_audio_connection_s16, producer, consumer); 316 | } else { 317 | audio_complete_connection(&alsa_blocking_give_upsample_audio_connection_s16, producer, consumer); 318 | } 319 | } else if (producer->format->format == AUDIO_BUFFER_FORMAT_PCM_S8) { 320 | if (consumer->format->sample_freq == producer->format->sample_freq) { 321 | audio_complete_connection(&alsa_blocking_give_audio_connection_s8, producer, consumer); 322 | } else { 323 | audio_complete_connection(&alsa_blocking_give_upsample_audio_connection_s8, producer, consumer); 324 | } 325 | } else { 326 | return false; 327 | } 328 | return true; 329 | } 330 | 331 | #endif 332 | #ifdef NATIVE_AUDIO_SDL2 333 | SDL_AudioDeviceID sdl_audio_device_id; 334 | SDL_AudioSpec* sdl_audio_spec = NULL; 335 | int bytes_per_frame; 336 | int max_latency_bytes; 337 | 338 | const struct audio_format *native_audio_setup(const struct audio_format *intended_audio_format) 339 | { 340 | SDL_AudioSpec *desired; 341 | desired = (SDL_AudioSpec *)calloc(1,sizeof(SDL_AudioSpec)); 342 | desired->freq = intended_audio_format->sample_freq; 343 | assert(intended_audio_format->format == AUDIO_BUFFER_FORMAT_PCM_S16 || intended_audio_format->format == AUDIO_BUFFER_FORMAT_PCM_S8); 344 | desired->format = AUDIO_S16SYS; 345 | desired->channels = intended_audio_format->channel_count; 346 | desired->samples = 1024; // todo random 347 | desired->callback = NULL;//audio_callback; 348 | desired->userdata = NULL; 349 | 350 | if (SDL_OpenAudio(desired, NULL) != 0) { 351 | return NULL; 352 | } 353 | bytes_per_frame = desired->channels * 2; 354 | uint max_latency_ms = 100; 355 | max_latency_bytes = (bytes_per_frame * desired->freq * max_latency_ms) / 1000; 356 | printf("Max latency bytes %d\n", max_latency_bytes); 357 | sdl_audio_device_id = 1; 358 | sdl_audio_spec = desired; 359 | consumer_format = *intended_audio_format; 360 | 361 | return intended_audio_format; 362 | } 363 | 364 | void sdl_producer_pool_blocking_give(struct audio_connection *connection, struct audio_buffer *buffer) 365 | { 366 | // todo yuck hack!!!! use the SDL callback now we have buffer pools 367 | while (SDL_GetQueuedAudioSize(sdl_audio_device_id) > max_latency_bytes) { 368 | tight_loop_contents(); 369 | } 370 | assert(buffer->format->sample_stride == bytes_per_frame); 371 | int __unused rc = SDL_QueueAudio(sdl_audio_device_id, buffer->buffer->bytes, buffer->sample_count * bytes_per_frame); 372 | assert(!rc); 373 | queue_free_audio_buffer(connection->producer_pool, buffer); 374 | } 375 | 376 | void sdl_producer_pool_blocking_give_s8(struct audio_connection *connection, struct audio_buffer *buffer) 377 | { 378 | // todo yuck hack!!!! use the SDL callback now we have buffer pools 379 | while (SDL_GetQueuedAudioSize(sdl_audio_device_id) > max_latency_bytes) { 380 | tight_loop_contents(); 381 | } 382 | static int16_t sample_buffer[16384]; 383 | // todo this is wrong for setting a single channel of stereo via non interleave 384 | int sample_count = buffer->sample_count; 385 | int channel_sample_count = sample_count * buffer->format->format->channel_count; 386 | assert(channel_sample_count < count_of(sample_buffer)); 387 | for(uint i=0;ibuffer->bytes[i] << 8u; 389 | } 390 | assert(buffer->format->sample_stride * 2 == bytes_per_frame); 391 | int __unused rc = SDL_QueueAudio(sdl_audio_device_id, sample_buffer, buffer->sample_count * bytes_per_frame); 392 | assert(!rc); 393 | queue_free_audio_buffer(connection->producer_pool, buffer); 394 | } 395 | 396 | 397 | static struct audio_connection sdl_blocking_give_audio_connection_s16 = { 398 | .consumer_pool_take = consumer_pool_take_buffer_default, 399 | .consumer_pool_give = consumer_pool_give_buffer_default, 400 | .producer_pool_take = producer_pool_take_buffer_default, 401 | .producer_pool_give = sdl_producer_pool_blocking_give 402 | }; 403 | 404 | static struct audio_connection sdl_blocking_give_audio_connection_s8 = { 405 | .consumer_pool_take = consumer_pool_take_buffer_default, 406 | .consumer_pool_give = consumer_pool_give_buffer_default, 407 | .producer_pool_take = producer_pool_take_buffer_default, 408 | .producer_pool_give = sdl_producer_pool_blocking_give_s8 409 | }; 410 | 411 | 412 | bool native_audio_connect(struct audio_buffer_pool *producer) 413 | { 414 | printf("Connecting SDL2 audio\n"); 415 | 416 | // todo we need to pick a connection based on the frequency - e.g. 22050 can be more simply upsampled to 44100 417 | consumer_buffer_format.sample_stride = consumer_format.channel_count * 2; 418 | 419 | // todo don't need a consumer pool, but have to specify one in current api 420 | struct audio_buffer_pool *consumer = audio_new_consumer_pool(&consumer_buffer_format, 0, 0); 421 | 422 | if (producer->format->format == AUDIO_BUFFER_FORMAT_PCM_S16) { 423 | audio_complete_connection(&sdl_blocking_give_audio_connection_s16, producer, consumer); 424 | } else if (producer->format->format == AUDIO_BUFFER_FORMAT_PCM_S8) { 425 | audio_complete_connection(&sdl_blocking_give_audio_connection_s8, producer, consumer); 426 | } else { 427 | return false; 428 | } 429 | return true; 430 | } 431 | 432 | // max_latency_ms may be -1 433 | // buffer_sample_count may be -1 and is per channel (it is the expected number of samples in buffered passed to audio_queue_samples (if known) 434 | extern void native_audio_enable(bool enable) { 435 | SDL_PauseAudio(!enable); 436 | } 437 | 438 | //extern uint32_t audio_get_time_ms(int channel); 439 | //extern uint32_t audio_set_time_ms(int channel, uint32_t time_ms); 440 | //uint32_t audio_buffered_ms(uint channel) { 441 | // assert(false); 442 | // return 0; 443 | //} 444 | // 445 | 446 | uint32_t audio_get_optimal_buffer_sample_count() { 447 | return sdl_audio_spec->size / bytes_per_frame; 448 | } 449 | #endif 450 | 451 | extern const struct audio_format *audio_pwm_setup(const struct audio_format *intended_audio_format, int32_t max_latency_ms, 452 | const struct audio_pwm_channel_config *channel_config0, ...) 453 | { 454 | return native_audio_setup(intended_audio_format); 455 | } 456 | 457 | bool audio_pwm_default_connect(struct audio_buffer_pool *producer_pool, bool dedicate_core_1) { 458 | return native_audio_connect(producer_pool); 459 | } 460 | 461 | void audio_pwm_set_enabled(bool enabled) { 462 | return native_audio_enable(enabled); 463 | } 464 | 465 | const struct audio_format *audio_i2s_setup(const struct audio_format *intended_audio_format, 466 | const struct audio_i2s_config *config) { 467 | return native_audio_setup(intended_audio_format); 468 | } 469 | 470 | bool audio_i2s_connect(struct audio_buffer_pool *producer_pool) { 471 | return native_audio_connect(producer_pool); 472 | } 473 | 474 | bool audio_i2s_connect_s8(struct audio_buffer_pool *producer_pool) { 475 | return native_audio_connect(producer_pool); 476 | } 477 | 478 | bool audio_i2s_connect_extra(audio_buffer_pool_t *producer_pool, __unused bool buffer_on_give, __unused uint buffer_count, 479 | __unused uint samples_per_buffer, __unused audio_connection_t *connection) { 480 | return native_audio_connect(producer_pool); 481 | } 482 | 483 | void audio_i2s_set_enabled(bool enable) { 484 | return native_audio_enable(enable); 485 | } 486 | 487 | bool audio_pwm_set_correction_mode(enum audio_correction_mode mode) { 488 | return true; 489 | } 490 | 491 | enum audio_correction_mode audio_pwm_get_correction_mode() { 492 | return (enum audio_correction_mode) 0; 493 | } 494 | 495 | #define AUDIO_UPSAMPLE_FRACTION_BITS 12u 496 | 497 | void audio_upsample(int16_t *input, int16_t *output, uint output_count, uint32_t step) { 498 | uint32_t pos = 0; 499 | for (int i = 0; i < output_count; i++) { 500 | uint32_t offset = (pos >> AUDIO_UPSAMPLE_FRACTION_BITS); 501 | int16_t a = input[offset]; 502 | int16_t b = input[offset + 1]; 503 | *output++ = a + (((b - a) * ((pos >> (AUDIO_UPSAMPLE_FRACTION_BITS - 8)) & 0xff)) >> 8); 504 | pos += step; 505 | } 506 | } 507 | 508 | void audio_upsample_words(int16_t *input, int16_t *output_aligned, uint output_word_count, uint32_t step) { 509 | audio_upsample(input, output_aligned, output_word_count * 2, step); 510 | } 511 | -------------------------------------------------------------------------------- /sdl_timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "pico/time.h" 8 | #include "SDL_timer.h" 9 | 10 | SDL_TimerID hardware_alarm_timers[NUM_GENERIC_TIMERS]; 11 | hardware_alarm_callback_t hardware_alarm_callbacks[NUM_GENERIC_TIMERS]; 12 | 13 | uint64_t time_us_64() { 14 | return SDL_GetTicks()*1000ul; 15 | } 16 | 17 | uint32_t time_us_32() { 18 | return time_us_64(); 19 | } 20 | 21 | void hardware_alarm_set_callback(uint alarm_num, hardware_alarm_callback_t callback) { 22 | hardware_alarm_callbacks[alarm_num] = callback; 23 | if (!callback) { 24 | SDL_RemoveTimer(hardware_alarm_timers[alarm_num]); 25 | } 26 | } 27 | 28 | static Uint32 alarm_callback(Uint32 interval, void *param) { 29 | uint32_t alarm_num = (uint)(intptr_t)param; 30 | assert(alarm_num < NUM_GENERIC_TIMERS); 31 | assert(hardware_alarm_callbacks[alarm_num]); 32 | hardware_alarm_callbacks[alarm_num](alarm_num); 33 | return 0; 34 | } 35 | 36 | // note this is a simple wrapper for the hardware timer... which only provides a 32 bit alarm, 37 | // which takes care of possible races around setting a time concurrently with when the timer should fire, 38 | // and compares the hi bits of the timer for correctness before calling the callback 39 | bool hardware_alarm_set_target(uint alarm_num, absolute_time_t target) { 40 | hardware_alarm_cancel(alarm_num); 41 | int64_t delay = to_us_since_boot(target) - time_us_64(); 42 | if (delay < 1000) delay = 1000; 43 | assert(delay <= UINT32_MAX); 44 | SDL_AddTimer((uint32_t)(delay / 1000), alarm_callback, (void *)(intptr_t)alarm_num); 45 | return false; // we don't miss 46 | } 47 | 48 | void hardware_alarm_cancel(uint alarm_num) { 49 | if (hardware_alarm_timers[alarm_num]) { 50 | SDL_RemoveTimer(hardware_alarm_timers[alarm_num]); 51 | hardware_alarm_timers[alarm_num] = 0; 52 | } 53 | } 54 | 55 | #include "pico/time_adapter.h" 56 | 57 | SDL_TimerID pool_timers[TA_NUM_TIMER_ALARMS]; 58 | void (*alarm_pool_irq_handler)(void); 59 | uint current_hardware_alarm_num; 60 | 61 | uint32_t pool_timer_callback(uint32_t period, void *param) { 62 | current_hardware_alarm_num = (uint32_t)(uintptr_t)param; 63 | alarm_pool_irq_handler(); 64 | return 0; 65 | } 66 | 67 | void clear_pool_timer(uint hardware_alarm_num) { 68 | SDL_RemoveTimer(pool_timers[hardware_alarm_num]); 69 | pool_timers[hardware_alarm_num] = 0; 70 | } 71 | 72 | void ta_clear_irq(__unused alarm_pool_timer_t *timer, uint hardware_alarm_num) { 73 | clear_pool_timer(hardware_alarm_num); 74 | } 75 | 76 | void ta_clear_force_irq(__unused alarm_pool_timer_t *timer, uint hardware_alarm_num) { 77 | clear_pool_timer(hardware_alarm_num); 78 | } 79 | 80 | void reset_pool_timer(uint hardware_alarm_num, uint delay) { 81 | clear_pool_timer(hardware_alarm_num); 82 | pool_timers[hardware_alarm_num] = SDL_AddTimer(delay, pool_timer_callback, (void *)(uintptr_t)hardware_alarm_num); 83 | } 84 | 85 | void ta_force_irq(__unused alarm_pool_timer_t *timer, uint hardware_alarm_num) { 86 | reset_pool_timer(hardware_alarm_num, 0); 87 | } 88 | 89 | static uint8_t timer_inst; 90 | 91 | uint ta_timer_num(alarm_pool_timer_t *timer) { 92 | return 0; 93 | } 94 | 95 | alarm_pool_timer_t *ta_default_timer_instance(void) { 96 | return &timer_inst; 97 | } 98 | 99 | alarm_pool_timer_t *ta_timer_instance(uint timer_alarm_num) { 100 | assert(!timer_alarm_num); 101 | return ta_default_timer_instance(); 102 | } 103 | 104 | alarm_pool_timer_t *ta_from_current_irq(uint *alarm_num) { 105 | *alarm_num = current_hardware_alarm_num; 106 | return ta_timer_instance(0); 107 | } 108 | 109 | void ta_set_timeout(__unused alarm_pool_timer_t *timer, uint hardware_alarm_num, int64_t target) { 110 | int64_t delta = target - (int64_t)time_us_64(); 111 | if (delta < 0) delta = 0; 112 | else delta /= 1000; 113 | reset_pool_timer(hardware_alarm_num, (uint32_t)delta); 114 | } 115 | 116 | void ta_enable_irq_handler(__unused alarm_pool_timer_t *timer, uint hardware_alarm_num, void (*irq_handler)(void)) { 117 | alarm_pool_irq_handler = irq_handler; 118 | } 119 | 120 | void ta_disable_irq_handler(__unused alarm_pool_timer_t *timer, uint hardware_alarm_num, void (*irq_handler)(void)) { 121 | clear_pool_timer(hardware_alarm_num); 122 | } 123 | 124 | void ta_hardware_alarm_claim(__unused alarm_pool_timer_t *timer, uint hardware_alarm_num) { 125 | hardware_alarm_claim(hardware_alarm_num); 126 | } 127 | 128 | int ta_hardware_alarm_claim_unused(__unused alarm_pool_timer_t *timer, bool required) { 129 | return hardware_alarm_claim_unused(required); 130 | } 131 | -------------------------------------------------------------------------------- /sdl_video.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include "SDL_image.h" 10 | #include "SDL_mutex.h" 11 | 12 | #include "pico.h" 13 | #include "pico/scanvideo.h" 14 | #include "pico/scanvideo/composable_scanline.h" 15 | #include "pico/multicore.h" 16 | #include "pico/mutex.h" 17 | #include "pico/sem.h" 18 | #include "pico/time.h" 19 | #include "hardware/sync.h" 20 | 21 | #undef main 22 | 23 | #define ALLOWED_PIXEL_OVERRUN 4 24 | 25 | void create_window(); 26 | void redraw(); 27 | void process_events(); 28 | 29 | extern int __real_main(); 30 | 31 | const int surface_pixel_format = SDL_PIXELFORMAT_BGRX8888; 32 | 33 | static char title[128]; 34 | 35 | static struct scanvideo_mode video_mode; 36 | static struct scanvideo_timing timing; 37 | bool video_mode_valid; 38 | volatile uint32_t last_scanline_id; 39 | struct semaphore vblank_begin; 40 | struct semaphore hblank_begin; 41 | static scanvideo_scanline_repeat_count_fn _scanline_repeat_count_fn; 42 | 43 | struct { 44 | int top, left, bottom, right; 45 | } screen_rect; 46 | 47 | enum { 48 | DO_UPDATE_SCREEN, 49 | DO_VSYNC 50 | }; 51 | 52 | bool mouse_down; 53 | double vsync_freq; 54 | 55 | #if PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 56 | #ifndef MAX_LINKED_SCANLINE_BUFFERS 57 | #define MAX_LINKED_SCANLINE_BUFFERS 2 58 | #endif 59 | #else 60 | #define MAX_LINKED_SCANLINE_BUFFERS 0 61 | #endif 62 | 63 | uint8_t key_states[SDL_NUM_SCANCODES]; 64 | 65 | void (*platform_key_down)(int scancode, int keysym, int modifiers); 66 | void (*platform_key_up)(int scancode, int keysym, int modifiers); 67 | void (*platform_mouse_move)(int dx, int dy); 68 | void (*platform_mouse_button_down)(int button); 69 | void (*platform_mouse_button_up)(int button); 70 | void (*platform_quit)(); 71 | 72 | #ifndef PICO_SCANVIDEO_SCALING_BLUR 73 | #define PICO_SCANVIDEO_SCALING_BLUR 1 74 | #endif 75 | 76 | SDL_Window *window; 77 | SDL_Renderer *renderer; 78 | SDL_Surface *pico_access_surface; 79 | SDL_Texture *texture_raw; 80 | SDL_Texture *texture_blurred; 81 | bool renderer_targettexture_supported; 82 | bool use_correct_aspect_ratio; 83 | bool force_aspect_ratio = true; 84 | bool use_integer_scaling; 85 | struct mutex scanline_mutex; 86 | 87 | bool scanline_buffer_in_use[NUM_CORES]; 88 | 89 | #define VIDEO_24MHZ_COMPOSABLE_PROGRAM_NAME "video_24mhz_composable" 90 | 91 | const struct scanvideo_pio_program video_24mhz_composable = { 92 | .id = VIDEO_24MHZ_COMPOSABLE_PROGRAM_NAME 93 | }; 94 | 95 | // right now needed on slower Pis 96 | #if PI_BUILD 97 | #define COALESCE_SCREEN_UPDATES 1 98 | #endif 99 | 100 | #ifdef COALESCE_SCREEN_UPDATES 101 | static struct mutex update_screen_mutex; 102 | static bool update_screen_pending; 103 | #endif 104 | SDL_cond *cpu_event_condition; 105 | SDL_mutex *cpu_event_mutex; 106 | volatile uint32_t cpu_event_states; 107 | SDL_TLSID cpu_core_ids; 108 | 109 | typedef void pio_hw_t; 110 | 111 | // dummy bss variable used for host_safe conversion 112 | uint8_t _hardware_base; 113 | 114 | uint16_t *core_scanline_pixel_buffer[NUM_CORES]; 115 | 116 | uint32_t core_scanline_data[NUM_CORES][PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS]; 117 | #if PICO_SCANVIDEO_PLANE_COUNT > 1 118 | uint32_t core_scanline_data2[NUM_CORES][PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS]; 119 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 120 | uint32_t core_scanline_data3[NUM_CORES][PICO_SCANVIDEO_MAX_SCANLINE_BUFFER3_WORDS]; 121 | #endif 122 | #endif 123 | struct full_scanvideo_scanline_buffer { 124 | struct scanvideo_scanline_buffer core; 125 | uint screen_y; 126 | uint screen_height; 127 | }; 128 | 129 | struct full_scanvideo_scanline_buffer core_scaneline_buffers[NUM_CORES] = { 130 | { 131 | .core = { 132 | .data = core_scanline_data[0], 133 | .data_used = 0, 134 | .data_max = count_of(core_scanline_data[0]), 135 | #if PICO_SCANVIDEO_PLANE_COUNT > 1 136 | .data2 = core_scanline_data2[0], 137 | .data2_used = 0, 138 | .data2_max = count_of(core_scanline_data2[0]), 139 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 140 | .data3 = core_scanline_data3[0], 141 | .data3_used = 0, 142 | .data3_max = count_of(core_scanline_data3[0]), 143 | #endif 144 | #endif 145 | .status = SCANLINE_OK 146 | } 147 | }, 148 | { 149 | .core = { 150 | .data = core_scanline_data[1], 151 | .data_used = 0, 152 | .data_max = count_of(core_scanline_data[1]), 153 | #if PICO_SCANVIDEO_PLANE_COUNT > 1 154 | .data2 = core_scanline_data2[1], 155 | .data2_used = 0, 156 | .data2_max = count_of(core_scanline_data2[1]), 157 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 158 | .data3 = core_scanline_data3[1], 159 | .data3_used = 0, 160 | .data3_max = count_of(core_scanline_data3[1]), 161 | #endif 162 | #endif 163 | .status = SCANLINE_OK 164 | } 165 | } 166 | }; 167 | 168 | #ifdef PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 169 | struct full_scanvideo_scanline_buffer core_scaneline_buffers_linked[NUM_CORES][MAX_LINKED_SCANLINE_BUFFERS]; 170 | uint32_t core_scanline_data_linked[NUM_CORES][MAX_LINKED_SCANLINE_BUFFERS][PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS]; 171 | #endif 172 | 173 | int core0_thread_func(void *data) { 174 | SDL_TLSSet(cpu_core_ids, (void *) 1, 0); 175 | alarm_pool_init_default(); 176 | #ifdef COALESCE_SCREEN_UPDATES 177 | mutex_init(&update_screen_mutex); 178 | #endif 179 | int rc = __real_main(); 180 | exit(rc); 181 | } 182 | 183 | 184 | int main(int argc, char **argv) { 185 | memset(title, 0, sizeof(title)); 186 | strcpy(title, "Pico - "); 187 | char *s1 = strrchr(argv[0], '/'); 188 | char *s2 = strrchr(argv[0], '\\'); 189 | if (!s1 || (s2 && s2 > s1)) s1 = s2; 190 | if (s1) s1++; 191 | else s1 = argv[0]; 192 | strncpy(title + strlen(title), s1, sizeof(title) - 1 - strlen(title)); 193 | #ifdef SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING 194 | SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1"); 195 | #endif 196 | // SDL_INIT_GAMEControLLER seems to cause crash 197 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER/* | SDL_INIT_GAMECONTROLLER*/) != 0) { 198 | assert(false); 199 | } 200 | cpu_core_ids = SDL_TLSCreate(); 201 | cpu_event_mutex = SDL_CreateMutex(); 202 | cpu_event_condition = SDL_CreateCond(); 203 | 204 | create_window(); 205 | redraw(); 206 | 207 | __unused SDL_Thread *core0_thread = SDL_CreateThread(core0_thread_func, "Core 0", 0); 208 | 209 | process_events(); 210 | return 0; 211 | } 212 | 213 | int core1_thread_func(void *entry) { 214 | SDL_TLSSet(cpu_core_ids, (void *) 2, 0); 215 | // todo locking and cleanup 216 | ((void (*)(void)) entry)(); 217 | return 0; 218 | } 219 | 220 | bool scanvideo_setup(const struct scanvideo_mode *mode) { 221 | return scanvideo_setup_with_timing(mode, NULL); 222 | } 223 | 224 | void send_update_screen() { 225 | #ifdef COALESCE_SCREEN_UPDATES 226 | bool needed = false; 227 | mutex_enter_blocking(&update_screen_mutex); 228 | if (!update_screen_pending) needed = true; 229 | update_screen_pending = true; 230 | mutex_exit(&update_screen_mutex); 231 | if (!needed) return; 232 | #endif 233 | SDL_Event event; 234 | memset(&event, 0, sizeof(event)); 235 | event.type = SDL_USEREVENT; 236 | event.user.code = DO_UPDATE_SCREEN; 237 | SDL_PushEvent(&event); 238 | } 239 | 240 | static uint default_scanvideo_scanline_repeat_count_fn(uint32_t scanline_id) { 241 | return 1; 242 | } 243 | 244 | void scanvideo_set_scanline_repeat_fn(scanvideo_scanline_repeat_count_fn fn) { 245 | _scanline_repeat_count_fn = fn ? fn : default_scanvideo_scanline_repeat_count_fn; 246 | } 247 | 248 | 249 | void default_simulate_scanvideo_pio(const uint32_t *dma_data, uint32_t dma_data_size, 250 | uint16_t *pixel_buffer, int32_t max_pixels, int32_t expected_width, bool overlay) { 251 | assert(false); // don't know how to display this mode in native 252 | } 253 | 254 | simulate_scanvideo_pio_fn current_simulate_scanvideo_pio_fn = default_simulate_scanvideo_pio; 255 | 256 | const char *client_current_video_mode_id = "none"; 257 | simulate_scanvideo_pio_fn client_simulate_scanvideo_pio_fn = default_simulate_scanvideo_pio; 258 | 259 | void scanvideo_set_simulate_scanvideo_pio_fn(const char *id, simulate_scanvideo_pio_fn fn) { 260 | client_current_video_mode_id = id; 261 | client_simulate_scanvideo_pio_fn = fn; 262 | } 263 | 264 | static simulate_composable_cmd_fn composable_cmd_fns[32]; 265 | 266 | void scanvideo_set_simulate_composable_cmd(uint cmd, simulate_composable_cmd_fn fn) { 267 | assert(cmd < count_of(composable_cmd_fns)); 268 | composable_cmd_fns[cmd] = fn; 269 | } 270 | 271 | SDL_TimerID vsync_timer; 272 | 273 | SDL_sem *internal_vsync_sem; 274 | 275 | Uint32 vsync_callback(Uint32 interval, void *param) { 276 | // todo this is a bit dodgy, but at worst we wait for the next sem 277 | if (!SDL_SemValue(internal_vsync_sem)) { 278 | SDL_SemPost(internal_vsync_sem); 279 | } 280 | 281 | SDL_Event event; 282 | memset(&event, 0, sizeof(event)); 283 | event.type = SDL_USEREVENT; 284 | event.user.code = DO_VSYNC; 285 | event.user.data1 = param; 286 | SDL_PushEvent(&event); 287 | return interval; 288 | } 289 | 290 | struct scanvideo_mode scanvideo_get_mode() { 291 | return video_mode; 292 | } 293 | 294 | extern void scanvideo_timing_enable(bool enable) { 295 | if (enable) { 296 | if (vsync_timer != 0) { 297 | if (!SDL_RemoveTimer(vsync_timer)) { 298 | assert(false); 299 | } 300 | } 301 | uint32_t delay_ms = (uint32_t) (1000.0 / vsync_freq); 302 | vsync_timer = SDL_AddTimer(delay_ms, vsync_callback, NULL); 303 | if (vsync_timer == 0) { 304 | assert(false); 305 | } 306 | 307 | } 308 | } 309 | 310 | // takes effect after the next vsync 311 | extern void video_display_enable(bool enable) { 312 | 313 | } 314 | 315 | static inline uint32_t scanline_id_after(uint32_t scanline_id) { 316 | uint32_t tmp = scanline_id & 0xffff; 317 | if (tmp < video_mode.height - 1) { 318 | return scanline_id + 1; 319 | } else { 320 | return scanline_id + 0x10000 - tmp; 321 | } 322 | } 323 | 324 | void scanvideo_default_configure_pio(pio_hw_t *pio, uint sm, uint wrap_trarget, uint wrap, bool overlay) { 325 | 326 | } 327 | 328 | void simulate_scanvideo_pio_video_24mhz_composable(const uint32_t *dma_data, uint32_t dma_data_size, 329 | uint16_t *pixel_buffer, int32_t max_pixels, int32_t expected_width, 330 | bool overlay) { 331 | const uint16_t *it = (uint16_t *) dma_data; 332 | assert(!(3u & (uintptr_t) dma_data)); 333 | const uint16_t *const __unused dma_data_end = (uint16_t *) (dma_data + dma_data_size); 334 | const uint16_t *const pixels_end = (uint16_t *) (pixel_buffer + max_pixels); 335 | uint16_t *pixels = pixel_buffer; 336 | bool __unused ok = false; 337 | bool done = false; 338 | bool __unused last_was_black = true; // in case no pixels 339 | const uint16_t display_enable_bit = PICO_SCANVIDEO_ALPHA_MASK; // for now 340 | do { 341 | uint16_t cmd = *it++; 342 | switch (cmd) { 343 | case video_24mhz_composable_program_extern(end_of_scanline_skip_ALIGN): 344 | it++; 345 | // fall thru 346 | case video_24mhz_composable_program_extern(end_of_scanline_ALIGN): 347 | done = ok = true; 348 | break; 349 | case video_24mhz_composable_program_extern(color_run): { 350 | uint16_t c = *it++; 351 | uint16_t len = *it++; 352 | if (!overlay || (c & display_enable_bit)) { 353 | for (int i = 0; i < len + 3; i++) { 354 | assert(pixels < pixels_end); 355 | *pixels++ = c; 356 | } 357 | } else { 358 | pixels += len + 3; 359 | } 360 | last_was_black = !c; 361 | break; 362 | } 363 | case video_24mhz_composable_program_extern(raw_run): { 364 | assert(pixels < pixels_end); 365 | uint16_t c = *it++; 366 | if (!overlay || (c & display_enable_bit)) 367 | *pixels++ = c; 368 | else 369 | pixels++; 370 | uint16_t len = *it++; 371 | for (int i = 0; i < len + 2; i++) { 372 | assert(pixels < pixels_end); 373 | c = *it++; 374 | if (!overlay || (c & display_enable_bit)) 375 | *pixels++ = c; 376 | else 377 | pixels++; 378 | } 379 | last_was_black = !c; 380 | break; 381 | } 382 | case video_24mhz_composable_program_extern(raw_2p): 383 | assert(pixels < pixels_end); 384 | uint16_t c = *it++; 385 | if (!overlay || (c & display_enable_bit)) 386 | *pixels++ = c; 387 | else 388 | pixels++; 389 | // fall thru 390 | case video_24mhz_composable_program_extern(raw_1p): 391 | if (pixels == pixels_end) { 392 | c = *it++; 393 | assert(!c); // must end with black 394 | } else { 395 | assert(pixels < pixels_end); 396 | c = *it++; 397 | if (!overlay || (c & display_enable_bit)) 398 | *pixels++ = c; 399 | else 400 | pixels++; 401 | } 402 | last_was_black = !c; 403 | break; 404 | break; 405 | #if !PICO_SCANVIDEO_USE_RAW1P_2CYCLE 406 | case video_24mhz_composable_program_extern(raw_1p_skip_ALIGN): 407 | assert(pixels < pixels_end); 408 | c = *it++; 409 | if (!overlay || (c & display_enable_bit)) 410 | *pixels++ = c; 411 | else 412 | pixels++; 413 | last_was_black = !c; 414 | it++; 415 | break; 416 | #else 417 | case video_24mhz_composable_program_extern(raw_1p_2cycle): 418 | // skip half pixel (so we don't overshoot 419 | c = *it++; 420 | last_was_black = !c; 421 | break; 422 | #endif 423 | default: 424 | assert(cmd < 32); 425 | if (composable_cmd_fns[cmd]) { 426 | composable_cmd_fns[cmd](&it, &pixels, pixels_end - pixels, overlay); 427 | break; 428 | } 429 | assert(false); 430 | done = true; 431 | } 432 | } while (!done); 433 | assert(ok); 434 | assert(it == dma_data_end); 435 | assert(!(3u & (uintptr_t) (it))); // should end on dword boundary 436 | #if 0 437 | // should probably have this back ignored for now because of overlays which don't bother 438 | if (!overlay) { 439 | assert(!expected_width || pixels == pixel_buffer + 440 | expected_width); // with the correct number of pixels (one more because we stick a black pixel on the end) 441 | } 442 | #else 443 | if (expected_width && pixels < pixel_buffer + expected_width) { 444 | // black out rest of line 445 | if (!overlay) memset(pixels, 0, (expected_width - (pixels - pixel_buffer)) * sizeof(uint16_t)); 446 | } 447 | #endif 448 | assert(last_was_black); 449 | } 450 | 451 | bool scanvideo_setup_with_timing(const struct scanvideo_mode *mode, const struct scanvideo_timing *timing_override) { 452 | video_mode = *mode; 453 | if (!video_mode.yscale_denominator) video_mode.yscale_denominator = 1; 454 | assert(video_mode.yscale >= video_mode.yscale_denominator); 455 | timing = timing_override ? *timing_override : *mode->default_timing; 456 | 457 | sem_init(&vblank_begin, 0, 1); 458 | sem_init(&hblank_begin, 0, 1); 459 | mutex_init(&scanline_mutex); 460 | last_scanline_id = -1; 461 | screen_rect.right = video_mode.width; 462 | screen_rect.bottom = timing.v_active; 463 | video_mode_valid = true; 464 | vsync_freq = ((double) timing.clock_freq) / (timing.h_total * timing.v_total); 465 | pico_access_surface = SDL_CreateRGBSurfaceWithFormat(0, screen_rect.right, screen_rect.bottom, 16, 466 | surface_pixel_format); 467 | assert (pico_access_surface); 468 | for (int i = 0; i < NUM_CORES; i++) { 469 | core_scanline_pixel_buffer[i] = calloc(video_mode.width + ALLOWED_PIXEL_OVERRUN, sizeof(uint16_t)); 470 | } 471 | #if PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 472 | for(int c=0; c< NUM_CORES; c++) { 473 | for (int i = 0; i < MAX_LINKED_SCANLINE_BUFFERS; i++) { 474 | struct scanvideo_scanline_buffer *sb = &core_scaneline_buffers_linked[c][i].core; 475 | sb->data = &core_scanline_data_linked[c][i][0];//calloc(4, PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS); 476 | sb->data_used = 0, 477 | sb->data_max = PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS; 478 | #if PICO_SCANVIDEO_PLANE_COUNT > 1 479 | sb->data2 = calloc(4, PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS); 480 | sb->data2_used = 0, 481 | sb->data2_max = PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS; 482 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 483 | sb->data3 = calloc(4, PICO_SCANVIDEO_MAX_SCANLINE_BUFFER3_WORDS); 484 | sb->data3_used = 0, 485 | sb->data2_max = PICO_SCANVIDEO_MAX_SCANLINE_BUFFER3_WORDS; 486 | #endif 487 | #endif 488 | sb->status = SCANLINE_OK; 489 | } 490 | } 491 | #endif 492 | 493 | printf("VSync freq %g\n", vsync_freq); 494 | if (!strcmp(mode->pio_program->id, VIDEO_24MHZ_COMPOSABLE_PROGRAM_NAME)) 495 | current_simulate_scanvideo_pio_fn = simulate_scanvideo_pio_video_24mhz_composable; 496 | else if (!strcmp(mode->pio_program->id, client_current_video_mode_id)) 497 | current_simulate_scanvideo_pio_fn = client_simulate_scanvideo_pio_fn; 498 | scanvideo_set_scanline_repeat_fn(NULL); 499 | send_update_screen(); 500 | return true; 501 | } 502 | 503 | struct scanvideo_scanline_buffer *scanvideo_begin_scanline_generation_linked(uint n, bool block) { 504 | int core = get_core_num(); 505 | assert(!scanline_buffer_in_use[core]); 506 | struct full_scanvideo_scanline_buffer *fsb = core_scaneline_buffers + core; 507 | mutex_enter_blocking(&scanline_mutex); 508 | uint32_t next_scanline_id = scanline_id_after(last_scanline_id); 509 | 510 | if (scanvideo_frame_number(next_scanline_id) != scanvideo_frame_number(last_scanline_id)) { 511 | if (!block) { 512 | if (0 != SDL_SemTryWait(internal_vsync_sem)) { 513 | mutex_exit(&scanline_mutex); 514 | return NULL; 515 | } 516 | } else { 517 | SDL_SemWait(internal_vsync_sem); 518 | } 519 | send_update_screen(); 520 | sem_release(&vblank_begin); 521 | } 522 | fsb->core.scanline_id = last_scanline_id = next_scanline_id; 523 | scanline_buffer_in_use[core] = true; 524 | #if PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 525 | assert(n <= MAX_LINKED_SCANLINE_BUFFERS); 526 | fsb->core.link = NULL; 527 | fsb->core.link_after = 0; 528 | for(int i=0; i < n - 1; i++) { 529 | core_scaneline_buffers_linked[core][i].core.scanline_id = next_scanline_id; 530 | core_scaneline_buffers_linked[core][i].core.link = fsb->core.link; 531 | fsb->core.link = &core_scaneline_buffers_linked[core][i].core; 532 | } 533 | #endif 534 | mutex_exit(&scanline_mutex); 535 | sem_release(&hblank_begin); 536 | return &fsb->core; 537 | } 538 | 539 | struct scanvideo_scanline_buffer *scanvideo_begin_scanline_generation(bool block) { 540 | return scanvideo_begin_scanline_generation_linked(1, block); 541 | } 542 | 543 | uint32_t scanvideo_get_next_scanline_id() { 544 | return scanline_id_after(last_scanline_id); 545 | } 546 | 547 | bool scanvideo_in_vblank() { 548 | return scanvideo_scanline_number(scanvideo_get_next_scanline_id()) == 0; 549 | } 550 | 551 | uint32_t scanvideo_wait_for_scanline_complete(uint32_t scanline_id) { 552 | // next_scanline_id is potentially the scanline_id in progress, so we need next_scanline_id to 553 | // be more than the scanline_id after the passed one 554 | scanline_id = scanline_id_after(scanline_id); 555 | uint32_t next_scanline_id; 556 | // scanline_id > video_get_next_scanline_id() but with wrapping support 557 | while (0 < (int32_t) (scanline_id - (next_scanline_id = scanvideo_get_next_scanline_id()))) { 558 | sem_acquire_blocking(&hblank_begin); 559 | } 560 | return next_scanline_id; 561 | } 562 | 563 | void scanvideo_wait_for_vblank() { 564 | sem_acquire_blocking(&vblank_begin); 565 | } 566 | 567 | uint32_t host_safe_hw_ptr_impl(uintptr_t x) { 568 | if (!x) return 0; 569 | int64_t offset = x - (uintptr_t) &_hardware_base; 570 | // printf("Encode 0x%lx 0x%lx\n", x, offset); 571 | assert(offset >= INT32_MIN && offset <= INT32_MAX); 572 | return (uint32_t) offset; 573 | } 574 | 575 | void *decode_host_safe_hw_ptr(uint32_t ptr) { 576 | if (!ptr) return 0; 577 | // printf("Decode 0x%08x 0x%lx\n", ptr, (((int32_t) ptr) + (uintptr_t) &_hardware_base)); 578 | return (void *) (((int32_t) ptr) + (uintptr_t) &_hardware_base); 579 | } 580 | 581 | int merge_dma_chain_variable(uint32_t *dma_chain, uint32_t dma_chain_size, uint32_t *out, int out_size) { 582 | const uint32_t *end = dma_chain + dma_chain_size; 583 | uint32_t *p = dma_chain; 584 | int pos = 0; 585 | while (p < end - 1) { 586 | uint32_t len = *p++; 587 | uint32_t ptr = *p++; 588 | if (!ptr) break; 589 | uint32_t *data = decode_host_safe_hw_ptr(ptr); 590 | int i; 591 | for (i = 0; i < len && pos < out_size; i++) { 592 | out[pos++] = data[i]; 593 | } 594 | assert(i == len); 595 | } 596 | assert(p == end); 597 | return pos; 598 | } 599 | 600 | int merge_dma_chain_fixed(uint32_t *dma_chain, uint32_t dma_chain_size, uint32_t *out, int out_size, 601 | int fragment_words) { 602 | const uint32_t *end = dma_chain + dma_chain_size; 603 | uint32_t *p = dma_chain; 604 | int pos = 0; 605 | while (p < end) { 606 | uint32_t ptr = *p++; 607 | if (!ptr) break; 608 | uint32_t *data = decode_host_safe_hw_ptr(ptr); 609 | int i; 610 | for (i = 0; i < fragment_words && pos < out_size; i++) { 611 | out[pos++] = data[i]; 612 | } 613 | assert(i == fragment_words); 614 | } 615 | assert(p == end); 616 | return pos; 617 | } 618 | 619 | void scanvideo_end_scanline_generation(struct scanvideo_scanline_buffer *scanline_buffer) { 620 | int core = get_core_num(); 621 | assert(scanline_buffer_in_use[core]); 622 | assert(scanline_buffer == &(core_scaneline_buffers + core)->core); 623 | struct full_scanvideo_scanline_buffer *fsb = (struct full_scanvideo_scanline_buffer *) scanline_buffer; 624 | 625 | // graham 7/24/20 moved this from being scanline generation to better match on device where the repeat 626 | // count function isn't called until after the scanline is generated 627 | { 628 | static int screen_y; 629 | static int accum; 630 | if (scanvideo_scanline_number(scanline_buffer->scanline_id) == 0) { 631 | screen_y = 0; 632 | accum = 0; 633 | } 634 | fsb->screen_y = screen_y; 635 | int multiplier = 0; 636 | do { 637 | multiplier++; 638 | accum += video_mode.yscale_denominator; 639 | } while (accum < video_mode.yscale); 640 | accum -= video_mode.yscale; 641 | fsb->screen_height = _scanline_repeat_count_fn(scanline_buffer->scanline_id) * multiplier; 642 | screen_y += fsb->screen_height; 643 | 644 | } 645 | // note plus one for black pixel 646 | mutex_enter_blocking(&scanline_mutex); 647 | 648 | uint16_t *pixels = (uint16_t *) ((uint8_t *) pico_access_surface->pixels + pico_access_surface->pitch * fsb->screen_y); 649 | bool need_new_row = true; 650 | for (int i = 0; i < fsb->screen_height; i++) { 651 | if (fsb->screen_y + i >= timing.v_active) { 652 | //printf("Warning exceeded total: %d\n", fsb->screen_y); 653 | break; 654 | } 655 | if (need_new_row) { 656 | uint32_t *data = scanline_buffer->data; 657 | int data_used = scanline_buffer->data_used; 658 | int expected_width = video_mode.width; 659 | #if PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 660 | static uint32_t buf[1024]; 661 | data_used = merge_dma_chain_variable(data, data_used, buf, count_of(buf)); 662 | data = buf; 663 | expected_width = 0; // for now don't assert on width for this 664 | #endif 665 | #if PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA 666 | static uint32_t buf[1024]; 667 | data_used = merge_dma_chain_fixed(data, data_used, buf, count_of(buf), scanline_buffer->fragment_words); 668 | data = buf; 669 | expected_width = 0; // for now don't assert on width for this 670 | #endif 671 | current_simulate_scanvideo_pio_fn(data, data_used, core_scanline_pixel_buffer[core], 672 | video_mode.width + ALLOWED_PIXEL_OVERRUN, expected_width, false); 673 | #if PICO_SCANVIDEO_PLANE_COUNT > 1 674 | data = scanline_buffer->data2; 675 | data_used = scanline_buffer->data2_used; 676 | expected_width = video_mode.width; 677 | #if PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA 678 | static uint32_t buf2[1024]; 679 | data_used = merge_dma_chain_variable(data, data_used, buf2, count_of(buf2)); 680 | data = buf2; 681 | expected_width = 0; // for now don't assert on width for this 682 | #endif 683 | current_simulate_scanvideo_pio_fn(data, data_used, core_scanline_pixel_buffer[core], video_mode.width, expected_width, true); 684 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 685 | current_simulate_scanvideo_pio_fn(scanline_buffer->data3, scanline_buffer->data3_used, core_scanline_pixel_buffer[core], video_mode.width, 0, true); 686 | #endif 687 | #endif 688 | need_new_row = false; 689 | } 690 | memcpy(pixels, core_scanline_pixel_buffer[core], video_mode.width * sizeof(uint16_t)); 691 | pixels = (uint16_t *) (((uint8_t *) pixels) + pico_access_surface->pitch); 692 | #if PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 693 | if (scanline_buffer->link_after) { 694 | if (!--scanline_buffer->link_after) { 695 | assert(scanline_buffer->link); 696 | scanline_buffer = scanline_buffer->link; 697 | need_new_row = true; 698 | } 699 | } 700 | #endif 701 | } 702 | mutex_exit(&scanline_mutex); 703 | scanline_buffer_in_use[core] = false; 704 | } 705 | 706 | void window_resized() { 707 | #if SDL_VERSION_ATLEAST(2, 0, 5) // SDL_RenderSetIntegerScale 708 | if (use_integer_scaling) { 709 | int window_width, window_height; 710 | SDL_GetRendererOutputSize(renderer, &window_width, &window_height); 711 | int render_width, render_height; 712 | SDL_RenderGetLogicalSize(renderer, &render_width, &render_height); 713 | // Disable integer scaling if it would result in downscaling. 714 | // Because then the only suitable integer scaling factor is zero, i.e. the picture disappears. 715 | SDL_bool makes_sense = (window_width >= render_width && window_height >= render_height); 716 | SDL_RenderSetIntegerScale(renderer, makes_sense); 717 | } 718 | #endif 719 | } 720 | 721 | void apply_aspect_ratio() { 722 | // todo this is only called once at start 723 | // Allow us to use a consistent set of screen co-ordinates, even if the screen size changes 724 | if (force_aspect_ratio) { 725 | int wwidth = timing.h_active; 726 | int wheight = timing.v_active; 727 | 728 | SDL_SetWindowSize(window, wwidth, wheight); 729 | SDL_RenderSetLogicalSize(renderer, wwidth, wheight); 730 | } else { 731 | if (use_correct_aspect_ratio) { 732 | SDL_RenderSetLogicalSize(renderer, screen_rect.right * 5, screen_rect.bottom * 6); // 4:3 733 | } else { 734 | SDL_RenderSetLogicalSize(renderer, screen_rect.right, screen_rect.bottom); // 16:10 735 | } 736 | } 737 | window_resized(); 738 | } 739 | 740 | void check_textures() { 741 | if (texture_raw == NULL) { 742 | apply_aspect_ratio(); 743 | texture_raw = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR565, SDL_TEXTUREACCESS_STREAMING, 744 | video_mode.width, timing.v_active); 745 | printf("tr %p\n", texture_raw); 746 | } 747 | #if PICO_SCANVIDEO_SCALING_BLUR 748 | int effective_width = video_mode.width * video_mode.xscale; 749 | int effective_height = timing.v_active; 750 | if (texture_blurred == NULL) { 751 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); 752 | int access = renderer_targettexture_supported ? SDL_TEXTUREACCESS_TARGET : SDL_TEXTUREACCESS_STREAMING; 753 | texture_blurred = SDL_CreateTexture(renderer, surface_pixel_format, access, effective_width, 754 | effective_height); 755 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 756 | } 757 | #endif 758 | } 759 | 760 | const uint8_t icon_bytes[] = { 761 | 0x3c, 0x3f, 0x78, 0x6d, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x30, 762 | 0x22, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3d, 0x22, 0x55, 0x54, 763 | 0x46, 0x2d, 0x38, 0x22, 0x20, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x3d, 0x22, 0x6e, 764 | 0x6f, 0x22, 0x3f, 0x3e, 0x0a, 0x3c, 0x73, 0x76, 0x67, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 765 | 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 766 | 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 767 | 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x63, 0x63, 0x3d, 0x22, 768 | 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x76, 769 | 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6e, 0x73, 0x23, 0x22, 0x20, 770 | 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 771 | 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 772 | 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 773 | 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x76, 774 | 0x67, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 775 | 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x73, 0x76, 0x67, 0x22, 0x20, 0x78, 776 | 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 777 | 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x73, 0x76, 0x67, 778 | 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6c, 0x69, 0x6e, 0x6b, 0x3d, 779 | 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 780 | 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x78, 0x6c, 0x69, 0x6e, 0x6b, 0x22, 0x20, 0x76, 781 | 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x31, 0x22, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 782 | 0x3d, 0x22, 0x35, 0x37, 0x30, 0x22, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 783 | 0x22, 0x37, 0x32, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 784 | 0x20, 0x31, 0x35, 0x38, 0x2e, 0x33, 0x37, 0x35, 0x2c, 0x31, 0x2e, 0x36, 0x35, 0x36, 785 | 0x32, 0x35, 0x20, 0x63, 0x20, 0x2d, 0x33, 0x2e, 0x36, 0x31, 0x39, 0x33, 0x2c, 0x30, 0x2e, 0x31, 0x31, 0x32, 786 | 0x33, 0x31, 0x39, 0x32, 0x20, 0x2d, 0x37, 0x2e, 0x35, 0x31, 0x37, 0x31, 0x35, 0x2c, 787 | 0x31, 0x2e, 0x34, 0x34, 0x39, 0x33, 0x32, 0x36, 0x36, 0x20, 0x2d, 0x31, 0x31, 0x2e, 0x39, 0x33, 0x37, 0x35, 788 | 0x2c, 0x34, 0x2e, 0x39, 0x33, 0x37, 0x35, 0x20, 0x43, 0x20, 0x31, 0x33, 0x35, 0x2e, 789 | 0x36, 0x31, 0x30, 0x35, 0x34, 0x2c, 0x32, 0x2e, 0x34, 0x31, 0x37, 0x34, 0x34, 0x39, 0x36, 0x20, 0x31, 0x32, 790 | 0x35, 0x2e, 0x31, 0x31, 0x30, 0x34, 0x31, 0x2c, 0x30, 0x2e, 0x39, 0x36, 0x36, 0x35, 791 | 0x36, 0x31, 0x32, 0x20, 0x31, 0x31, 0x35, 0x2e, 0x37, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x39, 0x2e, 0x34, 0x36, 792 | 0x38, 0x37, 0x35, 0x20, 0x31, 0x30, 0x31, 0x2e, 0x32, 0x32, 0x34, 0x38, 0x39, 0x2c, 793 | 0x37, 0x2e, 0x35, 0x38, 0x37, 0x39, 0x39, 0x32, 0x32, 0x20, 0x39, 0x36, 0x2e, 0x35, 0x30, 0x38, 0x34, 0x36, 794 | 0x31, 0x2c, 0x31, 0x31, 0x2e, 0x34, 0x36, 0x39, 0x34, 0x39, 0x34, 0x20, 0x39, 0x32, 795 | 0x2e, 0x39, 0x33, 0x37, 0x35, 0x2c, 0x31, 0x36, 0x20, 0x38, 0x39, 0x2e, 0x37, 0x35, 0x34, 0x39, 0x35, 0x33, 796 | 0x2c, 0x31, 0x35, 0x2e, 0x39, 0x33, 0x34, 0x31, 0x33, 0x35, 0x20, 0x36, 0x39, 0x2e, 797 | 0x31, 0x31, 0x38, 0x36, 0x35, 0x32, 0x2c, 0x31, 0x32, 0x2e, 0x37, 0x32, 0x37, 0x39, 0x33, 0x20, 0x35, 0x39, 798 | 0x2e, 0x36, 0x35, 0x36, 0x32, 0x35, 0x2c, 0x32, 0x36, 0x2e, 0x38, 0x34, 0x33, 0x37, 799 | 0x35, 0x20, 0x33, 0x35, 0x2e, 0x38, 0x37, 0x34, 0x36, 0x30, 0x32, 0x2c, 0x32, 0x34, 0x2e, 0x30, 0x33, 0x30, 800 | 0x33, 0x32, 0x39, 0x20, 0x32, 0x38, 0x2e, 0x33, 0x35, 0x39, 0x34, 0x37, 0x32, 0x2c, 801 | 0x34, 0x30, 0x2e, 0x38, 0x33, 0x31, 0x36, 0x32, 0x35, 0x20, 0x33, 0x36, 0x2e, 0x38, 0x37, 0x35, 0x2c, 0x35, 802 | 0x36, 0x2e, 0x35, 0x20, 0x63, 0x20, 0x2d, 0x34, 0x2e, 0x38, 0x35, 0x36, 0x39, 0x31, 803 | 0x31, 0x2c, 0x37, 0x2e, 0x35, 0x31, 0x38, 0x39, 0x35, 0x35, 0x20, 0x2d, 0x39, 0x2e, 0x38, 0x38, 0x39, 0x35, 804 | 0x30, 0x33, 0x2c, 0x31, 0x34, 0x2e, 0x39, 0x34, 0x37, 0x32, 0x32, 0x36, 0x20, 0x31, 805 | 0x2e, 0x34, 0x36, 0x38, 0x37, 0x35, 0x2c, 0x32, 0x39, 0x2e, 0x32, 0x38, 0x31, 0x32, 0x35, 0x20, 0x2d, 0x34, 806 | 0x2e, 0x30, 0x31, 0x38, 0x30, 0x30, 0x36, 0x2c, 0x37, 0x2e, 0x39, 0x38, 0x33, 0x35, 807 | 0x31, 0x34, 0x20, 0x2d, 0x31, 0x2e, 0x35, 0x32, 0x37, 0x34, 0x33, 0x31, 0x2c, 0x31, 0x36, 0x2e, 0x36, 0x34, 808 | 0x34, 0x30, 0x33, 0x20, 0x37, 0x2e, 0x39, 0x33, 0x37, 0x35, 0x2c, 0x32, 0x37, 0x2e, 809 | 0x31, 0x32, 0x35, 0x20, 0x2d, 0x32, 0x2e, 0x34, 0x39, 0x37, 0x38, 0x35, 0x37, 0x2c, 0x31, 0x31, 0x2e, 0x32, 810 | 0x32, 0x32, 0x36, 0x20, 0x32, 0x2e, 0x34, 0x31, 0x32, 0x30, 0x37, 0x37, 0x2c, 0x31, 811 | 0x39, 0x2e, 0x31, 0x34, 0x30, 0x38, 0x36, 0x20, 0x31, 0x31, 0x2e, 0x32, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x32, 812 | 0x35, 0x2e, 0x33, 0x31, 0x32, 0x35, 0x20, 0x2d, 0x31, 0x2e, 0x36, 0x34, 0x37, 0x30, 813 | 0x39, 0x2c, 0x31, 0x35, 0x2e, 0x33, 0x35, 0x37, 0x35, 0x36, 0x20, 0x31, 0x34, 0x2e, 0x30, 0x38, 0x33, 0x35, 814 | 0x30, 0x35, 0x2c, 0x32, 0x34, 0x2e, 0x32, 0x38, 0x37, 0x34, 0x33, 0x20, 0x31, 0x38, 815 | 0x2e, 0x37, 0x38, 0x31, 0x32, 0x35, 0x2c, 0x32, 0x37, 0x2e, 0x34, 0x36, 0x38, 0x37, 0x35, 0x20, 0x31, 0x2e, 816 | 0x38, 0x30, 0x33, 0x36, 0x37, 0x37, 0x2c, 0x38, 0x2e, 0x39, 0x34, 0x38, 0x36, 0x38, 817 | 0x20, 0x35, 0x2e, 0x35, 0x36, 0x32, 0x39, 0x31, 0x2c, 0x31, 0x37, 0x2e, 0x33, 0x39, 0x32, 0x37, 0x20, 0x32, 818 | 0x33, 0x2e, 0x35, 0x33, 0x31, 0x32, 0x35, 0x2c, 0x32, 0x32, 0x2e, 0x30, 0x36, 0x32, 819 | 0x35, 0x20, 0x32, 0x2e, 0x39, 0x36, 0x33, 0x32, 0x33, 0x2c, 0x31, 0x33, 0x2e, 0x33, 0x33, 0x36, 0x31, 0x20, 820 | 0x31, 0x33, 0x2e, 0x37, 0x36, 0x32, 0x30, 0x36, 0x2c, 0x31, 0x35, 0x2e, 0x36, 0x33, 821 | 0x39, 0x30, 0x36, 0x20, 0x32, 0x34, 0x2e, 0x32, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x31, 0x38, 0x2e, 0x34, 0x33, 822 | 0x37, 0x35, 0x20, 0x2d, 0x33, 0x34, 0x2e, 0x35, 0x36, 0x31, 0x39, 0x32, 0x39, 0x2c, 823 | 0x32, 0x30, 0x2e, 0x30, 0x38, 0x39, 0x35, 0x34, 0x20, 0x2d, 0x36, 0x34, 0x2e, 0x32, 0x30, 0x30, 0x36, 0x37, 824 | 0x2c, 0x34, 0x36, 0x2e, 0x35, 0x32, 0x32, 0x36, 0x36, 0x20, 0x2d, 0x36, 0x34, 0x2c, 825 | 0x31, 0x31, 0x31, 0x2e, 0x33, 0x37, 0x35, 0x20, 0x6c, 0x20, 0x2d, 0x35, 0x2e, 0x30, 0x36, 0x32, 0x35, 0x2c, 826 | 0x39, 0x2e, 0x30, 0x33, 0x31, 0x32, 0x35, 0x20, 0x43, 0x20, 0x31, 0x35, 0x2e, 0x33, 827 | 0x33, 0x37, 0x38, 0x38, 0x32, 0x2c, 0x33, 0x35, 0x30, 0x2e, 0x36, 0x39, 0x36, 0x30, 0x34, 0x20, 0x2d, 0x32, 828 | 0x30, 0x2e, 0x33, 0x31, 0x36, 0x35, 0x34, 0x37, 0x2c, 0x34, 0x32, 0x38, 0x2e, 0x31, 829 | 0x36, 0x30, 0x30, 0x31, 0x20, 0x33, 0x35, 0x2e, 0x34, 0x33, 0x37, 0x35, 0x2c, 0x34, 0x39, 0x31, 0x2e, 0x31, 830 | 0x32, 0x35, 0x20, 0x63, 0x20, 0x33, 0x2e, 0x36, 0x34, 0x31, 0x38, 0x37, 0x31, 0x2c, 831 | 0x31, 0x39, 0x2e, 0x37, 0x30, 0x38, 0x33, 0x38, 0x20, 0x39, 0x2e, 0x37, 0x34, 0x39, 0x35, 0x38, 0x39, 0x2c, 832 | 0x33, 0x33, 0x2e, 0x38, 0x36, 0x33, 0x39, 0x36, 0x20, 0x31, 0x35, 0x2e, 0x31, 0x38, 833 | 0x37, 0x35, 0x2c, 0x34, 0x39, 0x2e, 0x35, 0x33, 0x31, 0x32, 0x35, 0x20, 0x38, 0x2e, 0x31, 0x33, 0x33, 0x38, 834 | 0x33, 0x34, 0x2c, 0x36, 0x33, 0x2e, 0x31, 0x33, 0x30, 0x35, 0x38, 0x20, 0x36, 0x31, 835 | 0x2e, 0x32, 0x31, 0x37, 0x36, 0x33, 0x2c, 0x39, 0x32, 0x2e, 0x36, 0x39, 0x31, 0x36, 0x31, 0x20, 0x37, 0x35, 836 | 0x2e, 0x32, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x39, 0x36, 0x2e, 0x31, 0x38, 0x37, 0x35, 837 | 0x20, 0x32, 0x30, 0x2e, 0x35, 0x31, 0x36, 0x35, 0x33, 0x2c, 0x31, 0x35, 0x2e, 0x36, 0x32, 0x38, 0x31, 0x32, 838 | 0x20, 0x34, 0x32, 0x2e, 0x33, 0x36, 0x38, 0x31, 0x38, 0x2c, 0x33, 0x30, 0x2e, 0x34, 839 | 0x35, 0x36, 0x37, 0x32, 0x20, 0x37, 0x31, 0x2e, 0x39, 0x33, 0x37, 0x35, 0x2c, 0x34, 0x30, 0x2e, 0x38, 0x34, 840 | 0x33, 0x37, 0x35, 0x20, 0x32, 0x37, 0x2e, 0x38, 0x37, 0x35, 0x31, 0x35, 0x2c, 0x32, 841 | 0x38, 0x2e, 0x37, 0x34, 0x39, 0x34, 0x36, 0x20, 0x35, 0x38, 0x2e, 0x30, 0x37, 0x33, 0x38, 0x38, 0x2c, 0x33, 842 | 0x39, 0x2e, 0x37, 0x30, 0x36, 0x34, 0x20, 0x38, 0x38, 0x2e, 0x34, 0x33, 0x37, 0x35, 843 | 0x2c, 0x33, 0x39, 0x2e, 0x36, 0x38, 0x37, 0x35, 0x20, 0x30, 0x2e, 0x34, 0x34, 0x35, 0x31, 0x35, 0x2c, 0x2d, 844 | 0x32, 0x2e, 0x38, 0x65, 0x2d, 0x34, 0x20, 0x30, 0x2e, 0x38, 0x39, 0x38, 0x35, 0x33, 845 | 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x35, 0x20, 0x31, 0x2e, 0x33, 0x34, 0x33, 0x37, 0x35, 0x2c, 0x30, 0x20, 0x33, 846 | 0x30, 0x2e, 0x33, 0x36, 0x33, 0x36, 0x33, 0x2c, 0x30, 0x2e, 0x30, 0x31, 0x38, 0x39, 847 | 0x20, 0x36, 0x30, 0x2e, 0x35, 0x36, 0x32, 0x33, 0x35, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x39, 0x33, 0x38, 0x30, 848 | 0x34, 0x20, 0x38, 0x38, 0x2e, 0x34, 0x33, 0x37, 0x35, 0x2c, 0x2d, 0x33, 0x39, 0x2e, 849 | 0x36, 0x38, 0x37, 0x35, 0x20, 0x32, 0x39, 0x2e, 0x35, 0x36, 0x39, 0x33, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 850 | 0x33, 0x38, 0x37, 0x30, 0x33, 0x20, 0x35, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x39, 0x37, 851 | 0x2c, 0x2d, 0x32, 0x35, 0x2e, 0x32, 0x31, 0x35, 0x36, 0x33, 0x20, 0x37, 0x31, 0x2e, 0x39, 0x33, 0x37, 0x35, 852 | 0x2c, 0x2d, 0x34, 0x30, 0x2e, 0x38, 0x34, 0x33, 0x37, 0x35, 0x20, 0x31, 0x34, 0x2e, 853 | 0x30, 0x30, 0x31, 0x31, 0x32, 0x2c, 0x2d, 0x33, 0x2e, 0x34, 0x39, 0x35, 0x38, 0x39, 0x20, 0x36, 0x37, 0x2e, 854 | 0x30, 0x38, 0x34, 0x39, 0x32, 0x2c, 0x2d, 0x33, 0x33, 0x2e, 0x30, 0x35, 0x36, 0x39, 855 | 0x32, 0x20, 0x37, 0x35, 0x2e, 0x32, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x2d, 0x39, 0x36, 0x2e, 0x31, 0x38, 0x37, 856 | 0x35, 0x20, 0x35, 0x2e, 0x34, 0x33, 0x37, 0x39, 0x31, 0x2c, 0x2d, 0x31, 0x35, 0x2e, 857 | 0x36, 0x36, 0x37, 0x32, 0x39, 0x20, 0x31, 0x31, 0x2e, 0x35, 0x34, 0x35, 0x36, 0x32, 0x2c, 0x2d, 0x32, 0x39, 858 | 0x2e, 0x38, 0x32, 0x32, 0x38, 0x37, 0x20, 0x31, 0x35, 0x2e, 0x31, 0x38, 0x37, 0x35, 859 | 0x2c, 0x2d, 0x34, 0x39, 0x2e, 0x35, 0x33, 0x31, 0x32, 0x35, 0x20, 0x35, 0x35, 0x2e, 0x37, 0x35, 0x34, 0x30, 860 | 0x34, 0x2c, 0x2d, 0x36, 0x32, 0x2e, 0x39, 0x36, 0x34, 0x39, 0x39, 0x20, 0x32, 0x30, 861 | 0x2e, 0x30, 0x39, 0x39, 0x36, 0x31, 0x2c, 0x2d, 0x31, 0x34, 0x30, 0x2e, 0x34, 0x32, 0x38, 0x39, 0x36, 0x20, 862 | 0x2d, 0x31, 0x39, 0x2e, 0x35, 0x33, 0x31, 0x32, 0x35, 0x2c, 0x2d, 0x31, 0x36, 0x34, 863 | 0x2e, 0x35, 0x33, 0x31, 0x32, 0x35, 0x20, 0x4c, 0x20, 0x35, 0x31, 0x33, 0x2e, 0x37, 0x35, 0x2c, 0x33, 0x31, 864 | 0x37, 0x2e, 0x35, 0x36, 0x32, 0x35, 0x20, 0x63, 0x20, 0x30, 0x2e, 0x32, 0x30, 0x30, 865 | 0x36, 0x37, 0x2c, 0x2d, 0x36, 0x34, 0x2e, 0x38, 0x35, 0x32, 0x33, 0x34, 0x20, 0x2d, 0x32, 0x39, 0x2e, 0x34, 866 | 0x33, 0x38, 0x30, 0x37, 0x2c, 0x2d, 0x39, 0x31, 0x2e, 0x32, 0x38, 0x35, 0x34, 0x36, 867 | 0x20, 0x2d, 0x36, 0x34, 0x2c, 0x2d, 0x31, 0x31, 0x31, 0x2e, 0x33, 0x37, 0x35, 0x20, 0x31, 0x30, 0x2e, 0x34, 868 | 0x35, 0x36, 0x36, 0x39, 0x2c, 0x2d, 0x32, 0x2e, 0x37, 0x39, 0x38, 0x34, 0x34, 0x20, 869 | 0x32, 0x31, 0x2e, 0x32, 0x35, 0x35, 0x35, 0x32, 0x2c, 0x2d, 0x35, 0x2e, 0x31, 0x30, 0x31, 0x34, 0x20, 0x32, 870 | 0x34, 0x2e, 0x32, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x2d, 0x31, 0x38, 0x2e, 0x34, 0x33, 871 | 0x37, 0x35, 0x20, 0x31, 0x37, 0x2e, 0x39, 0x36, 0x38, 0x33, 0x34, 0x2c, 0x2d, 0x34, 0x2e, 0x36, 0x36, 0x39, 872 | 0x38, 0x20, 0x32, 0x31, 0x2e, 0x37, 0x32, 0x37, 0x35, 0x38, 0x2c, 0x2d, 0x31, 0x33, 873 | 0x2e, 0x31, 0x31, 0x33, 0x38, 0x32, 0x20, 0x32, 0x33, 0x2e, 0x35, 0x33, 0x31, 0x32, 0x35, 0x2c, 0x2d, 0x32, 874 | 0x32, 0x2e, 0x30, 0x36, 0x32, 0x35, 0x20, 0x34, 0x2e, 0x36, 0x39, 0x37, 0x37, 0x35, 875 | 0x2c, 0x2d, 0x33, 0x2e, 0x31, 0x38, 0x31, 0x33, 0x32, 0x20, 0x32, 0x30, 0x2e, 0x34, 0x32, 0x38, 0x33, 0x34, 876 | 0x2c, 0x2d, 0x31, 0x32, 0x2e, 0x31, 0x31, 0x31, 0x31, 0x39, 0x20, 0x31, 0x38, 0x2e, 877 | 0x37, 0x38, 0x31, 0x32, 0x35, 0x2c, 0x2d, 0x32, 0x37, 0x2e, 0x34, 0x36, 0x38, 0x37, 0x35, 0x20, 0x38, 0x2e, 878 | 0x38, 0x30, 0x36, 0x36, 0x38, 0x2c, 0x2d, 0x36, 0x2e, 0x31, 0x37, 0x31, 0x36, 0x34, 879 | 0x20, 0x31, 0x33, 0x2e, 0x37, 0x31, 0x36, 0x36, 0x31, 0x2c, 0x2d, 0x31, 0x34, 0x2e, 0x30, 0x38, 0x39, 0x39, 880 | 0x20, 0x31, 0x31, 0x2e, 0x32, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x2d, 0x32, 0x35, 0x2e, 881 | 0x33, 0x31, 0x32, 0x35, 0x20, 0x39, 0x2e, 0x34, 0x36, 0x34, 0x39, 0x34, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x34, 882 | 0x38, 0x30, 0x39, 0x37, 0x20, 0x31, 0x31, 0x2e, 0x39, 0x35, 0x35, 0x35, 0x2c, 0x2d, 883 | 0x31, 0x39, 0x2e, 0x31, 0x34, 0x31, 0x34, 0x38, 0x37, 0x20, 0x37, 0x2e, 0x39, 0x33, 0x37, 0x35, 0x2c, 0x2d, 884 | 0x32, 0x37, 0x2e, 0x31, 0x32, 0x35, 0x20, 0x43, 0x20, 0x35, 0x34, 0x36, 0x2e, 0x37, 885 | 0x39, 0x35, 0x37, 0x35, 0x2c, 0x37, 0x31, 0x2e, 0x34, 0x34, 0x37, 0x32, 0x32, 0x36, 0x20, 0x35, 0x34, 0x31, 886 | 0x2e, 0x37, 0x36, 0x33, 0x31, 0x36, 0x2c, 0x36, 0x34, 0x2e, 0x30, 0x31, 0x38, 0x39, 887 | 0x35, 0x35, 0x20, 0x35, 0x33, 0x36, 0x2e, 0x39, 0x30, 0x36, 0x32, 0x35, 0x2c, 0x35, 0x36, 0x2e, 0x35, 0x20, 888 | 0x35, 0x34, 0x35, 0x2e, 0x34, 0x32, 0x31, 0x37, 0x38, 0x2c, 0x34, 0x30, 0x2e, 0x38, 889 | 0x33, 0x31, 0x36, 0x32, 0x35, 0x20, 0x35, 0x33, 0x37, 0x2e, 0x39, 0x30, 0x36, 0x36, 0x35, 0x2c, 0x32, 0x34, 890 | 0x2e, 0x30, 0x33, 0x30, 0x33, 0x32, 0x39, 0x20, 0x35, 0x31, 0x34, 0x2e, 0x31, 0x32, 891 | 0x35, 0x2c, 0x32, 0x36, 0x2e, 0x38, 0x34, 0x33, 0x37, 0x35, 0x20, 0x35, 0x30, 0x34, 0x2e, 0x36, 0x36, 0x32, 892 | 0x36, 0x2c, 0x31, 0x32, 0x2e, 0x37, 0x32, 0x37, 0x39, 0x33, 0x20, 0x34, 0x38, 0x34, 893 | 0x2e, 0x30, 0x32, 0x36, 0x33, 0x2c, 0x31, 0x35, 0x2e, 0x39, 0x33, 0x34, 0x31, 0x33, 0x35, 0x20, 0x34, 0x38, 894 | 0x30, 0x2e, 0x38, 0x34, 0x33, 0x37, 0x35, 0x2c, 0x31, 0x36, 0x20, 0x34, 0x37, 0x37, 895 | 0x2e, 0x32, 0x37, 0x32, 0x37, 0x39, 0x2c, 0x31, 0x31, 0x2e, 0x34, 0x36, 0x39, 0x34, 0x39, 0x34, 0x20, 0x34, 896 | 0x37, 0x32, 0x2e, 0x35, 0x35, 0x36, 0x33, 0x36, 0x2c, 0x37, 0x2e, 0x35, 0x38, 0x37, 897 | 0x39, 0x39, 0x32, 0x20, 0x34, 0x35, 0x38, 0x2e, 0x30, 0x36, 0x32, 0x35, 0x2c, 0x39, 0x2e, 0x34, 0x36, 0x38, 898 | 0x37, 0x35, 0x20, 0x34, 0x34, 0x38, 0x2e, 0x36, 0x37, 0x30, 0x38, 0x34, 0x2c, 0x30, 899 | 0x2e, 0x39, 0x36, 0x36, 0x35, 0x36, 0x31, 0x33, 0x32, 0x20, 0x34, 0x33, 0x38, 0x2e, 0x31, 0x37, 0x30, 0x37, 900 | 0x31, 0x2c, 0x32, 0x2e, 0x34, 0x31, 0x37, 0x34, 0x35, 0x20, 0x34, 0x32, 0x37, 0x2e, 901 | 0x33, 0x34, 0x33, 0x37, 0x35, 0x2c, 0x36, 0x2e, 0x35, 0x39, 0x33, 0x37, 0x35, 0x20, 0x34, 0x31, 0x34, 0x2e, 902 | 0x34, 0x38, 0x34, 0x35, 0x35, 0x2c, 0x2d, 0x33, 0x2e, 0x35, 0x35, 0x33, 0x36, 0x36, 903 | 0x33, 0x31, 0x20, 0x34, 0x30, 0x35, 0x2e, 0x39, 0x37, 0x31, 0x34, 0x39, 0x2c, 0x34, 0x2e, 0x35, 0x38, 0x30, 904 | 0x34, 0x35, 0x34, 0x20, 0x33, 0x39, 0x36, 0x2e, 0x32, 0x35, 0x2c, 0x37, 0x2e, 0x36, 905 | 0x35, 0x36, 0x32, 0x35, 0x20, 0x33, 0x38, 0x30, 0x2e, 0x36, 0x37, 0x36, 0x31, 0x35, 0x2c, 0x32, 0x2e, 0x35, 906 | 0x36, 0x38, 0x34, 0x37, 0x32, 0x20, 0x33, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x36, 0x39, 907 | 0x38, 0x2c, 0x39, 0x2e, 0x35, 0x33, 0x37, 0x31, 0x35, 0x37, 0x38, 0x20, 0x33, 0x36, 0x39, 0x2e, 0x34, 0x36, 908 | 0x38, 0x37, 0x35, 0x2c, 0x31, 0x32, 0x2e, 0x33, 0x37, 0x35, 0x20, 0x33, 0x35, 0x32, 909 | 0x2e, 0x34, 0x39, 0x33, 0x35, 0x2c, 0x38, 0x2e, 0x37, 0x38, 0x36, 0x39, 0x32, 0x33, 0x38, 0x20, 0x33, 0x34, 910 | 0x37, 0x2e, 0x33, 0x33, 0x33, 0x31, 0x35, 0x2c, 0x31, 0x36, 0x2e, 0x35, 0x39, 0x38, 911 | 0x35, 0x33, 0x32, 0x20, 0x33, 0x33, 0x39, 0x2e, 0x31, 0x38, 0x37, 0x35, 0x2c, 0x32, 0x34, 0x2e, 0x38, 0x34, 912 | 0x33, 0x37, 0x35, 0x20, 0x6c, 0x20, 0x2d, 0x39, 0x2e, 0x34, 0x36, 0x38, 0x37, 0x35, 913 | 0x2c, 0x2d, 0x30, 0x2e, 0x31, 0x38, 0x37, 0x35, 0x20, 0x63, 0x20, 0x2d, 0x32, 0x35, 0x2e, 0x36, 0x31, 0x30, 914 | 0x35, 0x34, 0x2c, 0x31, 0x35, 0x2e, 0x30, 0x39, 0x33, 0x31, 0x31, 0x35, 0x20, 0x2d, 915 | 0x33, 0x38, 0x2e, 0x33, 0x33, 0x33, 0x37, 0x38, 0x2c, 0x34, 0x35, 0x2e, 0x38, 0x32, 0x35, 0x35, 0x30, 0x31, 916 | 0x20, 0x2d, 0x34, 0x32, 0x2e, 0x38, 0x34, 0x33, 0x37, 0x35, 0x2c, 0x36, 0x31, 0x2e, 917 | 0x36, 0x32, 0x35, 0x20, 0x2d, 0x34, 0x2e, 0x35, 0x31, 0x32, 0x30, 0x36, 0x2c, 0x2d, 0x31, 0x35, 0x2e, 0x38, 918 | 0x30, 0x31, 0x39, 0x37, 0x39, 0x20, 0x2d, 0x31, 0x37, 0x2e, 0x32, 0x30, 0x36, 0x34, 919 | 0x37, 0x2c, 0x2d, 0x34, 0x36, 0x2e, 0x35, 0x33, 0x34, 0x35, 0x34, 0x32, 0x20, 0x2d, 0x34, 0x32, 0x2e, 0x38, 920 | 0x31, 0x32, 0x35, 0x2c, 0x2d, 0x36, 0x31, 0x2e, 0x36, 0x32, 0x35, 0x20, 0x6c, 0x20, 921 | 0x2d, 0x39, 0x2e, 0x34, 0x36, 0x38, 0x37, 0x35, 0x2c, 0x30, 0x2e, 0x31, 0x38, 0x37, 0x35, 0x20, 0x43, 0x20, 922 | 0x32, 0x32, 0x36, 0x2e, 0x34, 0x34, 0x38, 0x31, 0x2c, 0x31, 0x36, 0x2e, 0x35, 0x39, 923 | 0x38, 0x35, 0x33, 0x32, 0x20, 0x32, 0x32, 0x31, 0x2e, 0x32, 0x38, 0x37, 0x37, 0x35, 0x2c, 0x38, 0x2e, 0x37, 924 | 0x38, 0x36, 0x39, 0x32, 0x33, 0x35, 0x20, 0x32, 0x30, 0x34, 0x2e, 0x33, 0x31, 0x32, 925 | 0x35, 0x2c, 0x31, 0x32, 0x2e, 0x33, 0x37, 0x35, 0x20, 0x31, 0x39, 0x36, 0x2e, 0x36, 0x36, 0x34, 0x32, 0x37, 926 | 0x2c, 0x39, 0x2e, 0x35, 0x33, 0x37, 0x31, 0x35, 0x38, 0x33, 0x20, 0x31, 0x39, 0x33, 927 | 0x2e, 0x31, 0x30, 0x35, 0x31, 0x2c, 0x32, 0x2e, 0x35, 0x36, 0x38, 0x34, 0x37, 0x32, 0x39, 0x20, 0x31, 0x37, 928 | 0x37, 0x2e, 0x35, 0x33, 0x31, 0x32, 0x35, 0x2c, 0x37, 0x2e, 0x36, 0x35, 0x36, 0x32, 929 | 0x35, 0x20, 0x63, 0x20, 0x2d, 0x36, 0x2e, 0x33, 0x37, 0x39, 0x37, 0x33, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x31, 930 | 0x38, 0x34, 0x39, 0x31, 0x31, 0x20, 0x2d, 0x31, 0x32, 0x2e, 0x32, 0x34, 0x36, 0x36, 931 | 0x37, 0x2c, 0x2d, 0x36, 0x2e, 0x32, 0x31, 0x34, 0x34, 0x32, 0x37, 0x36, 0x20, 0x2d, 0x31, 0x39, 0x2e, 0x31, 932 | 0x35, 0x36, 0x32, 0x35, 0x2c, 0x2d, 0x36, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 0x79, 933 | 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x20, 934 | 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 935 | 0x20, 0x31, 0x30, 0x37, 0x2e, 0x33, 0x39, 0x31, 0x38, 0x34, 0x2c, 0x36, 0x38, 0x2e, 0x30, 0x35, 0x35, 0x35, 936 | 0x38, 0x33, 0x20, 0x63, 0x20, 0x36, 0x37, 0x2e, 0x39, 0x34, 0x37, 0x36, 0x37, 0x2c, 937 | 0x33, 0x35, 0x2e, 0x30, 0x33, 0x31, 0x33, 0x35, 0x37, 0x20, 0x31, 0x30, 0x37, 0x2e, 0x34, 0x34, 0x36, 0x38, 938 | 0x39, 0x2c, 0x36, 0x33, 0x2e, 0x33, 0x36, 0x38, 0x39, 0x36, 0x37, 0x20, 0x31, 0x32, 939 | 0x39, 0x2e, 0x30, 0x38, 0x37, 0x31, 0x37, 0x2c, 0x38, 0x37, 0x2e, 0x35, 0x30, 0x34, 0x34, 0x36, 0x37, 0x20, 940 | 0x2d, 0x31, 0x31, 0x2e, 0x30, 0x38, 0x32, 0x33, 0x35, 0x2c, 0x34, 0x34, 0x2e, 0x34, 941 | 0x31, 0x37, 0x35, 0x39, 0x20, 0x2d, 0x36, 0x38, 0x2e, 0x38, 0x39, 0x36, 0x33, 0x38, 0x2c, 0x34, 0x36, 0x2e, 942 | 0x34, 0x34, 0x34, 0x36, 0x34, 0x20, 0x2d, 0x39, 0x30, 0x2e, 0x30, 0x33, 0x35, 0x35, 943 | 0x39, 0x2c, 0x34, 0x35, 0x2e, 0x31, 0x39, 0x38, 0x35, 0x38, 0x20, 0x34, 0x2e, 0x33, 0x32, 0x38, 0x34, 0x32, 944 | 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x31, 0x34, 0x37, 0x34, 0x20, 0x37, 0x2e, 0x39, 0x33, 945 | 0x39, 0x38, 0x38, 0x2c, 0x2d, 0x34, 0x2e, 0x34, 0x32, 0x37, 0x37, 0x38, 0x20, 0x39, 0x2e, 0x32, 0x32, 0x30, 946 | 0x35, 0x31, 0x2c, 0x2d, 0x38, 0x2e, 0x31, 0x33, 0x35, 0x37, 0x34, 0x20, 0x2d, 0x35, 947 | 0x2e, 0x33, 0x30, 0x34, 0x34, 0x39, 0x2c, 0x2d, 0x33, 0x2e, 0x37, 0x36, 0x39, 0x38, 0x31, 0x20, 0x2d, 0x32, 948 | 0x34, 0x2e, 0x31, 0x31, 0x32, 0x38, 0x39, 0x2c, 0x2d, 0x30, 0x2e, 0x33, 0x39, 0x37, 949 | 0x31, 0x39, 0x20, 0x2d, 0x33, 0x37, 0x2e, 0x32, 0x34, 0x33, 0x36, 0x33, 0x2c, 0x2d, 0x37, 0x2e, 0x37, 0x37, 950 | 0x34, 0x31, 0x36, 0x20, 0x35, 0x2e, 0x30, 0x34, 0x34, 0x30, 0x37, 0x2c, 0x2d, 0x31, 951 | 0x2e, 0x30, 0x34, 0x34, 0x39, 0x39, 0x20, 0x37, 0x2e, 0x34, 0x30, 0x33, 0x34, 0x38, 0x2c, 0x2d, 0x32, 0x2e, 952 | 0x30, 0x36, 0x33, 0x30, 0x32, 0x20, 0x39, 0x2e, 0x37, 0x36, 0x32, 0x38, 0x39, 0x2c, 953 | 0x2d, 0x35, 0x2e, 0x37, 0x38, 0x35, 0x34, 0x32, 0x20, 0x2d, 0x31, 0x32, 0x2e, 0x34, 0x30, 0x35, 0x37, 0x31, 954 | 0x2c, 0x2d, 0x33, 0x2e, 0x39, 0x35, 0x36, 0x37, 0x20, 0x2d, 0x32, 0x35, 0x2e, 0x37, 955 | 0x36, 0x38, 0x36, 0x32, 0x2c, 0x2d, 0x37, 0x2e, 0x33, 0x36, 0x36, 0x34, 0x32, 0x20, 0x2d, 0x33, 0x33, 0x2e, 956 | 0x36, 0x32, 0x37, 0x37, 0x34, 0x36, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x39, 0x32, 0x31, 957 | 0x31, 0x36, 0x20, 0x34, 0x2e, 0x32, 0x34, 0x31, 0x32, 0x35, 0x33, 0x2c, 0x30, 0x2e, 0x30, 0x35, 0x32, 0x34, 958 | 0x20, 0x38, 0x2e, 0x32, 0x30, 0x31, 0x31, 0x35, 0x36, 0x2c, 0x30, 0x2e, 0x39, 0x34, 959 | 0x38, 0x38, 0x20, 0x31, 0x33, 0x2e, 0x37, 0x34, 0x30, 0x33, 0x36, 0x36, 0x2c, 0x2d, 0x32, 0x2e, 0x38, 0x39, 960 | 0x32, 0x37, 0x31, 0x20, 0x2d, 0x31, 0x31, 0x2e, 0x31, 0x31, 0x31, 0x36, 0x39, 0x34, 961 | 0x2c, 0x2d, 0x35, 0x2e, 0x39, 0x38, 0x38, 0x31, 0x39, 0x20, 0x2d, 0x32, 0x32, 0x2e, 0x39, 0x36, 0x39, 0x31, 962 | 0x30, 0x38, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x37, 0x33, 0x33, 0x35, 0x31, 0x20, 0x2d, 963 | 0x33, 0x32, 0x2e, 0x31, 0x38, 0x31, 0x33, 0x39, 0x2c, 0x2d, 0x31, 0x39, 0x2e, 0x38, 0x38, 0x37, 0x33, 0x38, 964 | 0x20, 0x35, 0x2e, 0x37, 0x34, 0x35, 0x32, 0x31, 0x33, 0x2c, 0x2d, 0x30, 0x2e, 0x31, 965 | 0x34, 0x30, 0x36, 0x33, 0x20, 0x31, 0x31, 0x2e, 0x39, 0x33, 0x39, 0x34, 0x35, 0x32, 0x2c, 0x2d, 0x30, 0x2e, 966 | 0x30, 0x35, 0x36, 0x38, 0x20, 0x31, 0x33, 0x2e, 0x37, 0x34, 0x30, 0x33, 0x37, 0x31, 967 | 0x2c, 0x2d, 0x32, 0x2e, 0x31, 0x36, 0x39, 0x35, 0x33, 0x20, 0x2d, 0x31, 0x30, 0x2e, 0x31, 0x37, 0x30, 0x34, 968 | 0x34, 0x2c, 0x2d, 0x36, 0x2e, 0x33, 0x30, 0x30, 0x36, 0x38, 0x20, 0x2d, 0x31, 0x38, 969 | 0x2e, 0x37, 0x35, 0x31, 0x32, 0x34, 0x32, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x33, 0x30, 0x37, 0x38, 0x37, 0x20, 970 | 0x2d, 0x32, 0x35, 0x2e, 0x38, 0x35, 0x33, 0x35, 0x39, 0x32, 0x2c, 0x2d, 0x32, 0x30, 971 | 0x2e, 0x39, 0x37, 0x32, 0x31, 0x35, 0x20, 0x38, 0x2e, 0x30, 0x33, 0x39, 0x39, 0x37, 0x39, 0x2c, 0x30, 0x2e, 972 | 0x39, 0x37, 0x30, 0x35, 0x32, 0x20, 0x31, 0x31, 0x2e, 0x34, 0x33, 0x35, 0x32, 0x38, 973 | 0x34, 0x2c, 0x30, 0x2e, 0x31, 0x33, 0x34, 0x37, 0x38, 0x20, 0x31, 0x33, 0x2e, 0x33, 0x37, 0x38, 0x37, 0x38, 974 | 0x32, 0x2c, 0x2d, 0x31, 0x2e, 0x32, 0x36, 0x35, 0x35, 0x36, 0x20, 0x2d, 0x37, 0x2e, 975 | 0x36, 0x38, 0x37, 0x37, 0x39, 0x35, 0x2c, 0x2d, 0x37, 0x2e, 0x38, 0x37, 0x34, 0x31, 0x39, 0x20, 0x2d, 0x31, 976 | 0x37, 0x2e, 0x34, 0x31, 0x37, 0x35, 0x35, 0x39, 0x2c, 0x2d, 0x31, 0x34, 0x2e, 0x35, 977 | 0x32, 0x33, 0x31, 0x39, 0x20, 0x2d, 0x32, 0x32, 0x2e, 0x30, 0x35, 0x36, 0x39, 0x31, 0x31, 0x2c, 0x2d, 0x32, 978 | 0x34, 0x2e, 0x32, 0x32, 0x36, 0x34, 0x34, 0x20, 0x35, 0x2e, 0x39, 0x36, 0x39, 0x36, 979 | 0x30, 0x36, 0x2c, 0x32, 0x2e, 0x30, 0x35, 0x37, 0x34, 0x38, 0x34, 0x20, 0x31, 0x31, 0x2e, 0x34, 0x33, 0x31, 980 | 0x32, 0x34, 0x39, 0x2c, 0x32, 0x2e, 0x38, 0x34, 0x35, 0x30, 0x36, 0x20, 0x31, 0x35, 981 | 0x2e, 0x33, 0x36, 0x37, 0x35, 0x32, 0x2c, 0x2d, 0x30, 0x2e, 0x31, 0x38, 0x30, 0x37, 0x39, 0x35, 0x20, 0x2d, 982 | 0x32, 0x2e, 0x36, 0x31, 0x32, 0x33, 0x36, 0x35, 0x2c, 0x2d, 0x35, 0x2e, 0x38, 0x39, 983 | 0x33, 0x34, 0x35, 0x33, 0x20, 0x2d, 0x31, 0x33, 0x2e, 0x38, 0x30, 0x35, 0x34, 0x31, 0x33, 0x2c, 0x2d, 0x39, 984 | 0x2e, 0x33, 0x36, 0x39, 0x36, 0x31, 0x38, 0x20, 0x2d, 0x32, 0x30, 0x2e, 0x32, 0x34, 985 | 0x38, 0x39, 0x36, 0x37, 0x2c, 0x2d, 0x32, 0x33, 0x2e, 0x31, 0x34, 0x31, 0x36, 0x37, 0x36, 0x20, 0x36, 0x2e, 986 | 0x32, 0x38, 0x34, 0x33, 0x35, 0x39, 0x2c, 0x30, 0x2e, 0x36, 0x30, 0x39, 0x33, 0x37, 987 | 0x37, 0x20, 0x31, 0x32, 0x2e, 0x39, 0x34, 0x39, 0x36, 0x30, 0x36, 0x2c, 0x31, 0x2e, 0x33, 0x37, 0x31, 0x31, 988 | 0x30, 0x38, 0x20, 0x31, 0x34, 0x2e, 0x32, 0x38, 0x32, 0x37, 0x35, 0x33, 0x2c, 0x30, 989 | 0x20, 0x43, 0x20, 0x36, 0x31, 0x2e, 0x38, 0x30, 0x32, 0x30, 0x36, 0x38, 0x2c, 0x35, 0x38, 0x2e, 0x35, 0x31, 990 | 0x37, 0x33, 0x34, 0x36, 0x20, 0x35, 0x36, 0x2e, 0x37, 0x39, 0x36, 0x39, 0x31, 0x39, 991 | 0x2c, 0x35, 0x31, 0x2e, 0x38, 0x33, 0x35, 0x38, 0x38, 0x35, 0x20, 0x35, 0x31, 0x2e, 0x38, 0x38, 0x37, 0x39, 992 | 0x37, 0x38, 0x2c, 0x34, 0x34, 0x2e, 0x39, 0x31, 0x33, 0x39, 0x30, 0x36, 0x20, 0x36, 993 | 0x35, 0x2e, 0x33, 0x33, 0x38, 0x30, 0x32, 0x31, 0x2c, 0x34, 0x34, 0x2e, 0x37, 0x31, 0x34, 0x31, 0x37, 0x37, 994 | 0x20, 0x38, 0x35, 0x2e, 0x37, 0x31, 0x35, 0x37, 0x33, 0x34, 0x2c, 0x34, 0x34, 0x2e, 995 | 0x39, 0x36, 0x36, 0x32, 0x35, 0x33, 0x20, 0x38, 0x34, 0x2e, 0x37, 0x39, 0x32, 0x35, 0x34, 0x39, 0x2c, 0x34, 996 | 0x33, 0x2e, 0x38, 0x32, 0x39, 0x31, 0x34, 0x20, 0x6c, 0x20, 0x2d, 0x38, 0x2e, 0x33, 997 | 0x31, 0x36, 0x35, 0x34, 0x2c, 0x2d, 0x38, 0x2e, 0x34, 0x39, 0x37, 0x33, 0x33, 0x35, 0x20, 0x63, 0x20, 0x31, 998 | 0x33, 0x2e, 0x31, 0x33, 0x37, 0x36, 0x31, 0x37, 0x2c, 0x2d, 0x33, 0x2e, 0x35, 0x33, 999 | 0x37, 0x32, 0x34, 0x31, 0x20, 0x32, 0x36, 0x2e, 0x35, 0x38, 0x30, 0x36, 0x35, 0x31, 0x2c, 0x30, 0x2e, 0x35, 1000 | 0x36, 0x38, 0x31, 0x36, 0x34, 0x20, 0x33, 0x36, 0x2e, 0x33, 0x33, 0x39, 0x36, 0x36, 1001 | 0x31, 0x2c, 0x33, 0x2e, 0x36, 0x31, 0x35, 0x38, 0x38, 0x37, 0x20, 0x34, 0x2e, 0x33, 0x38, 0x31, 0x38, 0x36, 1002 | 0x2c, 0x2d, 0x33, 0x2e, 0x34, 0x35, 0x37, 0x36, 0x38, 0x31, 0x20, 0x2d, 0x30, 0x2e, 1003 | 0x30, 0x37, 0x37, 0x36, 0x2c, 0x2d, 0x37, 0x2e, 0x38, 0x32, 0x39, 0x39, 0x38, 0x20, 0x2d, 0x35, 0x2e, 0x34, 1004 | 0x32, 0x33, 0x38, 0x33, 0x2c, 0x2d, 0x31, 0x32, 0x2e, 0x32, 0x39, 0x34, 0x30, 0x31, 1005 | 0x35, 0x20, 0x31, 0x31, 0x2e, 0x31, 0x36, 0x34, 0x39, 0x36, 0x2c, 0x31, 0x2e, 0x34, 0x39, 0x30, 0x36, 0x34, 1006 | 0x36, 0x20, 0x32, 0x31, 0x2e, 0x32, 0x35, 0x33, 0x38, 0x32, 0x2c, 0x34, 0x2e, 0x30, 1007 | 0x35, 0x37, 0x33, 0x38, 0x39, 0x20, 0x33, 0x30, 0x2e, 0x33, 0x37, 0x33, 0x34, 0x35, 0x2c, 0x37, 0x2e, 0x35, 1008 | 0x39, 0x33, 0x33, 0x36, 0x32, 0x20, 0x34, 0x2e, 0x38, 0x37, 0x32, 0x33, 0x38, 0x2c, 1009 | 0x2d, 0x34, 0x2e, 0x33, 0x39, 0x39, 0x33, 0x32, 0x39, 0x20, 0x2d, 0x33, 0x2e, 0x31, 0x36, 0x33, 0x38, 0x39, 1010 | 0x2c, 0x2d, 0x38, 0x2e, 0x37, 0x39, 0x38, 0x36, 0x35, 0x38, 0x20, 0x2d, 0x37, 0x2e, 1011 | 0x30, 0x35, 0x30, 0x39, 0x38, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x31, 0x39, 0x37, 0x39, 0x38, 0x37, 0x20, 0x31, 1012 | 0x37, 0x2e, 0x32, 0x34, 0x39, 0x33, 0x36, 0x2c, 0x33, 0x2e, 0x32, 0x37, 0x32, 0x35, 1013 | 0x36, 0x38, 0x20, 0x32, 0x34, 0x2e, 0x35, 0x35, 0x37, 0x31, 0x36, 0x2c, 0x37, 0x2e, 0x38, 0x37, 0x30, 0x36, 1014 | 0x38, 0x20, 0x33, 0x31, 0x2e, 0x38, 0x31, 0x39, 0x38, 0x31, 0x2c, 0x31, 0x32, 0x2e, 1015 | 0x34, 0x37, 0x34, 0x38, 0x31, 0x20, 0x35, 0x2e, 0x32, 0x36, 0x39, 0x33, 0x35, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 1016 | 0x35, 0x30, 0x37, 0x39, 0x39, 0x20, 0x30, 0x2e, 0x33, 0x30, 0x31, 0x36, 0x36, 0x2c, 1017 | 0x2d, 0x39, 0x2e, 0x33, 0x34, 0x33, 0x32, 0x39, 0x39, 0x20, 0x2d, 0x33, 0x2e, 0x32, 0x35, 0x34, 0x33, 0x2c, 1018 | 0x2d, 0x31, 0x33, 0x2e, 0x37, 0x34, 0x30, 0x33, 0x37, 0x31, 0x20, 0x31, 0x33, 0x2e, 1019 | 0x30, 0x30, 0x35, 0x36, 0x36, 0x2c, 0x34, 0x2e, 0x38, 0x31, 0x37, 0x30, 0x34, 0x38, 0x20, 0x31, 0x39, 0x2e, 1020 | 0x37, 0x30, 0x34, 0x37, 0x38, 0x2c, 0x31, 0x31, 0x2e, 0x30, 0x33, 0x35, 0x35, 0x35, 1021 | 0x31, 0x20, 0x32, 0x36, 0x2e, 0x37, 0x35, 0x37, 0x35, 0x36, 0x2c, 0x31, 0x37, 0x2e, 0x31, 0x37, 0x35, 0x34, 1022 | 0x36, 0x33, 0x20, 0x32, 0x2e, 0x33, 0x39, 0x31, 0x31, 0x39, 0x2c, 0x2d, 0x33, 0x2e, 1023 | 0x32, 0x32, 0x37, 0x30, 0x35, 0x33, 0x20, 0x36, 0x2e, 0x30, 0x37, 0x34, 0x39, 0x34, 0x2c, 0x2d, 0x35, 0x2e, 1024 | 0x35, 0x39, 0x32, 0x34, 0x30, 0x38, 0x20, 0x31, 0x2e, 0x36, 0x32, 0x37, 0x31, 0x35, 1025 | 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x33, 0x37, 0x38, 0x37, 0x38, 0x31, 0x20, 0x39, 0x2e, 0x32, 0x33, 0x34, 0x31, 1026 | 0x36, 0x2c, 0x35, 0x2e, 0x33, 0x32, 0x32, 0x37, 0x32, 0x35, 0x20, 0x31, 0x36, 0x2e, 1027 | 0x31, 0x38, 0x39, 0x32, 0x36, 0x2c, 0x31, 0x31, 0x2e, 0x35, 0x39, 0x35, 0x30, 0x36, 0x20, 0x32, 0x31, 0x2e, 1028 | 0x33, 0x33, 0x33, 0x37, 0x34, 0x2c, 0x31, 0x38, 0x2e, 0x36, 0x32, 0x31, 0x38, 0x31, 1029 | 0x37, 0x20, 0x35, 0x2e, 0x37, 0x31, 0x33, 0x33, 0x36, 0x2c, 0x2d, 0x33, 0x2e, 0x36, 0x33, 0x37, 0x39, 0x34, 1030 | 0x31, 0x20, 0x33, 0x2e, 0x34, 0x30, 0x33, 0x38, 0x37, 0x2c, 0x2d, 0x38, 0x2e, 0x36, 1031 | 0x31, 0x33, 0x30, 0x32, 0x33, 0x20, 0x33, 0x2e, 0x34, 0x33, 0x35, 0x30, 0x39, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 1032 | 0x31, 0x39, 0x37, 0x39, 0x38, 0x37, 0x20, 0x39, 0x2e, 0x35, 0x39, 0x36, 0x36, 0x35, 1033 | 0x2c, 0x37, 0x2e, 0x38, 0x30, 0x36, 0x35, 0x31, 0x36, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x38, 0x36, 0x38, 0x37, 1034 | 0x2c, 0x31, 0x36, 0x2e, 0x31, 0x31, 0x33, 0x39, 0x35, 0x20, 0x32, 0x33, 0x2e, 0x31, 1035 | 0x34, 0x31, 0x36, 0x38, 0x2c, 0x32, 0x34, 0x2e, 0x32, 0x32, 0x36, 0x34, 0x34, 0x33, 0x20, 0x31, 0x2e, 0x35, 1036 | 0x30, 0x31, 0x36, 0x39, 0x2c, 0x2d, 0x31, 0x2e, 0x30, 0x39, 0x33, 0x34, 0x33, 0x37, 1037 | 0x20, 0x32, 0x2e, 0x38, 0x31, 0x36, 0x36, 0x31, 0x2c, 0x2d, 0x34, 0x2e, 0x38, 0x30, 0x31, 0x37, 0x31, 0x20, 1038 | 0x33, 0x2e, 0x39, 0x37, 0x37, 0x34, 0x37, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x36, 0x36, 1039 | 0x36, 0x38, 0x36, 0x37, 0x20, 0x32, 0x32, 0x2e, 0x38, 0x39, 0x35, 0x33, 0x39, 0x2c, 0x32, 0x32, 0x2e, 0x32, 1040 | 0x31, 0x31, 0x38, 0x31, 0x35, 0x20, 0x35, 0x35, 0x2e, 0x32, 0x34, 0x35, 0x39, 0x31, 1041 | 0x2c, 0x37, 0x38, 0x2e, 0x31, 0x35, 0x38, 0x32, 0x34, 0x31, 0x20, 0x38, 0x2e, 0x33, 0x31, 0x36, 0x35, 0x34, 1042 | 0x2c, 0x31, 0x30, 0x30, 0x2e, 0x33, 0x34, 0x30, 0x38, 0x36, 0x31, 0x20, 0x43, 0x20, 1043 | 0x32, 0x30, 0x37, 0x2e, 0x39, 0x35, 0x30, 0x32, 0x38, 0x2c, 0x31, 0x30, 0x39, 0x2e, 0x39, 0x35, 0x37, 0x32, 1044 | 0x38, 0x20, 0x31, 0x36, 0x30, 0x2e, 0x32, 0x35, 0x32, 0x39, 0x32, 0x2c, 0x38, 0x36, 1045 | 0x2e, 0x30, 0x31, 0x36, 0x39, 0x30, 0x39, 0x20, 0x31, 0x30, 0x37, 0x2e, 0x33, 0x39, 0x31, 0x38, 0x34, 0x2c, 1046 | 0x36, 0x38, 0x2e, 0x30, 0x35, 0x35, 0x35, 0x38, 0x33, 0x20, 0x7a, 0x22, 0x20, 0x73, 1047 | 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x37, 0x35, 0x61, 0x39, 0x32, 0x38, 1048 | 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 1049 | 0x22, 0x4d, 0x20, 0x34, 0x36, 0x37, 0x2e, 0x39, 0x32, 0x34, 0x38, 0x37, 0x2c, 0x36, 0x38, 0x2e, 0x30, 0x35, 1050 | 0x35, 0x35, 0x38, 0x33, 0x20, 0x43, 0x20, 0x33, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x37, 1051 | 0x32, 0x2c, 0x31, 0x30, 0x33, 0x2e, 0x30, 0x38, 0x36, 0x39, 0x34, 0x20, 0x33, 0x36, 0x30, 0x2e, 0x34, 0x37, 1052 | 0x37, 0x39, 0x38, 0x2c, 0x31, 0x33, 0x31, 0x2e, 0x34, 0x32, 0x34, 0x35, 0x35, 0x20, 1053 | 0x33, 0x33, 0x38, 0x2e, 0x38, 0x33, 0x37, 0x37, 0x2c, 0x31, 0x35, 0x35, 0x2e, 0x35, 0x36, 0x30, 0x30, 0x35, 1054 | 0x20, 0x63, 0x20, 0x31, 0x31, 0x2e, 0x30, 0x38, 0x32, 0x33, 0x35, 0x2c, 0x34, 0x34, 1055 | 0x2e, 0x34, 0x31, 0x37, 0x35, 0x39, 0x20, 0x36, 0x38, 0x2e, 0x38, 0x39, 0x36, 0x33, 0x38, 0x2c, 0x34, 0x36, 1056 | 0x2e, 0x34, 0x34, 0x34, 0x36, 0x34, 0x20, 0x39, 0x30, 0x2e, 0x30, 0x33, 0x35, 0x35, 1057 | 0x39, 0x2c, 0x34, 0x35, 0x2e, 0x31, 0x39, 0x38, 0x35, 0x38, 0x20, 0x2d, 0x34, 0x2e, 0x33, 0x32, 0x38, 0x34, 1058 | 0x32, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x31, 0x34, 0x37, 0x34, 0x20, 0x2d, 0x37, 0x2e, 1059 | 0x39, 0x33, 0x39, 0x38, 0x38, 0x2c, 0x2d, 0x34, 0x2e, 0x34, 0x32, 0x37, 0x37, 0x38, 0x20, 0x2d, 0x39, 0x2e, 1060 | 0x32, 0x32, 0x30, 0x35, 0x31, 0x2c, 0x2d, 0x38, 0x2e, 0x31, 0x33, 0x35, 0x37, 0x34, 1061 | 0x20, 0x35, 0x2e, 0x33, 0x30, 0x34, 0x34, 0x39, 0x2c, 0x2d, 0x33, 0x2e, 0x37, 0x36, 0x39, 0x38, 0x31, 0x20, 1062 | 0x32, 0x34, 0x2e, 0x31, 0x31, 0x32, 0x38, 0x39, 0x2c, 0x2d, 0x30, 0x2e, 0x33, 0x39, 1063 | 0x37, 0x31, 0x39, 0x20, 0x33, 0x37, 0x2e, 0x32, 0x34, 0x33, 0x36, 0x33, 0x2c, 0x2d, 0x37, 0x2e, 0x37, 0x37, 1064 | 0x34, 0x31, 0x36, 0x20, 0x2d, 0x35, 0x2e, 0x30, 0x34, 0x34, 0x30, 0x37, 0x2c, 0x2d, 1065 | 0x31, 0x2e, 0x30, 0x34, 0x34, 0x39, 0x39, 0x20, 0x2d, 0x37, 0x2e, 0x34, 0x30, 0x33, 0x34, 0x38, 0x2c, 0x2d, 1066 | 0x32, 0x2e, 0x30, 0x36, 0x33, 0x30, 0x32, 0x20, 0x2d, 0x39, 0x2e, 0x37, 0x36, 0x32, 1067 | 0x38, 0x39, 0x2c, 0x2d, 0x35, 0x2e, 0x37, 0x38, 0x35, 0x34, 0x32, 0x20, 0x31, 0x32, 0x2e, 0x34, 0x30, 0x35, 1068 | 0x37, 0x31, 0x2c, 0x2d, 0x33, 0x2e, 0x39, 0x35, 0x36, 0x37, 0x20, 0x32, 0x35, 0x2e, 1069 | 0x37, 0x36, 0x38, 0x36, 0x32, 0x2c, 0x2d, 0x37, 0x2e, 0x33, 0x36, 0x36, 0x34, 0x32, 0x20, 0x33, 0x33, 0x2e, 1070 | 0x36, 0x32, 0x37, 0x37, 0x35, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x39, 0x32, 0x31, 0x31, 1071 | 0x36, 0x20, 0x2d, 0x34, 0x2e, 0x32, 0x34, 0x31, 0x32, 0x36, 0x2c, 0x30, 0x2e, 0x30, 0x35, 0x32, 0x34, 0x20, 1072 | 0x2d, 0x38, 0x2e, 0x32, 0x30, 0x31, 0x31, 0x36, 0x2c, 0x30, 0x2e, 0x39, 0x34, 0x38, 1073 | 0x38, 0x20, 0x2d, 0x31, 0x33, 0x2e, 0x37, 0x34, 0x30, 0x33, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x38, 0x39, 0x32, 1074 | 0x37, 0x31, 0x20, 0x31, 0x31, 0x2e, 0x31, 0x31, 0x31, 0x36, 0x39, 0x2c, 0x2d, 0x35, 1075 | 0x2e, 0x39, 0x38, 0x38, 0x31, 0x39, 0x20, 0x32, 0x32, 0x2e, 0x39, 0x36, 0x39, 0x31, 0x31, 0x2c, 0x2d, 0x31, 1076 | 0x30, 0x2e, 0x37, 0x33, 0x33, 0x35, 0x31, 0x20, 0x33, 0x32, 0x2e, 0x31, 0x38, 0x31, 1077 | 0x33, 0x39, 0x2c, 0x2d, 0x31, 0x39, 0x2e, 0x38, 0x38, 0x37, 0x33, 0x38, 0x20, 0x2d, 0x35, 0x2e, 0x37, 0x34, 1078 | 0x35, 0x32, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x31, 0x34, 0x30, 0x36, 0x33, 0x20, 0x2d, 1079 | 0x31, 0x31, 0x2e, 0x39, 0x33, 0x39, 0x34, 0x35, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x35, 0x36, 0x38, 0x20, 0x2d, 1080 | 0x31, 0x33, 0x2e, 0x37, 0x34, 0x30, 0x33, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x31, 0x36, 1081 | 0x39, 0x35, 0x33, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x37, 0x30, 0x34, 0x34, 0x2c, 0x2d, 0x36, 0x2e, 0x33, 0x30, 1082 | 0x30, 0x36, 0x38, 0x20, 0x31, 0x38, 0x2e, 0x37, 0x35, 0x31, 0x32, 0x34, 0x2c, 0x2d, 1083 | 0x31, 0x33, 0x2e, 0x33, 0x30, 0x37, 0x38, 0x37, 0x20, 0x32, 0x35, 0x2e, 0x38, 0x35, 0x33, 0x35, 0x39, 0x2c, 1084 | 0x2d, 0x32, 0x30, 0x2e, 0x39, 0x37, 0x32, 0x31, 0x35, 0x20, 0x2d, 0x38, 0x2e, 0x30, 1085 | 0x33, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x39, 0x37, 0x30, 0x35, 0x32, 0x20, 0x2d, 0x31, 0x31, 0x2e, 0x34, 1086 | 0x33, 0x35, 0x32, 0x38, 0x2c, 0x30, 0x2e, 0x31, 0x33, 0x34, 0x37, 0x38, 0x20, 0x2d, 1087 | 0x31, 0x33, 0x2e, 0x33, 0x37, 0x38, 0x37, 0x38, 0x2c, 0x2d, 0x31, 0x2e, 0x32, 0x36, 0x35, 0x35, 0x36, 0x20, 1088 | 0x37, 0x2e, 0x36, 0x38, 0x37, 0x37, 0x39, 0x2c, 0x2d, 0x37, 0x2e, 0x38, 0x37, 0x34, 1089 | 0x31, 0x39, 0x20, 0x31, 0x37, 0x2e, 0x34, 0x31, 0x37, 0x35, 0x36, 0x2c, 0x2d, 0x31, 0x34, 0x2e, 0x35, 0x32, 1090 | 0x33, 0x31, 0x39, 0x20, 0x32, 0x32, 0x2e, 0x30, 0x35, 0x36, 0x39, 0x31, 0x2c, 0x2d, 1091 | 0x32, 0x34, 0x2e, 0x32, 0x32, 0x36, 0x34, 0x34, 0x20, 0x2d, 0x35, 0x2e, 0x39, 0x36, 0x39, 0x36, 0x31, 0x2c, 1092 | 0x32, 0x2e, 0x30, 0x35, 0x37, 0x34, 0x38, 0x34, 0x20, 0x2d, 0x31, 0x31, 0x2e, 0x34, 1093 | 0x33, 0x31, 0x32, 0x35, 0x2c, 0x32, 0x2e, 0x38, 0x34, 0x35, 0x30, 0x36, 0x20, 0x2d, 0x31, 0x35, 0x2e, 0x33, 1094 | 0x36, 0x37, 0x35, 0x32, 0x2c, 0x2d, 0x30, 0x2e, 0x31, 0x38, 0x30, 0x37, 0x39, 0x35, 1095 | 0x20, 0x32, 0x2e, 0x36, 0x31, 0x32, 0x33, 0x37, 0x2c, 0x2d, 0x35, 0x2e, 0x38, 0x39, 0x33, 0x34, 0x35, 0x33, 1096 | 0x20, 0x31, 0x33, 0x2e, 0x38, 0x30, 0x35, 0x34, 0x31, 0x2c, 0x2d, 0x39, 0x2e, 0x33, 1097 | 0x36, 0x39, 0x36, 0x31, 0x38, 0x20, 0x32, 0x30, 0x2e, 0x32, 0x34, 0x38, 0x39, 0x37, 0x2c, 0x2d, 0x32, 0x33, 1098 | 0x2e, 0x31, 0x34, 0x31, 0x36, 0x37, 0x36, 0x20, 0x2d, 0x36, 0x2e, 0x32, 0x38, 0x34, 1099 | 0x33, 0x36, 0x2c, 0x30, 0x2e, 0x36, 0x30, 0x39, 0x33, 0x37, 0x37, 0x20, 0x2d, 0x31, 0x32, 0x2e, 0x39, 0x34, 1100 | 0x39, 0x36, 0x31, 0x2c, 0x31, 0x2e, 0x33, 0x37, 0x31, 0x31, 0x30, 0x38, 0x20, 0x2d, 1101 | 0x31, 0x34, 0x2e, 0x32, 0x38, 0x32, 0x37, 0x36, 0x2c, 0x30, 0x20, 0x32, 0x2e, 0x39, 0x32, 0x32, 0x33, 0x31, 1102 | 0x2c, 0x2d, 0x31, 0x31, 0x2e, 0x38, 0x38, 0x38, 0x35, 0x36, 0x33, 0x20, 0x37, 0x2e, 1103 | 0x39, 0x32, 0x37, 0x34, 0x36, 0x2c, 0x2d, 0x31, 0x38, 0x2e, 0x35, 0x37, 0x30, 0x30, 0x32, 0x34, 0x20, 0x31, 1104 | 0x32, 0x2e, 0x38, 0x33, 0x36, 0x34, 0x2c, 0x2d, 0x32, 0x35, 0x2e, 0x34, 0x39, 0x32, 1105 | 0x30, 0x30, 0x33, 0x20, 0x2d, 0x31, 0x33, 0x2e, 0x34, 0x35, 0x30, 0x30, 0x34, 0x2c, 0x2d, 0x30, 0x2e, 0x31, 1106 | 0x39, 0x39, 0x37, 0x32, 0x39, 0x20, 0x2d, 0x33, 0x33, 0x2e, 0x38, 0x32, 0x37, 0x37, 1107 | 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x35, 0x32, 0x33, 0x35, 0x20, 0x2d, 0x33, 0x32, 0x2e, 0x39, 0x30, 0x34, 0x35, 1108 | 0x37, 0x2c, 0x2d, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x37, 0x36, 0x36, 0x20, 0x6c, 0x20, 1109 | 0x38, 0x2e, 0x33, 0x31, 0x36, 0x35, 0x34, 0x2c, 0x2d, 0x38, 0x2e, 0x34, 0x39, 0x37, 0x33, 0x33, 0x35, 0x20, 1110 | 0x63, 0x20, 0x2d, 0x31, 0x33, 0x2e, 0x31, 0x33, 0x37, 0x36, 0x32, 0x2c, 0x2d, 0x33, 1111 | 0x2e, 0x35, 0x33, 0x37, 0x32, 0x34, 0x31, 0x20, 0x2d, 0x32, 0x36, 0x2e, 0x35, 0x38, 0x30, 0x36, 0x35, 0x2c, 1112 | 0x30, 0x2e, 0x35, 0x36, 0x38, 0x31, 0x36, 0x34, 0x20, 0x2d, 0x33, 0x36, 0x2e, 0x33, 1113 | 0x33, 0x39, 0x36, 0x36, 0x2c, 0x33, 0x2e, 0x36, 0x31, 0x35, 0x38, 0x38, 0x37, 0x20, 0x2d, 0x34, 0x2e, 0x33, 1114 | 0x38, 0x31, 0x38, 0x36, 0x2c, 0x2d, 0x33, 0x2e, 0x34, 0x35, 0x37, 0x36, 0x38, 0x31, 1115 | 0x20, 0x30, 0x2e, 0x30, 0x37, 0x37, 0x36, 0x2c, 0x2d, 0x37, 0x2e, 0x38, 0x32, 0x39, 0x39, 0x38, 0x20, 0x35, 1116 | 0x2e, 0x34, 0x32, 0x33, 0x38, 0x33, 0x2c, 0x2d, 0x31, 0x32, 0x2e, 0x32, 0x39, 0x34, 1117 | 0x30, 0x31, 0x35, 0x20, 0x2d, 0x31, 0x31, 0x2e, 0x31, 0x36, 0x34, 0x39, 0x36, 0x2c, 0x31, 0x2e, 0x34, 0x39, 1118 | 0x30, 0x36, 0x34, 0x36, 0x20, 0x2d, 0x32, 0x31, 0x2e, 0x32, 0x35, 0x33, 0x38, 0x32, 1119 | 0x2c, 0x34, 0x2e, 0x30, 0x35, 0x37, 0x33, 0x38, 0x39, 0x20, 0x2d, 0x33, 0x30, 0x2e, 0x33, 0x37, 0x33, 0x34, 1120 | 0x35, 0x2c, 0x37, 0x2e, 0x35, 0x39, 0x33, 0x33, 0x36, 0x32, 0x20, 0x2d, 0x34, 0x2e, 1121 | 0x38, 0x37, 0x32, 0x33, 0x38, 0x2c, 0x2d, 0x34, 0x2e, 0x33, 0x39, 0x39, 0x33, 0x32, 0x39, 0x20, 0x33, 0x2e, 1122 | 0x31, 0x36, 0x33, 0x38, 0x39, 0x2c, 0x2d, 0x38, 0x2e, 0x37, 0x39, 0x38, 0x36, 0x35, 1123 | 0x38, 0x20, 0x37, 0x2e, 0x30, 0x35, 0x30, 0x39, 0x38, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x31, 0x39, 0x37, 0x39, 1124 | 0x38, 0x37, 0x20, 0x2d, 0x31, 0x37, 0x2e, 0x32, 0x34, 0x39, 0x33, 0x36, 0x2c, 0x33, 1125 | 0x2e, 0x32, 0x37, 0x32, 0x35, 0x36, 0x38, 0x20, 0x2d, 0x32, 0x34, 0x2e, 0x35, 0x35, 0x37, 0x31, 0x36, 0x2c, 1126 | 0x37, 0x2e, 0x38, 0x37, 0x30, 0x36, 0x38, 0x20, 0x2d, 0x33, 0x31, 0x2e, 0x38, 0x31, 1127 | 0x39, 0x38, 0x31, 0x2c, 0x31, 0x32, 0x2e, 0x34, 0x37, 0x34, 0x38, 0x31, 0x20, 0x2d, 0x35, 0x2e, 0x32, 0x36, 1128 | 0x39, 0x33, 0x35, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x35, 0x30, 0x37, 0x39, 0x39, 0x20, 1129 | 0x2d, 0x30, 0x2e, 0x33, 0x30, 0x31, 0x36, 0x36, 0x2c, 0x2d, 0x39, 0x2e, 0x33, 0x34, 0x33, 0x32, 0x39, 0x39, 1130 | 0x20, 0x33, 0x2e, 0x32, 0x35, 0x34, 0x33, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x37, 0x34, 1131 | 0x30, 0x33, 0x37, 0x31, 0x20, 0x2d, 0x31, 0x33, 0x2e, 0x30, 0x30, 0x35, 0x36, 0x36, 0x2c, 0x34, 0x2e, 0x38, 1132 | 0x31, 0x37, 0x30, 0x34, 0x38, 0x20, 0x2d, 0x31, 0x39, 0x2e, 0x37, 0x30, 0x34, 0x37, 1133 | 0x38, 0x2c, 0x31, 0x31, 0x2e, 0x30, 0x33, 0x35, 0x35, 0x35, 0x31, 0x20, 0x2d, 0x32, 0x36, 0x2e, 0x37, 0x35, 1134 | 0x37, 0x35, 0x36, 0x2c, 0x31, 0x37, 0x2e, 0x31, 0x37, 0x35, 0x34, 0x36, 0x33, 0x20, 1135 | 0x2d, 0x32, 0x2e, 0x33, 0x39, 0x31, 0x31, 0x39, 0x2c, 0x2d, 0x33, 0x2e, 0x32, 0x32, 0x37, 0x30, 0x35, 0x33, 1136 | 0x20, 0x2d, 0x36, 0x2e, 0x30, 0x37, 0x34, 0x39, 0x34, 0x2c, 0x2d, 0x35, 0x2e, 0x35, 1137 | 0x39, 0x32, 0x34, 0x30, 0x38, 0x20, 0x2d, 0x31, 0x2e, 0x36, 0x32, 0x37, 0x31, 0x35, 0x2c, 0x2d, 0x31, 0x33, 1138 | 0x2e, 0x33, 0x37, 0x38, 0x37, 0x38, 0x31, 0x20, 0x2d, 0x39, 0x2e, 0x32, 0x33, 0x34, 1139 | 0x31, 0x36, 0x2c, 0x35, 0x2e, 0x33, 0x32, 0x32, 0x37, 0x32, 0x35, 0x20, 0x2d, 0x31, 0x36, 0x2e, 0x31, 0x38, 1140 | 0x39, 0x32, 0x36, 0x2c, 0x31, 0x31, 0x2e, 0x35, 0x39, 0x35, 0x30, 0x36, 0x20, 0x2d, 1141 | 0x32, 0x31, 0x2e, 0x33, 0x33, 0x33, 0x37, 0x34, 0x2c, 0x31, 0x38, 0x2e, 0x36, 0x32, 0x31, 0x38, 0x31, 0x37, 1142 | 0x20, 0x2d, 0x35, 0x2e, 0x37, 0x31, 0x33, 0x33, 0x36, 0x2c, 0x2d, 0x33, 0x2e, 0x36, 1143 | 0x33, 0x37, 0x39, 0x34, 0x31, 0x20, 0x2d, 0x33, 0x2e, 0x34, 0x30, 0x33, 0x38, 0x37, 0x2c, 0x2d, 0x38, 0x2e, 1144 | 0x36, 0x31, 0x33, 0x30, 0x32, 0x33, 0x20, 0x2d, 0x33, 0x2e, 0x34, 0x33, 0x35, 0x30, 1145 | 0x39, 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x31, 0x39, 0x37, 0x39, 0x38, 0x37, 0x20, 0x2d, 0x39, 0x2e, 0x35, 0x39, 1146 | 0x36, 0x36, 0x35, 0x2c, 0x37, 0x2e, 0x38, 0x30, 0x36, 0x35, 0x31, 0x36, 0x20, 0x2d, 1147 | 0x31, 0x35, 0x2e, 0x36, 0x38, 0x36, 0x38, 0x37, 0x2c, 0x31, 0x36, 0x2e, 0x31, 0x31, 0x33, 0x39, 0x35, 0x20, 1148 | 0x2d, 0x32, 0x33, 0x2e, 0x31, 0x34, 0x31, 0x36, 0x38, 0x2c, 0x32, 0x34, 0x2e, 0x32, 1149 | 0x32, 0x36, 0x34, 0x34, 0x33, 0x20, 0x2d, 0x31, 0x2e, 0x35, 0x30, 0x31, 0x36, 0x39, 0x2c, 0x2d, 0x31, 0x2e, 1150 | 0x30, 0x39, 0x33, 0x34, 0x33, 0x37, 0x20, 0x2d, 0x32, 0x2e, 0x38, 0x31, 0x36, 0x36, 1151 | 0x31, 0x2c, 0x2d, 0x34, 0x2e, 0x38, 0x30, 0x31, 0x37, 0x31, 0x20, 0x2d, 0x33, 0x2e, 0x39, 0x37, 0x37, 0x34, 1152 | 0x37, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x36, 0x36, 0x36, 0x38, 0x36, 0x37, 0x20, 0x2d, 1153 | 0x32, 0x32, 0x2e, 0x38, 0x39, 0x35, 0x33, 0x39, 0x2c, 0x32, 0x32, 0x2e, 0x32, 0x31, 0x31, 0x38, 0x31, 0x35, 1154 | 0x20, 0x2d, 0x35, 0x35, 0x2e, 0x32, 0x34, 0x35, 0x39, 0x31, 0x2c, 0x37, 0x38, 0x2e, 1155 | 0x31, 0x35, 0x38, 0x32, 0x34, 0x31, 0x20, 0x2d, 0x38, 0x2e, 0x33, 0x31, 0x36, 0x35, 0x34, 0x2c, 0x31, 0x30, 1156 | 0x30, 0x2e, 0x33, 0x34, 0x30, 0x38, 0x36, 0x31, 0x20, 0x33, 0x39, 0x2e, 0x39, 0x31, 1157 | 0x38, 0x37, 0x37, 0x2c, 0x2d, 0x33, 0x32, 0x2e, 0x39, 0x34, 0x37, 0x31, 0x36, 0x20, 0x38, 0x37, 0x2e, 0x36, 1158 | 0x31, 0x36, 0x31, 0x33, 0x2c, 0x2d, 0x35, 0x36, 0x2e, 0x38, 0x38, 0x37, 0x35, 0x33, 1159 | 0x31, 0x20, 0x31, 0x34, 0x30, 0x2e, 0x34, 0x37, 0x37, 0x32, 0x31, 0x2c, 0x2d, 0x37, 0x34, 0x2e, 0x38, 0x34, 1160 | 0x38, 0x38, 0x35, 0x37, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 1161 | 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x37, 0x35, 0x61, 0x39, 0x32, 0x38, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 1162 | 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 0x20, 0x33, 0x36, 1163 | 0x35, 0x2e, 0x32, 0x30, 0x34, 0x36, 0x2c, 0x35, 0x32, 0x31, 0x2e, 0x38, 0x34, 0x39, 0x33, 0x37, 0x20, 0x61, 1164 | 0x20, 0x37, 0x31, 0x2e, 0x39, 0x35, 0x36, 0x31, 0x35, 0x34, 0x2c, 0x36, 0x36, 0x2e, 1165 | 0x35, 0x33, 0x32, 0x33, 0x31, 0x38, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, 0x2d, 0x31, 0x34, 0x33, 0x2e, 1166 | 0x39, 0x31, 0x32, 0x33, 0x31, 0x2c, 0x30, 0x20, 0x37, 0x31, 0x2e, 0x39, 0x35, 0x36, 1167 | 0x31, 0x35, 0x34, 0x2c, 0x36, 0x36, 0x2e, 0x35, 0x33, 0x32, 0x33, 0x31, 0x38, 0x20, 0x30, 0x20, 0x31, 0x20, 1168 | 0x31, 0x20, 0x31, 0x34, 0x33, 0x2e, 0x39, 0x31, 0x32, 0x33, 0x31, 0x2c, 0x30, 0x20, 1169 | 0x7a, 0x22, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3d, 0x22, 0x6d, 0x61, 0x74, 0x72, 1170 | 0x69, 0x78, 0x28, 0x31, 0x2e, 0x31, 0x33, 0x31, 0x31, 0x30, 0x37, 0x2c, 0x30, 0x2c, 1171 | 0x30, 0x2c, 0x31, 0x2e, 0x31, 0x32, 0x38, 0x30, 0x34, 0x39, 0x37, 0x2c, 0x2d, 0x34, 0x33, 0x2e, 0x31, 0x33, 1172 | 0x39, 0x31, 0x33, 0x35, 0x2c, 0x2d, 0x36, 0x38, 0x2e, 0x33, 0x31, 0x30, 0x39, 0x38, 1173 | 0x33, 0x29, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 1174 | 0x63, 0x31, 0x31, 0x34, 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 1175 | 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 0x20, 0x32, 0x36, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x39, 0x31, 0x2c, 1176 | 0x32, 0x37, 0x36, 0x2e, 0x36, 0x34, 0x37, 0x37, 0x34, 0x20, 0x61, 0x20, 0x36, 0x31, 1177 | 0x2e, 0x38, 0x37, 0x35, 0x2c, 0x32, 0x38, 0x2e, 0x31, 0x32, 0x35, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, 1178 | 0x2d, 0x31, 0x32, 0x33, 0x2e, 0x37, 0x35, 0x2c, 0x30, 0x20, 0x36, 0x31, 0x2e, 0x38, 1179 | 0x37, 0x35, 0x2c, 0x32, 0x38, 0x2e, 0x31, 0x32, 0x35, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, 0x31, 0x32, 1180 | 0x33, 0x2e, 0x37, 0x35, 0x2c, 0x30, 0x20, 0x7a, 0x22, 0x20, 0x74, 0x72, 0x61, 0x6e, 1181 | 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3d, 0x22, 0x6d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x28, 0x30, 0x2e, 0x37, 0x36, 1182 | 0x37, 0x34, 0x31, 0x36, 0x38, 0x34, 0x2c, 0x2d, 0x31, 0x2e, 0x31, 0x36, 0x31, 0x33, 1183 | 0x31, 0x31, 0x32, 0x2c, 0x32, 0x2e, 0x31, 0x37, 0x31, 0x31, 0x31, 0x35, 0x2c, 0x31, 0x2e, 0x34, 0x32, 0x32, 1184 | 0x34, 0x33, 0x36, 0x38, 0x2c, 0x2d, 0x35, 0x36, 0x30, 0x2e, 0x38, 0x38, 0x38, 0x35, 1185 | 0x38, 0x2c, 0x32, 0x31, 0x37, 0x2e, 0x36, 0x38, 0x38, 0x35, 0x39, 0x29, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 1186 | 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 0x31, 0x34, 1187 | 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 0x20, 1188 | 0x32, 0x36, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x39, 0x31, 0x2c, 0x32, 0x37, 0x36, 0x2e, 1189 | 0x36, 0x34, 0x37, 0x37, 0x34, 0x20, 0x61, 0x20, 0x36, 0x31, 0x2e, 0x38, 0x37, 0x35, 0x2c, 0x32, 0x38, 0x2e, 1190 | 0x31, 0x32, 0x35, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, 0x2d, 0x31, 0x32, 0x33, 1191 | 0x2e, 0x37, 0x35, 0x2c, 0x30, 0x20, 0x36, 0x31, 0x2e, 0x38, 0x37, 0x35, 0x2c, 0x32, 0x38, 0x2e, 0x31, 0x32, 1192 | 0x35, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, 0x31, 0x32, 0x33, 0x2e, 0x37, 0x35, 1193 | 0x2c, 0x30, 0x20, 0x7a, 0x22, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3d, 0x22, 0x6d, 1194 | 0x61, 0x74, 0x72, 0x69, 0x78, 0x28, 0x2d, 0x30, 0x2e, 0x37, 0x36, 0x37, 0x34, 0x31, 1195 | 0x36, 0x38, 0x34, 0x2c, 0x2d, 0x31, 0x2e, 0x31, 0x36, 0x31, 0x33, 0x31, 0x31, 0x32, 0x2c, 0x2d, 0x32, 0x2e, 1196 | 0x31, 0x37, 0x31, 0x31, 0x31, 0x35, 0x2c, 0x31, 0x2e, 0x34, 0x32, 0x32, 0x34, 0x33, 1197 | 0x36, 0x38, 0x2c, 0x31, 0x31, 0x33, 0x34, 0x2e, 0x38, 0x32, 0x38, 0x38, 0x2c, 0x32, 0x31, 0x33, 0x2e, 0x36, 1198 | 0x38, 0x38, 0x35, 0x39, 0x29, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 1199 | 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 0x31, 0x34, 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 1200 | 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x20, 0x37, 0x32, 0x2e, 1201 | 0x39, 0x31, 0x30, 0x32, 0x35, 0x33, 0x2c, 0x33, 0x34, 0x32, 0x2e, 0x30, 0x38, 0x37, 0x38, 0x20, 0x43, 0x20, 1202 | 0x31, 0x30, 0x39, 0x2e, 0x33, 0x32, 0x34, 0x34, 0x37, 0x2c, 0x33, 0x33, 0x32, 0x2e, 1203 | 0x33, 0x33, 0x30, 0x38, 0x38, 0x20, 0x38, 0x35, 0x2e, 0x32, 0x30, 0x31, 0x38, 0x34, 0x35, 0x2c, 0x34, 0x39, 1204 | 0x32, 0x2e, 0x37, 0x32, 0x34, 0x33, 0x31, 0x20, 0x35, 0x35, 0x2e, 0x35, 0x37, 0x36, 1205 | 0x38, 0x37, 0x31, 0x2c, 0x34, 0x37, 0x39, 0x2e, 0x35, 0x36, 0x33, 0x35, 0x37, 0x20, 0x32, 0x32, 0x2e, 0x39, 1206 | 0x39, 0x30, 0x31, 0x30, 0x33, 0x2c, 0x34, 0x35, 0x33, 0x2e, 0x33, 0x35, 0x30, 0x38, 1207 | 0x39, 0x20, 0x31, 0x32, 0x2e, 0x34, 0x39, 0x33, 0x38, 0x30, 0x31, 0x2c, 0x33, 0x37, 0x36, 0x2e, 0x35, 0x38, 1208 | 0x38, 0x31, 0x34, 0x20, 0x37, 0x32, 0x2e, 0x39, 0x31, 0x30, 0x32, 0x35, 0x33, 0x2c, 1209 | 0x33, 0x34, 0x32, 0x2e, 0x30, 0x38, 0x37, 0x38, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 1210 | 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 0x31, 0x34, 0x32, 0x22, 1211 | 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 0x20, 0x34, 0x39, 1212 | 0x33, 0x2e, 0x36, 0x37, 0x38, 0x32, 0x38, 0x2c, 0x33, 0x34, 0x30, 0x2e, 0x30, 0x38, 1213 | 0x37, 0x38, 0x20, 0x63, 0x20, 0x2d, 0x33, 0x36, 0x2e, 0x34, 0x31, 0x34, 0x32, 0x32, 0x2c, 0x2d, 0x39, 0x2e, 1214 | 0x37, 0x35, 0x36, 0x39, 0x32, 0x20, 0x2d, 0x31, 0x32, 0x2e, 0x32, 0x39, 0x31, 0x36, 1215 | 0x2c, 0x31, 0x35, 0x30, 0x2e, 0x36, 0x33, 0x36, 0x35, 0x31, 0x20, 0x31, 0x37, 0x2e, 0x33, 0x33, 0x33, 0x33, 1216 | 0x38, 0x2c, 0x31, 0x33, 0x37, 0x2e, 0x34, 0x37, 0x35, 0x37, 0x37, 0x20, 0x33, 0x32, 1217 | 0x2e, 0x35, 0x38, 0x36, 0x37, 0x37, 0x2c, 0x2d, 0x32, 0x36, 0x2e, 0x32, 0x31, 0x32, 0x36, 0x38, 0x20, 0x34, 1218 | 0x33, 0x2e, 0x30, 0x38, 0x33, 0x30, 0x37, 0x2c, 0x2d, 0x31, 0x30, 0x32, 0x2e, 0x39, 1219 | 0x37, 0x35, 0x34, 0x33, 0x20, 0x2d, 0x31, 0x37, 0x2e, 0x33, 0x33, 0x33, 0x33, 0x38, 0x2c, 0x2d, 0x31, 0x33, 1220 | 0x37, 0x2e, 0x34, 0x37, 0x35, 0x37, 0x37, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 0x79, 1221 | 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 0x31, 0x34, 0x32, 0x22, 0x20, 1222 | 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 1223 | 0x20, 0x33, 0x36, 0x39, 0x2e, 0x39, 0x37, 0x31, 0x35, 0x38, 0x2c, 0x32, 0x32, 0x30, 0x2e, 0x36, 0x35, 0x33, 1224 | 0x34, 0x20, 0x63, 0x20, 0x36, 0x32, 0x2e, 0x38, 0x33, 0x34, 0x38, 0x36, 0x2c, 0x2d, 1225 | 0x31, 0x30, 0x2e, 0x36, 0x31, 0x30, 0x31, 0x33, 0x20, 0x31, 0x31, 0x35, 0x2e, 0x31, 0x31, 0x35, 0x39, 0x34, 1226 | 0x2c, 0x32, 0x36, 0x2e, 0x37, 0x32, 0x32, 0x32, 0x39, 0x20, 0x31, 0x31, 0x33, 0x2e, 1227 | 0x30, 0x31, 0x31, 0x33, 0x38, 0x2c, 0x39, 0x34, 0x2e, 0x38, 0x35, 0x37, 0x39, 0x36, 0x20, 0x2d, 0x32, 0x2e, 1228 | 0x30, 0x36, 0x36, 0x39, 0x33, 0x2c, 0x32, 0x36, 0x2e, 0x31, 0x32, 0x31, 0x31, 0x32, 1229 | 0x20, 0x2d, 0x31, 0x33, 0x36, 0x2e, 0x31, 0x35, 0x38, 0x37, 0x32, 0x2c, 0x2d, 0x39, 0x30, 0x2e, 0x39, 0x36, 1230 | 0x39, 0x30, 0x37, 0x20, 0x2d, 0x31, 0x31, 0x33, 0x2e, 0x30, 0x31, 0x31, 0x33, 0x38, 1231 | 0x2c, 0x2d, 0x39, 0x34, 0x2e, 0x38, 0x35, 0x37, 0x39, 0x36, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 1232 | 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 0x31, 0x34, 1233 | 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x20, 1234 | 0x31, 0x39, 0x36, 0x2e, 0x33, 0x35, 0x39, 0x37, 0x35, 0x2c, 0x32, 0x31, 0x38, 0x2e, 1235 | 0x36, 0x35, 0x33, 0x34, 0x20, 0x43, 0x20, 0x31, 0x33, 0x33, 0x2e, 0x35, 0x32, 0x34, 0x38, 0x39, 0x2c, 0x32, 1236 | 0x30, 0x38, 0x2e, 0x30, 0x34, 0x33, 0x32, 0x37, 0x20, 0x38, 0x31, 0x2e, 0x32, 0x34, 1237 | 0x33, 0x38, 0x31, 0x2c, 0x32, 0x34, 0x35, 0x2e, 0x33, 0x37, 0x35, 0x36, 0x39, 0x20, 0x38, 0x33, 0x2e, 0x33, 1238 | 0x34, 0x38, 0x33, 0x37, 0x2c, 0x33, 0x31, 0x33, 0x2e, 0x35, 0x31, 0x31, 0x33, 0x36, 1239 | 0x20, 0x38, 0x35, 0x2e, 0x34, 0x31, 0x35, 0x33, 0x2c, 0x33, 0x33, 0x39, 0x2e, 0x36, 0x33, 0x32, 0x34, 0x38, 1240 | 0x20, 0x32, 0x31, 0x39, 0x2e, 0x35, 0x30, 0x37, 0x30, 0x39, 0x2c, 0x32, 0x32, 0x32, 1241 | 0x2e, 0x35, 0x34, 0x32, 0x32, 0x39, 0x20, 0x31, 0x39, 0x36, 0x2e, 0x33, 0x35, 0x39, 0x37, 0x35, 0x2c, 0x32, 1242 | 0x31, 0x38, 0x2e, 0x36, 0x35, 0x33, 0x34, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 0x79, 1243 | 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 0x31, 0x34, 0x32, 0x22, 0x20, 1244 | 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 1245 | 0x20, 0x32, 0x38, 0x36, 0x2e, 0x36, 0x31, 0x39, 0x33, 0x32, 0x2c, 0x32, 0x30, 0x32, 0x2e, 0x37, 0x35, 0x35, 1246 | 0x36, 0x38, 0x20, 0x63, 0x20, 0x2d, 0x33, 0x37, 0x2e, 0x35, 0x30, 0x32, 0x35, 0x39, 1247 | 0x2c, 0x2d, 0x30, 0x2e, 0x39, 0x37, 0x35, 0x34, 0x38, 0x20, 0x2d, 0x37, 0x33, 0x2e, 0x34, 0x39, 0x35, 0x34, 1248 | 0x38, 0x2c, 0x32, 0x37, 0x2e, 0x38, 0x33, 0x34, 0x31, 0x38, 0x20, 0x2d, 0x37, 0x33, 1249 | 0x2e, 0x35, 0x38, 0x31, 0x35, 0x38, 0x2c, 0x34, 0x34, 0x2e, 0x35, 0x34, 0x34, 0x34, 0x33, 0x20, 0x2d, 0x30, 1250 | 0x2e, 0x31, 0x30, 0x34, 0x36, 0x32, 0x2c, 0x32, 0x30, 0x2e, 0x33, 0x30, 0x34, 0x32, 1251 | 0x36, 0x20, 0x32, 0x39, 0x2e, 0x36, 0x35, 0x31, 0x32, 0x2c, 0x34, 0x31, 0x2e, 0x30, 0x39, 0x32, 0x36, 0x36, 1252 | 0x20, 0x37, 0x33, 0x2e, 0x38, 0x33, 0x37, 0x32, 0x36, 0x2c, 0x34, 0x31, 0x2e, 0x36, 1253 | 0x32, 0x30, 0x33, 0x35, 0x20, 0x34, 0x35, 0x2e, 0x31, 0x32, 0x33, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x33, 0x32, 1254 | 0x33, 0x32, 0x31, 0x20, 0x37, 0x33, 0x2e, 0x39, 0x31, 0x35, 0x36, 0x31, 0x2c, 0x2d, 1255 | 0x31, 0x36, 0x2e, 0x36, 0x34, 0x30, 0x34, 0x39, 0x20, 0x37, 0x34, 0x2e, 0x30, 0x36, 0x31, 0x31, 0x2c, 0x2d, 1256 | 0x33, 0x37, 0x2e, 0x35, 0x39, 0x34, 0x30, 0x39, 0x20, 0x30, 0x2e, 0x31, 0x36, 0x34, 1257 | 0x38, 0x34, 0x2c, 0x2d, 0x32, 0x33, 0x2e, 0x37, 0x33, 0x39, 0x39, 0x36, 0x20, 0x2d, 0x34, 0x31, 0x2e, 0x30, 1258 | 0x33, 0x38, 0x37, 0x39, 0x2c, 0x2d, 0x34, 0x38, 0x2e, 0x39, 0x33, 0x37, 0x34, 0x34, 1259 | 0x20, 0x2d, 0x37, 0x34, 0x2e, 0x33, 0x31, 0x36, 0x37, 0x38, 0x2c, 0x2d, 0x34, 0x38, 0x2e, 0x35, 0x37, 0x30, 1260 | 0x36, 0x39, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 1261 | 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 0x31, 0x34, 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 1262 | 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x6d, 0x20, 0x32, 0x38, 0x38, 0x2e, 1263 | 0x39, 0x30, 0x39, 0x33, 0x37, 0x2c, 0x36, 0x31, 0x39, 0x2e, 0x31, 0x31, 0x36, 0x37, 0x35, 0x20, 0x63, 0x20, 1264 | 0x33, 0x32, 0x2e, 0x36, 0x39, 0x37, 0x34, 0x34, 0x2c, 0x2d, 0x31, 0x2e, 0x34, 0x32, 1265 | 0x37, 0x31, 0x31, 0x20, 0x37, 0x36, 0x2e, 0x35, 0x37, 0x30, 0x38, 0x33, 0x2c, 0x31, 0x30, 0x2e, 0x35, 0x33, 1266 | 0x31, 0x39, 0x36, 0x20, 0x37, 0x36, 0x2e, 0x36, 0x35, 0x36, 0x38, 0x2c, 0x32, 0x36, 1267 | 0x2e, 0x33, 0x39, 0x35, 0x39, 0x38, 0x20, 0x30, 0x2e, 0x35, 0x34, 0x32, 0x37, 0x2c, 0x31, 0x35, 0x2e, 0x34, 1268 | 0x30, 0x35, 0x32, 0x20, 0x2d, 0x33, 0x39, 0x2e, 0x37, 0x38, 0x39, 0x36, 0x39, 0x2c, 1269 | 0x35, 0x30, 0x2e, 0x32, 0x31, 0x30, 0x35, 0x35, 0x20, 0x2d, 0x37, 0x38, 0x2e, 0x38, 0x32, 0x36, 0x33, 0x34, 1270 | 0x2c, 0x34, 0x39, 0x2e, 0x35, 0x33, 0x37, 0x36, 0x35, 0x20, 0x2d, 0x34, 0x30, 0x2e, 1271 | 0x34, 0x32, 0x37, 0x32, 0x39, 0x2c, 0x31, 0x2e, 0x37, 0x34, 0x33, 0x39, 0x31, 0x20, 0x2d, 0x38, 0x30, 0x2e, 1272 | 0x30, 0x36, 0x39, 0x30, 0x38, 0x2c, 0x2d, 0x33, 0x33, 0x2e, 0x31, 0x31, 0x35, 0x35, 1273 | 0x39, 0x20, 0x2d, 0x37, 0x39, 0x2e, 0x35, 0x34, 0x39, 0x35, 0x31, 0x2c, 0x2d, 0x34, 0x35, 0x2e, 0x31, 0x39, 1274 | 0x38, 0x35, 0x39, 0x20, 0x2d, 0x30, 0x2e, 0x36, 0x30, 0x35, 0x30, 0x36, 0x2c, 0x2d, 1275 | 0x31, 0x37, 0x2e, 0x37, 0x31, 0x35, 0x39, 0x33, 0x20, 0x34, 0x39, 0x2e, 0x32, 0x32, 0x36, 0x2c, 0x2d, 0x33, 1276 | 0x31, 0x2e, 0x35, 0x34, 0x37, 0x39, 0x36, 0x20, 0x38, 0x31, 0x2e, 0x37, 0x31, 0x39, 1277 | 0x30, 0x35, 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x37, 0x33, 0x35, 0x30, 0x34, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 1278 | 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 1279 | 0x31, 0x34, 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 1280 | 0x6d, 0x20, 0x31, 0x36, 0x38, 0x2e, 0x31, 0x33, 0x38, 0x37, 0x34, 0x2c, 0x35, 0x32, 1281 | 0x35, 0x2e, 0x31, 0x30, 0x33, 0x36, 0x39, 0x20, 0x63, 0x20, 0x32, 0x33, 0x2e, 0x32, 0x37, 0x39, 0x31, 0x2c, 1282 | 0x32, 0x38, 0x2e, 0x30, 0x34, 0x35, 0x37, 0x33, 0x20, 0x33, 0x33, 0x2e, 0x38, 0x39, 1283 | 0x30, 0x36, 0x36, 0x2c, 0x37, 0x37, 0x2e, 0x33, 0x31, 0x38, 0x39, 0x39, 0x20, 0x31, 0x34, 0x2e, 0x34, 0x36, 1284 | 0x33, 0x35, 0x35, 0x2c, 0x39, 0x31, 0x2e, 0x38, 0x34, 0x33, 0x35, 0x33, 0x20, 0x2d, 1285 | 0x31, 0x38, 0x2e, 0x33, 0x37, 0x39, 0x31, 0x37, 0x2c, 0x31, 0x31, 0x2e, 0x30, 0x38, 0x37, 0x38, 0x34, 0x20, 1286 | 0x2d, 0x36, 0x33, 0x2e, 0x30, 0x31, 0x32, 0x32, 0x38, 0x2c, 0x36, 0x2e, 0x35, 0x32, 1287 | 0x31, 0x36, 0x32, 0x20, 0x2d, 0x39, 0x34, 0x2e, 0x37, 0x33, 0x36, 0x32, 0x33, 0x37, 0x2c, 0x2d, 0x33, 0x39, 1288 | 0x2e, 0x30, 0x35, 0x31, 0x35, 0x37, 0x20, 0x2d, 0x32, 0x31, 0x2e, 0x33, 0x39, 0x35, 1289 | 0x30, 0x35, 0x32, 0x2c, 0x2d, 0x33, 0x38, 0x2e, 0x32, 0x34, 0x31, 0x36, 0x38, 0x20, 0x2d, 0x31, 0x38, 0x2e, 1290 | 0x36, 0x33, 0x37, 0x35, 0x38, 0x34, 0x2c, 0x2d, 0x37, 0x37, 0x2e, 0x31, 0x35, 0x36, 1291 | 0x36, 0x33, 0x20, 0x2d, 0x33, 0x2e, 0x36, 0x31, 0x35, 0x38, 0x38, 0x37, 0x2c, 0x2d, 0x38, 0x38, 0x2e, 0x35, 1292 | 0x38, 0x39, 0x32, 0x34, 0x20, 0x32, 0x32, 0x2e, 0x34, 0x36, 0x34, 0x34, 0x32, 0x34, 1293 | 0x2c, 0x2d, 0x31, 0x33, 0x2e, 0x36, 0x38, 0x34, 0x32, 0x39, 0x20, 0x35, 0x37, 0x2e, 0x31, 0x37, 0x33, 0x34, 1294 | 0x32, 0x34, 0x2c, 0x34, 0x2e, 0x37, 0x39, 0x39, 0x30, 0x32, 0x20, 0x38, 0x33, 0x2e, 1295 | 0x38, 0x38, 0x38, 0x35, 0x37, 0x34, 0x2c, 0x33, 0x35, 0x2e, 0x37, 0x39, 0x37, 0x32, 0x38, 0x20, 0x7a, 0x22, 1296 | 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 1297 | 0x62, 0x63, 0x31, 0x31, 0x34, 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 1298 | 0x64, 0x3d, 0x22, 0x6d, 0x20, 0x34, 0x30, 0x35, 0x2e, 0x30, 0x32, 0x30, 0x39, 0x2c, 1299 | 0x35, 0x31, 0x36, 0x2e, 0x32, 0x31, 0x31, 0x37, 0x37, 0x20, 0x63, 0x20, 0x2d, 0x32, 0x35, 0x2e, 0x31, 0x38, 1300 | 0x36, 0x38, 0x32, 0x2c, 0x32, 0x39, 0x2e, 0x35, 0x30, 0x31, 0x36, 0x35, 0x20, 0x2d, 1301 | 0x33, 0x39, 0x2e, 0x32, 0x31, 0x32, 0x32, 0x37, 0x2c, 0x38, 0x33, 0x2e, 0x33, 0x30, 0x39, 0x35, 0x31, 0x20, 1302 | 0x2d, 0x32, 0x30, 0x2e, 0x38, 0x33, 0x37, 0x38, 0x35, 0x2c, 0x31, 0x30, 0x30, 0x2e, 1303 | 0x36, 0x34, 0x32, 0x38, 0x20, 0x31, 0x37, 0x2e, 0x35, 0x36, 0x38, 0x32, 0x38, 0x2c, 0x31, 0x33, 0x2e, 0x34, 1304 | 0x36, 0x33, 0x36, 0x31, 0x20, 0x36, 0x34, 0x2e, 0x37, 0x32, 0x39, 0x32, 0x2c, 0x31, 1305 | 0x31, 0x2e, 0x35, 0x38, 0x31, 0x36, 0x32, 0x20, 0x39, 0x39, 0x2e, 0x35, 0x36, 0x35, 0x36, 0x36, 0x2c, 0x2d, 1306 | 0x33, 0x36, 0x2e, 0x37, 0x35, 0x35, 0x37, 0x34, 0x20, 0x32, 0x35, 0x2e, 0x32, 0x39, 1307 | 0x35, 0x39, 0x39, 0x2c, 0x2d, 0x33, 0x32, 0x2e, 0x34, 0x36, 0x34, 0x37, 0x31, 0x20, 0x31, 0x36, 0x2e, 0x38, 1308 | 0x32, 0x30, 0x31, 0x33, 0x2c, 0x2d, 0x38, 0x36, 0x2e, 0x36, 0x38, 0x32, 0x32, 0x35, 1309 | 0x20, 0x32, 0x2e, 0x33, 0x37, 0x30, 0x37, 0x37, 0x2c, 0x2d, 0x31, 0x30, 0x31, 0x2e, 0x30, 0x37, 0x35, 0x31, 1310 | 0x31, 0x20, 0x2d, 0x32, 0x31, 0x2e, 0x34, 0x36, 0x34, 0x30, 0x38, 0x2c, 0x2d, 0x31, 1311 | 0x36, 0x2e, 0x36, 0x30, 0x32, 0x31, 0x33, 0x20, 0x2d, 0x35, 0x32, 0x2e, 0x32, 0x37, 0x36, 0x39, 0x31, 0x2c, 1312 | 0x34, 0x2e, 0x36, 0x34, 0x34, 0x38, 0x39, 0x20, 0x2d, 0x38, 0x31, 0x2e, 0x30, 0x39, 1313 | 0x38, 0x35, 0x38, 0x2c, 0x33, 0x37, 0x2e, 0x31, 0x38, 0x38, 0x30, 0x35, 0x20, 0x7a, 0x22, 0x20, 0x73, 0x74, 1314 | 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x3a, 0x23, 0x62, 0x63, 0x31, 1315 | 0x31, 0x34, 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0a, 0x3c, 0x2f, 0x73, 0x76, 0x67, 0x3e, 0x0a, 1316 | }; 1317 | 1318 | void create_window() { 1319 | internal_vsync_sem = SDL_CreateSemaphore(0); 1320 | Uint32 flags = 0; 1321 | flags |= SDL_WINDOW_RESIZABLE; 1322 | flags |= SDL_WINDOW_ALLOW_HIGHDPI; 1323 | 1324 | int width = 640; 1325 | int height = 480; 1326 | window = SDL_CreateWindow(title, 1327 | SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1328 | width, height, flags); 1329 | #if 0 1330 | SDL_RWops *ops = SDL_RWFromFile("Raspberry_Pi_Logo.svg", "r"); 1331 | assert(ops); 1332 | printf("const uint8_t icon_bytes[] = {\n"); 1333 | char buf[32]; 1334 | size_t size = SDL_RWread(ops, buf, 1, count_of(buf)); 1335 | while (size > 0) { 1336 | printf("\t\t"); 1337 | for (int j = 0; j < size; j++) { 1338 | printf("0x%02x, ", buf[j]); 1339 | } 1340 | printf("\n"); 1341 | size = SDL_RWread(ops, buf, 1, count_of(buf)); 1342 | } 1343 | printf("};\n"); 1344 | SDL_RWseek(ops, 0, RW_SEEK_SET); 1345 | #else 1346 | SDL_RWops *ops = SDL_RWFromConstMem(icon_bytes, count_of(icon_bytes)); 1347 | #endif 1348 | SDL_Surface *icon = IMG_Load_RW(ops, true); 1349 | if (icon) { 1350 | SDL_SetWindowIcon(window, icon); 1351 | } 1352 | 1353 | SDL_SetHint(SDL_HINT_RENDER_VSYNC, "0"); 1354 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); 1355 | SDL_RendererInfo renderer_info; 1356 | if (SDL_GetRendererInfo(renderer, &renderer_info) == 0) { 1357 | if (renderer_info.flags & SDL_RENDERER_TARGETTEXTURE) { 1358 | renderer_targettexture_supported = true; 1359 | } 1360 | } 1361 | 1362 | if (use_integer_scaling) { 1363 | #if SDL_VERSION_ATLEAST(2, 0, 5) // SDL_RenderSetIntegerScale 1364 | SDL_RenderSetIntegerScale(renderer, SDL_TRUE); 1365 | #else 1366 | printf("Warning: You need to compile with SDL 2.0.5 or newer for the use_integer_scaling option.\n"); 1367 | #endif 1368 | } 1369 | 1370 | window_resized(); 1371 | } 1372 | 1373 | void redraw() { 1374 | SDL_Texture *draw_texture = NULL; 1375 | if (video_mode_valid) { 1376 | check_textures(); 1377 | SDL_Surface *surface = pico_access_surface; 1378 | // there can be a race with the creation of pico_access_surface which is done by SDK api 1379 | if (surface) { 1380 | SDL_UpdateTexture(texture_raw, NULL, surface->pixels, surface->pitch); 1381 | if (renderer_targettexture_supported && PICO_SCANVIDEO_SCALING_BLUR) { 1382 | #if PICO_SCANVIDEO_SCALING_NEAREST 1383 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 1384 | #else 1385 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); 1386 | #endif 1387 | SDL_SetRenderTarget(renderer, texture_blurred); 1388 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 1389 | SDL_RenderClear(renderer); 1390 | SDL_RenderCopy(renderer, texture_raw, NULL, NULL); 1391 | SDL_SetRenderTarget(renderer, NULL); 1392 | draw_texture = texture_blurred; 1393 | } else { 1394 | draw_texture = texture_raw; 1395 | } 1396 | } 1397 | } 1398 | SDL_RenderClear(renderer); 1399 | if (draw_texture) SDL_RenderCopy(renderer, draw_texture, NULL, NULL); 1400 | SDL_RenderPresent(renderer); 1401 | } 1402 | 1403 | void toggle_fullscreen() { 1404 | uint32_t flags = SDL_GetWindowFlags(window); 1405 | if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { 1406 | SDL_SetWindowFullscreen(window, 0); 1407 | SDL_ShowCursor(SDL_ENABLE); 1408 | } else { 1409 | SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); 1410 | SDL_ShowCursor(SDL_DISABLE); 1411 | } 1412 | } 1413 | 1414 | void process_events() { 1415 | SDL_Event event; 1416 | while (SDL_WaitEvent(&event)) { 1417 | switch (event.type) { 1418 | case SDL_KEYDOWN: { 1419 | int modifier = event.key.keysym.mod; 1420 | int scancode = event.key.keysym.scancode; 1421 | 1422 | if (scancode == SDL_SCANCODE_ESCAPE) { 1423 | uint32_t flags = SDL_GetWindowFlags(window); 1424 | if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { 1425 | toggle_fullscreen(); 1426 | } 1427 | } 1428 | if ((modifier & KMOD_ALT) && 1429 | scancode == SDL_SCANCODE_RETURN) { 1430 | if (key_states[scancode] == 0) { 1431 | toggle_fullscreen(); 1432 | key_states[scancode] = 1; 1433 | } 1434 | } else { 1435 | key_states[scancode] = 1; 1436 | if (platform_key_down) 1437 | platform_key_down(event.key.keysym.scancode, event.key.keysym.sym, event.key.keysym.mod); 1438 | } 1439 | break; 1440 | } 1441 | case SDL_KEYUP: 1442 | key_states[event.key.keysym.scancode] = 0; 1443 | if (platform_key_up) 1444 | platform_key_up(event.key.keysym.scancode, event.key.keysym.sym, event.key.keysym.mod); 1445 | break; 1446 | case SDL_WINDOWEVENT: 1447 | switch (event.window.event) { 1448 | case SDL_WINDOWEVENT_SIZE_CHANGED: 1449 | window_resized(); 1450 | case SDL_WINDOWEVENT_EXPOSED: 1451 | redraw(); 1452 | break; 1453 | } 1454 | break; 1455 | case SDL_USEREVENT: 1456 | if (event.user.code == DO_UPDATE_SCREEN) { 1457 | #ifdef COALESCE_SCREEN_UPDATES 1458 | mutex_enter_blocking(&update_screen_mutex); 1459 | update_screen_pending = false; 1460 | mutex_exit(&update_screen_mutex); 1461 | #endif 1462 | redraw(); 1463 | } 1464 | break; 1465 | case SDL_MOUSEBUTTONDOWN: 1466 | switch (event.button.button) { 1467 | case SDL_BUTTON_LEFT: 1468 | mouse_down = true; 1469 | break; 1470 | default: 1471 | break; 1472 | } 1473 | if (platform_mouse_button_down) platform_mouse_button_down(event.button.button); 1474 | break; 1475 | case SDL_MOUSEBUTTONUP: 1476 | switch (event.button.button) { 1477 | case SDL_BUTTON_LEFT: 1478 | mouse_down = false; 1479 | break; 1480 | default: 1481 | break; 1482 | } 1483 | if (platform_mouse_button_up) platform_mouse_button_up(event.button.button); 1484 | break; 1485 | case SDL_MOUSEMOTION: 1486 | if (platform_mouse_move) platform_mouse_move(event.motion.xrel, event.motion.yrel); 1487 | break; 1488 | case SDL_QUIT: 1489 | if (platform_quit) platform_quit(); 1490 | else exit(0); 1491 | break; 1492 | } 1493 | } 1494 | } 1495 | 1496 | void multicore_launch_core1_with_stack(void (*entry)(void), uint32_t *stack_base, size_t stack_size) { 1497 | // todo we should kill an existing thread 1498 | multicore_launch_core1(entry); 1499 | } 1500 | 1501 | void multicore_launch_core1(void (*entry)(void)) { 1502 | SDL_CreateThread(core1_thread_func, "Core 1", (void *) entry); 1503 | } 1504 | 1505 | void multicore_lockout_victim_init() { 1506 | panic_unsupported(); 1507 | } 1508 | 1509 | // start locking out the other core (it will be 1510 | bool multicore_lockout_start_timeout_us(uint64_t timeout_us) { 1511 | panic_unsupported(); 1512 | } 1513 | 1514 | void multicore_lockout_start_blocking() { 1515 | panic_unsupported(); 1516 | } 1517 | 1518 | bool multicore_lockout_end_timeout_us(uint64_t timeout_us) { 1519 | panic_unsupported(); 1520 | } 1521 | 1522 | void multicore_lockout_end_blocking() { 1523 | panic_unsupported(); 1524 | } 1525 | 1526 | uint get_core_num() { 1527 | int core = ((int) (intptr_t) SDL_TLSGet(cpu_core_ids)) - 1; 1528 | //assert(core>=0 && core= 0 && lock_num < NUM_SPIN_LOCKS); 1546 | return spin_locks + lock_num; 1547 | } 1548 | 1549 | uint spin_lock_get_num(spin_lock_t *lock) { 1550 | return lock - spin_locks; 1551 | } 1552 | 1553 | void spin_lock_unsafe_blocking(spin_lock_t *lock) { 1554 | // Note we don't do a wfe or anything, because by convention these spin_locks are VERY SHORT LIVED and NEVER BLOCK and run 1555 | // with INTERRUPTS disabled (to ensure that)... therefore nothing on our core could be blocking us, so we just need to wait on another core 1556 | // anyway which should be finished soon 1557 | //while (!__builtin_expect(SDL_AtomicCAS(lock, SPINLOCK_UNLOCKED, SPINLOCK_LOCKED), SDL_TRUE)); 1558 | SDL_AtomicLock(&lock->sdl_spin_lock); 1559 | atomic_thread_fence(memory_order_acquire); 1560 | } 1561 | 1562 | void spin_unlock_unsafe(spin_lock_t *lock) { 1563 | atomic_thread_fence(memory_order_release); 1564 | SDL_AtomicUnlock(&lock->sdl_spin_lock); 1565 | } 1566 | 1567 | bool is_spin_locked(const spin_lock_t *lock) { 1568 | return lock->sdl_spin_lock != 0; 1569 | } 1570 | 1571 | uint32_t spin_lock_blocking(spin_lock_t *lock) { 1572 | uint32_t save = save_and_disable_interrupts(); 1573 | spin_lock_unsafe_blocking(lock); 1574 | return save; 1575 | } 1576 | 1577 | void spin_unlock(spin_lock_t *lock, uint32_t saved_irq) { 1578 | spin_unlock_unsafe(lock); 1579 | restore_interrupts(saved_irq); 1580 | } 1581 | 1582 | int mouse_down_pin = 22; 1583 | 1584 | int gpio_get(int i) { 1585 | if (i == mouse_down_pin) { 1586 | return mouse_down; 1587 | } 1588 | return 0; 1589 | } 1590 | 1591 | void tight_loop_contents() { 1592 | SDL_Delay(1); 1593 | } 1594 | 1595 | void __sev() { 1596 | SDL_LockMutex(cpu_event_mutex); 1597 | cpu_event_states = (1 << NUM_CORES) - 1; 1598 | SDL_CondBroadcast(cpu_event_condition); 1599 | SDL_UnlockMutex(cpu_event_mutex); 1600 | } 1601 | 1602 | void __wfe() { 1603 | SDL_LockMutex(cpu_event_mutex); 1604 | uint32_t bit = 1 << get_core_num(); 1605 | while (!(cpu_event_states & bit)) { 1606 | SDL_CondWait(cpu_event_condition, cpu_event_mutex); 1607 | } 1608 | cpu_event_states &= ~bit; 1609 | SDL_UnlockMutex(cpu_event_mutex); 1610 | } 1611 | 1612 | void irq_set_enabled(uint num, bool enable) { 1613 | panic_unsupported(); 1614 | } 1615 | 1616 | bool irq_is_enabled(uint int_num) { 1617 | return 0; 1618 | } 1619 | 1620 | void irq_clear(uint int_num) { 1621 | panic_unsupported(); 1622 | } 1623 | 1624 | bool multicore_fifo_rvalid() { 1625 | panic_unsupported(); 1626 | } 1627 | 1628 | bool multicore_fifo_wready() { 1629 | panic_unsupported(); 1630 | } 1631 | 1632 | void multicore_fifo_push(uint32_t data) { 1633 | panic_unsupported(); 1634 | } 1635 | 1636 | uint32_t multicore_fifo_pop() { 1637 | panic_unsupported(); 1638 | } 1639 | 1640 | void multicore_fifo_drain() { 1641 | panic_unsupported(); 1642 | } 1643 | --------------------------------------------------------------------------------