From 20b96b208611d8ad554129767b992cef24b35047 Mon Sep 17 00:00:00 2001 From: c0repwn3r Date: Sun, 15 May 2022 17:42:53 -0400 Subject: [PATCH] Fix up interrupt controller, add version.h and change welcome message, also ANSI and printf --- .vscode/settings.json | 3 +- Makefile | 21 +- bin/shade.bin | Bin 32320 -> 31848 bytes bin/shade.iso | Bin 26613760 -> 26613760 bytes include/printf.h | 22 +- include/shade/cansid.h | 26 ++ include/shade/kmsg.h | 10 + .../shade/platform/drivers/vga_text_mode.h | 21 +- include/shade/platform/interrupts/isr.h | 2 +- include/shade/version.h | 9 + obj/kernel/kernel.o | Bin 3904 -> 3360 bytes src/kernel/cansid.c | 114 +++++++++ src/kernel/kernel.c | 38 ++- src/kernel/kmsg.c | 26 ++ src/kernel/platform/drivers/vga_text_mode.c | 222 +----------------- src/kernel/platform/interrupts/int.asm | 26 +- src/kernel/platform/interrupts/isr.c | 6 +- src/libc/printf.c | 12 +- 18 files changed, 268 insertions(+), 290 deletions(-) create mode 100644 include/shade/cansid.h create mode 100644 include/shade/kmsg.h create mode 100644 include/shade/version.h create mode 100644 src/kernel/cansid.c create mode 100644 src/kernel/kmsg.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 0300e4e..fe3c9e9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "files.associations": { "types.h": "c", - "vga_text_mode.h": "c" + "vga_text_mode.h": "c", + "kmsg.h": "c" } } diff --git a/Makefile b/Makefile index 219db38..35e3688 100644 --- a/Makefile +++ b/Makefile @@ -33,8 +33,27 @@ sboot_object_files = $(sboot_asm_object_files) shade_bin_ldfile = src/linker.ld +version = 0.1.1-alpha +stream = shade-development +git_build = $(shell git rev-parse --short HEAD) +date = $(shell date) +codename = willow + default: run +.FORCE: + +include/shade/version.h: .FORCE + echo "#ifndef VERSION_H" > include/shade/version.h + echo "#define VERSION_H" >> include/shade/version.h + echo "// This file was autogenerated by the shadeOS build system. It should not be modified." >> include/shade/version.h + echo "#define SHADE_OS_KERNEL_VERSION \"$(version)\"" >> include/shade/version.h + echo "#define SHADE_OS_KERNEL \"$(stream)\"" >> include/shade/version.h + echo "#define SHADE_OS_BUILD \"$(git_build)\"" >> include/shade/version.h + echo "#define SHADE_OS_COMPILE_DATE \"$(date)\"" >> include/shade/version.h + echo "#define SHADE_OS_CODENAME \"$(codename)\"" >> include/shade/version.h + echo "#endif" >> include/shade/version.h + bin/shade.iso: bin/shade.bin cp bin/shade.bin src/iso/boot/shade.bin grub-mkrescue src/iso/ -o $@ @@ -64,4 +83,4 @@ run: clean bin/shade.iso clean: rm -rf bin/* - rm -rf obj/* \ No newline at end of file + rm -rf obj/* diff --git a/bin/shade.bin b/bin/shade.bin index a5688a281799957f3e52f05b07c92d11079571a9..67dff2af2b8007707f9cdd00194671b49a62f271 100755 GIT binary patch delta 9761 zcmcgx3wTsTmagukLx4ahgd~vgXdr|<2zl^SQ39l)MZgFmuONve(asx^fb!~SKocSu zs2Rl(6x|sWc1LH80xFXYT(U5Pu$blVvRSd_>_djGn0>O!KZ-?uYc-W|S5 z{`IuG9Fx3WuRmj~Y4c=giO_6Yt%0_x%_(Twwr)-nqH42EYi$+Qw(4I;J2Wm4qR#6O z;hJ2}PY|w~1!k){R-c>_$POYJbM+X7=LzA^bOwBQCr}>!md$XgZUe%}`ZGrM2C5kog+@3?`o*wkm6f zc|VYy=KZNAvawaPTWnPuTvZn;t>@tDGVf3Et+ZY{X7w`BtY#G*eîyTx|+Nynz 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 Ukk+~DWoZKMv8V&@w!!y*0Pa^n2LJ#7 diff --git a/bin/shade.iso b/bin/shade.iso index bc0b69a3f57ea454a0e8457e2708131ec684a8b0..58744399686198cf92dc1c640170ebdcefe54589 100644 GIT binary patch delta 70528 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 zⅆ}zo$xbr@t~em|#VA0z4N zUD)N;nkrpvqhSpI0qkL06-}^qubql!0^oSE1KFKgu)t10v`9yd?g3P(ksw;Sv!*lv zz)R6C>R2YwW^~n{0RTEkH??k}QIfj@(d0Lr%1OEjRhSfj%{|o8OaOF{o|;C!*b8_$ z|E8vq0jkU%ygRys&kTgatrvZq1=yqB1aeLu59ImG&wH$gZz_bar^B4>y#Zhr(UL72 z?o^I~#9H;Q8Q@30e}q$cogtuwM}gq3j&!DZ6BW?=;Z9|jZlckEdZRULbc&=&c_PO+ zFDQLe6tXN@q2&QUX(hK9?0y)hnbQ9luK^UAr~%ZNqynIfP!>Absk}`W`Z9cqqDUUt z?WwOIM8)E!I+bH9QHPJ-RaY|t@YOU8N9U)j9Pu(d(Cl3lL5b2ooat0HVFIx4J%yGh zd6rW>trWQn%+ZjkGgl$Q1V;B*4d9)58d9t0tBT3D6rRr)IGg43R#_pbrc0dO@8>*$ zOPv~X$F~!Sg+8B;qd#ye$HQVXBR*QOe3?^uFDgo3m87T$~?NTUs0D#_EuL=Sz*E%zwPn?R8zhE-&2BnQW z85^AHn@*A2wMh|~FidA{HmgjFrXIY-slIIIFKnVdRT~p_?48-dt!fSVR^IdeXUBcWdOq9l zTu2ESJnK@)EpQ9AK8+AE@Bh0Gs3Wmp>+C_bu0n~gA5vr~_3N+0DnMWqIjSKA?$&!DYWEUTLRaG)Beb4kCogPZQE!dn4rFrG# zDnIFQ-KkiMh>rO2PwLdmnRchoje<4lnHxmU=I65DrQpZ@yr~(Y{C{z(Zy9*+se4PM zCuG$AwpwEWcso-AnEI;%@N~^0wsV3Ee564+mJ$b`hwrKyz{f_<)qBnnil{~R6umI{ zfu`=e9;!z{f`xB?Q&|u%Qttk)&NBid?<1#TkSGAv{!~{J0ub_8EnK{;8u&z6GBa_= zJpC`VUkXyopK1!<{miL21qzHS&(-cH0C)b@$O^tI;qNJw?R-@cG{;-E(B-{iliefP za4B|vLYu+6S=oI^4)Q3ZJgGh|&El_UUYGYq4NUo5nxmUW`CZBppD?B9RKS(yJyD6{ z?STbd%2}sibBv$DCKDB#>+kYjad@B%nj_D~Lar5xM1{5|1}Y_cU@1t*fzQJ`i@Ma6 zCjb7`@nQ-)Osq666j$iWCbNACBjj&EE@gErUbH-Z#pQhx?MW};QjccD6jY}aQ92)8 z8kcrucne&}HKL45In@<_Ib~hS_E!Ls%Bd~E*UX-+WEM|lf4V8kWmtW0BDu#H4 z*ZegCgMWIlTVu6F1jeu?Y6}ZMOj8X8yyx{}YnrL@+6If3xbP6G@W==s5;UK z?c3f$WkI|}Jls;Prkq1-!Akb0RxWx2cQZ`WUxmYe!8WRriPB26RYfF}%+}7OY}y5& zV|x`KtoXt^5MvBKsh!kOnH%H~J+G50173z_MQ4}lOe7A6=65A%@xHXYn@e4-aP0t} zRX_Ii8-&<+Z-#x7GSGg3-b@1!hwMIL?Jlvz&XNPWwa0_fm0qWlnsLwS{^w4Q0G)} zM#Vqa!EO#w`4Z5FL)Airg5@8swxpQEDvod|Cx&9GZ!=PDEAf84SGcNRVmUD?LLJD& z!g9eVb@UMc_h>@9x#E2qNo%ABY|d44Cb7zy9qm$14Mj_WKdB$vF;<;N#2~Y0oLU

;aaNbuMpI{nJP=Bp)s7ez2{OkcH+cs%Ph57#G>Py*(yNHp1;pg z7SA5Q&}@0D%~R?vKV#BlzN(F)BlcOK5#i{C>J%lUH8oBdo#eR^i&yI)Fuq!(HZ0e8 z&*{ak@rry!lP^h7*%GItA1_hWN?`0;s&*?;`ng1xvh5WZVASZBv`k@bL;5n8!jx!> zicBqA3^eeS4zTQ6t`0*|C7J=W|A#76us3pr+PTHZ^3F<^Vr3_~FgQIZJxh?;VN=w;rOut@}tbLTy{8somG>iQ4h6NuT~$?Lc6&-kIJ0gqTP9 z_9knCiXrL{yu}s3@@;adMjL$a4BV`8V4`{NCv$d1jTzSX7d{P}3$8Tgq?eJLhf zC+BT*DTlmbrUBm+?*ZDAu*0Q!TlRFPy0?ZF>C7Box|DaNVx(!dOI_m$UG3sg0pi?i z@NRX85Q3VqN3AyOD?78zU#X*wz}UZ6oh=05`>zQNib7R)rm3nbX2!GwDnO7rb5Lbkj1#vGsU1-OU|#ZL#gC}lXYm=?Qb#of zRXau$#9extA6JJkp+!AU5ULOB-g!e$x**ht&;UY12#p}T2B9&8CJ>rJXa?bR2+bjc zKnR7vaa%%Y1tAPVYY1&1w1v^HzD+b z&>KP@2z?>+gU}zs00?hE7zkkyguxKrhA;%ePzb{y42LiR!bk|=5F#Lqf-o9FB!n># zq98;=7z<$>gz*q2K$r+&5`@VRra*WH!c++FLYM|&I)oVzVj#?f@E(L&5N1P|17R+N zSP1hV%!jZ5!a@jf5aJ;$g0L9E`w$W!EP=2TLL!6@AS{EB1c5maO$@G*o>AZ&oJ5yB=2n;~q0@F|3?5I%$OIfQKxwnNwfVJCzy zAbbg77X%N4-4OOb_zJ>a2wy|k2Vp;i6bPvh(jXjwa1g>F2!|mYfp8SUF$l*YoPcl= z!YK%+A)JBm4TN+EXCa(}a2~?95Wa)(J%kGoE<(5j;RgtpAzXoQ6~d1Yu0hCva2>)= z5N<%Y3E^i5zd*PJ;WmU!2){zef^Y}IT?qFe+=uW0!b1qZLHHfQ9}pfv_!Gio2u~pV z1>q@#XAqu4_#46t2-$H%Pa5w-;E|O_d3fZ*BVQinyfW9u?$~ACLTb6u_fG zJSxniKpqw0QBfWh<56)Q1@Y(=9+luxNgkErQE48P;Za#0mE%!)9#!B`Fpnzos1lDV z^Qa1ss`98BkE-*i29Ij;s1}cE^XOF`)!|WH9@XQKjYoDKIe6sck&8$5dDMVM4SCdv zN3ZdyF^`(?s40({@#u9PHRn+Xk3xCWf=4ZR)QU%8JZjCOHau#}qjo%M&!Y}J>d2!` zJnGD&Eu2oc{GDZF+7^dqxX0;i$}A0G>1oXc@)c|c|4lWqXj%#Xr#bM zWE`4iz5R~6R@->vb?eaI*!IQwOW;3Zf`3BvdA|L<r|FJb(0TyWr=x@7>9m zX|c3ju&+jp``*HMqeD!i?Ck9P@Q-+o&TR^JHn?lMC@iq;g1I5^*mlv}uKcoKZd>uR zi05t_x2#5tfZ5=(KO6qhPum4YZ?9>clkWq zm%ZJ802g8Z_qh0>{y(_roWn&+=l@48Uil{%5jkAMIC8i+8uo;X=SbM|!bigXF5EUQ zEau4oA*BUR=7-&ByW8Sp&1?AP$=7y4*ptG$AZ2;sU9EgEPutmdg4-^betW@#`C&H! z_jci3AwKiNUK|O_w(!R|%j_q43(s2(|MzM^YTG!U!}EcM?2MVyZ&B%<43=@_+r5X_tvEGm-z_r_8c4 z(KfM!q>>jB`Q4Ea-@=*Mzyy?NpI8}^Vt^?V3f1wQeJuZi13c^euD+Sy%bd+j+Wh}Y zWu`%*qzX^suJEp|K8Rwnx=Lc!o6#3aY8cZ)BK)JEj0(J<7*P^On|<3>cvmVYb@Yqu zY`9Cy@R6KwwicuL2XaA@C0;Wt7 zG7n;{kMip4>c0j+LdL;FnHi6Bip+|cA~Wb|WxWZ6noRVU`JiDfS*;)?a$*?;)0d~r z@9LAqTNWBN7otW&VNckL;|F{3AtL2iPG{*;&tlpC3p}Pg$w>?2Y2`y9%?6PiVC;~# zrz<>u6%En;DI6M;11(n1{j^WKQ6?rFbW|c_12g+MLn%DEJqYwvKEnSvv)rx?k15GH z_>cd}&X#EHVDL*hmIL^tE+^2!^i$cXOSuAlquPpd}!O`2}t?rur;tgAua~C1eHyvV;?f~(d!zv#P zj)pz3%=EJy_1z3P_-}V9h!%Ctj`Lj(DX$%vQ9RCfG2G2gH5T~Jz3AQuRF|~*?Emdk zQK_e;@SJ!kb>{Rt1tW1UGR(emwRq0I^_v6j07G65RbE zZ++vMdnsINTPK9MQ{cwl@DvC=F6?>Qll;76A4&NR8u_*d68G~qPx+QVZj^am%8=x) zJ`jXd=yv`me7f@e@H>$C0C(v@@kT`>#N8CGqpF2~U;tL?XNzU{QQtyv$%YX<+mh7? zq?FxudD}Su!+DCjPebN%r3XC>v=}@cY64^K%<0*6CmkqklskCS?K3#uXcqVlH0%Cg z&J&Mrr@FgAa_N}GkPP`#*7C&n#KVjIMRKymLW-1WI>#4)WS%uL2jGFbxUf62b`?3S zTMlfcfWaKXMI(x+p9715yZtdB8cT}6;^Vn2YB+~Lut=0VIe<-ok zoXr800RWc@6kn0E<^c%TF_Rr*gFE8wc*9`~aIc1IHKUV!w&pS1PavVX@z5PSB;E+F zmk#JT)lBBJc-Kx5cdOwJ>X??1f5Aa1-Z*y@U^)BFz7x3MDa@rkpM!QkDeUP4Gj7l4 zalW;0fO;KCxtP;ik5uDYR~(XsD*C}qoUbo`eBwK!NJ_qz=5VkYGCuty;dshjC?Jm! zGrVr$3Ui=*i)G+j!{aY@9lkrzviRcS)WzwyW1t)K=J##CiLgXivVMfv$hruzHR~M2 zd9qGH?2~l_V&AM(i1TKB1#!NtFCy}1ZG*c4S(_sYW_=Rjm-SJEf7Y6afUFe}g|dDWtQVN**ygtRNrHEv#0A7pYwG3XX;I$538{oA$Ufbfe8(#a0D=gB7U_u05 z$K!PxUgzRHpwcJ`sb%>01!Tj4<8Vzt~`$Hp5qtsPssFTzb( z>$#flKjEf^e>cF%${V`-O}M(_CIQUddK}R7A6L_2nOvA-N7rZiy@^>wYac3kl+6> zrvNx<ngcE5m|8rI;-?i%mJ8%_M5RkB#dwS?The^qivK~kzUrDBSGYP`|Lu%-vOr@k9+ zOt%hdnerFh6tfz)Qvznh8}$t9%vLF{$HW^Q{jIly-0^YoMw)eXkh@EKys@9(6kY^U z=AiCy(}<@mf}69}pjT1`Esi&)=4Gj$j?e2Zv^?IZ64Ng@xJ*A=6Q?7xO9!}#3=SR; zTy;FZD`@E_LC(ltp>K2+Km}%xY#rLJqZiC#$?qe3XidEK!0QXV&i0k?=P`KGDz8lF z3M#`FWxa-t9ugHfEOi_xRcGk1NyA1*MMn-BGr6pW!pDpVRx?Y&-6xI- z?lkzFV0-;wTm8l^dt-aU;I5s5!&>zScGw&a%iFGD4c>MPvrG;jJvwS~4j)y=2iK@N zzUHgJP>bmB(ZhyE;d@3Xj4IiJf0a~G);b+zxa=06=fG%ZBaSwxQ zznDqYrDQgGUElY5_J;pY&jY;hTX{VrevG9y0w&j#jQoPvuf3i_yq?W`|Mt3p#D`H? zN8l_X^rJ3`P++XJWk6nQihn*h)GG@~ zO(E3yN)PW3@Kd?KrvW^AZ+12xw{qzB5ab2@RTfK(B^)xq{PUj-1OJoZYe#V)g`_JGg7;@<_lIi^+}bOr0?#42Kfrgq4CnXoW?|u}p&uk08Uue`JU@Ggwvuy!`=r8k{4?mj5Z>Yk)g)fnNc5 zX@J9V?|<4M%EX7R_8c9CWuA4~O8P*imdCVOls{;>yy`F z*#raxqzXKofO5n`D|kqUa>T<>cnCc1j`$daWBu#6yB6G(&buB`eNMRxiibn+aMyge z2@l_&b}tkVpL`Ze!}IRV;-LyWY`Ne*As$-6LzP?ZU&X^vcsOc4%z=l9+wNMQh;rbe zV5YmhcsK+PmEb|t|0X;%&2%pW$fW%nc{`2M~-79^QtVKJMCw?F$ zeqKC3Fw0e=3d`SABySb-rr1*!gTX2z)YWt>E_vhnLZcO24=CTTn5P>iptw*R!(yIxnCZcoZk~Ra>91nCc^YD- zH^OxDbi_<=h3Rlw@{;@k+7$uj@rjAhAJfg_6f=D!rklqrX8Ht7m&Yw;s>L!B)6LTt zQGX7OM}T=8W1=j_bn|$|Oy@HOmz;TAW2SGzbo2PeO#d3w&Ep(1{g^n*1w!WejtTe{ z5zO-*Gd%;-&GR2K{SKy^=Rs!r6HGVHhs<;z7%h1PV$1_06A*|1^8m?AFN^8sfs&bC z1Jlg|CNtfI>Egi2OoVF)ra$BdQ83KpfI1@JsRZPt_jVg!@M$5r#TXd8eBi$LOH)J$ z95(Tvs3u&^DWa8E`Y52zC!N(nGC#b47Qe}uo{s4yG5tMEzm4g9#lnAam>%>Bs%}%6 zZutNK-CmIegkriI)0bj;HB8@x>ANv~5T<{Q=_N}@It6eZ*p2BGOUm@xR_r>55zw*p zKM~R~y=R$!re|RKTV-WBuPOiig6Y>P%JdIVq<0{lPk{WujsLoe7#2)1h%mmI1boO} z-oP&#(u)GhqvA5XhfKEQh4lwdpM@fYcW~lY7}F;dmibFydNE8-z;%My^Z?9-=`qa< zNCdM9Dj*=gRi+<8LNzhHLVlTUHjNY0-$goNX8}0PF@2%G%nzG4@#~4{tqhSKV=)UD zfPilrNQ8rkFapyDVZqlheIlg8nbj09RuA8@Q)!goHlE^c7e$u{!|R4ovsKf+aP0Z-nV#*p#!7&S9P&12ss-!!)rA z0N}TX@NO|l;shdG!t|wHB3%aQMPS9k-)HilDe4E9{}W`0Zx;CP4^009)<8XZg2|}` z7M)zVmZBtPb0GWzF#WT9GN;&=LV5{IhfRmbXI7w^yT~3GZDMQ)Z->hIn#cufW=s#q zrl^MLZ6KXjqzYEV9FIC+{y40Nsq|ejy(uoc8Y7)vGCjr;gpv{)L;&)YIIrMBY*G^; z8q*J9x~XIDV)`ks&JYUeMfh;pRaP8_7t37Ce@!q7y7dZjvQ1Q^5g16Z?i zn12hhw0Bz_rzHQ zz%F9?QB+oV=P!QOF#UaOGO_0e$URK=_v*EeA-x$F^%F#xhlGj&fVXk5R~!3+*7E$t zQ4)hv?0e&lPS(ni<44pTVi_`c%=qx3IX44`M2&$%;|Xvb9W~B9Z6BP6krdWJb2s~_m^pKN?SxcM~$+K8Xr9_e9VO51Krmly;cCvJaGJk!4rmA zMon`2ABZ=)=LsJ(**zKV#?}}$Y5W+>Zy7aw$OL&Wi4PwfKH3r<1%-r9SaUGmc(oLk zA3ZK=$guI_p(qpAT^r!#gK{W|v+seN!>Cd2K>%CD(m&4cQ=V%RbW!|aL*r%LYwoo9 z!;SLpkVEip*hqE{hHJfo$!#P&xfgr5DE}+0Y}}$i7Whg2{O)4_?`;fqS3DeV!~{h1 z_vrBBL&O{wnKJX${MeaGxVH~kU^Fb?en(`mv`I0}4>LS@79Wc@79U##VKIdFjs3?G zjG|4^&zWcaaYL1M?+5!VF(6vXqeLEkV59{5EHfVL3ADGHW{Dd6dds*&(}(zcJ>+0w zaE<8yBp$`YJ#S1vvg(a=6)zJGz!e<@bMJwlasNW{IOn`9l^qC31(6wDw2W-g{91{E@ z`U5iR1^p{0%G0@73-#}=`#T@R2 z#U&B??c$m`?$)A9eh)%$zO`@a;DoMgMo5639S^iV15 z(COjh<#l|xs$N39bqr5i#k%nsURCO*<_s@Mn_g4AY~lo1Z1X~d?%m9ZDC?Hu2=#1X zgplyT7A3$>Sm8+eGdC6!x0NgA%vM#*6VbPRuNyiuyW3qREHWPA!$2_XM1g3cw5Wu`1OUv|0n9nL_L{l&xA55O1v#n zn6eXAr6>*jI)%e6ysvmxSzY*9@RvDa-rpX*0tpHGl<>CitII&M?k|h7tG=HrV&;Cu z-j@9wZpH(pgI{7GOGNXwZ4!!}4r7X$bU+bWe}E&Dc0l#gvIjXSj60~L@X0}@^%EW) zRMjhRh@%{HNL6ns3w>}%m0RdAM|r?uMS0F)j;m{j-Ad`c;>wNwsysrMvd{-#QMpGr zp+_H4vYLN{qnvuAH0rS3>a|G z_~3P`)x48x-pY59lS}kT7i!3glbl@com6tkeTu`4I;CVf{}flZ8>dvwgHLm~y-$~Y zdDO^KM|4ZHxEbu)X(jwDXShPToKc0uo#C41>=}jooP{?3RzXL8%TYe^ZONSfYMNFZ zTC{0y7*`^{Q)~?Sj;rhb@05<;|BfrD&i4v;;P)JE%J)jfx4-8qSnDiSLCooAamYK) zc9xARf}>b)^f{z0=0sT>@zirlKEdZ1+M-K;D{sQs^SQ8q8T~CzeXgEI^%2kev*maC zL6tv#09*dqAE@=s!AtWXwl0G+Tb!*;boUcVL#4yg`*sGMd8l5!d3X% z6;-96s~m2ZtHoa)qcYcv5Je~PBk)yKyT>e@hCeAD2LHtIfAA-z=l6f&c!;>BaQj{3 z)XaBHm3#3TSG!8rRqfhe=gQr9y&}r(M-D2yBdz=iO@3BNeqto6#lD}_9Q(`99Q_)< zC}{s*IB3!@N=eMhxp>AM``gqtc)!t;kr{lw)S&es!${-($TPhl&0={KboLBBJFBy{~<@fjS) z&LZc3$4bt7#7Us#BLpT+<*Jx)?2)28p*L5%g!+G|fo1R?9O;xlkhGXHjb*FapGx-K z{;XYuTfzlfj()vI^%@a9ST5qi=RKxH++(ht`#x3!VEkiFK$jmYJ~RErG(bX^zm#&$ z_=~H>g}+oS2F+w^(fWz1#powo!N;Gdg8z8JF&+80V!Hp|T*15kRs}bk#TFd)R2AIu zDOab@pQ<`tWTBzY)Uew58OPR!XNs+p<*LbF?#0LiOjUt_dxkb6G+MIg2J7qAVAm zbCs)*rYd)35UW^g(gtCl?;PXg3fLUu%(Y;Gg$l7?A*Zb{_I_Rav zZKi}F$1Cf*4sp%0)Buvn$rU3yIjNk2cTP)H^r@5Oqh&fv4Xb0*vG7yrENn>e zJRMtp%k)(K==5y)$I@HH|Fvyu(zI4w8_vf?3cG>WL3s`tq2ExCxfLr^C zbs0#QCo-^=DVmWgb25yXL+K9V9SrnMCH%P#Nz*)iG&@TnT1C$vhs?tnJt>d z7qOY{Ewb{6r*@_Si`km=&O$YrorUG}au(t=+Y%PP`w}ac7#K*L%viz_ITJ`ko&>T) zng$UQ zQFVUJ#IcreVsK8Plx-qQ=|WCwrP(~ZQ!W}If+snd(OjI( zMb&wlixqp*+$4^tlenQ+9L`N*zn_~Gd!0N4ADD-wzb6mTA2@}rU&<7aRI}Ge2p2zL z+hpr&REZXASS_rOmxOdBj&1XGd98ssq0Cv!R$@RtszlH_mVWnjpuo@b5tH$JrmHg3 z`24nFtmi4MxAG>mdMq)&Rk;j8fWcl}%vff2?2-?XCF{-rP+@)Fi*Gen; ziMv>E*D_Rm@FG?lPVIsL;$a!Pq4`WLcSEM3WhqneVzwgZ%PK|_%dz!nS&qO&;Pmu-u0%B!xESb+s!TI$R%22~&$LCaW1&Q>6KR%GSbx}q)5 z6t;|0745|_kKeKq;b+^&_K72vtR`x7aIy5GT||E|55MQ4(IX_3ZH6JCq)rP%S^9TE ziGBe$TmNV`iRU1Te#=e0Wx_!gJu1u=-zv75TOP9VCwi!bCLChR7~mlm7kXF`-tf?{ z5>%PR?_QaBjOXE3ZFta|JpP+D{+q0bFTH8^*dr`{hbn|0e3XTsuVU3y3j-b=UDZ;j zVoR&C<=?4lm%o->ef6xS(I)gf_}uEWCLx8W=5u=G>T!iFzXUD`f{)MYc> z+Y0OT8FlUI@|j}3aK0{bM^dJ;ku0UyNE%x{<>6L6s;;XZi=Xx-Ofk#rQ6Uw#v4XcQ zz;s%qK4toBn=?E=ci0H;i{Be6l1f<-Vnq&@JFx)?AzuSl@%lC(#f#_R7YLr%kmda9 zMPRCbLt<(IpQ-yLE5EpFXPR?~ZPK=lsL97RVi`Tw$R0r&v7DUx5jg4Cm^c~SnAOYu zjY%(WH|Ch|)oNm$`~PD2Ba&KJudvM#Wm>9ACa}giVpU7UUIJ^KBMP^&{)>5zNNA-* zmB8BPh^(!ZwkEI!I$~yP;*qt`5gFT9|J6iCOlm`TYJ-t7pTHPsQ<+F7r#)e+I{RfkVtjdjF>_BL;v zwT_7HKw@Lfbwp|h>p$A-h@Krugsj1ixYUtE$Xe`(PMxg(Y_cQHKxR1uC9pO-qIqYk zGHbLWj&vqQS*soKb{8Vfn(YXm4QB0jMCiMuXRP6l*a+a4--;V8cSM=4*8gm}lh{oS z#0jkJj)?ANq4ADb(T&x|N8ObCL`ZiQKB7Cxmo@$o_qvmOS?eDW)q}8E^B-}e2MLh1 z{}H`=TK~-eNL=el?Z{dHi7vgU9M%L#ob5%}tPPN8-kZRz5s*02+xqWTKq9gavBH`G ziT!=3=dgA_qE27JW(|QvN?!}#_b0HHKq9K2s))f9Nc`N7bb+@861}4c&Km=Xt5GB< zqcxCvQ^Q*ViSzww=;Td-MC$=mJZ}pmz8OH(`(psB%8dt-GCt>wg~Z{3gnoA*JI6;v zE7^~-P*fEUwU@e41 z=x`Eo`{8U0ZWvB{vL-~L*a#||wILEqMi92ih^V0Oj8x0W1fwO9+Dh|QM55SxH2bq= zL}JZ*7QPcsFxe616_ZC1114jlJR<%3l*MFGluJwmvmx9Rn$3#TD_gTkk%F7ei3p!J z6N(u_1Hob*4rWA3jz$xr{33oVRg<+N5`T_Wp(kl#$l<~qVjEcnI$;6`Bsz^McJi=&JlwXXWLUdT$B5`91 z@yZ()iSBW#s;qU9I8RXCyhya3O2m2lBJs^ss~(Q}tbvh;nnoiYZ($^^55nJpmtfi62`yuhjn;Omi(3*_R zZ#FX$v**}MaCS!G(HyEKZ)hZ9<|-lbRz%_!frZJ8Xx@J`BbrA8hRKX5!7BMLH;*Q* zBU&WPeaLuui0=}pN@kOwT%yN(B^F_@3pz5Nh!{+LA{G$bVCu8Oh8s+MDkV~d&89xN z#Lh%w(qQlN`g?HHSY1#igVE1QVGYOWI^RNeb6YHVRVf`o%_jAezp3 zQ=sU@O2dS~6zIlclC;?rNKFO?E1(NY2;X1@)OIPAZ?FP7vy|jxwgOV+n+F?z57V@RjC1cBWUF`+P){ zOy)ixQFEBgeOA%Z&1CMgimGEW_gPKiFq!+TCUKk0eLg0*$=v5-qHi|$$t9Y6V&(bQ zaM!F&le!|JKIc;*zWjtJ8f=5gt)UtlY=b^tV-3au!eAQ|wboV;-ZtpkTB@>ZlFwNYalRPv@@Aag7S#5pW2jWvTs$uE(j>u znTqhnKlfy&u^Vkld?v6E0t*>Cli5rNEUP{vN@iOju+g$JnGJ^WiV2?+C4<$_tDV2HuABH39pYVkj(~6dBnu+HWNlGraZ#AgEARRnFj129Wj_P{k((3 zVK8OtvXkI*cvGgcI~9F{DO2lRRJ_@gNeyaqj8;qvZZKdvzFVaULAk0QkFkl>^O!2%WRMZi>!}FF+ z@%$(;VK9g4eavQpH;1}#Ov%h(4%PB&Lch=3J01R-L~gKms&$<34fal7+VG3V+2K9( z1j)c)54GV0m2a?zD*6qTZ?K12@{KhbwZUKy6?M`UA8!wJ)rK4Fq1v4y{tWg|$4?P` zvpp0Tex9}sfQ&{_WHbE=d#K@ONKwr8P-qDt45mMF08ZD%r@UgZ}#e;`V)vcF2SGnwtL)Iva-UNMKT<1Ub343<{EU7&_DSX%YH zsNiNxD^d@pW3twWxldoFLN1;U4pDg_HmvH^W;>Z4Ez47u+CS%x4h>I%m`-_FSiyzs|!e z|4i`CKeMaC&wr*79@i@(u^pZkTyuW*C#+uvaEH{7uL<6M*PCW{|^ zla@D&Z?gDzZjxrcmdfG}NTu=6K}Ej zskR4j&f6@!_id`*?AxsVT)0j8^ZYieKTYrWX?0|4lU66)@zZL^YS8M?yQG!x+_h@5 z->b`FIr>CZjE))ZSXdCgh2$u>@JJS?!{N+wM18ML9JzNY^5*v zS^U2D2|xZmi+}z;$?GYP-}nLH4}QSn?|DG@S01qCS9<6t?*?s6@?QDSPu`H#An#ni z5m!}zW4W608*#PvHfEdGVxRr!gJSoEfk2z~G)7Cq^apHLb% zHwh*54?m$mR)bJh{Xs(c>JOHyJbw~bRsUqUn);`$N{?Cm-j4}?+G7^~>|-kbDUaXq zFTx-A7mL5^FT%g_7h8UXCxqYP35)N2;wRM>#tAFO-+oestOlu0`kS~~`!~zgBOs+Z z;8T{XzE251{wa%p{wd)<5oEq~*4D*xnjmjBn& z{G{5-*n#J!sTaQs@^Nl23;H_on+yx$oy>A%!FeZ&alum#S2VU1vKVY}*kX^R(8U#t z#joUa{(CXg`W;h`Tj_NEdocr94Z4vl9dT7P9m~~}bi~!#bSzhY+W3Xiv-nZz2|qqP zi+>@#DnBuRMQ<8F=z{}T^rQe{|9Sws$giBislI~IIRgvdXv4qd;UzK>ev^zW{<4ft z`u5lVv_h62)Q!lP80@4k%=}M8L{hL5ofyQqU`|NBQaPM=Gsq5}z<#J!q-9t53H;Yl zMN&SSem>UOs$71jdQsaZKYQM_F27T~$o`&(mn%T<_61n{jW+xT9$u-SQ*EW*Ey&_; zwc$VU@G6C<{9c7v{2hge{!JcUt1!U_7H08N3KRXig<1JU6mcqB^5I2T{3Au2YAg3U z4{!K7!H2)j_S79Jf!_zQXXoe-xwcPUbo(=QQSlse(OqAdLzMTvgSH&}Sz zHwZqDhu^T_Ig9c5#cX^Yexn%ipRYKJA6=Z_%X#>3HoSNV7JqIjaHEee;Vg(kUjK?r zIZIL|S4o!Al#;~BHXaToNzLF>Rn7UIeX6QcEet+Y)k#DKpQ;+p{}(=0kQv+3 z;8RtTs%-G7s!5C*e5z^@af45l4L0~xy+wLt@Tmgu%g0}@@Tq#+`M>*Ah1&}q?t920 zI$X_z2?n32aFUO~Cn|!ZYw(GRAORSBqG~(;t4~yIY6pW)R2?eE;1gAcunj&@bqQ?n ziK^@Tf8!GsNvs%rq9RH24L(uz2;1NjRgc8Y`$W~MuPS2liP}-$`Ks?Di|7W5yx{}M zBDDbt%IFgXudW+9)d$rKj!{<|Dupt9BUyB6L_8QAqs}(6+mdyBYTnpxOYSqt;#gx6 zKl`0z(YOhTpZ!p>IBbL2FC~jQP3^{Gouc+MrPeVxMTIq^uEsk(t!qX#7ApsRB}Zd-WoC-A7YwnK@DC_?Rpb& zgICiTf+z4^O-=i#vJKx&76%D#@M?OyFAZAY)s&C>)JYJ0S6Q5_WVzsDnK={t(OQsq zavIQ&h#8%n5~E1aypL1gD5{LX$LTV`4L(jC`cwJ5kJG9CG=B3wPW1;+;RYY4B!a)f z$Enpos-D@$>9n1N_i?HcO+4~GPVhNpwed9iIC*d{!+u6t>=;BfG`Kf~#gHnRKcw7C zk~g?N4H!)52KT4b!Os8m73H2olnjKy{prFGD#!2@<+ekqnuf0^pBhRds^KfjEr-#F zZ*ZA9HH^wPcuO@N?)>jxQQi-kafCIvR@E3maD!{rRvT_`ttvB;glTZCS~-#`WA>_2 z=a^=Ps;)3JWJnyPh6Z79oQfVrLN_>0-5EtfH#knkyifRsZz$h+pXy<7oa#H82aq7D^bWAJ+lol4~!zKOhHs#6&dnf;z%Q9q5Aa%Sfz*d9!$OlA)#_;`bz$?OKD)WPfl zrQok}e#$hHIC+)x(=2rM5cI~67Sc9&QH_qOyMZBHK;QaLGY*HJO^HT_XmTu06&M7!H7`~evHOJO1-UI5|9O^qkMh_^} zm}ZYA^{&(4%+z5X^$mlw(l_&nKZCPUWP*)vbXF=Ten=p38$6aG=2IcB@>n`CpNJSd zmf(}g@OktFl<8G&Mr9_(&{{>}VA~lS`?I=`GoeXYA>x6TlnlyN~qX7#Q zeS_Q44FU_Z+fg1ddJ)yZ?0b|)Jh3ww9FV#%RuwTgAf30tyaQ6}B_t-Z1Cn}I`zqg~ zp-WXg%)UqJeY(M|sOvHkCGQb*fxxeHD;m6x1v1a_tjL$tK5o)e@vNP(S`gIiJCb=24SuPh&1XAkzg*U{VS z32yK@`f@#SXYe|zuz}zPucB2Oi2h0bE6e#mC47TdQNpLBmIkk)tQ!e`4)0Yo#fBTa zivHe6{29E8hI~f!U*%Pl>2ummnm@IiU(Ej8IaMtyjIKrWxyMbcO$B~sIrnB#dxLAy zxXmOkvuja)k$a0xg#Xm?m@QOigTK#jHr(v*178}0Pbp({%}zgR2e88E@1qVvU*+l( z{3X@oRjxks?M!A@A2k=5y?pYBh1;kkgR4*4Hj9F>Sk9T*pF-{ z9?bqe`9+2ul*!=q6TO2NH8}lT+d+()oqph7IP9cC44yw1c9I(MolT zCMMGvEpIp2HQA~DE7rMW&Un*TBE@MLWI2 zk!)ev4I{$bI(+yGc>6a!Aqda$F9O#cju!vOBh(q^Nf+lyALj{(^JIwgWQ_ATydV-!0;yi`pJVoL>ug7^p;ygv;Ja5E#ip6<~$9YP`c}m84O2v6f$9c-cdCJCl%Efug z$9XEmc`9!6REpYKY5A=?DX|X6gx;^CjFOMso$(Bw9Uy)AQ<8(~QMyPObuE|Zx;9fl zvvVu%BI@_&x@=C%?r>Z=p-?w14nfo(|J*#e(mM{viqBO(5F-98h-H!T7XU#P@hnza z5IfT+-ma80o3rqbYn+88SYCZ0w<|3#PKh!>2GK01tCmR4IXu0{n!{DfA3AtQSRL=O z=R>SmIEF3gz3=O9r5D9=x(fSe1Ty29vmRU5hkYSmpcqd!-j^^1F)p_ZD_Vib{oD`TYUNPDqz7>4jjRfc7qn5&m`e z^kP5}R|!9;Z+Q{dQjIq$Cg1cBR|O7ITV!~{b;ys7IN8@#M-(omi@e23PaRRAxNDDw zu>=|^cAz^@)GOgi_J;xswZ+%1LW4w)lCCdUXrEH9qby)TX`P+N?okeQ#B89q|Xq8u`*!a#i#fst+I;{1ZjD zqELzmb$!Fp5y`hfGW*=Fz5dWE?*Lj_TafC-FkNm4z3S1WfX(+;WxbWK9WqpLeeGWm z@>i^?E5#p>d>1&VTuldH;UDe^3ly!Y>x?4@-D~I^46Y3g6jPJYENTa>mtR>M+RV53 zE!T2QsIJ{WYus*NVSc!4r$6VYBi;ztem{T@SV*tYLd8BPcgSTj?H#iEfZ6jT~5ip>!-bz^7*e0%h zenJoknBCNMzz^W-*35NIlg^b4ZWw}FxQ_Yd7U7!!@Nr8`$cp~8?KZC6EZSdfb!j77j&?dd6lKx&I?o~R0|&J_=q-vG)3~Ev1}evgg`rgj zb#iTESsT$==K!mEX)B=3>B1|5H$2lME4^>|yUehCQmWC+ZaV+ibf0x`2Z=4+UEBS+ z!SQTY51kuCJK9t4tB7&FmyUs*IJGygRo>+N@vtCx-bYsyo^RYBmyhRd@?c%-aOn~CBk1JPA-bsTp&?Lg z8fuuljt+C3^lu?#@Ymt`5(5GEufbf9Z=^maBedds`q+(tMx(R}`#{mN@O2$MTFb4b zsDv^4RELAh#<6<0!1^B?rvoHMzKi2s{j}26_%;-}#EJ=e6>+lheV|X$SjBHA>P%zr zzcWc^8Zk0VF-X5;oT0tKr@BsP?R62xi>}jjuCAN|1sylt&_fr_FciLhrt4d+47otM z7_WCutjDjj^qE2eyy0*6g5l%&Y*$T9(%7wL&e7)zq_|?PE`0=So9DXd$DD6P0<$W2 zJprktTVSC7>Fux}Q9P09oymY!M(DjgTuQmxLY-cVS}?^lU!+YjzL3StJdR`dh$T9G zTr5mks?$d;Ub2iIW_)XxyXI-KMM=$Ap{okgTDMX!9cPeoHDDk)@R2T*kiT5b#J8*T zIRZ88@@k$p0Yf0S(yk+(eWG)KJO{1ShYwVgGVAm%gwt1@^?KnT_&TEH2G?ajMNyOf zgij59Yw<>A83OAE-^R~evosl_#Aa^N1{WX5IFpup!X8kP9b2^A0$b$NR@W|nUUAmD z^o6EY0_WY0Z@>`s^h3UAj(3E-f)sK3MI^XCBN+kB@!y<*quzDoo6 zpbTbWl^ti&9xXSvhHw5}ec|j2PiD3#SgJ_TMF*w10Eop1ZC|8Vjfy zbMG5U+VH?o5^hdTJk;u`CWs$@)7Kuj(4N`N9hmq?=MRBd|IiD=A*;lnIsgSx>#^%Q zzZUhuOskEcJ|Lr?*x=@8>fibp7GBZaOl*3pPu3``UC;QA=lklpYoI1yIcY4I5~`i1 zUC99YnqY;V@#9|!5Y=d(GgRA&Vlz)z3LA)T(}ij~L*(yL`cUoA3>EEBfDS+zWX-4p zlHc((6Ru2#GOA?`P4QVHfla3vQLqyc>4Swr=0ja(`DmShWE zrO6nDGCMd_I|9KKz{(stOQ=?xb800k=^w}ysx9AficHO|w-*Akz80#T`yrr0UZxDW zK#s`A0OXj{e+Km7Zu#}fU@eCh2=zbD5x9JAcobHi^9qJ)BOF2(6bjW2c2HS17uIWZ zQtrxUo^%KLt`rGW6HNE*Gk;$FYVp%ZHxw?+~c>8!Kq^ zg&f^|2P=lYugMnMg|cs3%lAg+mU`RyNSP z{|PAP!#6{<1Agqve^$}?!~IP_HJu)ctwv4YuW0qq9saVjcT?J6VxwsQ+_E&TrE?$; zFnt}~3caOC8-*}G+@MRD5}~eJB0{y3bfvkq^^SwG`>jr>_O=Rl4h17aFZfpzIl#HJ zRlQK{d>f&i>+AfX=EgM8$6)Nbh4aJCCcdGLhC=l=(uXSCTO~Et0f=$AiLS!P+8<3r z*%vy#;O3#)TN8nU@rNyRA)weU9RN)@+%oh_Rz&b(N$c0h!Vj$tfSYYXwS#G#*Mr^( z^?w=WgBI1+3~DNHw>Qua@1WC1X?5tR4{4~IF`e{IAnV{8-#PTtivHEcW#*WfIxQSk z=Em!^u=JI)m<}Kd`D}Km;gkc$2yIM5*{+z&H?P<^PshLk;8=prFD_-T&JX2Z%cU*Q z`Nb}tRfMu{~O*A)v{yiy-zuv7Rx(z`ffe6>pN-N@F3)wvOKv8rTQR8GUxgf$hU?u(_(eULU_uCz@{1MTSBiPupyu;J@6b0}uliaGJ#I89-a3V$~lX)W=|K^^Ax0GH~#C{VQFykgL!m1`Jp} zYg{1)zTE3|OqVgTHt=g+lw!hh9UzY=d<#y5dNmzJT9Z#|)%3xfsc(cZ_xjU%i{L1; z>x^D^dwvfT=f2gKe2Dh*ce((upx?jOYl596^Em@XzVli|l;UmA1uH7|5Blm63wrZ{ zULyq5yQt0B0=rU|OQG7KuUs1W;CLi@s*SdA|M)T`dvg-J3cFc9!>2^>H36IQLhb6dty%yQKrL3@}a6 zYK~ zucn=kbTBS63;&_7H*hCX<4?n&_|9XV1@D!=CqPM~|1yB@SBwSl@+bQ2hZR`+x31Lc zO~zAZevmKRzImntBq`tZ=gekYLKe($`@hhWTg+x+pXJsL3vg;U>vZ$m?%V0y+FB6h z_AI?y8y|5L$eKYT<%1J#eW`$3lj!ntsgTL7oonHE-Y~OX2zK-iS+qjbYoC}vw|24y zB7t|dK{^ID$h@p>?e#Bq99Ucg`gUh?kJ2<7rz2?@I$~bpr&!-n|?+`EhZ#b}wCI#Hu zau)%83c9t^H3W<*N|0#Y!QX>vg&11HtsjEPt{YoZYd;?h1^UP))y%i%E%%4IR?C;6Q^WOMh-}Y~ z(Ah?n{k*o;s%o_j%^f7p)G^SzQdb`!u%M@rIsm81{Phi_N;c3(Cv5R54K<+%b@J9o z2jI)PcN^=ZkgGvW-1-TPeA7L(soo868jyydzSYg#vo%2@7+%c>irFpn=EUOg-Pk8B z^}!Y29Bydk<~JX({0kDNTWfXjt!U#mywC${Uafy4gL&H-2I|oEI)-H0o7`m>sA!W8 zy5ggvbnmEhjev2T3?+%qd`SWhA+^~>vfzXBmcg35@cBzmA5= zG;n}EwBgJO_VL;}2}ykzt&@^tyKmVb_Xe&0SjfV`dLei~vSEl`e=G!6>{^Rp{m%~5 zOUE+49tM-@{o#6_!4`NnLZ^pf1IzP35%QjnhNV>)rSpfQN5uPj&&Elp^=M6OJ~+10 z$6921+Dvy=F=?z@v$sL$jB)z7g6|Gjjdw5MB|UqBE@>PfSAL-JrRMO>6ZI-$>1g+T za+1yh&hV*|-P#LZ1Z0hKYcCKHP+_XJjM_HMB$#)m>jMRH1uqq~Rj(~dZHs^gDa+z@ zjX}VMS%xtO-a7~SPS19a)MSi3W6>PF?NM7c%ry`^JWrP~E-f!4=xT|iew(ieOYQFA zU5>t2mv8ys6ne{}U>Ypcmvz|r+AY!xSMOsM>i}FjPF=zPU}zP~mul0Bzya>_Wp4g$ zA{-$H`YtYachh8y{dLv~eb~o=Va-ZikFg}*M|v|O(+5}S7$}%;SL?lAjc_g)N^gIx zPq2svZw|Hn4sQCawK~s;QDB|k0=Td)vtDNn_h|4oF;LXqpbzG#HO)UYpuyHo8?)t_ z#uxLMd%wS+eegO_cVI(?KiX{2r7yO)&-pFLg?+fF4+nD1XnpDCUm}*-=H_1_w(;rv zFyyNLc3q4J7_~zOpiWHPsp~6tFYhkB3#009-mL+AaLN(vFFds~gzg*YyS>-lUIXLF z$bw{jD8vT*GDYVLYx4CzeIbp2U-s(&9M`Rbx)@Q3D;&~lqDBx{K5E-M+?zH1is?bJ zcXvFZPXZ{$VMq1ejp_iN3t%;W%+P~Yf30!g8+@GE*2%u$J9feyqsbQsVzBJc)Llx; zxA>I1zsp}waSh+b8KzYp0X@=%Y5PV53``%U?W$}*Rxu?YY$H?9b0)2sSe!BJGk?H} zGGGR{Fq2+}4ah1sXAa}GeGy_;mN0IqkAQ6AT42~#wzS7t!QTC0P?&afg+&5X+ssIS zFF1Rc{wf?%0FqTK2@ca51EK44glRLj4FKb>oVwf*`eQDh4*`IzB3&L`CWw~pwJ`16 z#|D5KTi!5j>_%wadEiW>9@09nPVH*}pvv=zk+XaEQj+l%Yki*?;rLPtZu zzLJKv{koKnfgAuJNL(+i7X(nPPeEF9@poChGB{#BE~l4a1G0)76&N&Wk(;d|gGPWL zU&%^gx;;Nq9q(fB*Hln;dMgCOZVoh|qbn0nj;W>!T)CX?tDZd~2OB-K<9n5TIIV6C(90L0WThp`M{i z=jt0+13*^spn;Bttle#>quBsBo@~r^r`jU8DG<%k#Grcs)oO%93p6v71^{>|+FT#Y z5N&)50~!FJgS6D^hK;hKH4t6VDoi^`x1k!72w2}nFU><0OH{?WDINW;PAuO|a)Sf|pk;eylJ`408>*31^;o?c}utxp>FpH=o7W4_zj)HKl z`qy~yCg0mPOnaSy(Ch<1aF_aprT7y?=)I^gZI^DN@qkJL4Qw=x=1KXY2Zf#0`X&~# zV6aBZ2Y>=vZXE1>7;2c(e;;N5WE)`slpd)Apo|C+@m`qrHXZe)&nQiie6ZWoUqN8S zW{wWij;*i`AC1vhGYHr@*1*x3aXLq`3?DRmb4^fK`l1P8+9nJEdp^);`BqE}(@!fg zcjn0kG8Lw1WCUWgjxzw>pK2hrY?`i^a!cX+e0o@|jQ%Q%lBzj3%>VtIFL+*2EqQ zw`ZLuGBivV_1Ei6V^eqE5T?IumoIE$KGho&cI?f>jE#B?lVFJfcs+|ad2yxc8G$nA?|#kR|K2o?=Q81$aiv^Y4KWmyC!dCl@Pf@ zmj|+%)EXML+fHVQAjOE#k-PLACZ6rV%dZsw9*)fdUuf}v!7Zd^pzpK2VKX=(gJ)eb zy9KT-R;MsRmi>SGK7AxcwodKW>xxQz^?)WzrC)y?)B%W*z> zrMDXN%Q|A$X`Kb^ut(46^+dqAZ*|FF8L8hfDkHyW@b~w66Cg&eb6Vj(cz0rW4|6*m zygaJ?gWkuHt$G)9e$kew?M1B+)x~>V(v?g~-#6~ZFdrx1+G6q*t$Eetsy^v)HB7S> z!H)RxPx{m=nf9KX5(8_}6W5rYEzf1aOCeDFaosRP1^p7Hzh#iUr{WEr9?GcEO})kl z=#gpwjQ&*v_*&dzwsS}ZKGL8aOW^_N!8^JJ$g$CP>26qGP1M*uu@|nmZ>amW2l`PE zvheLgodtZ6a_2XFo z425t1J4|y5M2w41_3np&+s_QLg6~QM`Ld;jmDdC<@fOVR_^;Sh_gGqIsO z1!`;xtk{$wkN=9p2W9Xa`POFhEY>87+MW=smF$D1AR`Ak4{y!s(N~)CD_BQzY3vAG zX`Ici(N|68`V>aUhj~2O>KI?N{P~*4|0LRXGOtHJn!zckLVl)nIl5FY;JM;2aFlER zf*$Qu7XgzCd9>{>0ul@BEg{#;zKumZx_J{Wre=jO(vr)sRYg78u1;Bjyy4MK=Aqin z#hGFr?v;KWaiO@bQrN%ml<;Uvd8`P&hR<1wsith<5~V%bk_o@iQn!ppJKslojmk2l zr0}m?e=oi0R?efn?Z9^}VDGB+1*A8=f=7R&0`z?G6+NG7N(?BlXmjcEL^WC&sukiJ z?DpuJZCtQ{|A~GuB0t%e>S5^b27vXZ-oiK|Z{u5xA|t_B;f7AUG(sQgQ2RF5)>*)}hzINF z)l_q6Sy;*bRM*3A;I2m+`YSs8XQ{6%8J3o}fi5CcGFL;7wrNK|lSVoKt@xrEGh>WA zsU6uwn;X;+J+-MW16hV|aWjwZOoWF+(^@dJWM5j?(xb0dq;`PMYM|KNiV>Ua&9HCM z1{z$=o^4}j(O=qX_4mOC4|IDz>@lym*M@1elc~``FBs>W)*ba0K~mA3bPQw_KBWWZ z<(-)pmY+L?0|Ebu0y*%(Il>few7`pgY85c;5( zUI;2!rapR0;v`nIuSYvE#Hqf1KfSH+{d&77UBPfUF`&Obkm15|`T%|OL4bE4quo;R zK8@xz(g!x@x;YcBawZM-Xs3qQlHgAoD7FmI=Mfxab_~^PgAxNnJWcm;$oP4rCv@{-VAocDTZA|pJ#xipbR&K^Peqi&h9`Bi_ z=`RXu>I9u7+t3(J+&=JV8#_F4+b~gY7F=|EGf4;F?D^YdZSm{_48xYUN6%a zrs>*<9kIi7g9r!C(5EPr*65kq=%mh-M7&-H#Mn7YZ&<1EzT>k!!!-F~lh2)_vxTRl zAJ5g*3NiM~)4LUxemcRUZF>;|j2Z(I7ig@lIk~{2F@ z5A1KNJ~E6@n^qZWw|g~HJNZrN;~(oC2yE7yiJPA=^Qhe3+*+e!U>!m>c(RI&>pZ&A zh8#SD*XtbEX#V@j7rP>M@hjfi=+Somay0OD{mi4k6hqg^shd37AurA};G5zOxw z@fhBgJ=v=7t)WGl3HO&C?OiF3G_|(rYdqA|<~|*O=U&~n>q7(zYWxnp+OV%|Cf4uN zM;pZ0yGx%f5b*tOMnmPNoxz?LK6kiBQ%3xHkS{RF6RCAcBnTTRZL5f?x-~^tRh$`9 z_UQm5bz;BHG>#KD4(J^b0WdEGirk0v?K6HxHveHmK_$Op3X(28Zy(WzFw~;9M;XKD1>1UhC>(uVI+k2 zAdG_WK7`Q_#y}VgVH||<5Mm)rfbao?i4Z11m<(YGgg6LOAxwiX9l{I5dcp4s*7s5RV_aQuh@DRdp5Ppa72*Mu_{)F%t!e0=cK=>QNQwYx>Jcp1rv)3`} zLkKc*$|#+T(#t47Mj2$3QAU|$lvzeuWE3c)AQ@$qQ8pQ6mr<~ca>yvBjB?2+w~X@0 z=rtMTl~Fz!<(E+b85NXKAsH2xQ4txvE~5|`6_wE&GAbsc;xZ~BqmnWzC8N?ZDkGz^ zGAbvd@-nI*qlz-BBqNuMLS^KZQJ9Q8GO8@2H)T{sMpb20O-9vaR6|BJWmHQ>Z^`Iw z8HLLzLPoV^R7Xa2WfUo+dNQgnqXsf+D5FL)YAmBBGHNQLW-@9nqZTr1DWg_0YAvHS zGHNTMcVyH~M(t(PK}H>A)JaC2WzZ~G)YF2Wi&-baWa}JqiHglE~6P%5{yLFfw9iyG2XHb;;pxw3m=Nj zvoq!W=if0UW!&37-g?`L-3|Y4=l8Y^rvIE29vzXx&8qO< zdXk#FfJVK5!ax6U+$hx~2N57Dd^X)^=XK}xlK<8#jp z@WS1sWNUgr;wA4QpgOm9I!KurU_p#jN5MbCQbeaeo)-Bu``*Z>lO9)#%{b|?Yh0FF z*(crXKIw75dl|ROf8%s307d4VGbrBrFg8RY+@3J*cEJ(FvTqA0chTXPoIE_}P(TRW zIZ|gsbSNM<+*_#=;U2PNQdtJT?eh!3*L@2R+5m-4!quznlFCxvh;&!K>&kZj!wlBwPy-Q%q{VzZ10QcXVVSz6ky zQ8NQNLYAE^Ab+)!Qn_BOf=g_GR}){?TW;b7nvWfT6&j{Qz>#^P7wV9zZ-jlWg36P@1H}uYco6ssbpi9k+e46q&Q}%7i(+?$m2MvxrX|L4Yq;KWp9ndbw^HPQj z-iLraw+D2F77eD4+deaLdxKeRlN(^`j?E-h6?E_BBvA7Pvmzn=@Xhz6%2$x7(Qr32 za`!(7jRC>V){j89H@#ze#ap#vlR-Q8hRE8>=;mg4e5ngeA9s8Dh?_}8B`6j^Kq6_Sd>D!k z&-dnl2Zi|_q^K57fjb3QA+x^w0h-H*~0UHc=d!bz% zij%A_0PQ3If%7tXMgMw`kq6)jRF7ha|Vi9&!@GIS5x;^oc*$qQ9fh z_F%{CbF-6YpKLH|Z1Lcm?}HMyZ!c5YXMf`ggmVy1LP+lVZpRLt+IML%=|FIUZ~8m> zJ8rFl*t)e0V&^RZak^V`Ax?j5Cd2`^ra+t_Hgzm$BHU$69VTxwr4CfjnNxepn=GlF zLyoe9(9vDHAvm$ zNlhnjVjcZoOML=$0y241AE=vrsW;V4{?yCrraZV}oadlHD^?Fx*owREJ03F!Ueisr|TWM{z2C? zx@LmWO#X7vwE$g<(X~8XE7P?mT_fq*jIQnEwF?mHMhQ`L9ZJ_Rbe&AsS#(XLtCy~8 z>AIP&JL$SVThfRDFjhM=h=(|Ge-j;Vy_U2E;%pfUu7xfsr-3F0k z16G#zUKk#4l?nNja%8e|9Dv~sazsw+lf6`2FbF-&-r+0B+aA1cFYhB|RQL->=##zl z$%olHpOG0pE$J;iCf+LY_WYNyS}3fPhuO;~PmjFaC;Po453`qT`cxteSNT#P@iZj1 zy$y+3`eYwY-~Cx;f32kVE@Ut0v`Tsd$HrT) zJ8zcomWP|t&d*AC-+`MNLDNe(97F3s?trN!yvrad+1a#sQrB_uR(;F)QBLpL@$uHU zEXmL;X;&sSeQJ5T$HrT^lj=bHrE-oI7)R zcg~KtQk*e4z2iQNxAw}LT5~}B67&JwRFx^a;O3O`M$V+!bK|Yi8RQnDVmguh>97pm zYzyP9;ytjZIOOU|uE^T;9@sM`x_8L%m=H(ne%*WZb`;GrOkSI}4jIsU=%C&MLq_%< zIxH$?P>91-(OuD<#nEcSpg~cC`i96P%vzyW?~%O+#te?`J!p6qpqaFE5iFA}{NJJx z&004M=^4`tO6Waoc=eE8F(Y~m>>X0DQ&^3#&}ezZIMglgU6_D?ERODjhDH5@35bJw z196-07trbs(MJz!6P8@URx}eMN!^>C7^z>4T`HlVzl42 zyWg`v{Qr8c;E5DsSW?X5dM#JQ5*j+2EM7FBv6K~_VfRR_aER`{{Jk2wx#~RHci!k6R-&`tEkfL zXS>G#NWeP;jx2H~eX<-T5UWS=ooQ*iK;({yY{7BPI$1L~w*_T{y-6WRstF+p;Co+& z%X|1qcxMP*cBQ2akqIyG2fn}`3(24D@~;0FN~*UM9=?Ihm;aUdhZmW5L;CF9X=#;S zhWAG>7IqQRm+kf*mBcbUg@;wUz5Q3gL!UyhLca!>J730he))oOO@NQvla?lz@Bgs5 z^aZ>#z@Gy={AKulN^^0AB@g&&&8RHa=+mQpn(g4Ecf67vfBp+mcxdLDFu3Z%R%}>-aJOXWJJHUIh5B z0Pp-ScsqoLK-yD)mrO}ZYxgoda#A{HQ^-;Q5@jJFut)CznNZnn)oYA293cP?2YAC2 zZ#__uA|-plLuJU^5&$nab~bsz(PV)4N%78*+LU26JakF%uE&Q1@X$WRdlVn8!$X@C z@2~if9u{gXQ@mMLOPu2HP(Q_679Z-u!=MyzBYfxu4?iF9_Q!|G@bKY5?+koc4G#|v zde`H_0eDC|=sk)L*Wn@TkoQ-7NDoWKCWnxnf&p+}|B$yVc7eL^(D9JB5kB;ShgOHY z{qbQkJaj$eoq-Rl;b9Eml8yB;47z{9xjyhrijIz0Sw-TNy(q|fMZ zEc%%qio-+AU%X}4$nxsK!^K~`jqsrtJp2w1So_KFknM(d20%uxft8Q7cWu0tDLwp; z)KOOYSj*vbRD}~n`2(5ZPaXxx^qPL@-7p=hd{X6?$}4~JyuyP2mu~r`&!u#yU%EWJ zkonV{3|2FMO-QP}1f=&v_>R&uQM%1&JXB5Q5AsXDK>36H(r-|D2&FrMtEd{>vH@3A z&E$$r{(htMa7u^L@b|a(!3J20z9hqZuzr<4n>Aa8h5XWO88(0eDfzRjZp*NtU%D;B zMt#lH$gqhYf-S=qlx{O>%dm}Kx-G-@e(8bGgVQ@6q#^ehc?*P(va&Rh4Iz)& z@JAP0uHE45O#b}kD$msLM`Fl9#J!sqV$_X zi(RE^8jGrs2t#@UN?#r^)yfSTX*uo(5nl??AxAfvZXXX&vg1Qaw~q_#^u?4;#|L(@ z<6}y70I;GnO zBzAf|O1BS8?DSTYt`1P_REOhTO1IBbY)F4fkF^h2Y`{nY*at3l`b0{%4`A%{*_3V{ z$k^$NDcwGxvC}`HbULv4r~S7Oz&^pT5%BmHl+ixXvD1%Ie*1*SPXC_L?Gqn6{U=Jd zPk>Z9lz*21_K}c{@PyLsBO*IJ1563Bci2Zpc6v@q$0H;=5w1lj-9AyWL1krnED*Af zm~6nCgkT>z+3B?@-9Cb{)0L2+7bSMGrGb|fnJf+)b zT6X#jO1ICr?DRxRx6i!n^p7ZgKb?g^dJ%+uMu4yX0bu%0@8&J?4CH{izBB=?@SAAt z323!nx;+6+ra{wx0-EBNZcjjQe(ClEH1(lj0-ENBU{65tl#a>-m9i(GIezK(1T^0- z{Wyq5P7Ehz|i{w@5c78Xdm&*6= z^xBjj>&mYH9{}g_*MI<UPZBhlq0>2?!L zqx3zLe8;7pPio;0QCuQgwo&5 zpz?=P`Wcxnhpd%h3b3C57YU&lF@#$|fTdFUERv92{3A-wOQN1gbe>XrU1Ai^82}at zOJCVEO@nF3I!Xw^1Ssey2`3R6mtGZFlJb|L{GF+3s#Cg~(nClRa>potfkbB&(dlL{ z8Qvs>L?UF1I-JtS`PH}yr5DVk7@CCSVM|Ksdz>mAPeuUNlhTi7QR%q%h4i5ry>1`8 zF^C;&6QurJjR;Pn^ySoccFhwYJqIjW>-w#51?3N*n%l~~l+w3Rx-F?yDm|xTIn@M@ z6`=50-lE%Ku#PQFh`l_D^JHRd6Qy?`g0E5fc1r&uqsrfx(vMJjJ8D`yJpkAlO0P+D z3RC{FDm~WmqgxTOmGc}S3?@pEe)8W9A9smXtpaRuwDj^i1^k$?~cz^}4u26t%qHw<^Dnt2~kxsQt`d~-A z)zs&gi zSi`&}lH;w``9=;KL|{kMp!XbodJb2Q-Z20kk}oQz>wDd!hCBLoAJl7LZ&e-n=D?df zCEhCU?FezTZNn> z-t?wS>thx1_JhX|Z>fY4Ru1n;NXhP93n{^Q9QCR3X~(q!+Q1=l%$!wF1b>n#llMO4 zYVTY)&nB}!-s+Y$B$sMQXcxyK@9{rktn!(LsiYd-%{`}ERWc77qn_(0S!a4%m#x`f z#Y30{VK#&ht-W8(v2xZVpIVvq$2HEvABLo#Ye6(mMhP;SZzYAKUtrze5nRC0E-Yzn zt%lw&6F%$cE?PMur1ao_61P#}ikB08cFr;{<=mtax2`DD?VaVXC0fDht*WPmH#giC fFaDyCFI29}G|p}U1}8wcc>2r)U+CH{tF8YBVd~4< diff --git a/include/printf.h b/include/printf.h index e58f666..c67832f 100644 --- a/include/printf.h +++ b/include/printf.h @@ -57,8 +57,8 @@ void _putchar(char character); * \param format A string that specifies the format of the output * \return The number of characters that are written into the array, not counting the terminating null character */ -#define printf printf_ -int printf_(const char* format, ...); +#define kprintf kprintf_ +int kprintf_(const char* format, ...); /** @@ -68,8 +68,8 @@ int printf_(const char* format, ...); * \param format A string that specifies the format of the output * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character */ -#define sprintf sprintf_ -int sprintf_(char* buffer, const char* format, ...); +#define ksprintf ksprintf_ +int ksprintf_(char* buffer, const char* format, ...); /** @@ -82,10 +82,10 @@ int sprintf_(char* buffer, const char* format, ...); * null character. A value equal or larger than count indicates truncation. Only when the returned value * is non-negative and less than count, the string has been completely written. */ -#define snprintf snprintf_ -#define vsnprintf vsnprintf_ -int snprintf_(char* buffer, size_t count, const char* format, ...); -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); +#define ksnprintf ksnprintf_ +#define kvsnprintf kvsnprintf_ +int ksnprintf_(char* buffer, size_t count, const char* format, ...); +int kvsnprintf_(char* buffer, size_t count, const char* format, va_list va); /** @@ -94,8 +94,8 @@ int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); * \param va A value identifying a variable arguments list * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character */ -#define vprintf vprintf_ -int vprintf_(const char* format, va_list va); +#define kvprintf kvprintf_ +int kvprintf_(const char* format, va_list va); /** @@ -106,7 +106,7 @@ int vprintf_(const char* format, va_list va); * \param format A string that specifies the format of the output * \return The number of characters that are sent to the output function, not counting the terminating null character */ -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); +int kfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); #ifdef __cplusplus diff --git a/include/shade/cansid.h b/include/shade/cansid.h new file mode 100644 index 0000000..946f76d --- /dev/null +++ b/include/shade/cansid.h @@ -0,0 +1,26 @@ +#ifndef CANSID_H +#define CANSID_H + +typedef struct { + enum { + CANSID_ESC, + CANSID_BRACKET, + CANSID_PARSE, + CANSID_BGCOLOR, + CANSID_FGCOLOR, + CANSID_EQUALS, + CANSID_ENDVAL, + } state; + unsigned char style; + unsigned char next_style; +} cansid_state; + +typedef struct { + unsigned char style; + unsigned char ascii; +} color_char; + +cansid_state cansid_init(void); +color_char cansid_process(cansid_state *state, char x); + +#endif \ No newline at end of file diff --git a/include/shade/kmsg.h b/include/shade/kmsg.h new file mode 100644 index 0000000..9f57212 --- /dev/null +++ b/include/shade/kmsg.h @@ -0,0 +1,10 @@ +#ifndef KMSG_H +#define KMSG_H + +#include + +void kmsg_ok(char* format, ...); +void kmsg_warn(char* format, ...); +void kmsg_fail(char* format, ...); + +#endif \ No newline at end of file diff --git a/include/shade/platform/drivers/vga_text_mode.h b/include/shade/platform/drivers/vga_text_mode.h index deb00fb..abf8f2a 100644 --- a/include/shade/platform/drivers/vga_text_mode.h +++ b/include/shade/platform/drivers/vga_text_mode.h @@ -4,6 +4,7 @@ #define REG_SCREEN_DATA 0x3d5 #include +#include enum { PRINT_COLOR_BLACK = 0, @@ -24,11 +25,6 @@ enum { PRINT_COLOR_WHITE = 15, }; -typedef struct ansi_code_struct { - int flag; - int color; -} ansi_color_code_t; - const static int NUM_COLS = 80; const static int NUM_ROWS = 25; @@ -37,19 +33,6 @@ struct Char { char color; }; -static bool is_parsing_ansi_string = false; -static bool ansi_string_found_bracket = false; - -static bool ansi_string_found_semi = false; -static bool ansi_string_found_m = false; - -static bool ansi_string_found_number = false; - -static int _flag = 0; -static int _color = 0; - -static int working_num = 0; - void clear_row(int row); void clear_all(); @@ -63,6 +46,6 @@ void set_cursor_pos(int col, int row); void kernel_msg_ok(char* msg); -void handle_ansi_code(ansi_color_code_t code); +void init_state(); #endif \ No newline at end of file diff --git a/include/shade/platform/interrupts/isr.h b/include/shade/platform/interrupts/isr.h index b8309fb..26c1fc8 100644 --- a/include/shade/platform/interrupts/isr.h +++ b/include/shade/platform/interrupts/isr.h @@ -57,7 +57,7 @@ ds */ __attribute__((noreturn)) -void exception_handler(isr_xframe_t frame); +void exception_handler(uint8_t vector, uint16_t err); __attribute__((noreturn)) void interrupt_handler(isr_xframe_t frame); diff --git a/include/shade/version.h b/include/shade/version.h new file mode 100644 index 0000000..b2db027 --- /dev/null +++ b/include/shade/version.h @@ -0,0 +1,9 @@ +#ifndef VERSION_H +#define VERSION_H +// This file was autogenerated by the shadeOS build system. It should not be modified. +#define SHADE_OS_KERNEL_VERSION "0.1.1-alpha" +#define SHADE_OS_KERNEL "shade-development" +#define SHADE_OS_BUILD "b4e6b2e" +#define SHADE_OS_COMPILE_DATE "Sun May 15 05:41:17 PM EDT 2022" +#define SHADE_OS_CODENAME "willow" +#endif diff --git a/obj/kernel/kernel.o b/obj/kernel/kernel.o index 8d4907e194d27602112edd29e60d232c6b0da250..feca931de56bc7abc5b1bfb64ca57879f54d3d4e 100644 GIT binary patch literal 3360 zcmbVPOKcoP5bd=?V*CjvZ~&P@6J%sd@nqM|2MmHW%O;NSC-O(;zs%0Iy(7<0-MzLq zLLveQ3Ap&c0dYYbxkYdR4he!oIED*nUyzVMLYx2z5vpdo)%Y1x#!>dd363}^>=o4C|9Cev0UwYAZKb?qc{w+9CtRaPe8sKoRap;^3z{HvE(w? z4?^1zfft8@>zI$dRD5FQT)5hF8v^w3;Xzh(N=|9Y4U(p7;uU(ASNXP(kqG#Xkjjsv zEOsUwR~R_RQr1*M?1&&vLJ?`U(uyKK+T#D_W{Fo@5npuMymXcq&(5AH&6dvd%Zt4F z>>8ggPEWJ?8F8*YE!eIf1o3WG0ERKH_{6xHJjG!Wi64jtAcL6kQsisj4g4zrHGGu> zkXyApPbjt73W9b@0$wFf+S1=@YChpjaxtTY8>STsYY%c zSILjGkg}C%T&;_h$}FF_xU@dWOVhC9MOc?3&^WZapn-?=vBFn~kra$g-{k$qQMj_g z3VWl4vEgF_``~;CaBL_q%N({2@Q;cMz<5g#CKE8>PSN zO~F`yI-7q+;D#1*IL+}sgOg1?&ZUs9j>$8c^_*~H(?P*c6#3lXG&Ee}6J&s%t_}20 zLnA7v9hnCW`NT`t3a}+;-S{u`z`Y*0?18`81An&%{(cXfWfz9yv8DCJT4i}*HC0xY zH&&V7(Aa8;mTX6OI+n`NYrEO?#I_R&M|QKuwnNvCm=9lfO=(vP<^{r)H8%*D61wKK zq>5!Ni4{vDYAF~8v4lvqRg1S-;(IkILN`fkx=M)nl@ymw)T@U{E7XJgo zKj^{#C5PV&I@SFnho8vd^pw+helz)L9`-tLlcx`QY$Ro)5Ki|Bb5uLjZXJ3pv%X21 z6oKn#v8S2SaJ9>vx>C%Mab~~^oWAr<6l>vZMJ)%O$3#fo1`bAwW^EH*%g(1pL-}7l zhbWx-Q7gJ^?Bix#$ZeTy3>>&tWg?h<&m_EM77N!IW9c4v!0_-pM;z~nye!#;jMIis z0ggHC_}2`P<6+1N&*`X^pdmHCr-)PhkmaE9BM0SiXc%8aBPZKtK=}UUg~m_)GvGW1 z%__sLns_^}UH?Uh?Kb{vCjR++anS8}yob8&-@N&Mfa>G^VJyYd{&lMjG&X=qLB2H; o9ReLw+VP0ldj}A^Wgd`^(a4EO*FkW5yT$LB@i#imOtItt24Ak{umAu6 literal 3904 zcmb_eO^6&t6t3BgN!J z?oL1y1b=SvqJjqxA|CZ18WEC!pdcuC(vx~n2q=o+K|zi0b=RA%RA%-dKB%tuzW2RX zud1u6``M|Jj}FypjHJdMV(S*6&~IPY8>QM{I{;ib@$!$C(Y}a}tN6HtkITdGD9yg! zTQ>CW+|c{K#9nyn6DjzRW4T zlcmi3#n8qne)o`xGChLHL1wa8$3YUG7ZHzQRU|=|t4QJUJKg;Lhb1e8>iOE>bbS*3 zn!!gadN1Ew2ImH6hVTyI#-;=GuE86W$Q1M;;JbQNn|BM?r_hKBYEQldtsnj^;Fzb} z8=#ka0Qw&W@P8SA|2+WT2KDwEclQ9CRhO9;ve|`Ge)IIn`BIrXeRiJ3$}a-AexW5> z5uwhdGFAJl)h*2mC`>Q;RqrdI{V-2*$(GX~&RCd;K>9(Fu+r0!Dwlqd_p+p_<>$++ zh(lkBG$_i^0tL0hRRimcg7*Rn?i1X@v@h{1hJyQW7qq{wRdNr4Ac*7HBK%$iK`0O1 zBS$LuhCFmvxOpZGpYq&g?8gS=IcMQ^J-(CYeq+ZL-<}tqR}{CNXDYZiuj9sk(e&>6 zylUZgJ#RR9rj7l!#kcEu-@@%YUs?EW&}sd@x9~eG{8tON^ZagbTIZ*Y{ins>13Kj& zHunY2_f`wvYH-T)l8N7C@$Gr-arjpZfByjfL5IKB*kc3u4_o+NtIin<$NwOj*Q~** z{#9eoTYP&R&N+PiHljop-=1&m@IN=U8o=jeHU{76su`0t9Gvc1=BaL~gGK1Itok-- zQY3+=#hPYb6zG6?i%Kz1=9K}zf%pT*ye!wkYh@h|ey<84br(1oDcb%L{B6-qL6!Ws zoyD52Ear0KubTKLz-NmazW~mD@2|bBilXuH{b4M{)BEc;Hbyou tDM(=o!*uv6-1wUTbKf06>^-v~570s(2;!J!&VKQ)oBE?FtoL-|{{{aJb@>1Q diff --git a/src/kernel/cansid.c b/src/kernel/cansid.c new file mode 100644 index 0000000..93aba66 --- /dev/null +++ b/src/kernel/cansid.c @@ -0,0 +1,114 @@ +#include +#include + +#include + +#define ESC '\x1B' + +cansid_state cansid_init(void) +{ + cansid_state rv = { + .state = CANSID_ESC, + .style = 0x0F, + .next_style = 0x0F + }; + return rv; +} + +static inline unsigned char cansid_convert_color(unsigned char color) +{ + const unsigned char lookup_table[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + return lookup_table[(int)color]; +} + +color_char cansid_process(cansid_state *state, char x) +{ + color_char rv = { + .style = state->style, + .ascii = '\0' + }; + switch (state->state) { + case CANSID_ESC: + if (x == ESC) + state->state = CANSID_BRACKET; + else { + rv.ascii = x; + } + break; + case CANSID_BRACKET: + if (x == '[') + state->state = CANSID_PARSE; + else { + state->state = CANSID_ESC; + rv.ascii = x; + } + break; + case CANSID_PARSE: + if (x == '3') { + state->state = CANSID_FGCOLOR; + } else if (x == '4') { + state->state = CANSID_BGCOLOR; + } else if (x == '0') { + state->state = CANSID_ENDVAL; + state->next_style = 0x0F; + } else if (x == '1') { + state->state = CANSID_ENDVAL; + state->next_style |= (1 << 3); + } else if (x == '=') { + state->state = CANSID_EQUALS; + } else { + state->state = CANSID_ESC; + state->next_style = state->style; + rv.ascii = x; + } + break; + case CANSID_BGCOLOR: + if (x >= '0' && x <= '7') { + state->state = CANSID_ENDVAL; + state->next_style &= 0x1F; + state->next_style |= cansid_convert_color(x - '0') << 4; + } else { + state->state = CANSID_ESC; + state->next_style = state->style; + rv.ascii = x; + } + break; + case CANSID_FGCOLOR: + if (x >= '0' && x <= '7') { + state->state = CANSID_ENDVAL; + state->next_style &= 0xF8; + state->next_style |= cansid_convert_color(x - '0'); + } else { + state->state = CANSID_ESC; + state->next_style = state->style; + rv.ascii = x; + } + break; + case CANSID_EQUALS: + if (x == '1') { + state->state = CANSID_ENDVAL; + state->next_style &= ~(1 << 3); + } else { + state->state = CANSID_ESC; + state->next_style = state->style; + rv.ascii = x; + } + break; + case CANSID_ENDVAL: + if (x == ';') { + state->state = CANSID_PARSE; + } else if (x == 'm') { + // Finish and apply styles + state->state = CANSID_ESC; + state->style = state->next_style; + } else { + state->state = CANSID_ESC; + state->next_style = state->style; + rv.ascii = x; + } + break; + default: + break; + } + return rv; +} \ No newline at end of file diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index c7144de..aa7f52f 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -2,47 +2,43 @@ #include #include #include +#include #include +#include + +// Welcome to Shade! +// shadeOS kernel version 0.2.2 +// Running on shade-development +// shadeOS willow, build b4e5b2e, compiled blah void kernel_welcome() { - printf("Welcome to "); - print_set_color(PRINT_COLOR_CYAN, PRINT_COLOR_BLACK); - printf("Shade"); - print_set_color(PRINT_COLOR_WHITE, PRINT_COLOR_BLACK); - printf("!\nshadeOS kernel version "); - print_set_color(PRINT_COLOR_YELLOW, PRINT_COLOR_BLACK); - printf("0.2.2\n"); - print_set_color(PRINT_COLOR_WHITE, PRINT_COLOR_BLACK); - printf("Running on "); - print_set_color(PRINT_COLOR_YELLOW, PRINT_COLOR_BLACK); - printf("shade-development\n"); - print_set_color(PRINT_COLOR_WHITE, PRINT_COLOR_BLACK); + kprintf("Welcome to \x1b[0;36mShade\x1b[0m!\n"); + kprintf("shadeOS kernel version \x1b[0;33m%s\x1b[0m\n", SHADE_OS_KERNEL_VERSION); + kprintf("Running on \x1b[0;33m%s\x1b[0m\n", SHADE_OS_KERNEL); + kprintf("shadeOS %s (%s), compiled %s\n", SHADE_OS_CODENAME, SHADE_OS_BUILD, SHADE_OS_COMPILE_DATE); } // kMain void kmain() { // Initialize screen buffer struct Char* buffer = (struct Char*) 0xb8000; + init_state(); clear_all(); // Clear screen set_cursor_pos(0, 0); // Set cursor position print_set_color(PRINT_COLOR_WHITE, PRINT_COLOR_BLACK); // Set print color // welcome messages - kernel_msg_ok("Initialized display successfully\n"); + kmsg_ok("Initialized display successfully\n"); kernel_welcome(); - printf("Copyright (c) e3team 2022. All rights reserved.\n"); - printf("This program is provided \"as-is\" and no express or implied warranty is provided.\n"); - printf("The full license can be found at /sys/LICENCE on this system or ./LICENCE in the source tree.\n"); + kprintf("Copyright (c) e3team 2022. All rights reserved.\n"); + kprintf("This program is provided \"as-is\" and no express or implied warranty is provided.\n"); + kprintf("The full license can be found at /sys/LICENCE on this system or ./LICENCE in the source tree.\n"); pic_remap(0x20, 0x28); idt_assemble(); - kernel_msg_ok("Enabled interrupts\n"); - __asm__ __volatile__("int $2"); - - printf("ANSI code test: double \x1b[3;31m \x1b[31m \x1b[12m \x1b[0m \x1b[3;31m \n"); - printf("ansi code test done\n"); + kmsg_ok("Enabled interrupts\n"); for (;;) {} } \ No newline at end of file diff --git a/src/kernel/kmsg.c b/src/kernel/kmsg.c new file mode 100644 index 0000000..6cb9786 --- /dev/null +++ b/src/kernel/kmsg.c @@ -0,0 +1,26 @@ +#include +#include + +void kmsg_ok(char* format, ...) { + va_list args; + va_start(args, format); + char str[32767]; + kvsnprintf(str, 32767, format, args); + kprintf("[ \x1b[0;32mOK\x1b[0m ] %s\x1b[0m\n", str); +} + +void kmsg_warn(char* format, ...) { + va_list args; + va_start(args, format); + char str[32767]; + kvsnprintf(str, 32767, format, args); + kprintf("[ \x1b[0;33mWARN\x1b[0m ] %s\x1b[0m\n", str); +} + +void kmsg_fail(char* format, ...) { + va_list args; + va_start(args, format); + char str[32767]; + kvsnprintf(str, 32767, format, args); + kprintf("[ \x1b[0;31mFAIL\x1b[0m ] %s\x1b[0m\n", str); +} \ No newline at end of file diff --git a/src/kernel/platform/drivers/vga_text_mode.c b/src/kernel/platform/drivers/vga_text_mode.c index 8d77f12..f8173b5 100644 --- a/src/kernel/platform/drivers/vga_text_mode.c +++ b/src/kernel/platform/drivers/vga_text_mode.c @@ -6,6 +6,8 @@ int row = 0; int col = 0; char color = PRINT_COLOR_WHITE | PRINT_COLOR_BLACK << 4; +cansid_state state; + void clear_row(int row) { struct Char* buffer = (struct Char*) 0xb8000; @@ -48,214 +50,20 @@ void print_newline() { set_cursor_pos(col, row); } -bool is_hexchar(char character) { - switch (character) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - return true; - break; - default: - return false; - break; - } -} - -bool is_digit(char ch) { - return (ch >= '0') && (ch <= '9'); -} - -unsigned int _atoi(const char** str) { - unsigned int i = 0U; - while (is_digit(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; -} - - void print_char(char character) { - // ANSI parsing - // 1. is the current char \x1b? - if (character == '\x1b') { - // are we already printing an ansi string? - if (is_parsing_ansi_string) { - // string is invalid now - is_parsing_ansi_string = false; - return; - } else { - // no! set flag and continue - is_parsing_ansi_string = true; - return; - } - } - - // 2. are we working on an ansi string right now? - if (is_parsing_ansi_string) { - if (!ansi_string_found_bracket && character != '[') { - // string is invalid, stop - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _flag = 0; - _color = 0; - _print_char(character); - return; - } else { - // found bracket - ansi_string_found_bracket = true; - return; - } - if (ansi_string_found_bracket && !(ansi_string_found_semi || ansi_string_found_m)) { - // looking for flag OR color, idk yet - if (ansi_string_found_number) { - if (character == 'm') { - if (ansi_string_found_m) { - // invalid - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _flag = 0; - _color = 0; - _print_char(character); - return; - } - ansi_string_found_m = true; - // string is done - _color = working_num; - ansi_color_code_t code; - code.flag = _flag; - code.color = color; - handle_ansi_code(code); - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _flag = 0; - _color = 0; - return; - } else if (character == ';') { - if (ansi_string_found_semi) { - // invalid - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _flag = 0; - _color = 0; - _print_char(character); - return; - } - _flag = working_num; - working_num = 0; - ansi_string_found_number = false; - ansi_string_found_semi = true; - return; - } - } - if (!is_digit(character)) { - // not a digit, print out and discard existing string - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _print_char(character); - return; - } else { - // is digit, add to working - working_num *= 10; - working_num += _atoi(character); - ansi_string_found_number = true; - } - } - if (ansi_string_found_bracket && ansi_string_found_semi && !ansi_string_found_m) { - if (ansi_string_found_number) { - if (character == 'm') { - if (ansi_string_found_m) { - // invalid - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _flag = 0; - _color = 0; - _print_char(character); - return; - } - ansi_string_found_m = true; - // string is done - _color = working_num; - ansi_color_code_t code; - code.flag = _flag; - code.color = _color; - handle_ansi_code(code); - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _flag = 0; - _color = 0; - return; - } - if (!_is_digit(character)) { - // not a digit, print out and discard existing string - is_parsing_ansi_string = false; - ansi_string_found_bracket = false; - working_num = 0; - ansi_string_found_m = false; - ansi_string_found_semi = false; - ansi_string_found_number = false; - _flag = 0; - _color = 0; - _print_char(character); - return; - } else { - // is digit, add to working - working_num *= 10; - working_num += _atoi(character); - ansi_string_found_number = true; - } - } - } - } - - _print_char(character); - return; + color_char ch = cansid_process(&state, character); + color = ch.style; + _print_char(ch.ascii); } void _print_char(char character) { struct Char* buffer = (struct Char*) 0xb8000; + if (character == 0) { + return; + } + if (character == '\n') { print_newline(); return; @@ -315,14 +123,6 @@ void kernel_msg_ok(char* msg) { print_str(msg); } - -void handle_ansi_code(ansi_color_code_t code) { - char s[50]; - itoa(code.flag, s); - print_str("ansi code: ["); - print_str(s); - print_str(";"); - itoa(code.color, s); - print_str(s); - print_str("m found\n"); +void init_state() { + state = cansid_init(); } \ No newline at end of file diff --git a/src/kernel/platform/interrupts/int.asm b/src/kernel/platform/interrupts/int.asm index 624dc1e..fb80710 100644 --- a/src/kernel/platform/interrupts/int.asm +++ b/src/kernel/platform/interrupts/int.asm @@ -61,30 +61,24 @@ mov cr0, rax %endmacro isr_xframe_assembler: - push rbp - mov rbp, rsp - pushagrd - pushacrd - mov ax, ds - push rax - push qword 0 - mov ax, 0x10 - mov ds, ax - mov es, ax + + pop rdi ; Pop the interrupt number into rdi (first argument) + pop rsi ; Pop the error code into rsi (second argument) + + mov ax, ds ; Put ds into ax + push rax ; Push it to the stack + mov ax, 0x10 ; Set kernel data segment + mov ds, ax ; set kernel data segment + mov es, ax ; set kernel data segment mov ss, ax - lea rdi, [rsp + 0x10] call exception_handler - pop rax pop rax mov ds, ax mov es, ax - popacrd - popagrd - pop rbp - add rsp, 0x10 + iretq isr_no_err_stub 0 diff --git a/src/kernel/platform/interrupts/isr.c b/src/kernel/platform/interrupts/isr.c index 8476771..2790c63 100644 --- a/src/kernel/platform/interrupts/isr.c +++ b/src/kernel/platform/interrupts/isr.c @@ -1,11 +1,11 @@ #include #include -void exception_handler(isr_xframe_t frame) { - printf(" %i: cpu: check_exception 0x%x err_code => %i\n", err_count, frame.base_frame.vector, frame.base_frame.error_code); +void exception_handler(uint8_t vector, uint16_t err) { + kprintf(" %i: cpu: check_exception 0x%x err_code => %x\n", err_count, vector, err); err_count++; if (err_count > ERR_MAX) { - printf("cpu: ierr hit err_max, halt\n"); + kprintf("cpu: ierr hit err_max, halt\n"); __asm__ __volatile__("cli; hlt"); } } \ No newline at end of file diff --git a/src/libc/printf.c b/src/libc/printf.c index 916c9d9..8a68695 100644 --- a/src/libc/printf.c +++ b/src/libc/printf.c @@ -868,7 +868,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const /////////////////////////////////////////////////////////////////////////////// -int printf_(const char* format, ...) +int kprintf_(const char* format, ...) { va_list va; va_start(va, format); @@ -879,7 +879,7 @@ int printf_(const char* format, ...) } -int sprintf_(char* buffer, const char* format, ...) +int ksprintf_(char* buffer, const char* format, ...) { va_list va; va_start(va, format); @@ -889,7 +889,7 @@ int sprintf_(char* buffer, const char* format, ...) } -int snprintf_(char* buffer, size_t count, const char* format, ...) +int ksnprintf_(char* buffer, size_t count, const char* format, ...) { va_list va; va_start(va, format); @@ -899,20 +899,20 @@ int snprintf_(char* buffer, size_t count, const char* format, ...) } -int vprintf_(const char* format, va_list va) +int kvprintf_(const char* format, va_list va) { char buffer[1]; return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); } -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +int kvsnprintf_(char* buffer, size_t count, const char* format, va_list va) { return _vsnprintf(_out_buffer, buffer, count, format, va); } -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +int kfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) { va_list va; va_start(va, format);