From 570525cbd7785e1019b49ed6b3a07ed032dfa264 Mon Sep 17 00:00:00 2001 From: able Date: Thu, 2 Jun 2022 06:00:26 -0500 Subject: [PATCH] updates and planning --- TODO.md | 21 ++++++ ableos/TODO.md | 2 - ableos/src/bogomips.rs | 130 ++++++++++++++++++++++++++++++++++ ableos/src/graphics/mod.rs | 104 ++++++++++++++++++++++++++- ableos/src/lib.rs | 2 + ableos/src/scratchpad.rs | 23 ++++++ ableos/src/vga_e.rs | 4 +- api/file.axel | 12 ++++ shadeable/src/pixel_format.rs | 2 +- userland/bogomips.c | 77 ++++++++++++++++++++ userland/root_fs/ext2.img | Bin 4194304 -> 4194304 bytes 11 files changed, 370 insertions(+), 7 deletions(-) create mode 100644 TODO.md delete mode 100644 ableos/TODO.md create mode 100644 ableos/src/bogomips.rs create mode 100644 api/file.axel create mode 100644 userland/bogomips.c diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..a9a8d83d --- /dev/null +++ b/TODO.md @@ -0,0 +1,21 @@ +# AbleOS +## General +- [ ] Improve EXT2 + + +## ARM +- [ ] Get arm-version booting on real hardware + + + + + +# Drivers +- [ ] Slim down driver specific program code + - [ ] Remove entry/exit functions for drivers + + +# Tooling +## Repbuild +- [ ] make generation of the ext2 image possible + diff --git a/ableos/TODO.md b/ableos/TODO.md deleted file mode 100644 index cedd21b4..00000000 --- a/ableos/TODO.md +++ /dev/null @@ -1,2 +0,0 @@ -- [ ] Simple interactive "painting" "program" -- [ ] monotty like desktop environment diff --git a/ableos/src/bogomips.rs b/ableos/src/bogomips.rs new file mode 100644 index 00000000..1d499911 --- /dev/null +++ b/ableos/src/bogomips.rs @@ -0,0 +1,130 @@ +use crate::time::fetch_time; + + + + + + + +fn delay(ms: u64) { + let mut i = 0; + while i < ms { + i += 1; + } +} + +pub const CLOCKS_PER_SEC: u64 = 1000; + + + + +pub fn bogomips() -> u32 { + + let mut loops_per_sec:u64 = 1; + let mut ticks: u64 = 0; + info!("bogomips: starting"); + + while (loops_per_sec << 1) != 0 { + ticks = fetch_time() as u64; + delay(loops_per_sec); + ticks = fetch_time() as u64 - ticks; + if ticks >= CLOCKS_PER_SEC { + loops_per_sec = (loops_per_sec / ticks) * CLOCKS_PER_SEC; + println!("ok - {}.{} BogoMips\n", + loops_per_sec/500000, + (loops_per_sec/5000) % 100 + ); + return 0; + } + } + + + + + + + + println!("bogomips: failed to get a result"); + return 61; + + + +} + + + + + + + + + + + + +/* + +asm!{ + +} + + + + + + +#ifdef CLASSIC_BOGOMIPS +/* the original code from the Linux kernel */ +static __inline__ void delay(int loops) +{ + __asm__(".align 2,0x90\n1:\tdecl %0\n\tjns 1b": :"a" (loops):"ax"); +} +#endif + +#ifdef QNX_BOGOMIPS +/* version for QNX C compiler */ +void delay(int loops); +#pragma aux delay = \ + "l1:" \ + "dec eax" \ + "jns l1" \ + parm nomemory [eax] modify exact nomemory [eax]; +#endif + +#ifdef PORTABLE_BOGOMIPS +/* portable version */ +static void delay(int loops) +{ + long i; + for (i = loops; i >= 0 ; i--) + ; +} +#endif + +int +main(void) +{ + unsigned long loops_per_sec = 1; + unsigned long ticks; + + printf("Calibrating delay loop.. "); + fflush(stdout); + + while ((loops_per_sec <<= 1)) { + ticks = clock(); + delay(loops_per_sec); + ticks = clock() - ticks; + if (ticks >= CLOCKS_PER_SEC) { + loops_per_sec = (loops_per_sec / ticks) * CLOCKS_PER_SEC; + printf("ok - %lu.%02lu BogoMips\n", + loops_per_sec/500000, + (loops_per_sec/5000) % 100 + ); + return 0; + } + } + printf("failed\n"); + return -1; +} + +*/ \ No newline at end of file diff --git a/ableos/src/graphics/mod.rs b/ableos/src/graphics/mod.rs index c7dbd8b5..96f82140 100644 --- a/ableos/src/graphics/mod.rs +++ b/ableos/src/graphics/mod.rs @@ -1,6 +1,12 @@ use ab_glyph::{Font, FontRef, Glyph}; -use shadeable::{evaluate_shader, pixel_format::Rgba64}; +use shadeable::{ + evaluate_shader, + pixel_format::{get_b, get_g, get_r, Rgba64}, +}; use spin::Lazy; +use vga::{colors::Color16, writers::GraphicsWriter}; + +use crate::vga_e::VGAE; pub static SCREEN_BUFFER: Lazy> = Lazy::new(|| spin::Mutex::new(ScreenBuffer::new(640, 480))); @@ -123,7 +129,9 @@ impl ScreenBuffer { // TODO force clear pub fn force_redraw(&mut self) { - // VGAE.lock().clear_screen(vga::colors::Color16::Black); + let vga = VGAE.lock(); + vga.set_mode(); + vga.clear_screen(vga::colors::Color16::Black); } /// Draw a glyph on the screen at the given position @@ -240,3 +248,95 @@ pub fn get_coordinates(x1: i32, y1: i32, x2: i32, y2: i32) -> Vec<(usize, usize) coordinates } + +pub trait VgaBuffer { + fn copy_to_buffer(&self) -> GraphicsReturn; +} +impl VgaBuffer for ScreenBuffer { + fn copy_to_buffer(&self) -> GraphicsReturn { + let mode = VGAE.lock(); + for y in 0..self.size.y { + for x in 0..self.size.x { + // use shadeable::pixel_format::into_vga_16; + let vga_color = into_vga_16(self.buff[y * self.size.x + x]); + if into_vga_16(self.clear_color) != vga_color { + mode.set_pixel(x, y, vga_color); + } + } + } + GraphicsReturn::Ok + } +} + +pub fn into_vga_16(rgba_64: Rgba64) -> Color16 { + use shadeable::pixel_format::ChannelValue::*; + use vga::colors::Color16::*; + match ( + get_r(rgba_64).into(), + get_g(rgba_64).into(), + get_b(rgba_64).into(), + ) { + (Dark, Dark, Dark) => Black, + (Dark, Dark, Low) => Black, + (Dark, Dark, Mid) => Blue, + (Dark, Dark, High) => Blue, + (Dark, Low, Dark) => Black, + (Dark, Low, Low) => Black, + (Dark, Low, Mid) => Blue, + (Dark, Low, High) => Blue, + (Dark, Mid, Dark) => Green, + (Dark, Mid, Low) => Green, + (Dark, Mid, Mid) => Cyan, + (Dark, Mid, High) => Cyan, + (Dark, High, Dark) => Green, + (Dark, High, Low) => Green, + (Dark, High, Mid) => Green, + (Dark, High, High) => Cyan, + (Low, Dark, Dark) => Black, + (Low, Dark, Low) => Black, + (Low, Dark, Mid) => Blue, + (Low, Dark, High) => Blue, + (Low, Low, Dark) => Black, + (Low, Low, Low) => DarkGrey, + (Low, Low, Mid) => LightGrey, + (Low, Low, High) => Blue, + (Low, Mid, Dark) => DarkGrey, + (Low, Mid, Low) => LightGrey, + (Low, Mid, Mid) => Cyan, + (Low, Mid, High) => Cyan, + (Low, High, Dark) => Green, + (Low, High, Low) => Green, + (Low, High, Mid) => Cyan, + (Low, High, High) => Cyan, + (Mid, Dark, Dark) => Red, + (Mid, Dark, Low) => Red, + (Mid, Dark, Mid) => Magenta, + (Mid, Dark, High) => Magenta, + (Mid, Low, Dark) => Brown, + (Mid, Low, Low) => Red, + (Mid, Low, Mid) => DarkGrey, + (Mid, Low, High) => LightBlue, + (Mid, Mid, Dark) => Brown, + (Mid, Mid, Low) => Brown, + (Mid, Mid, Mid) => LightGrey, + (Mid, Mid, High) => LightBlue, + (Mid, High, Dark) => Green, + (Mid, High, Low) => Green, + (Mid, High, Mid) => LightGreen, + (Mid, High, High) => LightCyan, + (High, Dark, Dark) => Red, + (High, Dark, _) => Magenta, + (High, Low, Dark) => Red, + (High, Low, Low) => LightRed, + (High, Low, Mid) => Pink, + (High, Low, High) => Magenta, + (High, Mid, Dark) => Yellow, + (High, Mid, Low) => Yellow, + (High, Mid, Mid) => LightRed, + (High, Mid, High) => Pink, + (High, High, Dark) => Yellow, + (High, High, Low) => White, + (High, High, Mid) => White, + (High, High, High) => White, + } +} diff --git a/ableos/src/lib.rs b/ableos/src/lib.rs index 98892da3..864a0243 100644 --- a/ableos/src/lib.rs +++ b/ableos/src/lib.rs @@ -60,9 +60,11 @@ pub mod time; pub mod utils; pub mod virtio; pub mod wasm; +pub mod bogomips; pub mod wasm_jumploader; mod unicode_utils; +pub mod vga_e; #[prelude_import] pub use prelude::rust_2021::*; diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index 9b3ceeac..e34b5ab2 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -5,11 +5,13 @@ use crate::filesystem::FILE_SYSTEM; use crate::rhai_shell::shell; use crate::rhai_shell::KEYBUFF; use crate::wasm_jumploader::run_program; +use crate::{SCREEN_BUFFER, bogomips}; use acpi::{AcpiTables, PlatformInfo}; use cpuio::inb; use cpuio::outb; use genfs::Fs; use genfs::OpenOptions; +use vga::writers::GraphicsWriter; // TODO: move to a better place #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -93,8 +95,29 @@ pub fn scratchpad() { debug!("{}", generate_process_pass()); } + + debug!("start the graphics"); + let mut abcde = SCREEN_BUFFER.lock(); + + abcde.force_redraw(); + + abcde.draw_filled_circle(100, 100, 300, 0x0000ff00); + abcde.draw_unfilled_rect(100, 100, 400, 200, 0xff000000); + abcde.draw_filled_rect(300, 300, 400, 400, 0xff000000); + abcde.draw_line(100, 100, 400, 200, 0xff000000); + abcde.copy_to_buffer(); + + + debug!("end the graphics"); + // */ + +use crate::bogomips::bogomips; +// bogomips(); + + real_shell(); } +use crate::graphics::VgaBuffer; pub fn acpi() { let acpi_handler = AcpiStruct {}; diff --git a/ableos/src/vga_e.rs b/ableos/src/vga_e.rs index 3d41cea1..0c6091fe 100644 --- a/ableos/src/vga_e.rs +++ b/ableos/src/vga_e.rs @@ -7,14 +7,14 @@ pub static VGAE_BUFF_OFFSET_X: spin::Mutex = spin::Mutex::new(0); pub static VGAE_BUFF_OFFSET_Y: spin::Mutex = spin::Mutex::new(0); pub static VGAE: spin::Mutex = { let xyz = Graphics640x480x16::new(); - xyz.set_mode(); + // xyz.set_mode(); spin::Mutex::new(xyz) }; /// Converts a number to ... i forgor 💀 pub const fn num_to_vga16(num: u8) -> Color16 { use Color16::*; - + match num { 0 => Black, 1 => Blue, diff --git a/api/file.axel b/api/file.axel new file mode 100644 index 00000000..111c92ed --- /dev/null +++ b/api/file.axel @@ -0,0 +1,12 @@ +file { + val= + name: "Hi" + extension: "txt" + size: 123 + + fn| + open: (None)->() + read: (Num)->(String) + write: (Num, String)->(Bool) + close: (None)->(Bool) +} diff --git a/shadeable/src/pixel_format.rs b/shadeable/src/pixel_format.rs index cdff7ac3..149e567f 100644 --- a/shadeable/src/pixel_format.rs +++ b/shadeable/src/pixel_format.rs @@ -2,7 +2,7 @@ use core::ops::{BitAnd, BitOr, Shr}; pub type Rgba64 = u64; -enum ChannelValue { +pub enum ChannelValue { Dark, Low, Mid, diff --git a/userland/bogomips.c b/userland/bogomips.c new file mode 100644 index 00000000..7e601c81 --- /dev/null +++ b/userland/bogomips.c @@ -0,0 +1,77 @@ +// Utterly stolen from stack overflow + + + + + +/* + * Standalone BogoMips program + * + * Based on code Linux kernel code in init/main.c and + * include/linux/delay.h + * + * For more information on interpreting the results, see the BogoMIPS + * Mini-HOWTO document. + * + * version: 1.3 + * author: Jeff Tranter (Jeff_Tranter@Mitel.COM) + */ + +#include +#include + +#ifdef CLASSIC_BOGOMIPS +/* the original code from the Linux kernel */ +static __inline__ void delay(int loops) +{ + __asm__(".align 2,0x90\n1:\tdecl %0\n\tjns 1b": :"a" (loops):"ax"); +} +#endif + +#ifdef QNX_BOGOMIPS +/* version for QNX C compiler */ +void delay(int loops); +#pragma aux delay = \ + "l1:" \ + "dec eax" \ + "jns l1" \ + parm nomemory [eax] modify exact nomemory [eax]; +#endif + +#ifdef PORTABLE_BOGOMIPS +/* portable version */ +static void delay(int loops) +{ + long i; + for (i = loops; i >= 0 ; i--) + ; +} +#endif + +int +main(void) +{ + unsigned long loops_per_sec = 1; + unsigned long ticks; + + printf("Calibrating delay loop.. "); + fflush(stdout); + + while ((loops_per_sec <<= 1)) { + ticks = clock(); + delay(loops_per_sec); + ticks = clock() - ticks; + if (ticks >= CLOCKS_PER_SEC) { + loops_per_sec = (loops_per_sec / ticks) * CLOCKS_PER_SEC; + printf("ok - %lu.%02lu BogoMips\n", + loops_per_sec/500000, + (loops_per_sec/5000) % 100 + ); + return 0; + } + } + printf("failed\n"); + return -1; +} + + diff --git a/userland/root_fs/ext2.img b/userland/root_fs/ext2.img index 12e420771e949d6bf740cd1f4a308882aee47ec4..1b7a899a870d33304b5f3d9ffcd33e618f098153 100644 GIT binary patch delta 4467 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 UGF=_4tL3^{p{ql-sFk1n58uHD0RR91 delta 21609 zcmdU13w%`7nLp>=naMm7G6MuAAxH=h2NV(nB!FP3s6kPJBE}~H0nz%1Xf;~tjgO9~ zwMJ<>bk{amm15mikhO%hTG~`m?wZ>N0^kvbC?Eio6IrrX>2%*~UZ}a<2 za{u3XeXsMK@0@cdO-)T!6F%#Y4#h5;zI4&d>}Th^R?tK_U3H#W_VhU_Y9$;LZtSA^`m zq9v;mXRWZ`w3e*86Ms+nXzr@S{1u9RC;p=1s>InV?6+8szpA`Wm*cN0KWAUzW^2i! z>qFjW)xdL43$2|#;=FrgdR5c;i$niWbYwZ6?>#%JhDw~b&;5J;K9_|HR@faJ;(`_B zV9D!B;HSg3gvAH^oR3e~x<^h~wfMpZ-IfckpMT=lg}7|g{6*%&njN+jr}F_n5+6Gv zL9}6EFpAbG_d?K3? z^?Bp|U6yT{)<62QqjL98kCvy76iHEz0SoX6<8$PuXOE3elpB((t#3rrBXZ-S=^^>! zCeN!pX38-Wr&LaiZ;G?&P-)z_RY@hmz~)^JF_S|b3k_Hz_l~eO}QJ+E?$s(bZ2Zp=;2Z+a(@|} zn(IXG-Y3M*qe5iPf*Py|5Y4VXRf@8d5b0AQ(q6bX@ z1n)^{%kQBf!JA=eU;2a;;|Ly+a^Qaa^nsvx@7nk+TCRiIN9CcgT3B-&sKCDWA)$sS z*Hy+}v4VfYb3-43*|LAZX%|be^7PzI@5P4YDrTi(3q$=1jv6dQat=WMP-($-*e*k`(=JH<$N{Pb zN>8v#FkTgE*0C^+n*WWm27(t*^OpdtS+P^1Vsa*?%odjY#ts*d%XR{xxfHblF4F`( zatX#9DJoH364IR25Gc+tUOj+j>_jlu$g(IZ?n7BK!AoGQ6QGq9|BZ~rDL7T3OuFCh zC#)|)Dfav!t{W;JS@9L2{0nZeSo;MW#r2ClC-&Yi_K*kT7kdGWUnJ)S+#)mAcMGSR zTPWpKqrRuSdH~PFjRZX_?>i8wnGLDC=imYB2&lU)09yc3qm)q}Ma6cOMMF+$!OJM@ zB%<=Z0uVg|HGk+?wG5C}^)!jOCQ6hhmtykRKbaPo-R(CAX7{Ft19Q0ZVSf(W;S$T8 z_i;}A>+j{n&t3IKY@l=EYyND$A3kOO_m2aH`ky=e`DN8 z%4pq{e>5j$6(u%itOYP;T*hZJyPM7km{D16HQO1NUk~6}t&N~(tKBdZ^Og;1wKTw9 z0$MErkUZ1#_}c(8fXd^K0jMRQ(MAkC7n6ZCmCLKSxQ2y2SfG=b<6P5Jk@rFbq-jY62sA z9e|$fUqe~LJO_mgL$iTavnIpP@c>&`Q-vX6ZO6Q|H(Vsuf|I%j-nHs@*MU+QEI4m{ z=(=R;6)Y=r{_0Nq)Zc{eQ>e+9L9v>QjGS62zqvy@{tZWL%H%8#TkL( ztQCv_XvU5sV=QA%P>-@T1az1)0a^hL$O-(FOfpZs?Cij78#Tb2Y#3kVikRMy;!lzA|18PRHDpNV=aJZ>}3Q! ztMQu|DVo@j^Um3XyqkbpTn?}vK+n6isMy9bYVlN*?IEBRr*A?C-piVPc91+5g+0%^ zZ!#C(9dkpm%KiWJ3pX)Wx&Li55D3z+2~&V6a9J<+FGJMxa{t-)`FX=Yhf$S5(nQs7 zU*e|=D?%9W^j8A8|I|nP48h(r@xXaL!_4#kC-o{_DEQM+uwRA}8~arQ82i=n+065X zbN?apd?g1r^L#ykXZkjR9!;<2`5rdpqI=b7q!q;uX_~aS@M|uYWvmKA+5~ss z3>Au?qo_~d?i!KIBXrGc+sTKSV&cM}P6&F`iJD@SC{|O93aTOC6r+Ob350lM=d(Kn`;LZ95f*uuDu%iNNHXCx%KmRyZA_D5_=K$>h zdRck}6-7PDR66($3e!aNrq=)}0RqcX4a!B(vkN)qW0K zV*`*}1(4AI$$tfx&WR5NtbO}7FCCGNYwmBk$$zNj!$X^!*6waKrX%&#lreuJfH8kF zpH1jkM_?vW?kn%b(t&(r3%f8;WDkI6_`L)@8~$ZToLq>8H2h48Pe8*@0;mSihJOpN zj%AD@7Xma8(C%}`A&RVIO+O!5!@{89{n&pimAZ4I5StL-Q*9w>FB@Tb+dO;OUxZl4 zk`qu8MiRtzKzYH(?e0ud`^1yKF2q)r^u<2W8FZd|kdc~-B<6{TrIMJ`hf)fw;2leZ z7kpWW3p*@v)(jag8Yx88TX-IX(&2?L^HP-9-+AMwo#q%E(4#W3P_wt0gPYmAj=;>`EdZ_&6P&&5i>K5yWd8(bFe%DDux9+V$8#@r#73R) zIxOf4+lRZ_c62iTF;`CUG+G-u(*Q=!N*+0 z8iG~_PZ>#?8rL>Y@T8FxMW8x(+Gq$y9qn2jIEsNf>VF>_c=F&uMjiRwd9lT2mTIr4 zrzA!tjRBQ32UW7JEqB`KsiV6exGIO(av0j+PKrE~QZ8zPyD5)AZE#0r2wEH5RgvT< zU!ZqzYeh$vL;1JXe0ikX?9E0Rt(IyU1+*7c+tKxiWB! zkKF1A7D$lWgZkaH$X7@`lHrYr4l%mI;oq!obC>2~+kCDBD2$fXm#(FSj4FlgLyTtt@d(Q=GDBmO5zwo z=hdnG+F?}meJqtaQ#(u)oWuN>U~gi}s2pZWz+s#RjKlQd;XwvHdf^DqC4Vm0w}Xv9 z##x&AY(n(9Uj&`yK&yDG@!@z6xAruH=RMp+djxuq6(DHu;bz)lC8oA&qH#y<5jfGf zt2PAJi@Yrv4a|x0>72&ta^uyx`N}$v9LJ2byJnJwbax<3Td51@UsmrcwpfX=o~!NF$C=#JlP?spNSbf-El}!DI%T2 z(p5i`B;MLyX7n&W2di{pm&V-{IZyP?+4W?Q$qH|>8vz`Ztc(C^20E0-s9Yzw z?vM~nRum2}x+W{@$Yd|$i_t`enxMxRO(;T86C`L&@M3!l2TAiy z3|?%v6ERWY#daq(;T`~{#%nJzneFA>yyO-18-8-7RIgvnPr)1P;i$C!8B3JBikF`r zoM(ygyEVA4R)}+7(%`Biq?q@$7c{c+EOFgV4JOaC~MB>9{udD2dpynYcXlssjpRFtj8k()P7>Hy3%Y2dS&CM%x{PLl&uwa+*g`s7q7 z!Iasd^3;~`v(K5mxTR9&HPj6~ktuU4fbv8HSraE<(SQENCMkB2FSD`fcrN$xN3o%^ z{s*&#heotC6LM;aFCcoB7CQjEfCykLUCC!->6#t?e=Pm?B)VRnTR__d^ISzxE0oT2 z3mjB&gO6wNf68Wrren^T{-n)FZq)wRs98g|`4(rU&1T02EnXkAcw@lg%|VN=`+sC{ zlfn+9@q#I=B5;W@c{~X^RWlhZ&YN9}n+#SFXmOLl7Jf>L8}<%+n_(>OD2w-En*mX* zzapbn5&v5-y1HChi8l;Q3@|D6b=o6(zWdk`bdmNQI9W1Lp)=?s(VfAlV8Y+wOa_xq z4yO^ACYf|n5rXL?3EFZd$@b2Zy(BxqZh}cR3IC8CShv;4e+jT_ROn@vWE7!x8^OF< z5rT>!K`X+f)l@{mg0B^GAhjtoZ=~b zIEk@3Nb~YySRI#coUzLPduetf{77;aOpX;HC_D*TcqYef6rP88%#HUDykO(Hl*#el zow*(1_|RFCC%Lx+T>8!J7*1XMr9Zg~z*sl~U@TnCXJg^IUBO9yV3OxK`(Y$K>UOMw zgTvhFZhIvGq*Zs@YXJ0Nckgb;{u+sonl^_*Z>ZCTk8yy|S9a$yoQ&QfM)eulp-jUS8HC2D~*$XUS{-g6_&doh5)wY6klp{ z*|kSY-SZ;W8B{~`d0!1>d^T#RdOm1yvxddkb1x+~*Ip{wU;s1MUS9TLuDwV&;Mz;= zSSUDey$Wec1Z8&(o4zTRi$n6?zVAzJ?yXEh*R$lc5YTggWdX^Xf|9TP)RKRy3#>BA zNy&MCr6k9tf-Cvlak<@zc#-oWl5cAo0<*ONcMW#GuL*I%?UsmMhBpktX#XO1@Cv-W zM2LTX7GO0>Zg^3M2Urb2Sn9>L$W$p7k+covW3Lk8Wws>vU@~4|V9RG%c@;PaPx+1% z--M~6&XY3y=n^R&A@~`J)z>;6%adX=Ta3ri!5uBQ>1M|O!nz-O_;9#WH%IH)zw%oP z>os_}RZemE259){XQcSgpIahP4wGB2;;h`Izm4&iZns1qXN(LlZxbT>3jjQqAW*%l zpD#br68*lR=xEoDHOFl{?99(^&vPMkVnJOAg+ZJKhpc-e8H-@#>cA(&6`iBg9DXaym{1 zEPi#u-ZoT->Mw$lfMx4$mg1D#6v&2(v!Rk~Xizp(nhg!khK|UFhJ0f}bU?18Dq5PW zm|Zp`5|2sCwsQ>=qC;{`p-i8eMR}3Smt213^6T;5&(+r~Tz-wP@)s|^$#IF1iLaMJ##I@+Awe36kQuKSZmBjR;5McUiH)QaUr_3@eE* zI97Grn>Y z{wA}z!bs-xo96VX{1=IDhR%+@ z=Jy@Luj^seAm#ldF9xt;gB){+$MIm!{GNdkpTn{y^JVyc&{_Un{Ebw^VmWl+kbs$m zulUN+$Q`;%66^)x0ujShS8a}D6)9sL7pl!0{X8y+U1fCP^R1Lu!&gS^Dc$QNfjzbr z33Lkvg!LD{TjCyc{K3E;bnG!Zf}O-=*ke|ytVdQbew->mn&Ii}`AzulCc*%8rpoZ> zm5rWmiKpE%dpc(4U4v~AIaMmXZw}Ox*tPuXI9tetGJF>thhyMoOhoRCMd=Fle6Bq? z;n2=20hEoQ^%rlG;<}-Jr@uy-G3M+>yh%OP?esUgQzpBSyJOLd(9e3^>3lr52l|XI zlj6Qik9|5V$F^cEo9uQv5n+*>s!mtMqASpmfbz&`1D^5Hj>qU|M4FLcABDWKj$&h{DR-=gJAW9p{zBu#d#}b7fI%8hB)NU0fvMAEJyDRtfbj@;YR~ z3hcEYW)}yvNL6|*)Pzd;Exa-EoB|BHo*Wm)618}4!?X2Bf$-XLuN>;6$- z<9n?6jlUJ5ycneKf$}8k=Ko}p6c5s(KTvwTiV4_jTn@Po7rgqCTWUWRLIY-a1bZnE;HL>z*vsBB&Jp=6 zPQsk3D0-c43zY~tsw`5WJ14e&R^YbHpt>C6HmxyDRj3Fij?=+&8 zBJ?T>9zcvAi*PB+Wx?RhQam-xeg5MjyxcrQJqr~l3hukw5^XHalLaPb;92yc4(c$1 zae@J5QVc-k4xKEc&qApzuO$b|Vv(Q1i-^_ymnDNDuWLdcD*&4cXeVUx*skTpC6Q9t zjU6ZhB`XuNfY(v@WKsM$UBwrd3mNNt;A|#04t@WyFK%@r()}|ht zHCCPhC72e(uBihp93inzm?p~hP=aaV4U+gFI_)Ia#j(5}fd&2>7qv{7rd$?iHPh5z zCfYPVN`LTNQ-j!YxD1#YYI?%=O$}nTPH=Rp$rzp-80;<+1sjoQH6ymk=~Ag5Ut00h z-?UbWuOW(4{pVp=UX;s*bQGI!_Z7`s$b4;S=C|bDVt2vQ5!mC28!eOK#(2E>3DY`ev1;XPTtqIjB z0vY>M!>af>=7>*bL%G8|NAscDC<~4C*-#cDr(M~oIDicV)C1WN(f2iDO0;e8Ew$DLVQ~rEg!wq(hk?Aqg>gkSDxR{W}$#DaHS$%rfqf_ z>aZ?Hqs^|=b(OZbQpFstF2218qw${;umK$n2B?Ue7Dznw0u|FOjLl39-)1H_vJ=|u z9QC+hP6WMT$Mw^)?#%6{M-)3FdfX%wF0h=i{9L*Qm5YdM< zXU1sv1+X#NFa)rR12+2`Qnt{$BGPipAB4EWHCXgXovJ^ILb)OxWlljAB=DC;^U@2h zKeQSNP1Mh(O>Fe=tpw)!1#8J9TJpkJstgxRf0Mq)+(yu z@Sj-)2MSv(dCw>Rindtpnq!hM zj!6oqo@j|pXd1p9;KRvy%>b!Vcru6P0ZO4cKMB{Pd>;a+Um3SQer4XS6gJ>!&nvfa zDtEbKdIOua_=LpoXb^q$QdVV)9}M!3UQVNK{DTs8cE^-MoxiY9B_gEAIn2Jyan2z6 zsqXXAV{K85`ycl>XGpj693BGu=Q-I-SvE5?n<>v`hGjFuvzZau%*br!$ZRH)&5X)s zMrSieWiv--Gh?!uvDr*THdC3+jLT+@$!5l9GZV6ziP_At+03MDrYf76oXt$hW~OE{ Q$7M6qvYF{?Gsn08FVVg0N&o-=