From owner-freebsd-hackers Tue Jan 17 17:35:25 1995 Return-Path: hackers-owner Received: (from root@localhost) by freefall.cdrom.com (8.6.9/8.6.6) id RAA02619 for hackers-outgoing; Tue, 17 Jan 1995 17:35:25 -0800 Received: from skynet.ctr.columbia.edu (skynet.ctr.columbia.edu [128.59.64.70]) by freefall.cdrom.com (8.6.9/8.6.6) with ESMTP id RAA02607 for ; Tue, 17 Jan 1995 17:35:10 -0800 Received: (from wpaul@localhost) by skynet.ctr.columbia.edu (8.6.8/8.6.6) id UAA01724; Tue, 17 Jan 1995 20:29:16 -0500 From: Wankle Rotary Engine Message-Id: <199501180129.UAA01724@skynet.ctr.columbia.edu> Subject: Re: serial consoles and keyboard probes To: bde@zeta.org.au (Bruce Evans) Date: Tue, 17 Jan 1995 20:29:13 -0500 (EST) Cc: freebsd-hackers@FreeBSD.org In-Reply-To: <199501170417.PAA24105@godzilla.zeta.org.au> from "Bruce Evans" at Jan 17, 95 03:17:13 pm X-Mailer: ELM [version 2.4 PL23] Content-Type: text Content-Length: 20076 Sender: hackers-owner@FreeBSD.org Precedence: bulk They say this Bruce Evans person was kidding when he wrote: > > >> | - Merged biosboot and serialboot -- the keyboard i/o routines and the > >> | serial port i/o routines are contained in the same boot block: putchar() > >> | writes to both the serial port and the standard graphics display, and > >> | getchar() can be told to read either from the serial port of the > >> | PC's keyboard. > > My debugger uses this method (of broadcasting to all attached devices). > It also merges reads from all attached devices. I think this would be > good for both the bootstrap and for the console device. However, space > is short in the boostrap, and there are complications in the console > driver (it already messes up multiplexing between 3 devices: logical > console, physical console and virtual (TIOCCONS) console) as well as > the configuration of the physical console). Actually, I ran into a few problems with the 'poll all devices' method, the least of which being a small objection from Rod Grimes. :) At the moment, I've changed things such that the mode switch (from VGA/keyboard to serial) can't be reversed by pressing a key on the console. (Of course, the switch is triggered by unplugging the keyboard, so there shouldn't be anything wrong with this) I discovered that in the case where the system autoboots from the serial port, some of the messages would be echoed to the VGA display. This was a side effect of how I merged the two sets of i/o routines together. I could have worked around it by recoding gets() and getchar() a little, but I didn't think I'd be able to do that without bloating things more, so I chickened out and took away the 'probe both i/o channels' code instead. > >> Two problems i'm seeing here: i had already a hard job to even fit the > >> serial boot stuff w/o VGA/kbd BIOS into the existing boot blocks. > >> Having both there would bloat them again, and space is really a scarce > >> resource. > > I rewrote the bootstrap serial console i/o routines in C. This takes > only about 32 bytes more, but 32 is too many for Joerg :-). If I could reduce the keyboard probe code to just 32 bytes, I'd be tickled pink. Right now, probe_keyboard() is also written in C, but if anything deserved to be coded in assembly, this is it. Unfortunately, I'm not proficient enough in 386 protected mode assembly language to do it. > >Yes, I know. I had to cheat a bit to actually make all of my most recent > >changes fit. First, I had to compile with -O2, and second I had to yank > >out what appeared to me to be an archaic section of code from boot.c: > > >/* if(addr < ouraddr) > > You can have that :-). I've already made precisely these changes, plus > replacing the twiddle() code with putchar('.'), in order to enable my > debugging code (-DBDE_DEBUGGER) and the code to load the symbol table. I rather like twiddle(), but I replaced the one currently in the boot block with a much simpler and more compact version: static unsigned long tw_chars = 0x5C2D2F7C; /* "\-/|" */ twiddle() { putchar((char)tw_chars); tw_chars = (tw_chars >> 8) | ((tw_chars & 0xFF) << 24); putchar('\b'); } This is ridiculously obfuscated, but it works, and believe it or not, it shaves 144 bytes off the size of the second stage loader. :) If there was such a thing as a 'logical rotate right' operator in C, it might even be smaller. (Oddly enough, when I tried inserting an asm directive to use an 'rolr' instruction, the code got bigger.) Note that I also got rid of reset_twiddle(). Another way I found to save space: rather than calling printf() 4 times to print out what's essentially 4 consecutive lines of banner text, I just call it once and have it print all 4 lines in one shot. This saves another 48 bytes. :) > > >It seems to me that since the kernel is now always loaded into high > >memory that the condition that this code tests for can never happen: > >'addr' can never be less than 'ouraddr' unless things are compiled > >differently, so what's the point. > > It would be nice if the old capabilities of the boot code weren't > lost. The load address is defined in the Makefile (BOOTSEG). Nothing > forces it to be low except the fact that the boot won't actually work > if the kernel expects to be loaded low. I'd like the position- > independentness of the boot code to be preserved and for boot.c to > check that it doesn't overwrite itself, or write to nonexistent > memory... Well, now that I smushed twiddle() down a bit and merged a bunch of printf()s, I can put the other code back. Essentially, I managed to incorporate both the serial console code and the keyboard probe while keeping the second stage boot loader under 7168 bytes. What I did for now was to #ifdef out the '(addr < ouraddr)' stuff, and here's what I get: -rw-r--r-- 1 root wheel 512 Jan 17 18:48 boot1 -rw-r--r-- 1 root wheel 6896 Jan 17 18:48 boot2 Without the #ifdefs, I gain another 180 bytes or so: -rw-r--r-- 1 root wheel 512 Jan 17 18:55 boot1 -rw-r--r-- 1 root wheel 7072 Jan 17 18:55 boot2 But I'm still under the limit. > >- If the keyboard is not present, we do the opposite: the banner message > > goes to the serial port (along with an extra 'No keyboard found' message) > > but we still probe for characters on both the serial port and the > > keyboard (in case the user plugs the keyboard back in). > > Have you considered the case of keyboard without a screen? :-). This > isn't completely unreasonable. Screens worth using are large and > expensive compared with keyboards worth using, and after the system > has booted, the screen might be in the wrong (X) mode. Well, look: if you have the keyboard plugged in but you don't have a monitor plugged in, there's no way I can tell not to output to the video display (unless you don't have a display adapter plugged in either). I could try probing for a video adapter, but that would take more ugly code. Trying to probe for the lack of a monitor could easily drive me insane. The one thing I'm worried about now is that there might be some mutant machines out there (with well-known brand names on them) that won't let you boot without a keyboard at all. All my machines have AMI BIOSes in them, and they all let me boot with the keyboard unplugged (if I ask them nicely), but I have no clue what something like an IBM PS/whatever might do in the same situation. > >- In either case, 'options COMCONSOLE' becomes a thing of the past and > > booting from a serial port is now possible without any special > > modifications, like God intended it to be. :) We also don't need a > > seperate 'serialboot' boot block. > > The bootstrap should pass a list of console device names, not magic > boot flags or device numbers. Why a list? The kernel is only going to use one physical console device, and the bootblock has to tell it what that device is. We don' need no steenkeen leest. It's not like we're going to switch in mid-stream. Or are we. > >The cost of this extra functionality: an added 224 bytes: > > And more unmaintaiable assembler code? Nope. All my additions and changes are in C (except for the serial port code, which was already provided in assembly). > Expansion possibilities include > > - a multi-stage boot (no fancy stuff in the first stage). This would help in the 'special debugger' case; the debugging stuff could be moved into the 3rd stage boot loader (a /boot program, perhaps), but there's a lot of ugliness in the 2nd stage boot loader than really can't go anywhere else. > Bruce Well, anyway, here's what I have now: - The bootblock probes for the keyboard. If you don't have it plugged in, it defaults over to the serial port. If you _do_ have it plugged in, things proceed from the VGA display and keyboard just as always. - There's a new boot switch (-h) and flag (RB_SERIAL). If the RB_SERIAL bit in the boothowto word is set, the kernel uses COM1 as a console. Otherwise, it uses the VGA display and keyboard as usual. RB_SERIAL will be set by default if no keyboard is detected. - The -h switch toggles the RB_SERIAL flag; if you're on the VGA console and you want to force the kernel to boot with the serial port as a console, specify the -h switch. Likewise, if you're on the serial port and you want to force the kernel to boot with the VGA display as the console, use -h. End result (again :): The bootblock simulates the behavior of the Sun boot PROM, in that you can boot with a serial port as the console just by attaching a terminal to COM1 and unplugging the keyboard. (And you can install the system this way too. :) The only bad part is that there's no way to specify a different serial port on the fly; both the bootblock and the kernel need to be recompiled. I'm thinking about finding a way around this, but for the moment that's just a pipe dream. I'm plenty happy with what I have now. Since this is about as good as I'm likely to get it (unless I come up with a more compact keyboard probe), I may as well give y'all a peek at the diffs. Note that needs to be patched first, then both the kernel and the boot block need to be recompiled. -Bill -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~T~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Bill Paul (212) 854-6020 | System Manager Work: wpaul@ctr.columbia.edu | Center for Telecommunications Research Home: wpaul@skynet.ctr.columbia.edu | Columbia University, New York City ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Møøse Illuminati: ignore it and be confused, or join it and be confusing! ~~~~~~~~ FreeBSD 2.1.0-Development #0: Fri Jan 13 22:04:07 EST 1995 ~~~~~~~~~ begin 644 serialboot.tar.gz M'XL(".YC'"\``W-E]K&LN]7^E=,W"8&(T#BQ2\X3HM! MMFDQ^"*HFR?)]1%H,3H6$E<2=FB2__M\/#.[JQ&);6FU.SMO M^]N961&[LK];&GE>6!K97L`OSLT;-K$=5K3LR>2[+_#15'6W6H7O@#[JVE\` M;7=7`ZCMJ7OJKJ;MU;"EIFFU[T#]$I,_]ED$H>D#?.>CZ!_K=S=ES/D:#'W= MS\[.#L0&]WS[.F,L7/C%=-$(4*[4:UJ]5@7MX*#V?:%0B+MF!@LF>NV!ME^O M[=:U?=%K9_5#]U!1M`KP.P#\=]'OG1YER-?PY@?H^1;SP0[@EOE+L&=SSP]- M-P3/A7#*P.@W#7!LE\'$\[$%.\Y][_K[)_S)48;L%Q8-",T1BC`&HEO&>_J+ MMV8PHQMT;OQC>]ABV<$-_@F607$L&#IN=UOM_E$F4UH$?FEFL;%H/.^U]*-, MM5K%R9HGG<8I3I4''"\&Y(\R MHH>AG\H.>/5!#C,&C>:O43-=KPYL__BNV!SVD3<C$.@;UE8Y@RTSI,VHB^[4Z\J^3R$)?R>(H6VG'-&5N]"UZ] M@2-XAVV9K=(-\UWF;.'-!^KU@X7[%.)4=WC>;9SK1B8;V'\P;Y+E`W,E>2>( MY7+$6@%^L",OV;>C(7XQ)MP MV`U@IR0DRR#@9Y$?8NR0&(/GT+TZ;O>,JU.]=RZ:\GD2,I.)C%O<1X5,LENOW1@O?M"Q?H78R;U;-)==:#N=(33`,&$RMK*:8 M.>DZ$'K<6A!8*N!J=^$.+W"',YW`0\LA3C@.LXHXWSHI\YK5X=6K5T^#K(H$ MWSP-WKPJT(^//R;^C/''PI\1_MR^>1.SG+'8;?!*^`<\$ZJ`GQ`;ZU!^HX!P MX/_L0X&[M+'$/94$`?18NT!&I$[J+N7#"0/"-YKDV M0]8HJUE^1[['=^VZ\)+/^[PF(CO00*5%\^(^'X(=DG;)TR:VC^S.<4([M-'C MT(QQD2TB4^0.,[2`3+CV/$N*,$*QD!3:1:Y78:QH`>?S;R*?CI>WF`A;[`ED MDY5^%(-4CCM%&@-6#'^,WEF'R(HL)&]%!\H^BV<0C\0R]>;,]2UA93XC-3]! MFCE$'D)S0F8U'0M^#C(3$$.,PH3!:4!\&#U3B+D:_25=5B`R,M8G@^VC"+L1 M4+D"GZQQF,L)LBD+OS^"_O&5H??;C0X?)XT'!&E=#Z*QN#(7;H0EV.V#1-G_ M"S"]"B%Y6$41O,]S.0:4'>`_UPS"I0*C18BHNH0%HE<5I$OG`H&T&MS9C@.6 M5R1)!(E/V@H^!]97`_%5A2F#^R8`L%[0"CVQ="GQTGR!M9262I?KL M37(YL8=$9OK_A^^?#NS2T3\+9=<8HWO,;?84K586`3'!L%;5>(/$8=2"`MOF M=IXDB.[)B-$U.0HW:&D'?\$.7-KAE"N9H(TAQ`7C*4.`XZJW70HK:0R,,61W M&2;V*"(XY)3A%)LBWQ/$`D^D]2ZB*V;J(5$9>Q:C%>.8_C5#C[9Q&H?=FFY8 MA(&)('%-#N#A`@_,6X;]O#`0U'!P,#?'K,AO:67+^+6OMX;=5J,[$'`[R7(. MGT?,<'QZQR7&9^)AGN])1?,J)-;B.\L,S1R\2$9N5/JNJFB[M932><-N2ND^ M7Z#_88;C#V6$N(ZE]-K>11D9!L*X0 M;!*0DS@IWPTLCP7N-N[*'FX#8+I+L>[P`M&;`4-'9O[#OB)=!=:=!9X]@W4G M(1927I%RJ/GH#^9[O+LB'T,!LBA,3BZMC8H^J"IE-GANH(G!Q?UJR$!6("X@T=.?Y-\P22Y$6UC(")8XQG/0+A'A- MI8\4CLR/+"*;^XE??R*;/S`G8!Q''QJ?2C+_*H$VV:&,_ET^T!*'+Z-A*JJ6 M6LGDWU/O#C>QO>O^X9^CI MX?G[PZ?;]:@Y&O[?Z4#P_N0?Q`5I&=[)1AF5SD/_\$M7^1[^;*C_4='U"U;_ M'CW_J6GEU/E/I4SUO[)6_5;_^QH?6E/LT&\- MFB;&\M#[8[ M=A88YSR?(7NVRTKC^6*R<,?%Z0N^%46/@V50\AFOA.(3,5;6&G^]ZK[*@\LXW2/HA0,RLYYB,@B8#?+D9HK6D]F85.\F=O MA6EI>&=;EL-DQ'TWI8PK.\9A.SC]S`RCY);76"@2WWZZ+5HR\T5(%-&U#KF6 M]FH*I@(/^HVF'2A:>2]5NDX(Q!4%/LL1SO+:Q6EXPH^=LMNO_6V1V?/;,>=6 M)OF84W`JMGLU6DQRPH'+&(]6M%0QYK&Y9.F!&I/RP[-DT\G)`[:K-7[DIK/6 M^B&:X$_0&DM""9V/BK=1M142=W?%`\:RN.!XWKP>L9,='Q$]*K5PP7UI2S(X MJ2%.NZ1B1EPQD?T%"RM%+ZU25;2J&J^-C\Q,*>-#^O@IT@CG#K&J#O+JBW"Z M46>X:+1DU6#0!='II2S\(!F<+&H\!/N%>HAINF,NM5F0S2EVH4`\R;@M\AX[ MX.;*R47"Z1P>RKM,<&>C.;,[8FE&MJ6[`I!Y>:5A,LG%44PZ+D*QZT+K>YCT M[EOEG.CS7@VEHI6,5=;2<]B0K*:$H>A.',^'X6+'V3Y M1N2HHJC!5]O&[4[=5\H5+-;B6J)$3A3Q*-,IB8NA;C,76,Q1#3Y?-O$4<\+#'R$351_J)%,#,=IPAU'@\\29EFX0;VM8N) M$P8]U['J23=O:\URJWRRUSPDZENO"Z7W6W$TNJ?R-0/5;_O=5/K1CW#,XSP7//9D+8@JX6U#\-+!`M,/[E,Q>,\71F6^$#755$6@9] M=LW<,(B.'Y)TD5J:IF/C]N_:IJ@U0L-Q@!,)^!;FWS)+/$G5+J,BN,7H%,$2 MAW*!-PGO3)_Q`H]O(YY3CBOMY!* MF7FX9.VQ2504(/XPVY[9(7&'EKNU+5Z*,T-Q-N-A2GU'J3A*8?%C&UF^IY$S MQ@_U\$XKKC'(U2LYX^J9X6*B$T;3%J\+FB/OEAY)$PDR^'$]W-N8(JJ!XKAL MDIJGAH!S1M0H&FP*[4.&$F1H\L(&\AKQ+'0T'LPS%K7",XT1R#[S5YS/&-Z]TY MS+IF1%DX1F8@WP.UZ$T!62A)Z-+9)7.\.8HQ6MY?C8)$LB259/&0/NTP2!:7 MYP=2Z&H1NN*X@%/DI=/[:]WUDL?<6=>I)3*CJ"-&>N'K%[,,?,I(52C7S`M9 M)-\:`&S6H%R,$,S9F!8B'7?3&O5I";IB,09!;$*),F=M`XS>"29`?1WP^J+? M^ZW=TEMP_)(?"?7U4[T[,*#1;4&SUQWTV\?#0:]OP#_^T3!PP/8V/9*(UGT) M^N\7?=TPH->']OE%IXV4D'2_T1VT=4.!=K?9&;;:W5/4^'``W=X`.NWS]@"[ M#7H*S2A(W1\+O1,XU_O-,[QM'+<[[<%+SM1)>]"E"4]PQ@9<-/J#=G/8:?3A M8MB_Z!F2'DG7:AO-3J-]KK>*@(S@Y*#_AL*!<=;H=%:D15HKPA[KR&;CN".I M\;E0V%:[KS<')%5RU43M(8<=!8P+O=FF"_UW'<5I]%\JDK"A_]<0.^%#0:_5 M.&^AXHM MB:]HVD"7`2[\"#[:&P=,3-NA+15W@"!`=$ZVR+CCU`P0)Y@KR"S1SZQ;'G+&8C#&*8MJXEBWF M7:]E;RQU)P>O<8EFI0P<%V7D"UIY>5B87WU*'+TCKN2''E*)9U]5#U/-LH!; M*-CP@HI?J4?11TQDNZ.L^G:_RF=,G/6=`Z3%YCZW"E2IO59TFP4,B)>3*(^Z/LK\=78H7C M!6*P/LB)93:.)PVXKZC[RF7DC9B$U\U]B-#_D MY"_"2:G%$%F5RJJ"-WFFR0>A-XN3T77!I/:R4C(Z]J`2&UX>#T^N3H:=#B^C MJCE8'[E^_Y"&[BE(RA=I`UE;[?@DUL7@JM7KZK+_FG1<)5&;)IWQ[T[B/N.S M(?^/OICSE;[_IU7*N^4D_R]7Z?M_ZMZW[W]\E0^EX['!-W__3TN=`4==[W]/ MI/*1<^"5?%\[V$\2?K$!;CX1?J`4\/B)<)S3]T5.WU_)Z4'\OHB#<-IG<>-6 M>,:GB.A")!]QML56`_TH,Q&45E,Y*O$RG^&F?>V;+F;3&$7II.F!$/5KIZD_3\WS+=DW,4X',%W;HNSXVV2@7%*K^`_W$8TJD>5=`']NB6$_MJVZ_`:J M<@M:D=LR_!OWM''Y,!]8"KU*8A%BV3WBT?\!1 M2178Q'^7082R"`1MA`$'1_FX6&1-SD!=F-!T3-]4UNJ1&R+@#5@F.CX"8LJC M*$:Q,]4T5B",HE2."@OD.!#6BTH:$\82B*,@;S/(;2BCK:)8(&$,24@@6P6; M%8!\'!N1S..PE`*T=-&(FP<)T#N+HR1C3!?4J`ZT&#GVF*I*.#E5.*D=E9ZN MR"$1F8K&.EXO!2E)%8C7A%9K0?B/QOW'I1N%DU@OQPB6XL)+7'.)JRI)T87#[$I9)4;;J%#S&/@B@7NEDE2E M9*6VT6C2GD!=XRH'#D^J%TIJ\`]+ANHZW M`N1`[Z/HYPU>%MFP,ZS"Q`X8H@Y`0?6(OUF%22CS)R9Z,F;XZ)\L>"#(J2KP MRY:'$'2)">IXFJJ"?`M;_NJP):K&ZM`8#L[(:S\W8$G1^NJQ2@ED0:/(_]N. M+1G`!UMKI:(M^K\9IEN\)WV?X?NUM_?92E7KOB-3,.#P4(`*8/P&N](WJ\`N MR7`G&86N:](K#J[%7VU,**.V6@L6?9F(?W>$BG/HSN.4I2>RKL^_U\67%CJ% MZP%]SRVR+GDTA_(L(CA2X<\H1+/]:-FQMW.*5VYI"M.A<^^0C>F,;$:50_%& M$`O^W=X5[K2-!.'?[E-8/9#@&H+M-"$!M:="H$6EX93F)*K3*7(2!]PZMA4[ M)%3W\#??[-I>)P[07H'>R2NU`GL\WIW97<_,SC=L5Y)PG;!39B.79\Q4FDO+ M+QW-0@]G;\0'$S(?PJ0^(V,(^>X`X"0#(1'IR?.A'479\8*3EERA7F364H;_ M!KZ,>DXS/,)BL8?3`#:R##2&9&!,G!CV*!]3"H20)[N.D-8.PJ1\Z-%J&(9^ M2`N_">`/YBV?>Y*3-T@LL%'N&"N'DM010B;BE%CD^ M0G#HA3(9,LE);O1%Z7[:4MAPC":<15?:IC,(\Z'703Q::+^(U%>9:3MU+HGY% M,*6NV&!$_Q?HH9NJ4NI!?3/='R-'4-]BT.!V)C7)?C@)T:F]$\&?Z-D1:A^? M5:M5[J/O:-8XE0+UOYF2>D&`-&RB/\1)DK7_O2J6.8^9DI-@_%A\+<5Y2J;Q M$#%U9*8ECNITYAR(8^FY&SG)92.=";EI(-[V/1.!E.$M:>/;YL;*M*!/BSHK M-*'0_$SX[5L73S&,7=QCS#A=<[^*)`*Y;I(3'][=ES;9<+@D1X7UCUE.M;S( M^.@[\!*9\9:T`3QL*K;EO48"`]IG;P[5W2KC.W+)]:8/CV?'\,2]8$X>;^RD MW3$M\%8IR<-.I+#R1J')(0]*6^7.X`*%/1Z\*NZ[["U]:+4-ZUY2,&MKI<`? M1+8PZ'OJ`LUN`IH?XK)\D=IKF`^3@M<(N=TNZ%YWM]O[^"P]A/))H&/7)\N9 MWCKV2./"8/#Q@9#[DSB04GOP+S=/:RPW3X9EIMM#)%_(T8?E[7*ZN'V[C*^F M-!1[;M^D>I$/\TMINS0']]KKGCIB_O]J?/[C1O9N],-A?VF["_]7MT3^9Z.V M5S=,Q.VLET:C//]YC,;G/\4`P.:^8>Q;+>7PAQ&`>1*+2&KK3WX*<&E&'GN7 M1]:QOR*`=?D;]"^>W(7%R]]Q@V'LR1L"8*?U>I_:QR='[]YT/S*Z[N+B0N^] MMZI&45_B^`9/%V(L6GN5FI%"PS22#"B2*9!ZKW0AA?['WX^/ MVP<9_:PO&?>=:X3T#R`A\:$1`(`7^MQ!X(?<0.FA$#$Y^B+[D268[^'$_AQ, M#R1,NTD22W&7]Y/8>NET_NB<]@Y^PK$6S9:&T:HTS'HZ6XAZYS59>JY?'?8# MP)?22B6Y6T-Y2XZ!:TJN4GD9`PF+$:5@7NF9S!+84NY!-\_^-&5?W$%)=KZ& MK*BS^M_ZT=GY$4HA80XTC`;)(:MT\OARX$H1V"\$2%_%C#V]B`JG3LNJ-%I- M!8,J=Y'W;]N'":+PR^5HT!\YUQ@KO$Q4JTEF9(5+G&VK<-&"Z2%P9AD?["A8 M$4.;RW4X@]FE'J!R3,Z=$?4;-;'>.HU(\ M._3E+%ZS3K(:H5K.T$@> M#J=(PCSJ]+O''\Y[QZ+^U4G`X))SS*6#\["SG/4A`9L(C&_=1&_3G+8"HU6,2%5N8)(D6A<]IYNY\9\I%,QN(KG0(!7R)"HRI]3,YG]X7KU7G3;G<5AU!>T;:,16W<1-!532M7G@/K_'.X M0L\ISSRU+K^GJ2[TPT1_[E[_>U:V_AMFC>,_M3+^\R@-RS/5_TK^K[5OTL_(XOJPNI-NB%+$>NQ,G7T:)6-'Z/#E]2XQ>"D:N?QU\X=PNMO/' M[N5,I.+)!(;+%0ZR\AN.;`4+KG?(*2QA$`-6B9@\.(YGGHY"N7*OR<8C+!!- M5+4#"WZ]:FC8D6IKZ&I5)9S@XT]OB"0!HN)L-.HH!KT3Q3<>(RN18.//)@.8 KQ($>WX1.Y3^Y#Y6M;&4K6]G*5K:RE:UL92M;V