From 301dc6072831bed5552408cb2810bdbadf264d9a Mon Sep 17 00:00:00 2001 From: jonnosan Date: Wed, 19 Jan 2011 06:20:25 +0000 Subject: [PATCH] git-svn-id: http://svn.code.sf.net/p/netboot65/code@307 93682198-c243-4bdb-bd91-e943c89aac3b --- client/basic/bails.d64 | Bin 174848 -> 174848 bytes client/basic/kipperbas.d64 | Bin 174848 -> 174848 bytes client/drivers/lan91c96.s | 400 +++++++++++++++++++++++++++++++- client/inc/net.i | 6 +- client/ip65/cifs.s | 267 +++++++++++++++++---- client/ip65/udp.s | 1 - client/kipperterm2/timestamp.i | 2 +- client/test/Makefile | 13 +- client/test/test_cifs.s | 20 +- client/test/test_lancegs.s | 36 +-- client/wiznet/kipperwizdisk.d64 | Bin 174848 -> 174848 bytes 11 files changed, 652 insertions(+), 93 deletions(-) diff --git a/client/basic/bails.d64 b/client/basic/bails.d64 index db8a795ed286b3973eb116dcce04a4993f3f0917..48677e389c9ada60e6f11d0f6fbafb780ed73497 100644 GIT binary patch delta 2101 zcma)7T}%{L6y7_F(5SJy2ytyiMnF+n{;{CTPbexYpdbPZD0Pz3W~Yf6lg4EGP}iD3 ztj(Ql(uXNc7R@TASG*G*no!g1Q%{qw6+8N1ZA~?)F(I{(Z69pfG@<9tvVd((+>jmM zo}cf0=eysWxx4xd{(MeCKGJQx=OYEuwPVv!AK65thrPy^_2g&Y!(M)G*!*FbjtBJ% zK~@mkPj`U66ZntA=KW#$yPm0LRu>zV?>4jC7|4yUvpw>i1%3d`Hsf9cIoVJJi3KZT z{?nks$AZ>hd^~7g2#!pQUg70nR=~)m(aXFN%qo!I@q6&}L?vLakxTqS@I;j{(n#9Y z6@$JVQZVEzebYZRlH02!W5}4=OWu_p3>jPZktu2C8`IzKBaH-qF*dZ4iz489D+#W7 zFr=_uX7H4ZkzPL;7m+vpWUo|OepLRpM?OBJ$c`fssZNpmk4*i^E7SzBoE^K4CNl96 z?@ze#@z&6ELp!-mjK@)u9X<@y3$z+&4al=?bQkEafqbV|E^L?2ACF|RQL3C$7@2WF z@=3>6y$U@5dYc{E3c4SdL|wof^#97>S&29+1X=_!XS^Mvxe%|0=tj8V`i-}O>4z|N zgPsjG$w1eGo&)l^06z>Wp} z+a4t*hS66Hb1n z+-}43AiUIHDS&({nA?DE0lnaP%)b~+A%ZnR1mROQISOl8tF9eU8Lw9PQ@gh(ibcc5J6OX#q@b-|GU5KbyL&eA+ zAHg`Y$Y}2&ayE)5EiqM?0Bbki?;)OW8<+<`ZthN^{CI*u~u`hn#jhxIzN~q@IzEHTcGGCXf8K<6z&)2HJB1{c1#ZnF-cZ| zpy7Ix8ubBA{gmzZEz4MF*hyU=D!Q;8*8XB3oo?r~sL>V>*-aul$Wkzw^PP$B-O2K@R@GuK2OtGtZ7+KL|HLw zihyt$4&L6>mE>MX=A9Qa$X8%8Bp8ac)f~-$9-y;wt-FH65f=JuxxCWu9BDHJ#UG2cZRSUmh*4( zz+Sn+c(5?d0nubVAU=vl*r?*gL<>|D=30?f^B>cxP5J0^O{e2rz*u*XoR!?GjL1Q9 zGC#gK6wM38UkODwhT@w-(N{yW=d53D<%c}VMd$TyW6L43*%JWEIo}7o56l42N=>iQ zP*eMdjKCqXJM7Nv1G)ZKq!?X`_JckkyqWidIRI)^gv`pOe`-zyWU{m3y|EOA@6{o? zA*6dk%pu=DiaQP1sR2lkWPYl`?BYihd1U)H#%3Ku*AovcRp*)O2mkrG0k|4O>u6SI z`oLPn2n`q~C&?zGrIQ?#+*wAXlbkf9E}~2BmyAdksW3j8B$d-|2FUO0rhDEdlkU+F Jxe}79{{bihvTOhV delta 2191 zcma)7ZA=q)81Mgzu&6PuDN^3#)F2b8fPez!B@7)zSb*}iCfnx@y@ z|9ScSe$R8ip1Zs89KJs#VJ~qgZ+OX0uC;F_>Lq!EyBJhHuOs)p7lUkmK>J~UUJl7u zLh8=g^Ryh~{lM-GXg?0{Zw98D)auv(f451^j{)ECoLb9&yTrOc^C|Q7WU#=(m8 z;-!$l#zOjE>~cuE5*nTy`G_3^tr~_WMy|3N&}xByrPYn6r)mIu4NtHup;NWWa05BA zr4-~cuwd}MqccA>kUJZ=3coUah`i37^(%#k$u#$+f9Ctcq=DcUC98#u8v(N|B(&+Q zUr?*G&}kkcJ+0)j5&2CkImGS#x`#g#;Q#Iucw6@moM{~mq@9MwaE(p7Yar>-8 zdB2BjcV~ibUvvR;ftCrhT$1-o#Z6Id>zpIUH11$^0 z{i9@o*IK6^83F0)c`BS1R>IWpVtq6#EW5+1jX%??+GwVknh6P#EU+ppEWeB*57f6Y zA7h=iiQZ}fzfAOEv1<3DZP0q*Q5IYexdqlF&bdI}pc0y?3{H_e<&$3Wg2MHYLC#5( z*ZPR8aDGx25ne3^S_Sd}z)&|}_aM+ppkAPLz%Lx3^&mF@KNLhZ{Dl*Ue#Ahpk~udd zAGS+QE-270kbP!w2gseEa!)UWXB9@15}+Q4+2fIJh~`7w3(*~L%keww0Id^fHpn?( z?j6uvkn@1AJjQ|`5*h%cD*FwA)}Uc-NO1aYJo?QK$p^OspwBM`0aFqg4tx900qls} z4Be^)BiN?nim2WgM2wwPNzBF%`4A4`Xha-OP(X~bCo{}Mx1cu0%9m)CY?86KG()s-h0(+RU=&$L$!x02%RCkh9uL;=m z2sb8@>5HXsQ-93%g0>H=IE&9_^@6DZ)(L_^%H+>VK`S#!xsjCh3nzr6mMiQ5x!9Q2 zQ-4t)pwH8(V)k)cdpy*xdGvMC0ZxZB1tVk z08=9$vl4KNE?O*IV;<#HC&_Ujj3NZ^g#Vx$4>gaXbP_(YvX2!p(Ok#wRY7n~nOD^6 z%md~Ftqb@jU(&c;FKP|e?M~1-Qnzte(>$$W(!*yTpWe+%#6+dOk$J?#R&>5g{g#UF z83wXM(Vfw%BjJF+T8)58%o(+}pX@i5#hK3OI}Jf*8^vfdApgf|vnBiT^P<|R8;qI` zY>3qdUQFeiY0YA^4UFU#BRRQ}+_J(>?_kB^u<}_KaYt%k@+zXyGbSg@QtrvkVi7$E zswmoMHSpm;ssOY8VTOD>wkB5Vd+S@Lnyw;VZJH#)3Uw@M8bfm|;>@s?XXOEBm5_9$ z7A?it(f(n->ckI2FfM6tAM0lg;#?KzM{yo=2SE2(&X{gUL4;NP;uT`F+Z2LTfp!3> zX_c%_MDxT6N2m`>JyR@G9k*h$RFy(&3We*Z;7S=$%7(C0zF}fmmga%cA{P)HMN4fE z(Oc0>1wnPJORJ38ozE;k-geJ_#g&Ucp1keQ`3 H9I5yhSYYBm diff --git a/client/basic/kipperbas.d64 b/client/basic/kipperbas.d64 index 89e9b3656cbe735ef651fdaed894b4349c2df4eb..cfea55c42169b3295777d7b7d265c47ca8910b83 100644 GIT binary patch delta 2351 zcmbtWYitx%6yCerZh6XXX|br4OMz}{TONH#lZLheeJ}PQ-E9kP2PN2vnlT|H#2A-H z+6KZMw}=ikSyHyGavNq+6w?sXy;uFDjsEm- zEWY^YSsx6x_DrGA;cDI*?V*?2v~S8uHZ6qML(`M&#s~pf($|5p9(q6RJFjm5)+XzP z+S3)Ja)rna{=O>V+B@3!(X`!g-w4_nh1y#c6XSa;8zpTYRFd*+AE+~D<12<7eo~uT8PDMk^g~REa(7pff+Z~$HwbX? zcrr!ChAN52<-E|XeX@n@bvh)?x0Q@L{nEsZt)!gbFWT~IG9(N>uO>YU9g@ULjH~-7 zTCS}jr-bG88q!aMMX-((raPokDVAhDLFc!O&yULbYx{-msXB5}1S_p4ityp}M0L(< z?xc4E^kBDEv5lmAf}JrpHsgnmm=_=Y-4naFk*`TedOa6d0jO(%6XpR#-V7`sSP`&d zpveua1k~q&{t={=4fO1;u$%X>i~A%_u6ZDS$A2p*u{u!eEz=rMYk@l-9|q6zM2c0w zRzuV^*#MzPI!qQpWI0^++|z5os0HQ)H5K$aFzSIV1vL%mSi9Z~GGR?X2X?8AFt--n z12LzWO$WSW*Ds9_zj?h0%@(NH~c) z=+Jg72rJo$l|0C>tX%OF%P~XZ8>5i0M0U`ado3tY0Y;q$Be0Y~By$GQo&Q7>ngvM| z<}y`;C{RoZ6gOv~SlKok1zQ7Zwq29sjJ;jwhE1=NNau#*Zrr3x=6!t)7};Q3TEXLs z_)&`kjMNMQl!agSfstc@%0N)|UpB1i2rEI&vitRa+O^Oc9SelSx7dV+EM{?KY0dUH zw$OrZ3g}vQ9Fxmo@O_1Q^7Ft<;*Pj$G7|;|l}ndBw|Ena zD0-f@EI@prRgfX4v9NMRUk$Q@y{n9!%#as_$!%H}V5b!|iqS+~h8Z8w&sue>oD4Y! zP1pfN4Jo`hn#v9;>LG=@S)bznvGvht4n}2b-cW1>7QqfqtbSVdCK=XIXhg3EqYmgR z_Q|kpj94yuEf_V4*EqRJ`Gy?Nr|))*XX^uUtk_(jAD3gPr~#f%d!UZsNbrXY6}1rP zR6x=VabAorVVD2LPEnN8%o5yO7?mSsfK=S))n#@Qrgw+Cm!pXurV|0Z}?CsX`A)c0)vjjwsKedy4@kSJ)A5Pk%&^o7N40wz*xX{^Eh5O7FHUgW98a+hME+h2&%T xS^&&sybOcdi_K)S(>q@aHw%O++d5ilG?;#V1_c-6)`Zx1QCD{M~ delta 2340 zcmbtVYitx%6y7`A2T$20EnT3M3kDEqDUTLPD{ZB;<*@{4>29|L9FQR6BV#}kqKS)| zEdjY>L3CKclCte8L%5Tom`b4J#0X(2CF2i`M*pFP1gT?)kQfs@ceYzvjq!&~)7{Q| z=R4>7&Yf@ee=}x3{$3zADu_q#s~|I^7n^&2tsq~L35{`}r7PNrOkb&`-?!*rEGI=7 z3qf0?-(QZn%d(zVN#3k32LGA5#5K$9|GCz1v0`8gAkmksvc?&%NFQ%eA~*fKmHznX ztUdnFxnhVm?Ha(Ecj}C$_%8a|X8r3*GK-c&@{#4q%tD5MBJ-tSmOr<;p^)%5P{B5IfPgc#Twx2d!ScjU@>10Tsy^?fGcS1eitt6EMzvz?eNQVGisw2C`o(#!Gsd;lR zMdbW?a!Md?)sq86AcCvOqTG|AGC7%HJwZn|bdQcJ{H1*YdulZ~F38H(5LMI|YltR| zx)-3)7FrO}tJaZR&;39$3zzZk)}$9do(lEsSVz7hdZLN6>QaEbDtU>%J3wl2-=7D_ zlXG*yau4JH&jB+Rn67Ha)ehIijF6objjGqJ-;ev0e70Xjt8^(yW`}ct{t;}SO^r1|jiAAMi$#-pzzQMm?hQ9WtN?l|AT}Ltcz)yS z!Q23BGH6qPZv?Xe*i_K+fd;nl5GaH-1FF=5!kJ;Sa33TkYZl)6B?$KITg_m7I}ida zNoIT8{;XE)h}wiqvU4mln;YZj6m{k#~=1&$;iN=p6bi1KE^ zAjcfg=8Ap&zjcjuj*jD~Y^)WAhS7cDET$E$-(;s(XbI_8heZ9EzP*9WE}aKHC5N@EXLunfF7}>! z7Qxa}?>v_8p(SK+pzVI?gbg^=`px&^l=l9wsgIMDr4`bJAxkt=Qwg zA{$=(BkYoFxBrsNyOjP?uv%~*{Y$`VNJU|mu!6{s{p*b?u|~%TUJB+CU`{&nxPp0! z8xFGuaQsZGOn01!&C)D0Q(Krhatk?sPaf=4AaV<7Sw3*@7;G{Xa6X1P+NEHiVuH%J mwqb=O|H!PWR0{uVX88kKdO{&GZ~TGCPSO+EDZR0hxc&klx+z@% diff --git a/client/drivers/lan91c96.s b/client/drivers/lan91c96.s index b6529cc..681fad5 100644 --- a/client/drivers/lan91c96.s +++ b/client/drivers/lan91c96.s @@ -1,4 +1,5 @@ -; Ethernet driver for W5100 W5100 chip + +; Ethernet driver for SMC LAN91C96 chip ; .ifndef KPR_API_VERSION_NUMBER @@ -18,14 +19,148 @@ .import eth_outp .import eth_outp_len - - .code + .import cfg_mac + +; LANceGS hardware addresses +ethbsr := $c00E ; Bank select register R/W (2B) + +; Register bank 0 +ethtcr := $c000 ; Transmission control register R/W (2B) +ethephsr := $c002 ; EPH status register R/O (2B) +ethrcr := $c004 ; Receive control register R/W (2B) +ethecr := $c006 ; Counter register R/O (2B) +ethmir := $c008 ; Memory information register R/O (2B) +ethmcr := $c00A ; Memory Config. reg. +0 R/W +1 R/O (2B) + +; Register bank 1 +ethcr := $c000 ; Configuration register R/W (2B) +ethbar := $c002 ; Base address register R/W (2B) +ethiar := $c004 ; Individual address register R/W (6B) +ethgpr := $c00A ; General address register R/W (2B) +ethctr := $c00C ; Control register R/W (2B) + +; Register bank 2 +ethmmucr := $c000 ; MMU command register W/O (1B) +ethautotx := $c001 ; AUTO TX start register R/W (1B) +ethpnr := $c002 ; Packet number register R/W (1B) +etharr := $c003 ; Allocation result register R/O (1B) +ethfifo := $c004 ; FIFO ports register R/O (2B) +ethptr := $c006 ; Pointer register R/W (2B) +ethdata := $c008 ; Data register R/W (4B) +ethist := $c00C ; Interrupt status register R/O (1B) +ethack := $c00C ; Interrupt acknowledge register W/O (1B) +ethmsk := $c00D ; Interrupt mask register R/W (1B) + +; Register bank 3 +ethmt := $c000 ; Multicast table R/W (8B) +ethmgmt := $c008 ; Management interface R/W (2B) +ethrev := $c00A ; Revision register R/W (2B) +ethercv := $c00C ; Early RCV register R/W (2B) + + .segment "IP65ZP" : zeropage + +eth_packet: .res 2 + + .data ;can't be in "code" segment, because that may end up in ROM. ;initialize the ethernet adaptor ;inputs: none ;outputs: carry flag is set if there was an error, clear otherwise eth_init: - sec ;FIX ME ! + jsr lan_self_modify + lda #$01 +fixlan00: + sta ethbsr ; Select register bank 1 +fixlan01: + lda ethcr ; Read first four bytes - $31, $20, $67, $18 + cmp #$31 + bne lanerror +fixlan03: + lda ethbar + cmp #$67 + bne lanerror +fixlan04: + lda ethbar+1 + cmp #$18 + bne lanerror + ; we have the magic signature + + ; Reset ETH card + lda #$00 ; Bank 0 +fixlan05: + sta ethbsr + lda #%10000000 ; Software reset +fixlan06: + sta ethrcr+1 + + ldy #$00 +fixlan07: + sty ethrcr +fixlan08: + sty ethrcr+1 + + ; Delay +: cmp ($FF,x) ; 6 cycles + cmp ($FF,x) ; 6 cycles + iny ; 2 cycles + bne :- ; 3 cycles + ; 17 * 256 = 4352 -> 4,4 ms + + ; Enable transmit and receive + lda #%10000001 ; Enable transmit TXENA, PAD_EN + ldx #%00000011 ; Enable receive, strip CRC ??? +fixlan09: + sta ethtcr +fixlan10: + stx ethrcr+1 + + lda #$01 ; Bank 1 +fixlan11: + sta ethbsr + +fixlan12: + lda ethcr+1 + ora #%00010000 ; No wait (IOCHRDY) +fixlan13: + sta ethcr+1 + + lda #%00001001 ; Auto release +fixlan14: + sta ethctr+1 + + ; Set MAC address + lda cfg_mac + ldx cfg_mac + 1 +fixlan15: + sta ethiar +fixlan16: + stx ethiar + 1 + lda cfg_mac + 2 + ldx cfg_mac + 3 +fixlan17: + sta ethiar + 2 +fixlan18: + stx ethiar + 3 + lda cfg_mac + 4 + ldx cfg_mac + 5 +fixlan19: + sta ethiar + 4 +fixlan20: + stx ethiar + 5 + + ; Set interrupt mask + lda #$02 ; Bank 2 +fixlan21: + sta ethbsr + + lda #%00000000 ; No interrupts +fixlan22: + sta ethmsk + clc + rts + +lanerror: + sec rts @@ -37,7 +172,70 @@ eth_init: ; eth_inp contains the received packet, ; and eth_inp_len contains the length of the packet eth_rx: - sec ;FIX ME ! +fixlan38: + lda ethist + and #%00000001 ; Check receive interrupt + bne :+ + sec ; No packet available + rts + +: lda #$00 + ldx #%11100000 ; Receive, Auto Increment, Read +fixlan39: + sta ethptr +fixlan40: + stx ethptr + 1 + + ; Last word contains 'last data byte' and $60 or 'fill byte' and $40 +fixlan41: + lda ethdata ; Status word +fixlan42: + lda ethdata ; Only need high byte + + ; Move ODDFRM bit into carry: + ; - Even packet length -> carry clear -> subtract 6 bytes + ; - Odd packet length -> carry set -> subtract 5 bytes + lsr + lsr + lsr + lsr + lsr + + ; The packet contains 3 extra words +fixlan43: + lda ethdata ; Total number of bytes + sbc #$05 ; Actually 5 or 6 depending on carry + sta eth_inp_len +fixlan44: + lda ethdata + sbc #$00 + sta eth_inp_len+1 + + ; Read bytes into buffer + lda #eth_inp + sta eth_packet + stx eth_packet+1 + ldx eth_inp_len+1 + ldy #$00 +lanread: +fixlan46: + lda ethdata + sta (eth_packet),y + iny + bne :+ + inc eth_packet+1 +: cpy eth_inp_len + bne lanread + dex + bpl lanread + + ; Remove and release RX packet from the FIFO + lda #%10000000 +fixlan47: + sta ethmmucr + + clc rts @@ -49,10 +247,195 @@ eth_rx: ; if there was an error sending the packet then carry flag is set ; otherwise carry flag is cleared eth_tx: - sec ;FIX ME ! + lda eth_outp_len + 1 ; + ora #%00100000 +fixlan23: + sta ethmmucr ; Allocate memory for transmission +fixlan24: + lda ethist + and #%00001000 ; Allocation interrupt + bne :+ + sec + rts ; Not able to allocate; bail + +: lda #%00001000 +fixlan25: + sta ethack ; Acknowledge interrupt + +fixlan26: + lda etharr +fixlan27: + sta ethpnr ; Set packet number + + lda #$00 + ldx #%01000000 ; Auto increment +fixlan28: + sta ethptr +fixlan29: + stx ethptr + 1 + + lda #$00 ; Status written by CSMA +fixlan30: + sta ethdata +fixlan31: + sta ethdata + + lda eth_outp_len + eor #$01 + lsr + lda eth_outp_len + adc #$05 ; Actually will be 5 or 6 depending on carry +fixlan32: + sta ethdata + lda eth_outp_len + 1 + adc #$00 +fixlan33: + sta ethdata + + lda #eth_outp + sta eth_packet + stx eth_packet + 1 + ldx eth_outp_len + 1 + ldy #$00 +lanwrite: + lda (eth_packet),y +fixlan34: + sta ethdata + iny + bne :+ + inc eth_packet + 1 +: cpy eth_outp_len + bne lanwrite + dex + bpl lanwrite + + lda eth_outp_len ; Odd packet length? + lsr + bcc :+ + + lda #%001000000 ; Yes, Odd + bne fixlan36 ; Always + +: lda #$00 ; No +fixlan35: + sta ethdata ; Fill byte +fixlan36: + sta ethdata ; Control byte + lda #%11000000 ; Enqueue packet - transmit +fixlan37: + sta ethmmucr + + clc rts +; +; lan_self_modify - make all entry points variable so we can move the +; LANceGS card around in the Apple +; +lan_self_modify: + lda #$C0 ; FIXME - hardcoded to slot 4 + ; Make the accumulator contain slot number plus $80 + ; i.e. Slot 1 = $90 + ; i.e. Slot 2 = $A0 + ; i.e. Slot 3 = $B0 + ; i.e. Slot 4 = $C0 + ; i.e. Slot 5 = $D0 + ; i.e. Slot 6 = $E0 + ; i.e. Slot 7 = $F0 +; $C0s0: Save off all ethtcr, ethcr, ethmmucr, and ethmt mods + sta fixlan01 + 1 + sta fixlan09 + 1 + sta fixlan23 + 1 + sta fixlan37 + 1 +; sta fixlan45 + 1 ; Removed + sta fixlan47 + 1 + +; $C0s1: Save off all ethtcr+1, ethcr+1, ethmmucr+1, and ethmt+1 mods + adc #$01 +; sta fixlan02 + 1 ; Removed + sta fixlan12 + 1 + sta fixlan13 + 1 + +; $C0s2: Save off all ethephsr, ethbar, and ethpnr mods + adc #$01 + sta fixlan03 + 1 + sta fixlan27 + 1 + +; $C0s3: Save off all ethephsr+1, ethbar+1, ethpnr+1, and etharr mods + adc #$01 + sta fixlan04 + 1 + sta fixlan26 + 1 + +; $C0s4: Save off all ethrcr, ethiar, and ethfifo mods + adc #$01 + sta fixlan07 + 1 + sta fixlan15 + 1 + +; $C0s5: Save off all ethrcr+1, ethiar+1, and ethfifo+1 mods + adc #$01 + sta fixlan06 + 1 + sta fixlan08 + 1 + sta fixlan10 + 1 + sta fixlan16 + 1 + +; $C0s6: Save off all ethecr, ethptr, and ethiar+2 mods + adc #$01 + sta fixlan17 + 1 + sta fixlan28 + 1 + sta fixlan39 + 1 + +; $C0s7: Save off all ethecr+1, ethptr+1, and ethiar+3 mods + adc #$01 + sta fixlan18 + 1 + sta fixlan29 + 1 + sta fixlan40 + 1 + +; $C0s8: Save off all ethmir, ethdata, ethmgmt, and ethiar+4 mods + adc #$01 + sta fixlan19 + 1 + sta fixlan30 + 1 + sta fixlan31 + 1 + sta fixlan32 + 1 + sta fixlan33 + 1 + sta fixlan34 + 1 + sta fixlan35 + 1 + sta fixlan36 + 1 + sta fixlan41 + 1 + sta fixlan42 + 1 + sta fixlan43 + 1 + sta fixlan44 + 1 + sta fixlan46 + 1 + +; $C0s9: Save off all ethmir+1, ethdata+1, ethmgmt+1, and ethiar+5 mods + adc #$01 + sta fixlan20 + 1 + +; $C0sA: Save off all ethmcr, ethgpr, and ethrev mods +; $C0sB: Save off all ethmcr+1, ethgpr+1, and ethrev+1 mods + ; None + +; $C0sC: Save off all ethctr, ethist, ethack, and ethercv mods + adc #$03 ; Because there were no a or b mods + sta fixlan24 + 1 + sta fixlan25 + 1 + sta fixlan38 + 1 + +; $C0sD: Save off all ethmsk, ethctr+1 mods + adc #$01 + sta fixlan14 + 1 + sta fixlan22 + 1 + +; $C0sE: Save off all ethbsr mods + adc #$01 + sta fixlan00 + 1 + sta fixlan05 + 1 + sta fixlan11 + 1 + sta fixlan21 + 1 + + rts + .rodata eth_driver_name: .asciiz "LANceGS (91C96)" @@ -69,7 +452,8 @@ eth_driver_name: ; ; The Original Code is ip65. ; -; The Initial Developer of the Original Code is <## TBD ##> -; Portions created by the Initial Developer is Copyright (C) 2010 +; The Initial Developer of the Original Code is David Schmidt +; Portions created by the Initial Developer is Copyright (C) 2011 ; All Rights Reserved. ; -- LICENSE END -- + diff --git a/client/inc/net.i b/client/inc/net.i index ae05fd2..bb9b456 100644 --- a/client/inc/net.i +++ b/client/inc/net.i @@ -25,10 +25,12 @@ : print_ok - + print_dhcp_init - + + jsr dhcp_init + bcc :+ print_failed sec diff --git a/client/ip65/cifs.s b/client/ip65/cifs.s index 1c8ac49..567fe2d 100644 --- a/client/ip65/cifs.s +++ b/client/ip65/cifs.s @@ -11,6 +11,7 @@ .endif DEFAULT_CIFS_CMD_BUFFER = $6800 +DEFAULT_SMB_RESPONSE_BUFFER=$6000 .export cifs_l1_encode .export cifs_l1_decode .export cifs_start @@ -30,6 +31,7 @@ DEFAULT_CIFS_CMD_BUFFER = $6800 .import udp_send_dest_port .import udp_send_len .importzp ip_src +.importzp ip_dest .import ip_data .import ip_inp .import tcp_close @@ -53,8 +55,8 @@ nbns_nscount=8 nbns_arcount=10 nbns_question_name=12 nbns_service_type=43 -nbns_ttl=56 -nbns_additional_record_flags=62 + + nbns_my_ip=64 nbns_registration_message_length=68 @@ -202,6 +204,7 @@ cifs_start: jsr tcp_listen @loop: + inc $d020 ;FIXME jsr ip65_process lda connection_closed beq @loop @@ -238,7 +241,7 @@ cifs_advertise_hostname: ldax #$20 jsr copymem - ;copy our encode hostname to the host announcment + ;copy our raw hostname to the host announcment ldax #raw_local_hostname stax copy_src ldax #host_announce_servername @@ -309,6 +312,18 @@ nbns_callback: @name_request: ;this is a NB NAME REQUEST. + ;is it a unicast message? + ldx #3 +@check_unicast_loop: + lda ip_inp+ip_dest,x + cmp cfg_ip,x + bne @not_unicast + dex + bpl @check_unicast_loop + + jmp @looking_for_us + +@not_unicast: ;is it looking for our local hostname? ldax #udp_inp+udp_data+nbns_question_name+1 stax copy_src @@ -320,52 +335,44 @@ nbns_callback: iny cpy #30 bne @cmp_loop - + +@looking_for_us: ;this is a request for our name! - ;we will overwrite the input message to make our response + ;copy the txn id + ldax udp_inp+udp_data ;first 2 bytes of data are txn id + stax netbios_name_query_response - ;set the opcode & flags to make this a response - lda #$85 - ldx #$00 - sta udp_inp+udp_data+nbns_opcode - stx udp_inp+udp_data+nbns_opcode+1 - - ;set the question count to 0 - stx udp_inp+udp_data+nbns_qdcount+1 - - ;set the answer count to 1 - inx - stx udp_inp+udp_data+nbns_ancount+1 - -;set the sender & recipients IP address + ;set the sender & recipients IP address ldx #03 @copy_address_loop: lda ip_inp+ip_src,x sta udp_send_dest,x lda cfg_ip,x - sta udp_inp+udp_data+nbns_my_ip-6,x + sta netbios_name_query_response_ip,x dex bpl @copy_address_loop - - - -;set the answers - - ldax #nbns_ttl_etc - stax copy_src - ldax #udp_inp+udp_data+nbns_ttl-6 + + ;copy our encoded hostname + ldax #local_hostname + stax copy_src + ldax #netbios_name_query_response_hostname stax copy_dest - ldax #08 + ldax #30 jsr copymem + + ;copy the service identifier - last 2 bytes in the query hostname + .import eth_inp + ldax eth_inp+$55 + stax netbios_name_query_response_hostname+30 ldax #137 stax udp_send_dest_port stax udp_send_src_port - ldax #nbns_registration_message_length-6 + ldax #netbios_name_query_response_length stax udp_send_len - ldax #udp_inp+udp_data + ldax #netbios_name_query_response jmp udp_send @@ -453,7 +460,8 @@ nb_session_callback: jmp @message_handled ;this doesn't look like a NBT session message or SMB, so give up - @not_session_message: + @not_session_message: + @not_smb: jsr tcp_close @@ -471,25 +479,96 @@ nb_session_callback: @not_got_full_message: - .import print_hex - lda cifs_cmd_length+1 - jsr print_hex - lda cifs_cmd_length - jsr print_hex rts smb_handler: ; at this point, copy_src points to an SMB block encapsulated in an NBT session header - + + clc + lda copy_src + adc #4 + sta smb_ptr ;skip past the NBT header + lda copy_src+1 + adc #00 + sta smb_ptr+1 + ldy #8 lda (copy_src),y ;get the SMB type cmp #$72 - beq @negotiate_protcol -;unknown SMB + bne @not_negotiate_protocol + jmp @negotiate_protocol +@not_negotiate_protocol: + ;we assume it is an "AndX" command + + + sta andx_opcode + lda smb_ptr + clc + adc #$20 ;skip over SMB header + sta andx_ptr + + lda smb_ptr+1 + adc #00 + sta andx_ptr+1 + + jsr start_smb_response + +@parse_andx_block: + ldax andx_ptr + stax copy_src + lda andx_opcode + + cmp #$ff + beq @done_all_andx_blocks + + ldy #0 +@andx_opcode_scan: + lda andx_opcodes,y + beq @opcode_not_found + cmp andx_opcode + beq @opcode_found + iny + iny + iny + jmp @andx_opcode_scan +@opcode_found: + lda andx_opcodes+1,y + sta andx_handler+1 + lda andx_opcodes+2,y + sta andx_handler+2 + jsr andx_handler + jmp @go_to_next_andx_block + +@opcode_not_found: + jsr unknown_andx + +@go_to_next_andx_block: + ldy #3 + lda (copy_src),y ;get the AndX offset low byte + clc + adc smb_ptr + sta andx_ptr + iny + lda (copy_src),y ;get the AndX offset high byte + adc smb_ptr+1 + sta andx_ptr+1 + ldy #1 + lda (copy_src),y ;get the subsequent AndX opcode + sta andx_opcode + + jmp @parse_andx_block + +@done_all_andx_blocks: + ldax smb_response_length + inx ;FIXME! this is to force wireshark to dump as SMB even tho packet is incorrect + stax tcp_send_data_len + ldax smb_response_buffer + jsr tcp_send + rts -@negotiate_protcol: +@negotiate_protocol: ;copy the request TID,PID,UID,MID into the response ldy #28 ;offset of TID from start of message ldx #0 @@ -559,14 +638,83 @@ smb_handler: rts +start_smb_response: + ldax smb_response_buffer + stax copy_dest + ldy #0 +@copy_header_loop: + lda (copy_src),y ; copy_src should be the SMB request - cloning this will set PID / MID etc + sta (copy_dest),y + iny + cpy #smb_response_header_length + bne @copy_header_loop + lda #0 + sta smb_response_length+1 + lda #smb_response_header_length + sta smb_response_length + ldy #3 + sta (copy_dest),y + + ;set the flags correctly + ldy #smb_response_flags_offset + lda #$82 ;FLAGS byte + sta (copy_dest),y + iny + lda #$01 ;FLAGS2 low byte + sta (copy_dest),y + iny + lda #$00 ;FLAGS2 hi byte + sta (copy_dest),y + + rts + +add_andx_response: + rts + + +.import print_a +.import print_hex +session_setup_andx: + lda #'S' + jsr print_a + rts + +tree_connect_andx: + lda #'T' + jsr print_a + rts + +unknown_andx: + lda andx_opcode + jsr print_hex + lda #'?' + jsr print_a + rts + +.rodata + +andx_opcodes: + .byte $73 + .word session_setup_andx + .byte $75 + .word tree_connect_andx + .byte $00 + + + .data +andx_handler: + jmp $FFFF ;filled in later + +smb_response_header: negotiate_protocol_response_message: .byte $00 ;message type = session message .byte $00,$00,negotiate_protocol_response_message_length-4 ;NBT header .byte $FF,"SMB" ;SMB header .byte $72 ;command = negotiate protocol .byte $00,$00,$00,$00 ;status = OK +smb_response_flags_offset =*-smb_response_header .byte $82 ;flags : oplocks not supported, paths are case sensitive .byte $01,$00 ;flags 2 - long file names allowed .byte $00, $00 ;PID HIGH @@ -577,6 +725,7 @@ negotiate_protocol_response_tid: .byte $98,$76 ;PID - to be overwritten .byte $65,$64 ;USER ID - to be overwritten .byte $ab,$cd ;multiplex ID - to be overwritten +smb_response_header_length=*-smb_response_header .byte $11 ;word count dialect_index: .res 2 ;index of selected dialect .byte $00 ;security mode: share, no encryption @@ -666,6 +815,27 @@ host_announce_hostname: .byte $0 ;host comment host_announce_message_length=*-host_announce_message + +netbios_name_query_response: + .byte $12,$34 ;transaction id + .byte $85,$00 ;flags: name query response, no error + .byte $00,$00 ;questions = 0 + .byte $00,$01 ;answers = 1 + .byte $00,$00 ;authority records = 0 + .byte $00,$00 ;additional records = 0 + .byte $20 ; +netbios_name_query_response_hostname: + .res 30 ;will be replaced with encoded hostname + .byte $43,$41 ; + .byte $00 ; + .byte $00,$20 ; type = NB + .byte $00,$01 ;class = IN + .byte $00,$00,$01,$40 ; TTL = 64 seconds + .byte $00,$06 ;data length + .byte $00,$00 ;FLAGS = B-NODE, UNIQUE NAME +netbios_name_query_response_ip: + .res 4 ;filled in with our IP + netbios_name_query_response_length=*-netbios_name_query_response registration_request: @@ -685,7 +855,6 @@ registration_request_servername: .byte $c0,$0c ;additional record name : ptr to string in QUESTION NAME .byte $00,$20 ;question_type = NB .byte $00,$01 ;question_class = IN -nbns_ttl_etc: .byte $00,$00,$01,$40 ; TTL = 64 seconds .byte $00,$06 ;data length .byte $00,$00 ;FLAGS = B-NODE, UNIQUE NAME @@ -700,6 +869,12 @@ positive_session_response_packet: .byte $00, $00 ;message length positive_session_response_packet_length=*-positive_session_response_packet +.data + +cifs_cmd_buffer: .word DEFAULT_CIFS_CMD_BUFFER + +smb_response_buffer: .word DEFAULT_SMB_RESPONSE_BUFFER + .bss hostname_buffer: .res 33 @@ -716,11 +891,13 @@ connection_closed: .res 1 cifs_cmd_buffer_ptr: .res 2 cifs_cmd_length: .res 2 -.data +andx_opcode: .res 1 +andx_ptr: .res 2 ;pointer to next 'AndX' command +andx_length: .res 2 +smb_ptr: .res 2 +smb_response_length: .res 2 -cifs_cmd_buffer: .word DEFAULT_CIFS_CMD_BUFFER -;-- LICENSE FOR cifs.s -- ; The contents of this file are subject to the Mozilla Public License ; Version 1.1 (the "License"); you may not use this file except in ; compliance with the License. You may obtain a copy of the License at diff --git a/client/ip65/udp.s b/client/ip65/udp.s index 121eeb3..cabbb71 100644 --- a/client/ip65/udp.s +++ b/client/ip65/udp.s @@ -32,7 +32,6 @@ .export udp_send_dest_port .export udp_send_len - .import ip_calc_cksum .import ip_send .import ip_create_packet diff --git a/client/kipperterm2/timestamp.i b/client/kipperterm2/timestamp.i index b440265..5cc5090 100644 --- a/client/kipperterm2/timestamp.i +++ b/client/kipperterm2/timestamp.i @@ -1 +1 @@ -.byte "2011-01-15" +.byte "2011-01-15" diff --git a/client/test/Makefile b/client/test/Makefile index 5ad2717..ac01ae5 100644 --- a/client/test/Makefile +++ b/client/test/Makefile @@ -104,7 +104,6 @@ test_vic20.prg: test_vic20.o $(IP65TCPLIB) $(VIC20RRNETLIB) $(INCFILES) ../cfg/v test_lancegs.pg2: test_lancegs.o $(IP65LIB) $(A2LANCEGSLIB) $(INCFILES) ../cfg/a2bin.cfg $(LD) -C ../cfg/a2bin.cfg -o test_lancegs.pg2 $(AFLAGS) $< $(IP65LIB) $(A2LANCEGSLIB) - httpd_test.d64: test_httpd.prg index.html file1.html cp test_httpd.prg autoexec.prg ripxplore.rb --init CbmDos httpd_test.d64 -a autoexec.prg @@ -112,12 +111,12 @@ httpd_test.d64: test_httpd.prg index.html file1.html ripxplore.rb httpd_test.d64 -a file1.html -t C64Seq -ip65test.dsk: testdns.pg2 testdottedquad.pg2 testtftp.pg2 - ripxplore.rb --init BeautifulBoot ip65test.dsk -a testdns.pg2 -t AppleBinary - ripxplore.rb ip65test.dsk -a testdns.pg2 -t AppleBinary - ripxplore.rb ip65test.dsk -a testtftp.pg2 -t AppleBinary - ripxplore.rb ip65test.dsk -a testdottedquad.pg2 -t AppleBinary - ripxplore.rb ip65test.dsk -a testdns.pg2 -t AppleBinary +ip65test.dsk: testdns.pg2 testdottedquad.pg2 testtftp.pg2 test_lancegs.pg2 + ripxplore.rb --init AppleDos ip65test.dsk -a test_lancegs.pg2 -t AppleBinary +# ripxplore.rb --init BeautifulBoot ip65testb.dsk -a test_lancegs.pg2 -t AppleBinary +# ripxplore.rb ip65test.dsk -a testtftp.pg2 -t AppleBinary +# ripxplore.rb ip65test.dsk -a testdottedquad.pg2 -t AppleBinary +# ripxplore.rb ip65test.dsk -a testdns.pg2 -t AppleBinary test_disk_io.d64: test_disk_io.prg diff --git a/client/test/test_cifs.s b/client/test/test_cifs.s index 528d4ab..a42d136 100644 --- a/client/test/test_cifs.s +++ b/client/test/test_cifs.s @@ -13,6 +13,7 @@ .import __CODE_SIZE__ .import __RODATA_SIZE__ .import __DATA_SIZE__ + .import __IP65_DEFAULTS_SIZE__ .segment "STARTUP" ;this is what gets put at the start of the file on the C64 @@ -33,15 +34,26 @@ basicstub: .segment "EXEHDR" ;this is what gets put an the start of the file on the Apple 2 .addr __CODE_LOAD__-$11 ; Start address - .word __CODE_SIZE__+__RODATA_SIZE__+__DATA_SIZE__+4 ; Size + .word __CODE_SIZE__+__RODATA_SIZE__+__DATA_SIZE__+__IP65_DEFAULTS_SIZE__+4 ; Size jmp init .code init: - -; jsr print_cr + lda #$0E ;change to lower case + jsr print_a + jsr print_cr init_ip_via_dhcp + +; jsr ip65_init + + ldx #3 +: + lda static_ip,x + sta cfg_ip,x + dex + bpl :- + jsr print_ip_config ldax #hostname_1 @@ -96,6 +108,8 @@ hostname_3: cifs_hostname: .byte "KIPPERCIFS",0 +static_ip: + .byte 10,5,1,64 sample_msg: .byte $ff, $ff, $ff, $ff, $ff, $ff, $f8, $1e, $df, $dc, $47, $a1, $08, $00, $45, $00 .byte $00, $4e, $9e, $cf, $00, $00, $40, $11, $c4, $c5, $0a, $05, $01, $02, $0a, $05 diff --git a/client/test/test_lancegs.s b/client/test/test_lancegs.s index 16ca16c..76323e7 100644 --- a/client/test/test_lancegs.s +++ b/client/test/test_lancegs.s @@ -2,46 +2,30 @@ .include "../inc/commonprint.i" .include "../inc/net.i" - .import cfg_get_configuration_ptr + .import cfg_get_configuration_ptr .import copymem .importzp copy_src .importzp copy_dest - + .import exit_to_basic + .import __CODE_LOAD__ .import __CODE_SIZE__ - .import __RODATA_SIZE__ + .import __RODATA_SIZE__ .import __DATA_SIZE__ - + .import __IP65_DEFAULTS_SIZE__ .segment "EXEHDR" ;this is what gets put an the start of the file on the Apple 2 - .addr __CODE_LOAD__-$11 ; Start address - .word __CODE_SIZE__+__RODATA_SIZE__+__DATA_SIZE__+4 ; Size + .addr __CODE_LOAD__-$03 ; Start address + .word __CODE_SIZE__+__RODATA_SIZE__+__DATA_SIZE__+__IP65_DEFAULTS_SIZE__+4 ; file size jmp init .code init: +cld jsr print_cr init_ip_via_dhcp jsr print_ip_config jsr print_cr - rts - -;-- LICENSE FOR test_ping.s -- -; The contents of this file are subject to the Mozilla Public License -; Version 1.1 (the "License"); you may not use this file except in -; compliance with the License. You may obtain a copy of the License at -; http://www.mozilla.org/MPL/ -; -; Software distributed under the License is distributed on an "AS IS" -; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -; License for the specific language governing rights and limitations -; under the License. -; -; The Original Code is ip65. -; -; The Initial Developer of the Original Code is Jonno Downes, -; jonno@jamtronix.com. -; Portions created by the Initial Developer are Copyright (C) 2009 -; Jonno Downes. All Rights Reserved. -; -- LICENSE END -- + jmp exit_to_basic + \ No newline at end of file diff --git a/client/wiznet/kipperwizdisk.d64 b/client/wiznet/kipperwizdisk.d64 index c66a5430e81fca61283dd727bed85fd86ee88fc1..c62eb6a015abed59108248db537f41753de54af3 100644 GIT binary patch delta 3174 zcmZWr4OA4@72dZGa76>V5#ow}BWnExe*oRYXtc(t$&y+{%~4ZFYf@JQf0CNCiYO{< zky+Ph97sfG$D+*YtWXj+=-D_P>&E3McWisooI`U?PMfwi4V(2Z5jCav%@A^8IQrhZ z-@W&{ckaFK!Oo9UcYd7Oxc*z>I=JE6Ka+yyng5%en@nZ*M?Nu2mj7b6IkBGNRZF!nKhPLP-J~e^7 zG4HaZT5McHfKiW>&tfX|!%?iy?#A*BOns4}tQNABS?zKc7TKvHh{hWMMYOw6P47H<1d3-iAhsCVGtlXR;Up0@0V9-a zhQ#v3`k~j)n>Z}Ybi+|uB!}H*F&{U_V`c%#7-1q7*}~=iq0z2*43+vJ4PBt_;@@$S z7r$M6vw+NCIekz-O4vSn3YpIKUrizFv4bvNsWB-(okAuQA*Ini_uEz7ILoEOE1aXg z^HedTtLJFwJninT8Q$MZiDQoCRX@rC2a%sjj!b34lHRX-T-aFP{7TvE+xsd-Mg|xe zvw6ihJ9%Sl+NE%wiwvXI74_nhX(Zdaxjy{cX{0`>uL!;xP@ctazodxcW|Ga;)S!42 z_q00j9R-|Vi*O8lk~nx48Tv#mwA$}0ornd%CxcQbU*nbp!wYATkz@c<-V)qxm=XDb zUiANz9I~5{3gTz4hR$AP(eh`LSNo&}d8L2>7`A>k**e}NW{_ZHAk)YTS{>v5Mk(k{ zrYG>+XUQAHWR^Yg#h@+*#@sfC%)dNPMK>Y=TR>BVo5YC=U z&L@}6GDcHLm(O?ie1SQu1=U=pao1PM_I}1~FR1pU_{j}?msp3*d&D7_`aNjgCI+T` z51Mz1fscO=nwLshlqGB3gztVA*YkvFCgv<6Gp&;X;>JbfCuI$RD_V;FUtZV-zU^>N zu?VFg>;Rz*WP92+$lVV147C)B5iJFE2Na`j2dHIGT*i!A^}nIpC$bgS&~}KF!o274 zhpP^(9g~i08~Xp7k699_@onJrOyvI5#fP?meg}n6cG+zeD(NMcFmtIA~Q0x zG(Ce0`5M!>O$YrHPYMyT#QjBNwAEn|!$l-}ln7UvIH3k4(!>S5O`Q9ZqB-2h6LQ$6XIdRwUnSkZgf&qxxE!Lc# zG+_^3EzM&Q_JX=hb9JD&TvTDIo5ng#Kff*RWyw`STRAvwsM9Qo0i(TWVH)%hukhO3Lr;!YD;>!{ z;`PG(t{}#n-^FTv7}^usSjxAA@xjV@7u!a#tvqXqLky5@;zqC z8d(v1;M`WP8p){O0GhD2ttHF65B?b38x=wHN}q4pG1)t&)W zgxcev)QT12~ zV0TqX9JG)Oo)W+p--dk_ilYsx%;p-~z9AZ{aeDf)USWfco|~NOknJ06v$$~~{?W4F ygs3kiI{pz8mR}+V`Vdz*e+{{qNeaSW|AvgGy)15v#og6aIi{3^$CO(Cn)83GxgRb7 delta 3227 zcmZuzYj6|S71q_YjWEQP!GL&QY*L;!1V3<+hDQj&NdzI7S7}JLG!FzeHAza+^6&#J zgz?HUkgN^3$crtrj#tPq#W9S+cBZ3P9_+KzG9f>bX{JqEnqrNBghw8A&)s#LNm`A4 z?>*<9^PTUWb9V2Rf0$hUVRC5uw`Aeb;&1;<^3N~%&$8^qV5{>3uM|!q8B*I^a>nWo zNNcmm44XUPOsQa{0cn30d6;Y8&msb!SvK)aa0mQ#EVbN61koaRiNG#~6j^?c;eJmx znUd)apoaeyw>onx6)NUvN>r|7smbnuVvz-QDjyRq|%a!VoS!LF4xeht0>CqNJ>Ha)YWka-K z98Gf1Cp$*C5nBi%ZR9&JpJc2w`4$@xSLKVS=U{hmRT^Ny!-8_m!2QvoFNc(IqC;0E z;>kH(0}@lV<00~f73a?$A~X2>zyh)+17q4a1LZA*LSeWeRGLDv!L4Eec|IExbjnb& zt1WDdhbM7UN0jPBh*s%~l-(FBH3f}cG-ygIbI4R_b`F^^9CbT}bPscJuPN5%ke_m~ zW@vY+bUTMk;<`3d=g1|KcyaS{$p(j+jHbiR^=>6g?&M@QXgqfOf`3diU z=}NC5ah{kz^m?v?VPYOL9F-!u(`hDil-V9j@<{r0Q)7`WEc;x@uDFaX^?e$=Or0f% znBRkEm)^=Fi#VtE^2iQ8&&(%}@%hz!vK=c}ASm@F8=D2T+3&L!2<-R%(0%oF%|1Fxbn48OqiHz3fJacb zY0>5S3bjOYxKhNu7+7;z#C@P{(hB-f{JGY@59ZHMOSO1HbcsGi-Kpv0m2I+CHQPN$ zNyow-Xo_0&q)#oQbgM&%?FXI44ghwDMJ$uvT|=hZ%Rt?$MF-+v#H#qFTl&iyGEK4; zk*rY%n#7kP-j^eCV))T3jn0TYU0Pg39x2-m-aUW_=KmAp&J(! ze4|^x@sBxudqDrYS`Ns%9MlRvS3oGH?uMs*6WVu!r(?Fm^z+$b6`Vjh^z8;kdx@oxHq)`%|Y9d@8{Be-KDftx$ zfX0^7ytQybtb+bqw6MDWHf`IAi9Mk8#;Kl&{BBagRhpm2veK;5;`L-~Sv%IL9pYo! zdcw`nTcPQ;@EPbW*W$@N_I|~K$XKb9AWqT3eh{Z>Y%*3#RKXjBcJ!O1CGZC9LSGeh zTNnBUAwH%1N;~ME$J(Kx8LAu3fa-&Wlc3f?gCA3_9OSSC9Hwj#C*AWSh(wRQzd)$x z6~r);+qzmfR+j>Z*44<-x|%pzcaQ#AUps+IjW%wH^w@8z?*LX;l>lq4)+YfLS#6Mhwt-B}KMoF;4a+T-M9!!u_~U}rFOIa; zJ6t1q$*|5w=}pvah;|pNEwyhT8D&`VrN;fIq{^2DFP7Ejg_u{vG~+YEys24UjVp=6 zoidc>Rn3*j-z&`vn=4bkSDM#0SB~(?_xu$6yG;9Z>i*{@B9St+P^b>~P^wSR3P^2Li${BigVRa$f(<&vbCiA!KX=SHd z*}v+Ych6|k_3`GG-) z9~W0!aB%<^Sgd2KN%1CH$GX)pj)Jxt=~17y2B+RyZGrxP!S4n|e$JNDTDqR=r&!E8 z$U07AcW6TZw{H8BkWm}$PP5_sF7+px8En5JZF-Ju*?gDc@x^=8Z^h}pA$&#s2`GGR z2;WfkE)CwJ{P9xn)8JRsc)-4