From f359f3b71893d0d9eac40f7d5ea41f8892f5be11 Mon Sep 17 00:00:00 2001 From: JP Stringham Date: Fri, 26 Dec 2025 17:38:22 -0500 Subject: [PATCH] Initial commit --- .cargo/config | 42 ++++ .gitignore | 3 + Cargo.lock | 410 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 38 ++++ image.bin | Bin 0 -> 8192 bytes kenney_comp.bin | Bin 0 -> 12800 bytes kenney_mono.bin | Bin 0 -> 102400 bytes memory.x | 13 ++ sprites/sprites_atlas.bin | Bin 0 -> 400 bytes sprites/sprites_tex.bin | 1 + src/dpad.rs | 37 ++++ src/main.rs | 361 +++++++++++++++++++++++++++++++++ src/sprite.rs | 204 +++++++++++++++++++ 13 files changed, 1109 insertions(+) create mode 100644 .cargo/config create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 image.bin create mode 100644 kenney_comp.bin create mode 100644 kenney_mono.bin create mode 100644 memory.x create mode 100644 sprites/sprites_atlas.bin create mode 100644 sprites/sprites_tex.bin create mode 100644 src/dpad.rs create mode 100644 src/main.rs create mode 100644 src/sprite.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..202d43b --- /dev/null +++ b/.cargo/config @@ -0,0 +1,42 @@ +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# Copyright (c) The RP-RS Developers, 2021 +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# +# This file is MIT or Apache-2.0 as per the repository README.md file +# + +[build] +# Set the default target to match the Cortex-M0+ in the RP2040 +target = "thumbv6m-none-eabi" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * inline-threshold=5 makes the compiler more aggressive and inlining functions +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "inline-threshold=5", + "-C", "no-vectorize-loops", +] + +# This runner will make a UF2 file and then copy it to a mounted RP2040 in USB +# Bootloader mode: +runner = "elf2uf2-rs -d" + +# This runner will find a supported SWD debug probe and flash your RP2040 over +# SWD: +# runner = "probe-run --chip RP2040" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b1d491 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target + +.DS_Store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..98b6877 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,410 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield", + "embedded-hal", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "crc-any" +version = "2.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01a5e1f881f6fb6099a7bdf949e946719fd4f1fefa56264890574febf0eb6d0" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "frunk" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a351b59e12f97b4176ee78497dff72e4276fb1ceb13e19056aca7fa0206287" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2469fab0bd07e64ccf0ad57a1438f63160c69b2e57f04a439653d68eb558d6" + +[[package]] +name = "frunk_derives" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b54add839292b743aeda6ebedbd8b11e93404f902c56223e51b9ec18a13d2c" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "panic-halt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "proc-macro2" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f21deadb5f29f05be9e665049c9c6d6385c6ed3437574c995658a041f71453" +dependencies = [ + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal", + "frunk", + "fugit", + "itertools", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12d9d8375815f543f54835d01160d4e47f9e2cae75f17ff8f1ec19ce1da96e4c" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sh1106-pico-rs" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-hal", + "panic-halt", + "rp2040-boot2", + "rp2040-hal", + "rp2040-pac", + "tiny-rng", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tiny-rng" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d63628286617a0c67ee3c4ef92ccf62044e3813ed7376bb82a4586d2ff7974dd" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "usb-device" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2ff5db4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "sh1106-pico-rs" +version = "0.1.0" +edition = "2021" + +[dependencies] +cortex-m = "0.7.7" +cortex-m-rt = "0.7.3" +embedded-hal = "0.2.7" +panic-halt = "0.2.0" +rp2040-boot2 = "0.3.0" +rp2040-hal = "0.9.1" +rp2040-pac = "0.5.0" +critical-section = "1.0.0" +tiny-rng = "0.2.0" + + +[features] +default = ["rt", "critical-section-impl", "rom-func-cache"] + +# Minimal startup / runtime for Cortex-M microcontrollers +rt = ["rp2040-pac/rt"] + +# This enables a fix for USB errata 5: USB device fails to exit RESET state on busy USB bus. +# Only required for RP2040 B0 and RP2040 B1, but it also works for RP2040 B2 and above +rp2040-e5 = ["rp2040-hal/rp2040-e5"] + +# Memoize(cache) ROM function pointers on first use to improve performance +rom-func-cache = ["rp2040-hal/rom-func-cache"] + +# Disable automatic mapping of language features (like floating point math) to ROM functions +disable-intrinsics = ["rp2040-hal/disable-intrinsics"] + +# This enables ROM functions for f64 math that were not present in the earliest RP2040s +rom-v2-intrinsics = ["rp2040-hal/rom-v2-intrinsics"] + +# critical section that is safe for multicore use +critical-section-impl = ["rp2040-hal/critical-section-impl"] diff --git a/image.bin b/image.bin new file mode 100644 index 0000000000000000000000000000000000000000..a84889822ce8bc7d5daa4e4fb82a6679810db4ae GIT binary patch literal 8192 zcmeHK0S<#O2(17A^f)NCWY(#(l5E#xm5$1B2gbTA$)l9A6y%`O6Ui8m%z$38YZmhp z5LAUl8qt`KZ3sZdc#N{aai#)PfC^9nD)9RX;59EJi>L#qz2ox}@B(-yIi*M|fSzoH zdsYt$af43M-(mgwbossTQ}A0*vD%{N=>Ph)_kC@*pklQ}h-=T^z!`R)U$m$LeC<8+ z)iyj2vU7(O*FNOH|Lq~u?pFoG-Ll;YdCDbt8n&N806^Eu%;En5Uy@nzLt^(VG`4ay zYpV(2)^QJHh~lV~rBm(pct>owx?NXI#~;>T)fe__6|N#}-)F39i(w(ZIDn0T3PcZA zRDcRl0V+TRr~nn90{^T4&WD*NL>x|&@%50N(gQ+%))&G7J_fA?IIw%h7VuB&zudmB Ga=rk8zaJX_ literal 0 HcmV?d00001 diff --git a/kenney_comp.bin b/kenney_comp.bin new file mode 100644 index 0000000000000000000000000000000000000000..bc9036148f034e4e010ed026256b5c72b9344e29 GIT binary patch literal 12800 zcma)?4|G)3oyUJ~-b^MWArl0{&_pI~Tjk#ptS&_PGY{-qwp(ecB2>i0*4iAk5XA+m z(78a5wXH>ZR`*c<)VJF$ZRu_|rMo>U=scyZtlR2rkJW%8(@NPCJvc2{8bXq{-|xNi zX5J*xv%h!p-ktY8@Atd+cfa@EH*X?BOB5`{Ei+;^-#2G%k4F(p>4U`chV!-YmUvzy zeIVW`+5#@-+8#H(v$Mn5;p%RYFTEMh7de;5cwQvpa65L6+xLE-Jv*sQ#Q$?VFWRge z57iBZia4*{{@UBGz5T=A^h{`2EpqJbM4`xS7O}m_hfGx4dt>1n3y+=qkALl%VEiR| zL@rTi2fRmH8oSJ0B5y1_{OGZBPp<_BfBtZyU1XuE7PceiHg=iTTI$VvkE}U%?!Yre ze}3vokx3G(;B0Tw{f2vHvGJGnQ+$-2{&_06h4)9Cm=YNCVtdsMpZ!HSFO%Bh`PcmPkThKK(0R*i zH5boE9FfMZO{dCvP7lRTz3VR+n%@?!u4$jHImWisj44aDcsh~UVb=oZ-#apUXnsSg z`Qz6#7devcHO*~h9f)i~)cHfz4fWNtt_yU)*19o~Oi5Rha8IIu_G&A0VmT2N=2nYL z*cEffa1muWV&eL~vL>}zD!$A1Dmi>cR9C#BAhN@gR-Mv2S74-yj z${dm+5f$QAV@!LNN+xmMv6YF7S}L^2@$6Du_#@Zc*4At}RVrvlOknPFJ4)?XJ&~MO zH*e7*YdPCv@`CiCEzj-n>|$QpJ}|)g)psx1GNGNEA(1{LFqG%j7d&aWrrsEGVa)c- zk>0qu=-)&kj$*`_*z@7*%vJ6;b2mMtgVmT%G#70-zr1BZs-`i@oDN9JhOh6-w&>s7hX1MWyvY^TlG9kZGFtH;7Aw?dY$Va1R^LHc`k>w(4D;q373gB`Du zi{{DD0k6fA=xO zouQ{RLoVI!M3|TK@o<08UnLjhd3}XGO#Sq#Pz+3O+%h9!9k9J@M-jrItb;HYP#RAW@%(i7yF+9?<6e9mOU6N%H#g(Ib{gEyrKzFx0J(IDtIf@ra>)aieB;O8 z`0)cb`N#ZogKq3A7c!I7@1>AD{juL>^j|i|Y}fk#cFup%jeX~vC2nX!A>clAoLSw5 zZB^R;U;m^V`|*D=ZX7MSw8&9c=N+1`Yr=MHsskzTuW)0#f6sO>FNoV7v}3|9RJH+| zybR3$i5q+1PUa<^#~i&#WSz)Ht@{>m(t=HMr`_0nmzz9WYWjyvUdY)VIa&w(p}jjU zFrUpnh+95qxa{z@Zv=YbwfEwvg9>i%-k&n=XnzbI=Dx6UCGs5BpNX3xi1|$HQpqy9 zLL@r1Zt|b-{d-Iq8q{*+O#+VNmu1>o|0&;pI?EzPc}V9L`#0ibMl!rMa+817_cx?j zh7Lw^G57N)K2QCf=pb==4`<=e?fINYaXP+LgmG)yG4D}-&v*FfkwK4jg(P_hjP0mt z59a3%=+A%J<3-KwaUz}a;qCCuywv0t6;1c=5k(FO6uA%2ASm5zxY~9sh^$Jd=AJHd z@wid-D|1GF<^HH_N0~GF zyXcmSkmg%&>CpZ>1T*z>S<-Q-1B|mlL+Jf!k5|j>uvLux#`VLExws@T!zq@v7mp=8 z9tYgZm;RD=sqWuU{xlbgd+pb*IR*Q;=1v+f)SrSov0V5w?fBG-*KC4)oN-59EOR}Y zGjTaCe|h?sm(q~u{~Zd&)tud{>qpLwxx6#BDvfzE#3`QD9OEXF9fOSM4_-^%hx*qg zS8PX9@0>Xo-EtAqeCsW(zW>_D-5#%&^QYUvew#BV=r?d!7Zf?|rCcJnVg)bICZ9f$ zIeSR4IEoVqb9E%nlk4Q`8<~``&vf^&N&|?XJV&IZ_o8jpC_sIbJARp2ed6PztpJC$UM^G#bbos~EcNYFm^-LYvx6e3p_rQ<5bL)3U~HlG;x)H5x7pr8krxE^7WG`4!;_}f z-H01Y_PP`CWX;cah`hve8 zU9(OK+Z)osZeBf1UG=imG-uANe`)OctLsF%>ciX-UVR;Tfm>MZoHw3Rr8)-rcDoy~6)uWaljbfDbE2FGhe2ii zOeIrkS|tANWOeu!$6=hU@p<+{pyFjAPp23F;im zOUR$@kG1+l&|Bqp7!ECxle#}LMeZ?HshF2g{lMLY`V|CRg0+8KYTE zemTi>OKmy3ChjE<1RV3s_7-GqxAFJ7%q=^cc73?=gJW(fY%PCznjpAA;M3RSXX<(4VLCH_>o-x2+-`@90aAvu`k5 zNq=>QqyG3ify4`|^5lyCJSL6uK zN*r`geRzt{?q1lItsTz2w$-wH)V+i?6g*nUPu1^G9)`^J2J}(N=Q(Wq-SK zaCKNs3;f{~CUiV&40pt{oyhCCrhsELSijN!%shT@N5C;I+dG;I)(;hGhxYeJWjz?G z-)N5Y)BXy0SVI4{@K`kJ#i0%&if44cmDj;MaEY6|WJWp$`JFW5!*Ex5 zWzu;p`s2ZH#Axr$agKIMag~(ghMb|;+ApfLKUja64H)V?k4J6%_DIeG7p%WZOkN(b zdWuXk+15<5wcqwGJ|3*U0k<1cNy4zkHqU;et(jcJx%jwgMQHuS_%Y1vH>UQ@ekF*j zgXo=Ee{zVHv*5Tb@k7aK)O5h~ zhs&!K`qS;mSv_^=i|U5PK8MfaB^^je2h|PL4ZNM)0(|d}IA#3>Pkro}N~_eVL`pq~ zOdB&^ol4zDO8U!_Vr&z?Rga@A-C$;rh{wygSLy=>zcQent$JMYCli`PcAh+S^7tRV zdRXR?qqb<^hJSK(Xnu+8Jbvo<$=^PFm~Rh^)#eQMEast7t;%ts;$)IPHc##2dm_vm zTeJNpD&ktd-gMWdO&gAU9gkp|G5U*Ew+_>vV(s-fe|Xd1ZR)=BThLWlf6?lx^cR{> z%Xd`jZz}zTxR(Tj=7%pp_hJ3j)nOjrI%Xcf68Y0ypubpOba>yd#;gmvrj_~|cb&PD zqd#iK{1#J_DztMwfa$njl{GE$hh3IL$Z#|BUrjvUZhJ~#ntPa~KkUN#YZ%UL zSMN(!O^N4c#Pf>(9HTi;>hQGbHA!<=f6et%Y;Q)melK~JBa<;d-x!|%VtwGguj3|- zznv0okeQr!!}_xevn};dm_rRb+%Z8KEA=<~xgD=)&N2RuddTSp=&BLz2;cS>I&zQv?mZFif{vY~d+={&L{@Gl&Dvj;<#77iQ7W0hK--o%D zC@;>mR0X%2C-ogHv_xB?ix!bQAN>*br+E3!-Drhj7A8 zskf;&p6BnF)Iqo%n$!9-Oi6#x6`xyRx>hK4%QWaus4H$o{Urt}^!FI%wQfsIb2LZw zk!y&=9{jBvr~Zomj>_6s@7w_WNmzf=r*j;I>*w(f%sE|}3+BIx?{Hk&-Y#CnPkbUf ze{^ca{%?Nk^3nR^IJdp4$VINgAl|v+)Q|T6;E|^`j-LN8Z-hCIcOdRYL0d!mGpFbA zfH}f@&-K@E39f^MM8PS&VNtwLv_Oismm$Ze%W*kT<_0}JbpD)D+`9v3JSzO@c4X-f z>#u{s217f#c`O>^n!E(-pd)7eeLSd=Ci%hSS@AP;08X#Jk6T?h!KDC;0piwov)^oQ z&#f@L6)MN=0+m1pP%UsZ?ReGf+FlS>iCKSNflm29YTOV=Cic{H*H`GutiShLonole zfbHRHovIl~Rd5Pna2iz)I7G(D^9NGjkB9V!=fAx9-klnle25z&lN(BZ9~FfY;{_bQ zAEADRBFDUt3+qpjv((>^Gwn6pjG;7!W?6qmvFZnG?|I9)O+LhMGnRa*g7fGP>BYZN zEyv>!CO1BDf~)ET7w9ki{D;RJbgRfmKEe9UuoSpJf8#&@1sp?Vd|2_fSr#jv6pL|F zeW^Zk4JX)Iz2~lIitX4kWl3~NG|yA` zeU|&P)eRb}=D;?2-m+{~26 z%cHrqVSk274K(%Qjge6ITOwQc{dWO9|Mg1FC@Y?a67+M#mStP_Wln=-?U zOP!Z3B1=Ybf&L<>JIDEwk1mPkmf(k^3NEBSfnk`i-*OlD{*x74NPpyD)IqM+_j46o zvZOylnSCyffmXqVucE9c5WG37%Evq3kJG5_<*Eva_v1eO!ds7EVxE_)8c2=iO#hnr zOVdn!3Y8Lp|9+|uo9pUzc9oY7nho(@`~{O_5T=zDW5QBy;DY zP-Bs*|Y!>TmnfXSa@>$Jve){)_XxtD?=^V(Oj z-$3^B=yx#ewe8tcO=aI!R>iR1H6BIjx}bE;n0%MMW_f-0E{8r$?|NEQbmEJOu!T0o zt>T3_=X+7oAne{>Jv6p_*UE6^-{PFj2Sv3XOf4Jt|FNq-R_0W*7A7r<|KOF9#a4mt=daxW!BdF^?O=hnqN7C3AFfAE4e+O1u~y$b+!AJHihP$52Q*=AuL9AQSDa zzmEIzlz-!63|EeeJO>|fJA~pgBOWxPQAmO;n1jxhsHnzYc1R5s!jK}l-tTeI7j36lab(PsvhPOklA7EuB zk-)mQ1Oq*46apXa74FI~ctL0R@C}(@C-W!7r~WVz(ifABJY0}54jkZq81+u1$OI_@ z14- zM#6X?CS_njA-_w)tAPs^55NJQo;$$sup2DI1@shKpi-yzQtalA(#BwB^VqZbVfy8g zLl7H#cc|nbInUWgG;wC&6UPkL@ooDsQ(m_57)sg^6L0X?AwM2y%m7<3Kp%+`;i3Dd zcWjtO)}mqhh!k!Sw2FE+!$XYbgO4Gip28d4Rd`YmY6!4W(37vhBfvW= z6L4J=m<)m^KJ`!>soy&k>2$F*IAOXD$#LQDpR34RQH1v~(5_``kK>m|oU{kE??FcHjR4nYzu4H!yM0{ zX#ETf#OPEfcXbN(#)h|3Ot1XDKUWbu1eH!_brqEh0zrRvFS-vDO$)Nty$RTT zGPF%}#x)$od997O&sH2hd*eabsl*Hhk-%RiP?uiRHCpm?|Fey5y+gOr&y|Um+J>I) z5{D6vXGwS?HNU#W<9RC0hcU1bFIe#Wh1Uuf^Q3VUHSHr97o>h~bWt(lpH>_*YW zp=e2V>c&Vj3iG1D4q#uWE?;qs9i>3?Ls{sb37~PjceL#uh$|^ucV*IwPUR2m$mcv> z0M`my%cZ}8)?~IRBLQpUm=ZxI!q2+Qm(f~>YUm;X3><)Q+*7D|(Jdl@RDo{LWKHT1 zwq+R>YfvB{he&@|^(0sd)?_OzQ;?|3%MFvte!5lXK9l({q+_KL9%8}CVNkeCuy}y9 ztlNsHQa81*=T;(_`z^A_SY<1w)BG4{XX%ju1#w<$qeZh-htFPkm|tsJ_g5S++4Lliz1ow;MFe6H|nUMO;_rW+_s6ahfq>-HDPE`mk zX9>`$2qeL)8ngMgQ2dEQ{vQ#PD3nj07rqVBj_=NYB1sJvoW!l(sV^CXw@UvoU^Loa zF$((U_agL`j!2I3Cy%v7?T<6B$@JJSJu@h!1-PT~xu;xKZ7)Lw{h?JaceK9YbL6@^ zAEjcCenH)`ewYf}r3p?G5;Yac(hB{cTZk&w>#`J@bg1v6uiAEFDttY}0VzTerqss70sZtxjI$E28ZYF8G>^W4v$S>c z0((M`vqId5Q#>M1KG|^ma1M0Z{K68WHqkNhjQ4sA$PLjd{qapc zR9jb!4j&G`;in+w>qd?KXn=t%eKHV8p~zaydpZCr;-rer4=yP!YFb(EUgNVs(t|xj zv408_+q>KOur22kSHDw`^&noCIndx&I0!I^>2#VcGB)(QRgwoDtT?d4$4!*`K^yt- zh^6iI3X71!3Gw+EQ%6;=Cam0V|p?@G!?dmKn$zHY@3=N}OK0-vYb zfCX903ydyI6GkOo%tZ6i1v*q1zEk$7VSp;!?C;>UB$YiGK}ON&K_5jiqUSl1namS| z$M~ZQM4(FXGNqH_S*OHfQnV%q`q9S?qkPncpEU4~2X!q$rV99Qm&L8{U>cWnz=Q{_ zc4!`b0s?9P$HX(0f+0`05!`)&(U;x92X6Yfn;*Q9zwD9;GL`3=%V$FgsPHQ*)@{FmGI}id3)9EzZ=&%iflt$06%}_(4j(%aCsrgBHYh=4y)SIMq6N|}u z(X(IEVrs8;ibY~MTuy8zd$HCVgCx#h++QE^4{fQ{4Bz}*K}^rfDY~xOU*Og{7Gy>W z?)Jx51y<}k{)q2g{|ju*s{2B6-yc>-UprJSee5F+QAb}Z^Zm*D$^O*qr}xFYe;V%l z0@rW*f2yzQq3ln-R$RwN{ptsWFR6-QL0dn6?hnFFK3`HT>DKDo^98)$s3AKy=Kbm~ z6RhQ~VqU~}vFK%2!&LBWC6qU*C?i)b+LI8OA;9Y!rr+ij8V(E|En-i!4i)$+m8Ot?5 zWB~wid*Ft{K=@gb`6!^@*||T#jlw?>kZ)Bu=*iFKCu8Id1vJ2ncs(fi370tll@3b* z-7#Xig8%IHZffOvQstu0_~mn!@Bxm4@!*9LGyLN|68sRs68QiiAfL&jPXw_{LsEcg zk;mJCkyc!C0bw`6{_I|QfSCLkB^q`5$Wt=n^fjzcA2|;~b66gi&jILoe)_qSsLwnZ zgVR6}2i(NKCxGSdWA*3hVyuy{NG@p+WWz>f`Q4Zhe9^?^~o*xfh1keN=~&>|892I}XJeK5v?K+g~5 zdKj^6=@t?^X}J4KPbZJ2p(m*?eAeK>uhutQhA~b*zu{|tE)Gu~hM@_fc>baNRX^;b zy(OBDz5#Gpn^YR%_iuJ$M|Q3E)Ar}-+@vdnz`t-7R_?DgdE@6SK95(|kKniczCXS* zZSZn`sIZgkZ1eqw+2;q}cRxzHz`wjd&Aq(e*if|&O99;~{!hNA*5C7uslDOlb^8mf zR$(cid)Zs#`jMGr#SW{yZW{QhN%GwrjXz26D_&=f+C@rv1mwOZ`T%^|3i3`Dm$(&9 zU&j4ie8hSC`;yE$`1}Ou+x{j`?0$x?e5#sDNU4kAD7{=Kbg`ycNNqU-ndp%yRxUKB zduv=xlAJY7SPJOQ6`{|h+X54?`lKLBQNb!NO$(u4?TQZf>8apyQE>XPw&_a32&8*! zG@cE|TmiyTAaB)C5uz>#x25b+KCGT!e8RDj^V#B9-M&A-5a;1lbcn6qDG(46L|*oO zbG$u_KKR6qrtW4ST}iTAJcP?32dS*&{zeJ+Cr%W^@dO+t(eJQ7bV0(&1SShZw9@T) zme|7);Vq63bz3i=sX}fk!5burywSPJN7yXF+dPb|L9H`~Kh{+DfX6smDRCjXKF74eqV4odLG zK*B{p?ppQ79@@M;zQ2Y}O{S4QB27lWY5VH8(qVENBSu#V-q0v<5s-VT{e#%&{Vlyr z+j8V?aU}x%lwU{}hugqE?d>lXR@7lBpnIYFW9RGs+OO|qhMuYQtA9O~G2yfQF^acT zt-?}3_riUe;Y$oUULOO0K!bMT7ww$&b#nqA_*`IU)Fw>@rJVP-Dmr3T$l~!mV$K|6 zDvU%(fWR+j>KWzP^~c5#!xt9A*yUO6uVIy{xqqm@e}9bz=4pqFYu#^~Sy7{g(`KOg zmgx#N_$Zd5Se z?GH2SRn|z5sxPES`rU20Wx(4JNF<5X3*n1z=*+xCx2ZFC#qkhtRBjrfPeECTd7yf8 zcYH932iFCpqtaLW>J~W);y9bU<)#uSdV5gav8&fJzh`gzi$JauQ*iF+2~qkYB>T-x zo@Gb=veppSfK?#*Wl|Qlg1y>j;XEDZQ?v+#R(nm1;5^kMECqCf26IHMH2&-cDJq|j zw>$`*19U$%M{p}=V4?s%#d_598{ZB|reuumRV^}^joymg-3fi(9(v-HRZ|T4c)BWp zxAHA;Px;7qU+!7Xv5bQGd!_uR+iHyG^2`*$z0Z@a{=%&)5nW^pc70ERkL6D6BQLEt z-XK=|>h}H3J;g#Txt_nVzi%7R9NO@=>Z&rHpOuOtZxF3py7mCg!dJ%Qyc6)iI^iGX zh~3J5SSg0Qu|5B+ds}~ezT&^JzZCMy_@(_};9ola>+}8e4RU`KriJ}|Y7D}zbM=r} z(j9uXC&9<^GwB~ieSh)n3*_hu_#*%R{`OU$H>bU=_kYE|-};G$rGW0a1H|=3PWGD{ z|A5vV-Fg2l`|}yN2;T<+a<}!hjd6X=Z<{@C6YL-N_vybl)ZGL_#Coj#8o2i+!J(L& zS{CLbUNq|S1JZr4+?|hjyqGz_)3_^s^-z0QzzyYSe_Vd`a4SJ+^ae}NoW<|JlMOIx zULNl6#B&|1VH~rqYXYvRkQw_suD=-F%8jJadjNy+@mwuM&bOkfdbL8XyLW%ILEq1C zT}1~x*pGZ<^kKLW@cB=~uG8UGxj>)JeZDC~ufAXd^uzuVf0A*IVUV6#J!F=2hn{Ur z)H93Y2|#!_o5dm^x0fSJLh7U$4{+Rnqm(Fbc=Kd)P#f!q1I7+tJ!C#IPJ)HqI~61> z1$1{oK9{k$Qa!1RQDknvu|E=^yd4fl-}$+}rTe2ZHE;8N+aJVmAT{3jd{$3Kb7|16 zTHcr`54oG4i3-%sf!J#ShMaWW)XT3;zBB z`r?)ZI5{e)-=8=8d(UP_7f~e#HKi~t1>|x)J~xRF*B1fVZ*IV?;CQw;-p@fWMO-|f zp`YRYKA(^8Eu*CE0>F(-&W;>IUVv#NZmQJ<&FWBFeDe}^^jT89eTDW!N>9gDH#9gJ2{Ok z_;9VqQ)}$b`>)tv`;l;$-G8#}{d2~KLx3B!#k4R@#qV~u2x|gC?ju>Bo zZI~6mdf@oJzolYvLenXBlzR=FI-mR_{V_P1j-U+wjr}D#mcOsS1df;13YrPfw_#fG z&&^%%E!R}Il~2Gen{8|m4A-h1F<;zDtL$W3QGdnT1>d%r3b*nJNH$AsU~MNrM@SK3 zOXs5|g;nIQ-zMmN4_9CKL-7UM)xWLQoshk}A*%6(^#yxD@ioj9pzpgcHrTP-24exo zNqR8s9)k4$y8YSSz8vhXpZL#Hduu)WZ0~%HiVJ<_SP-N9;C8sB!wutrKZL9%qn+;A z&VFj!n4I#Nqa${{6qa-&a^o8FO*RRe0E^+j8!Y30pj+<7)YhZ&v1tOPz>VOh>AO_6 zasv(6L=PKPrZ2zwObmkQ8m1k~vH?&Qp5SMn>Kfs_>CFnW|M#kLKeUEqW4YcJGJT)) z2En5n{I)+#1IF4(_ze0(5eG*1mcKV4rXzN7J^LejP#VY5@s1O~QNHU19T|Bu56gkL zIH4Q+&%=_QG7cKNQUB^2JrgUoXoo9vZ#f69Q8^tHVr%|rCWGCFJ<7QZ0OM?F3s}m- zd+|PBVz@XT_e6S*K}FB3UeElN{jI|R9gB<0Wv#H-gzaJ%y@e{^BrDZUutuKuM=?jz z>P)5C-r~aNBx~2d;He|1$8Tck)7T1!L_h9+_T9?2z`ce?zWee+7XPuE z|GnF~d{lX8qtHjDO^JAP|2Cg^|5I=K7x`YA|F@;^hvE-x;F;Y&mY0;pI_O-a&iqpR z%^@$Qc=tu;j@{42sD$(eoIi()Ce zq363VP#yb6|10~Xz2(ufxy{_dSehR!uOVsrJA?`A`JVVm7$oF=@laDihd^UjoLHw)FW&t&rDu;QbGQw>;1?&?F z{+!(tK(msDAmXY!aeb97UFI9hB^B~w-Yr}eVL=u?E8*$^3eZ-35g;XY#R}aFpphLi zvMW9JnL+Z{D5}5`#7p;M&4O4Qmr$Xtd;$u3vN%Oc?A3du!8a3Vjj5ZFhD01Ndvx`y zS9rl#Qej!0Lb010o%X)wdX7&wN{lMzNVjCjq(xlQd}{kxm*HlnkE+yDr?A5O_21 zsm*~5F#9e7R`1mCK-5CuTTdh#anEOI%V*3DgVW&eZxI{efxmZWpN((=p6FKm>Y;Xz z+%am{#7N&0&2`=~MItNtGgpyYYxda)Eif@xguqSg&Nm|c*8_VcpMGX%;>mA?3FSYR zOoTB6%=Gk=?5f=OCT4QT2X10FIU8UsP-K2jbY^Gb$!~@Hhf9BdUp7D@!w7wQA}Ch; zp?n31^%%AFt%Ix{YWB#P$)lI(nRxQENmGd5U&du@5&xBL;lNLZ4QGI{6Lp$5U`-v< zll~vu-xQZVRmP9$G}vJQ7-|a#;Y@5e3mmFv8Q2J$iJfmmpsk-Al*XjX>`ZKaBBx=^ zOe>!>d-X+x;fq{vAbUbLU%+`ch3B2jJvr(UI~F!v ze_bdp9p+*0YtVoIHFYfLbl`nmVSBm<`m}Kj_F{u{lK}!PV{a#gAY-_OMhc^Z7)q8{#-@=ED?>}yv0%%2n+c-cg)H91CTFdLH@e6s-6wo--c9y;Fb5B0MU5$?K?1J+#w5Y%H~?{rK!r6(~!RyGqc5ND2o)m080 zSezevaugGExmnaIGdbEObqAp3i$i4tyH>(mBT0K1_8IFL6$5YXs&VAAibV8K;j}3N z#!90U_^VV=zz2S~J0NQ}Br@oc2>RWFz2-A}8D8kDJSql$_C2@Z2CTA3Jd!G)uRG87 z(6N;xKQULVCv2DF`5q~ziyjx@s)M?IcE4j^*k^f{g;U^GZh?upA_Hz>cfKt`x>*-J zF2Y?w-<~Vzk&ZMQsUDiXA$t1RUg-|y6L3#C_3YJqgpA+9_acs2DDx(6`4iBx=pCys zgH!jmegj0`OO8kY1}z=8ddTb%;=hG^3;V_7f6>@``b*c@Cw&=<{H4#)QJIokxdp~f z1f4fw&wN`4^31v*zX&g_xjkp)bNUAMAs~<+Gvbs_kV6;t2WJZ0rt{VhEKHlE{pYpw zZILKGwwUrxp!s{7J~p%O%OkZvEEeo%d8fXNO@Uka1Y9x&-HKm5K=w!!@|uqe^aA7H zt~)khSM#~P*kIomHn{jK^1`LToihb)<=*3tB66vvund(;Yc^E%)>vp1%&#~-t{)c{ zg4N;A%i{?lJj=c;viN^zf0sWQ4UxB|%zzo&S^zqn>#$h+Edr0lZhS$$zv#MyZ`W@(@PB{IW!37+Z>vIQY4uf5ZN$9s;a4zHz0|{hU;A z;McPSJbfKTW`0GY@-)m*=u>c!44)P*ML|3^o{qCxYPSMz27-kD6sIcrv!SRzA?bJ&C{KoBJ7l zxF*J1;WjE>YbWf_3iuLCaeliW;T#5=d z+Kh-X3t@=9D`vYuL3REe85X+Ld7SsKla)a#Vt~FdDm$UaKD0kUm;G4q)^~xv(U@LC z`F+}-I^kaPU4KVi*lMNpR_?;Ku<&^Y4x_fSdP)deK9`@B>%J0gAgiDYUXRE8a6Ru9 zsa?#vpXdn!nXDidtO~!o8xIS%@(H-7H5JJKv%FsCiOK~sDw#DKH>#$uakCLt#}hqJ z2OkgASH4Qxo-D{}-24R1tBV~PvUTw2drHVQB~cdYH$Fmuvaf?_L;%I^b9Pm0KIb+B zPL#7EBFZNM0V;`#03euW&9_7%ZVVrx09*MMDD24+pD$t2DY&O<0p-_JqKO)xUg=Dx zwsn1Y1ue4fCmAJY?AOqlc{D)X@r&ctDfW>J0LbpZ)yqtqjxqXO2z4b>uw0PkOA_9v zGBg8g*jf-a4zX)C7+pk96BeI$0-Ns5Rj*=za&NZ-oDJ;JCjgV&=aM`LMrE{*74ZrWB^$`Urn>gXA68f`Y2v zYxBPfTt+DzpQ*=NTZ-3wOj1U&0ym+Ex{Ju*Bd>b?^mck~;K;{dJt!ttI7|SNGh{^tF%_>w$E;0J8BhYM z3^x^Z%$@zn$KtUMjU-|X`pP%OzC5&c`4SPg-j8U}T`kjM%o=VBkcjiGR?2SW0D!xl z;d%x?2Ulf&BGy82SkylepmvlW{X{#0u04{1UA<>8Ad8bX1o3zgOb?Zh2y0mR`k@vo zh|Y3LLq#qu?+#_{tez6k%1;D*9PN)OW5Er;^bz3^o&4$ya+Q-pHBM$8jBrHe2fIRK;S z6&n-}=%n}3F|X!aCcA2Kim!Yw!*J*3CxRCHC}Q^5%C|u4bub_K4mJM?q164`^Zqv9 zf5NivH2n6w_G0jF^7J2*Ap@_F|9To(E#tYrB)}#YZF;UFi!VM5P z(P3T2taGHHi5L4(P9}e7RO_;CsT|h!7AE6=eSf#oGO1B?E3v%~C9dzHaBKI2w6B45 zgfz(F1RX_WOAQPvip;IukD_Rs-OE6) zY*OK3T9sMPWe8TN3H6W<)`8+MLeWqB1_mG%R^70Gh%XqDWzwT8*}|AU$}N`r*?p!P z^~4WxP7tlh3=t)Afv5B(dg(M3#BAjg;7n`}*@HOLMf3oOCg1k6?F;30viWc`UOHeo zN_DZo49b|r_kI&WU}=gv1ngUJE(K*30u-_k9ElaA8rK>fycqR)9`fuJ^G8#3AFY$k zAi>>vGYA|Kj&CZ8;M!7|5L>wg&}oaSKsRSv_jfZ82T`fZZ%WA3dfxgFXjpz+W-+-{ zOl+%BD$Mmom9izSS$nd2Ic!5r0APsWvw>_fxrl&UVrA4R5J>eCZK`Wdb-Ddt&m8`4*bsS4QWV@%|H^2#0dWgKE6y7|cP zWSj7-FMFksG`!Nw5NriTp?K0xLy#Vyv7!tV0!gk)M-43XEPMtS7!Vi)sJOz2K<2TM zs)h$YPPiFyKV3rwf_QF9#R($2qQpCShN4%yvi2snm2ZJ4K9N~!30rM1_r3YzFkpab z&@c2t6YEH_OeT3>7xcLwINzTfuVFhR;&^+ODm3rJE{W6<(G6t)BjWLho*s37w2$8v)_2?gze7d`0m4 z@i+U^uJgD2R>sr3Uk85Vdn@esaJ9edf4|l3mwzvOG5O=JHU~>O`^x7!4`qKnl)BI< z1o7W1624L{4Ji+OkTeI});|GP+-z?04Bt53R$sB6;+_7KpD_wjQ3|3?MD6QAJQwkk z=;S9~B-JOeNuQBh^f{p>7iZAhg zqM66>aPzaGSc^-irB$_J{WaZJVqSAcQkCY%>#1jCohLB|N9%>3gZN z@K$_w3L_R3Ewiq|z2I@q4rcEqu$7}7^)0X~M?3L6>JG>`@AT(>HXmJZl6$xr4)F9b zkgW$jaQ{@9P{AS>Z7^N=X08783W~BtWu~lVDH}d=_y~XNQy}4N2YsqblXMi0PDpop zC0N#qi{{#Lw#skiXtXp7pD|RsAdkW+20+TI9*8bEHn5l=tDk5`(6vWWfQ+sf%q5j> zqm6ttaJ;&$H!IiBo#&kjv6W9iK~Gkb%(PYGGoTU8Fy<*39m^>)eZI1#5gZ(~@d|Px zganh03NvC}Q`NwV=jjgK#Gh)Bgf#x)j;~(n8cD6BKJigDMj#IqCLvm+k3KmG#)BUx z+>E%NE;(9(CmKPRt)Qb>u2v6sXiTQks6s^84^YG97aN7YM9lGpL_E`?BO6Ls%XUr* z-_UX3wBvB4GnpXf)1%NRX(X3n!vGyn-03Xk2gkq*UL*y5O~sO?BYa5P^Z?e0-R;en zqJZ{_5@=Qws-FG8ufPI6I$h3Xo2}B3g@;w1!>K}v#~bBALhN~jm@CJd{wDMxu+~EY zns1}ru=M?*@ZAxi(DZXE&noxhTTTA%Jyd^fhUuc_+n{sV=C8fK(H+n$-Cy&D?2mn* z+e<$3s7(E~|7fiL9*rFpmtSOh-W(|VC`w|!+;3Xw?e4sNe?q;vId21E-jyAzdVflX z*I;+w7~c0Dx$PQ@~LXQ)gVtYkTGLCyx|$X}X`bcx3zR&C;4qgP((aD-Lkv%yy!JRHH?yY_|acNSaFchK5W zMMqCn)!lf|_)orvMr4Ev|3aGVej^C+mi7Y~`vD3kGVWC1$=?aFvUaGFEASu%RX zW%}*75uPAIT`iufk zxSiSGz89fGTJ2|nSaipoPQ)%R121@yR6!sraAN^02EKG>-zt292IWc<>@BPD$>+T4 zg$CVT8){AL3WYdcl0^yyQA%5xcFzdFk@zG`Xr?l9XEYsz9jm=*v^JQU3ancR<-@mq z#1R0k62!j!q5T1briok6rb4czMPNni=R}A{kd_y zz_vgBbQ7tB=GD(}@#Va}zm4#9Au0Y_n`iVj@v^4)eUL8_+>AFM$?BW;+*}Bhe)>i6 zzjti-?x9z#hD|p9dlV;(OJBHmx39_dp&~6U7BTOB=;%M$m(zipT(9ux`EB2>Qj3yW z-7|jWZ)?N!_-+XgV5Arb0hydMT!fh`A1(_od5P!`wgLRr`ly)u>l}ABD?%x7l#Fg( z^!DCb_x zRQAP)DDv{{>1nnIes5O=St$jYD|oNDvl*LM87kf%^+}pr!_-0G+z%o+ReSFzunbaW zy?aH-*{TECv2qraXFfgwWER_Ckg0628BvPj7=|>D57M*KMtKN$7fU#=5Y@#a&%SVR zy9zCY!CIcVzJdx_g9svtLr5%vKJs1}v;bY6NE#GWAC4=( z*TgH{Zigse)^YU1Ka$Cr$ zjwW277{pDde7M|*XA;MFe~7@*|AzhbxhSm7dGeL^`5h7S_qo!}Jt<-YFrz-^=Yl;P zd2$+vV8Amjq74oNaW5ax5HdRFg>6DYxYGyB!%DhpJ*iL*i`{`x4R+CO4{NA3!>EVY zVD&Lz;L%eQbt5DI?MgjcSYDQ|vJPI1>{mDj%l2U;p?p!fUqfW206)7mrsx(Q1B$jf z3xa#(vBTHrXZzD$#x0q;bB_x6jc|N|V9o!o*Y|_fO!gk)e*gadri6dc#}DIMyRpA@ zg{<>@7UkEFZb~DeLVS(TN4j~kNa~#{Tx~C zTFf*f=yI#BlqwZ-Nnql)CU~$&U;IvU`Gg#H6l=7V8${_|_!tW3s*fe|0~1JxvkY}~ zV>LcCLrD3$oSlUpV6%uuwLGT05*csu?rC6rULMFXXi>ASTnVZO0Odue3b1~Atof(? zxgxYdFMf)V#Yp%`0YI0NX=ugw^=UjJxw&0kCkObe-wMvXVg_gfJ#TB0t=vH4+LvS9 z2bLe?9RwmffdVN$JQ+jCOZ!Ckw)Y99$IH8RMwCd1%tyZwL`#pzi97hX0uE-ac#y!< zz8Y^ATR@aag zC{(?ObW6DM;g>=WWSVO?DJ6H)h13>|k{&Od;j02pW1!s#nQiTnzdu-BBj^$iwh4h2 z70i29C_mx|#|LbueYxmS_cPbJc^B`5?GzE5=Kn+ctNwq@!U9j^SMiPoMu#uEm@M3p zuzTo#WPkio@G*yd<8wpyh#NdA<9@W-yhk1zeSPrjwa?2VRWIQ`g;7%pLsv+&p`6hT zcoFE0)*Z1(DAetnDPE&HL-ry?tN@>vCl#|uW#Dq2_e?hpiwm>{L|5LTM?a6@9w%x7 zW0f8-l$m1aH7$(R{4)0D0c9uB-k@wuQr=5zsgMJ07xg6w+6-i0g0sX8#K-|I9fTNk za&Rd?$f;)~0xq%__fPi+>aJz_EvMOmSCK1gpk}VTD2lpl=?1vS*=o7HM$q=hmK|Dl zrv9n@v7t{v71J+iW&!5_nw!6XolYU&1op&5we6Fa`!n+P2jp>oR3P4YD@mac`1-J4 zYdD!#ACp+5*gZXbxX$ZiV>}+ti4u3U4ku|(w6WvO_;ex_FozCM!w;4gf4K6PV=xB1 zC^M@O$$*>p_CSSXt&}Iv&2gy4Vv4ca@3OvCTZII87gD=o%oBhlP2F{!nh!zoc z>H%;h|H1Hlz?1FSV82`D-Yit$f-Xs#K8HhIhkyo8pBPNY12H)6;VFWfTg6_K6zT>N z|GVKpDqtzb`JSHW_ev(aV{W$Mh?0mH`lL_KBT~jDkjQeW3N}a}Tma=Ap5DoDN=g7X z`Vi)l=v0!3s|WR4ZHPHF+hgAEigL!aBJn6ce7XK&nj&V2q>FQq&u zW4qc{vLOy9R=k!Sy@ZqU@d1HsaU?qgW2AL{j+r4;RbzVbFbJWz#!*3pL1yQGcHmMS zU}ae5j#I3G%p48&iuykkGR@zN3buLT zMZ6&b20VNu;e&x4#5UgbAXxiT$>v8_T>Hqbw{|9uObzA@d literal 0 HcmV?d00001 diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..7c21cc9 --- /dev/null +++ b/memory.x @@ -0,0 +1,13 @@ +MEMORY { + BOOT_LOADER : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +SECTIONS { + /* ### Boot loader */ + .boot_loader ORIGIN(BOOT_LOADER) : + { + KEEP(*(.boot_loader)); + } > BOOT_LOADER +} INSERT BEFORE .text; diff --git a/sprites/sprites_atlas.bin b/sprites/sprites_atlas.bin new file mode 100644 index 0000000000000000000000000000000000000000..047abcae2e3f5adc15953b4047df137563a3089c GIT binary patch literal 400 zcmW;DF(`#$6oBEcd)=I*Tq)fmk80O3phJu> z&Lq>!GS8xS%dD8!+;8X>JM3}55ht8+!4)^$@xUWbJoCaUZ@lxtCtrN?!!Idv|6iGZ zMU+I97PM9yXfiizD;;#vLmz_-GaA_#(+Rgz<{9%G3oNn9I-6{>%RYx3bILiFTzh}3 O_oRG8kf%UBMg9QgCoXUR literal 0 HcmV?d00001 diff --git a/sprites/sprites_tex.bin b/sprites/sprites_tex.bin new file mode 100644 index 0000000..24718cc --- /dev/null +++ b/sprites/sprites_tex.bin @@ -0,0 +1 @@ +]пџїџ§џпџџџџџпWпWеїѕWWUu}_џџџџеѕ}]_ееѕUUU_еUU_їѕ§ее}]_ееWѕ§]пwеџїпїџџџџн§]§ww§§џ_зѕWџџџ_ннп_w§џппї}пѕе}_зѕї}їѕ§]нUwwз}џїwџџџ}ѕп§џ_w§џ§пзѕWїWп]]нWпз§џппїwпїuн_зѕї§їїwџwзџ}џ§џїеїџџџ}нпз§пu]W§ѕ}Wџпџп§ззѕ§пе]W}_пї_пїѕѕ_зѕWе§їїwџџwѕ§їw§џїееѕџ}}пџU_ѕ§їппїџџїїзwѕ§пз§џU_їwпїѕ§Uwѕпџ}їїwwџ§Uwwїпџпww_з§§§нџwѕ§їппнWпWпџнеU§_w§џ_п}ї}пїѕ§_їеї}їїп]џwеп}wџпїџпз§џWUUеџ}_Wїѕ}}wїџїпзѕWееѕUџU_еW_UWѕ§еп§U§е§§_пџџџїџџџџџџџџџџџџџџџџџџџџџџџпџџџџѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ_еW_зџџ§џ§џџџпџзџпџџпџџџџџџџџџџпџџџџџџџџїпџџџџџџџџџџџџџџџџџџџнппїїџџџ§џџџпџ}џп§џїѕџџџџџџџџџпџџџџџџџџпппџџџџџџџџџџџџџџџџџџџнїппїїџџ§]Wеѕ]_}епџџз§їw_WUѕw_WWпзѕ§_еWпппџџџџџџџџџџџџџџџџџџџї§џпїнџџїѕ§_зї]}џз}неѕ§_еѕџппзѕ§нппп_пз_џџџџџџџџџџџџџџџџџџн§§џпїџїѕ§пе]_еWнџе§нзѕ§_зџWп_нннїѕ_пппwџџџџџџџџџџџџџџџџџн§їџїїџџџїе§_зџџ_нџз}пзѕ§UѕWџ§п_ннннџнџппп§џџџџџџџџџџџџџџџџџ}ѕW_їзџѕ§wWеѕ}__е_зе_зїWџзџWѕѕwеWїпџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџеџџѕџџџџџџпџџџџџџџџѕџџџџџџџџџџџџџџџџџџџџџџџѕџџџџџ§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџе_џџџџѕеWџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџе_џUеџ_UAџзџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФW]UбїUWUUU}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџеwUUѕѕUWAw}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕеWѕ§Awзџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕ_}нѕUUџAџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџеW}н§WwџUUџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ \ No newline at end of file diff --git a/src/dpad.rs b/src/dpad.rs new file mode 100644 index 0000000..918ad9b --- /dev/null +++ b/src/dpad.rs @@ -0,0 +1,37 @@ +use embedded_hal::digital::v2::InputPin; +use rp2040_hal::gpio::{Pin, DynPinId, FunctionSioInput, PullUp}; + +pub struct Dpad { + pinarray: [Pin; 4] +} + +impl Dpad { + + // expects pins clockwise starting from top + pub fn new(pinarray: [Pin; 4]) -> Self { + Self { + pinarray + } + } + + pub fn get_state(&self) -> DpadState { + let up = self.pinarray[0].is_low().unwrap(); + let right = self.pinarray[1].is_low().unwrap(); + let down = self.pinarray[2].is_low().unwrap(); + let left = self.pinarray[3].is_low().unwrap(); + + DpadState { + up, + down, + left, + right + } + } +} + +pub struct DpadState { + pub up: bool, + pub down: bool, + pub left: bool, + pub right: bool +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..93b26ca --- /dev/null +++ b/src/main.rs @@ -0,0 +1,361 @@ +#![no_std] +#![no_main] + +use embedded_hal::digital::v2::{OutputPin, InputPin}; + +use embedded_hal::blocking::i2c::Write; + +use hal::gpio::bank0::{Gpio0, Gpio1}; +use hal::gpio::PullDown; +use rp2040_hal as hal; +use hal::{ + pac as pac, + gpio::{FunctionI2C, Pin}, + fugit::RateExtU32, +}; + +use panic_halt as _; + +mod sprite; +mod dpad; + +use crate::dpad::Dpad; +use crate::sprite::*; + +#[link_section = ".boot_loader"] +#[used] +pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +#[repr(u8)] +#[derive(PartialEq, Copy, Clone)] +enum SH1106Cmd { + SetColumnLo = 0, + SetColumnHi = 0x10, + DisplayInvert = 0xA6, + DisplayOnOff = 0xAE, + ColDirectionNorm = 0xC0, + ColDirectionRev = 0xC8, + SetPage = 0xB0, +} + +const SH1106_ADDR:u8 = 0x78u8 >> 1; + +#[rp2040_hal::entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + const XTAL_FREQ_HZ: u32 = 12_000_000u32; + + // Configure the clocks + let clocks = hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let mut delay = cortex_m::delay::Delay::new(core.SYST, 133_000_000u32); + + let sio = hal::Sio::new(pac.SIO); + + let pins = hal::gpio::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS + ); + + + // Configure two pins as being IТВC, not GPIO + // let sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio0.reconfigure(); + // let scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio1.reconfigure(); + // let not_an_scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio20.reconfigure(); + + // Create the IТВC drive, using the two pre-configured pins. This will fail + // at compile time if the pins are in the wrong mode, or if this IТВC + // peripheral isn't available on these pins! + let mut i2c = hal::I2C::i2c0( + pac.I2C0, + pins.gpio0.into_function(), + pins.gpio1.into_function(), // Try `not_an_scl_pin` here + 400.kHz(), + &mut pac.RESETS, + &clocks.system_clock, + ); + + let mut led_pin = pins.gpio25.into_push_pull_output(); + led_pin.set_high().unwrap(); + + send_command(&mut i2c, &SH1106Cmd::DisplayOnOff, 0x00); + delay.delay_ms(100); + send_command(&mut i2c, &SH1106Cmd::DisplayInvert, 0); + send_command(&mut i2c, &SH1106Cmd::ColDirectionNorm, 0); + + set_col(&mut i2c, 2); + set_page(&mut i2c, 0); + clr_screen(&mut i2c); + delay.delay_ms(500); + send_command(&mut i2c, &SH1106Cmd::DisplayOnOff, 0x01); + + let mut gfx_buf = [0u8; 8192]; + + // gfx_buf.copy_from_slice(&TILES[0..8192]); + + + + let sprite_atlas = SpriteAtlas::new(include_bytes!("../sprites/sprites_atlas.bin")); + + let mut sprites = [Sprite::new(); 30]; + + // for (i, sprite) in sprites.iter_mut().enumerate() { + // sprite.sprite_index = i+30; + + // let i = i as i32; + // sprite.x = (i*10) % 128; + // sprite.y = (i*10) % 64; + // sprite.x_vel = i % 3 - 2; + // sprite.y_vel = i % 4 - 1; + // } + + sprites[0].sprite_index = 94; + sprites[0].x = 90; + sprites[0].y = 30; + sprites[0].visible = true; + + let dpad = Dpad::new([ + pins.gpio2.into_pull_up_input().into_dyn_pin(), + pins.gpio3.into_pull_up_input().into_dyn_pin(), + pins.gpio4.into_pull_up_input().into_dyn_pin(), + pins.gpio5.into_pull_up_input().into_dyn_pin(), + ]); + + // let dpad_down = pins.gpio2.into_pull_up_input(); + // let dpad_left = pins.gpio3.into_pull_up_input(); + // let dpad_up = pins.gpio4.into_pull_up_input(); + // let dpad_right = pins.gpio5.into_pull_up_input(); + + loop { + + gfx_buf.iter_mut().for_each(|x| *x = 0); + + sprites.iter_mut().for_each(|s| { + if s.visible { + sprite_atlas.draw_sprite(s.sprite_index, s.x as u8, s.y as u8, s.flip, &mut gfx_buf); + } + + let dpad_state = dpad.get_state(); + + if dpad_state.up { + s.y -= 1; + } + if dpad_state.down { + s.y += 1; + } + if dpad_state.left { + s.x -= 1; + s.flip = SpriteFlip::None; + } + if dpad_state.right { + s.x += 1; + s.flip = SpriteFlip::Horizontal; + } + + }); + + sprite_atlas.draw_string(10, 10, "Hello World!", &mut gfx_buf); + + sprite_atlas.draw_textfield(10, 20, 110, 10, "I think this font has a *lot* of 'character'! ;)", &mut gfx_buf); + + blit_framebuffer(&mut i2c, &gfx_buf); + + delay.delay_ms(1); + } +} + +fn draw_bresenham_line( + x0: u8, y0: u8, + x1: u8, y1: u8, + gfx_buf: &mut [u8; 8192], +) { + let mut dx = x1 as i32 - x0 as i32; + let mut dy = y1 as i32 - y0 as i32; + + let mut x = x0 as i32; + let mut y = y0 as i32; + + let mut err = 0; + + let mut step_x = 1; + let mut step_y = 1; + + if dx < 0 { + dx = -dx; + step_x = -1; + } + + if dy < 0 { + dy = -dy; + step_y = -1; + } + + if dx > dy { + for _ in 0..dx { + gfx_buf[(y*128+x) as usize] = 1; + + x += step_x; + err += 2 * dy; + + if err > dx { + y += step_y; + err -= 2 * dx; + } + } + } else { + for _ in 0..dy { + gfx_buf[(y*128+x) as usize] = 1; + + y += step_y; + err += 2 * dx; + + if err > dy { + x += step_x; + err -= 2 * dy; + } + } + } + +} + + + +fn blit_framebuffer( + i2c: &mut hal::I2C, Pin)>, + graphic: &[u8; 8192]) { + + // for each row of 8x8 pixels + for r in 0..8 { + set_page(i2c, r); + set_col(i2c, 2); + // for each cel of 8x8 pixels + for c in 0..16 { + let mut cel = [0u8; 8]; + + // so our x,y here would be r * 128 + c * 8 + + // start assembling the 8x8 cel + for x in 0..8 { + let mut col_byte = 0u8; + + // for each row of the cel + for y in 0..8 { + let pix_x = (c as usize) * 8 + x; + let pix_y = (r as usize) * 8 + y; + col_byte |= graphic[pix_y*128 + pix_x] << y; + } + + cel[x] = col_byte; + } + + write_cel_pixels(i2c, &cel); + } + + } + +} + +// as our display is in 8 pages of 128 columns, we need to transform the graphic +// to match the display's orientation +// fn transform_hcompress_graphic(graphic: [u8; 1024]) -> [u8; 1024] { + +// let mut cel:[u8;8]; +// let mut transformed_graphic = [0u8;1024]; + +// for row in 0..8 { +// for x in 0..16 { +// cel = [0u8; 8]; +// for y in 0..8 { +// let byte = graphic[(row*8+y)*16+x]; + +// for i in 0..8 { +// cel[i] |= ((byte & (0b1000_0000 >> i)) << i) >> y; +// } +// } + +// transformed_graphic[row*128+x*8..row*128+x*8+8].copy_from_slice(&cel[0..8]); +// // transformed_graphic[x*8..x*8+8].copy_from_slice(&cel[0..8]); +// } +// } + +// transformed_graphic +// } + +fn set_col(i2c: &mut hal::I2C, Pin)>, + col: u8) { + let col_lo = col & 0b0000_1111; + let col_hi = (col & 0b1111_0000) >> 4; + + send_command(i2c, &SH1106Cmd::SetColumnLo, col_lo); + send_command(i2c, &SH1106Cmd::SetColumnHi, col_hi); +} + +fn set_page(i2c: &mut hal::I2C, Pin)>, + page: u8) { + send_command(i2c, &SH1106Cmd::SetPage, page & 0b0000_0111); +} + +fn clr_screen(i2c: &mut hal::I2C, Pin)>) { + let mut writebuf = [0u8; 132*8+1]; + + writebuf[0] = 0b0100_0000; + + for i in 0..8 { + match i2c.write(SH1106_ADDR, &writebuf) { + _ => () + }; + + set_page(i2c, i); + } +} + + +fn write_cel_pixels( + i2c: &mut hal::I2C, Pin)>, + data: &[u8]) { + + let mut writebuf = [0b0100_0000u8; 9]; + + writebuf[0] = 0b0100_0000; + writebuf[1..].copy_from_slice(&data[0..8]); + + match i2c.write(SH1106_ADDR, &writebuf) { + _ => () + }; +} + + +fn send_command(i2c: &mut hal::I2C, Pin)>, + command: &SH1106Cmd, + data:u8) { + let mut writebuf: [u8; 2] = [0; 2]; + + writebuf[0] = 0b1000_0000; + writebuf[1] = (command.clone() as u8) | data; + + match i2c.write(SH1106_ADDR, &writebuf) { + _ => () + }; +} + +// static GRAPHIC:[u8;8192] = *include_bytes!("../image.bin"); +// static TILES_COMPRESSED:[u8;12800] = *include_bytes!("../kenney_comp.bin"); +// static TILES:[u8;102400] = *include_bytes!("../kenney_mono.bin"); \ No newline at end of file diff --git a/src/sprite.rs b/src/sprite.rs new file mode 100644 index 0000000..f29ffba --- /dev/null +++ b/src/sprite.rs @@ -0,0 +1,204 @@ +static SPRITES_COMPRESSED:[u8;16384] = *include_bytes!("../sprites/sprites_tex.bin"); + +const ATLAS_WIDTH:usize = 256; + +#[derive(Copy, Clone)] +pub struct Sprite { + pub x: i32, + pub y: i32, + pub sprite_index: usize, + pub visible: bool, + pub flip: SpriteFlip, +} + +impl Sprite { + pub fn new() -> Sprite { + Sprite { + x: 0, + y: 0, + sprite_index: 0, + visible: false, + flip: SpriteFlip::None, + } + } +} + +#[repr(u8)] +#[derive(Copy, Clone)] +pub enum SpriteFlip { + None = 0, + Horizontal = 1, + Vertical = 2, + Both = 3, +} + +#[derive(Copy, Clone)] +pub struct SpriteAtlasEntry { + pub x: u8, + pub y: u8, + pub width: u8, + pub height: u8, +} + +// For the current implementation we assume the first 94 sprites are ASCII font characters. +// So each ASCII character sprite index is: ASCII - 33 +pub struct SpriteAtlas { + entries: [SpriteAtlasEntry; 256], +} + +impl SpriteAtlas { + pub fn new(from_bin:&[u8]) -> SpriteAtlas { + + if from_bin.len() > 256*4 { + panic!("SpriteAtlas::new() - from_bin is too large"); + } + + let mut entries = [SpriteAtlasEntry { + x: 0, + y: 0, + width: 0, + height: 0, + }; 256]; + + from_bin.chunks(4).enumerate().for_each(|(i, chunk)| { + entries[i].x = chunk[0]; + entries[i].y = chunk[1]; + entries[i].width = chunk[2]; + entries[i].height = chunk[3]; + }); + + SpriteAtlas { + entries: entries, + } + } + + pub fn draw_textfield(&self, + x: u8, y: u8, + width: u8, height: u8, + string: &str, + gfx_buf: &mut [u8; 8192], + ) { + let mut cur_x = x; + let mut cur_y = y; + let words = string.split_ascii_whitespace(); + + for word in words { + let word_width = self.get_str_pixel_width(word); + + if cur_x + word_width as u8 > x + width { + cur_x = 0; + cur_y += 8; + } + + self.draw_string(cur_x, cur_y, word, gfx_buf); + cur_x += word_width as u8 + 4; + } + } + + pub fn draw_string(&self, + x: u8, y: u8, + string: &str, + gfx_buf: &mut [u8; 8192], + ) { + let mut x = x; + + for c in string.chars() { + if c == ' ' { + x += 4; + continue; + } + + let sprite_index = c as usize - 33; + self.draw_sprite(sprite_index, x, y, SpriteFlip::None, gfx_buf); + x += self.entries[sprite_index].width + 1; + } + } + + fn get_str_pixel_width(&self, str:&str) -> usize { + let mut width = 0; + + for c in str.chars() { + if c == ' ' { + width += 4; + continue; + } + + let sprite_index = c as usize - 33; + width += self.entries[sprite_index].width as usize + 1; + } + + width + } + + pub fn draw_sprite(&self, + sprite_index: usize, + x: u8, y:u8, + flip: SpriteFlip, + gfx_buf: &mut [u8; 8192]) { + + let entry = self.entries[sprite_index]; + + for sprite_y in 0..entry.height { + let tgt_y = sprite_y + y; + if tgt_y >= 64 { + continue; + } + + for sprite_x in 0..entry.width { + let tgt_x = sprite_x + x; + if tgt_x >= 128 { + continue; + } + + let px = match flip { + SpriteFlip::None => self.px_for_entry(sprite_index, sprite_x, sprite_y), + SpriteFlip::Horizontal => self.px_for_entry(sprite_index, entry.width - sprite_x - 1, sprite_y), + SpriteFlip::Vertical => self.px_for_entry(sprite_index, sprite_x, entry.height - sprite_y - 1), + SpriteFlip::Both => self.px_for_entry(sprite_index, entry.width - sprite_x - 1, entry.height - sprite_y - 1), + }; + + if px == 3 { + continue; + } + + let target_index = (tgt_y as usize)*128 + tgt_x as usize; + gfx_buf[target_index] = px; + } + } + + } + + pub fn px_for_entry(&self, entry_index:usize, x:u8, y:u8) -> u8 { + let entry = self.entries[entry_index]; + + // input is uncompressed coordinate system + // we need the pixels from the compressed coordinate system + + let x = x + entry.x; // uncompressed + let y = y + entry.y; // uncompressed + + // uncompressed, we're looking for x,y + // however as the compressed tiles are 2bpp (4px per byte) + // we need to find the byte that contains the pixel we're looking for + + let sprite_row_byte_index = (y as usize)*ATLAS_WIDTH/4 + (x as usize)/4; + let sprite_row_byte = SPRITES_COMPRESSED[sprite_row_byte_index]; + + let x_remain = x % 4; + + // eg if x = 0, we want the first 2 bits of the byte + // if x = 1, we want the second 2 bits of the byte + // therefore, @ x = 0: + // (sprite_row_byte & (0b1100_0000 >> 0)) >> 6 + // @ x = 1: + // (sprite_row_byte & (0b1100_0000 >> 2)) >> 4 + // @ x = 2: + // (sprite_row_byte & (0b1100_0000 >> 4)) >> 2 + // etc + + let px_byte = (sprite_row_byte & (0b1100_0000 >> (x_remain*2))) >> (6 - x_remain*2); + + px_byte + + } +} \ No newline at end of file