From 810c6975b1f3067bbb7156e0d234f60ebeea7b31 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Sat, 31 Aug 2024 01:58:07 -0500 Subject: [PATCH] registers: add graphics_engine_bits for DISPCNT --- .gitignore | 3 +- registers/Makefile | 2 + registers/csv_input.py | 27 +++++++ registers/format_bits.py | 56 +++++++++++++ registers/generate.py | 35 ++++++++ registers/generate.sh | 3 + registers/graphics_engine_bits.h | 50 ++++++++++++ registers/graphics_engine_bits.ods | Bin 0 -> 25983 bytes registers/parse_bits.py | 126 +++++++++++++++++++++++++++++ 9 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 registers/Makefile create mode 100644 registers/csv_input.py create mode 100644 registers/format_bits.py create mode 100644 registers/generate.py create mode 100644 registers/graphics_engine_bits.h create mode 100644 registers/graphics_engine_bits.ods create mode 100644 registers/parse_bits.py diff --git a/.gitignore b/.gitignore index ca1ffe7..2fd2ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ __pycache__ *.d *.elf *.bin -*.nds \ No newline at end of file +*.nds +.~lock* \ No newline at end of file diff --git a/registers/Makefile b/registers/Makefile new file mode 100644 index 0000000..6693a37 --- /dev/null +++ b/registers/Makefile @@ -0,0 +1,2 @@ +%.csv: %.ods + libreoffice --headless --convert-to csv:"Text - txt - csv (StarCalc)":44,34,76,,,,true --outdir $(dir $@) $< diff --git a/registers/csv_input.py b/registers/csv_input.py new file mode 100644 index 0000000..e694b84 --- /dev/null +++ b/registers/csv_input.py @@ -0,0 +1,27 @@ +import csv + +def as_dict(header, row0): + row = [s.strip() for s in row0] + return dict(zip(header, row)) + +def read_input(filename): + with open(filename) as f: + reader = csv.reader(f, delimiter=",", quotechar='"') + header, *rows = reader + + rows = [ + as_dict(header, row) + for row in rows + if "".join(map(str, row)).strip() + ] + + return rows + +def read_input_headerless(filename): + with open(filename) as f: + reader = csv.reader(f, delimiter=",", quotechar='"') + rows = [ + [s.strip() for s in row] + for row in reader + ] + return rows diff --git a/registers/format_bits.py b/registers/format_bits.py new file mode 100644 index 0000000..1a7e1c1 --- /dev/null +++ b/registers/format_bits.py @@ -0,0 +1,56 @@ +import sys + +from csv_input import read_input + +from parse_bits import aggregate_registers +from parse_bits import aggregate_all_enums +from parse_bits import Enum, Bit +from generate import renderer + +def mask_from_bits(bits): + h, l = max(bits), min(bits) + mask = 2 ** ((h - l) + 1) - 1 + return mask + +def render_bit(prefix, bit): + macro_name = f"{prefix}__{bit.name}" + if bit.value is None and bit.mask is None: + # value read macro + mask_value = mask_from_bits(bit.bits) + yield f"#define {macro_name}(v) (((v) >> {min(bit.bits)}) & {hex(mask_value)})" + elif bit.mask is None: + # constant value macro + yield f"#define {macro_name} ({hex(bit.value)} << {min(bit.bits)})" + elif bit.value is None: + # variable macro + mask_value = mask_from_bits(bit.bits) + assert bit.mask & mask_value == mask_value, (bit.mask, mask_value) + yield f"#define {macro_name}(v) (((v) & {hex(mask_value)}) << {min(bit.bits)})" + else: + assert False, bit + +def render_enum(prefix, enum): + enum_prefix = f"{prefix}__{enum.name}" + for bit in enum.defs: + yield from render_bit(enum_prefix, bit) + +def render_register(register): + for bit_or_enum in register.defs: + if type(bit_or_enum) is Enum: + yield from render_enum(register.name, bit_or_enum) + elif type(bit_or_enum) is Bit: + yield from render_bit(register.name, bit_or_enum) + else: + assert False, (bit_or_enum, type(bit_or_enum)) + +def render_registers(registers): + for register in registers: + yield from render_register(register) + +if __name__ == "__main__": + d = read_input(sys.argv[1]) + aggregated = aggregate_registers(d) + registers = aggregate_all_enums(aggregated) + render, out = renderer() + render(render_registers(registers)) + sys.stdout.write(out.getvalue()) diff --git a/registers/generate.py b/registers/generate.py new file mode 100644 index 0000000..2871189 --- /dev/null +++ b/registers/generate.py @@ -0,0 +1,35 @@ +import io + +def should_autonewline(line): + return ( + "static_assert" not in line + and "extern" not in line + and (len(line.split()) < 2 or line.split()[1] != '=') # hacky; meh + ) + +def _render(out, lines): + indent = " " + level = 0 + for l in lines: + if l and (l[0] == "}" or l[0] == ")"): + level -= 2 + assert level >= 0, out.getvalue() + + if len(l) == 0: + out.write("\n") + else: + out.write(indent * level + l + "\n") + + if l and (l[-1] == "{" or l[-1] == "("): + level += 2 + + if level == 0 and l and l[-1] == ";": + if should_autonewline(l): + out.write("\n") + return out + +def renderer(): + out = io.StringIO() + def render(lines): + return _render(out, lines) + return render, out diff --git a/registers/generate.sh b/registers/generate.sh index 426324a..1ce89da 100644 --- a/registers/generate.sh +++ b/registers/generate.sh @@ -1,2 +1,5 @@ python format.py 0x04000000.txt graphics_engine_a > graphics_engine_a.h python format.py 0x04001000.txt graphics_engine_b > graphics_engine_b.h + +make graphics_engine_bits.csv +python format_bits.py graphics_engine_bits.csv > graphics_engine_bits.h diff --git a/registers/graphics_engine_bits.h b/registers/graphics_engine_bits.h new file mode 100644 index 0000000..1be8ae1 --- /dev/null +++ b/registers/graphics_engine_bits.h @@ -0,0 +1,50 @@ +#define DISPCNT__obj_extended_palette (0x1 << 31) +#define DISPCNT__bg_extended_palette (0x1 << 30) +#define DISPCNT__bg_screen_base_offset(v) (((v) & 0x7) << 27) +#define DISPCNT__bg_character_base_offset(v) (((v) & 0x7) << 24) +#define DISPCNT__obj_processing_during_h_blank_period (0x1 << 23) +#define DISPCNT__obj_vram_capacity___128kb (0x0 << 22) +#define DISPCNT__obj_vram_capacity___256kb (0x1 << 22) +#define DISPCNT__character_vram_capacity___32kb (0x0 << 20) +#define DISPCNT__character_vram_capacity___64kb (0x1 << 20) +#define DISPCNT__character_vram_capacity___128kb (0x2 << 20) +#define DISPCNT__character_vram_capacity___256kb (0x3 << 20) +#define DISPCNT__display_vram_block__vram_a (0x0 << 18) +#define DISPCNT__display_vram_block__vram_b (0x1 << 18) +#define DISPCNT__display_vram_block__vram_c (0x2 << 18) +#define DISPCNT__display_vram_block__vram_d (0x3 << 18) +#define DISPCNT__display_mode__display_off (0x0 << 16) +#define DISPCNT__display_mode__graphics_display (0x1 << 16) +#define DISPCNT__display_mode__vram_display (0x2 << 16) +#define DISPCNT__display_mode__main_memory_display (0x3 << 16) +#define DISPCNT__obj_window__disable (0x0 << 15) +#define DISPCNT__obj_window__enable (0x1 << 15) +#define DISPCNT__window_1__disable (0x0 << 14) +#define DISPCNT__window_1__enable (0x1 << 14) +#define DISPCNT__window_0__disable (0x0 << 13) +#define DISPCNT__window_0__enable (0x1 << 13) +#define DISPCNT__obj__disable (0x0 << 12) +#define DISPCNT__obj__enable (0x1 << 12) +#define DISPCNT__bg3__disable (0x0 << 11) +#define DISPCNT__bg3__enable (0x1 << 11) +#define DISPCNT__bg2__disable (0x0 << 10) +#define DISPCNT__bg2__enable (0x1 << 10) +#define DISPCNT__bg1__disable (0x0 << 9) +#define DISPCNT__bg1__enable (0x1 << 9) +#define DISPCNT__bg0__disable (0x0 << 8) +#define DISPCNT__bg0__enable (0x1 << 8) +#define DISPCNT__2d_display_forced_blank (0x1 << 7) +#define DISPCNT__bitmap_obj_mapping_mode__2d_mapping_with_128_horizontal_dots (0x0 << 5) +#define DISPCNT__bitmap_obj_mapping_mode__2d_mapping_with_256_horizontal_dots (0x1 << 5) +#define DISPCNT__bitmap_obj_mapping_mode__1d_mapping (0x2 << 5) +#define DISPCNT__character_obj_mapping_mode__2d_mapping (0x0 << 4) +#define DISPCNT__character_obj_mapping_mode__1d_mapping (0x1 << 4) +#define DISPCNT__2d_3d_display_selection_for_bg0__display_2d_graphics (0x0 << 3) +#define DISPCNT__2d_3d_display_selection_for_bg0__display_3d_graphics (0x1 << 3) +#define DISPCNT__bg_mode__text0_text1_text2_text3 (0x0 << 0) +#define DISPCNT__bg_mode__text0_text1_text2_affine3 (0x1 << 0) +#define DISPCNT__bg_mode__text0_text1_affine2_affine3 (0x2 << 0) +#define DISPCNT__bg_mode__text0_text1_text2_extended3 (0x3 << 0) +#define DISPCNT__bg_mode__text0_text1_affine2_extended3 (0x4 << 0) +#define DISPCNT__bg_mode__text0_text1_extended2_extended3 (0x5 << 0) +#define DISPCNT__bg_mode__3d_large_screen_256_color_bitmap (0x6 << 0) diff --git a/registers/graphics_engine_bits.ods b/registers/graphics_engine_bits.ods new file mode 100644 index 0000000000000000000000000000000000000000..1e3b66519ad4251b72954fe281765e2f89761c92 GIT binary patch literal 25983 zcmb4q1yG#NwkPiH?(P~K26q@>a0%}2F2NxIf(8iggKKbtYjAhh;O@)+zJ0H1tM1;d z{i?dAYpRca-RJ!JbbmlaXc!y_2m}ZS3O;h(aC?CmHV6oaf7-`i5DqpD7VbXI7ADTl z_BLiF?lw-2?4FM1Y)&R_Hg0TA&K8d5PG%ks7LM+0ZqBY2CgyI|78dTn|AXfToBw$T zK6;W)j+Qo79!Miry}_`RL-tWR<0IqZvUB*=-*K~IeR$&BlQ1M zqKTQAg}udx+D`vT*MDcBrM;7hyTyN^`geM|xtq9q{6FmbchcOQob3Pqz48y0vx%dH z{eSQZ`M>d|jf07mg&Vtsjk|-1v)lhBMnFLL*L3W{d2ckah7K>hWs#lqU~r>L&u5o&X{tdo!I(s8o4}5L^KFp&%oEM6d19lG0M9289brpGDsx@RBlb$*bfNHxVjL zu^ik$&c7ebB7V?^AvZBGnwny`@(!lrF+&krmP74FReCp2(K1yW+J3eynmxXZsuk$U zw&!Qp?(AAQEpB^Eo}7JunM&jNJq3Nw>ZP#5f3lqo+r6s#O5loc#<|>4i-k^{?WGVl zx;L#28YN0qB0CxKXth2iL+lV(2YlEvlt=)mkU8o0-ka`l2rm3R>&@ z^&*&I)|kF1^n^f}BFLC4P~kQ1|Jq@+q@#?^0*wfXIjs1dz)M&E$PsuFXBuaXdx(Pn z`3ah}Of#2p6_t$<#eaB52MbjXtD6N(SFJnB`UgjY zui{Wslt-oYdtM}#8jC!KhH3c#O4SCaEN)zJ_^-nCa&~A>dr>;gj)v0EAEqo|SRnsk zYNx3yHNRHcF7Dwz5z&l~u#rDmTV2C0v}g7+PDJMq zef2P*C-scPg14cX&zf|*DDbxlH65`9(azY#(RqzUyu+X#V8RD;rNtLdG@e8sx%-%vRRf33%# z(`66Aj%Muqz4X1J*TK5`g_lG2)B9T)t(cq|6F7PQtA5H)PBID-aHMX0LGDfMq(-b zTRM0Z5LRbzx}+#)qF~YjSu`XlC4d)@Mu+IgiOeYel`t}Vbt?2ofX&uRUTQr}!}HFY zjWT2GLR%ayG_kxE1@8#2*c1WRmI6z9w2b31O*QGR0f0lr(mLI|pLDG+S}BcPNu~JQ zT8mRKh4uDk^0_)}6h)*VsiCNG`*>Dgjjy?~h3fT2JiLQC+w4n4eslp-&%PhE3RMtc z$!=@|JvG$k`q%{9_|{Ti(n=a2i=~_)t=NtKX<9nS{b*Tf$tXGidzS{!28i@|&gWBdAs6gcDz=tHdLF-wRDDmyDnQ7u zg_WvPGrhC|uPCzUSNX#xRESVa?%`3Gl`{=hiC1V96G6kWGJ-*$+!^+jQUOSgd2We5 zgiuoRYXz3pV29nGUXNcE2vPHrt-6eSkh|`&e@&x66Giv$It!*^QHTi&GPwk_0xWQ9 z6^8fq^s@mG&5M}b_)^Gw0v*|8N(2>3vGDchVep2&LFB^DF2BzTVjQNS(E_`6;d|6_ zerWGmdMLmv2SAq=xB$B27`PI*7L6s*%A%@%sN7~bG19i7e&Kxfz#*J!HGowhP2-Gc_UH^9 zWvT@?o@Ui-4Ln0QxH{aB9|QLI$$>J}W=IThiy{cMQsyHENYzn!KHY4XIEe*l%r420 zOwkhjiMVbqUQoDiOiwi;7Ry)mff^vNW9lsVHPmQ-PQ(=VrpdFCO;nAda+H)T01QE! zMWkb;oA4iX$wQMaONe!e5x^Eq{t_pUgA%2pC>$L9j^#{a>kRM~=9-sq^ttua9wck((^26o{j2&-fL3Roy_IK!(Te(Kt9!1!e zxF7mzjZTVqr6`KjYu~&*GUFAQu#=+0EdLd@9PF1$9i*opP_3!k$k7Xsc3+(wuMd7OQ<$C9iT=%vm+=F)@ zi5&zyGQd`@q0*-sEha7g8N}<|RueEs!5Xa;F|)eH4ajfEbJV|*TDPQGZgP^bo?bjY zXN{?Bhe-v9O^s;+|<)z;rNKQ*#%HN|FFR;7tqeWttMNfa$^jDuF4GKSL zh#re2kovZk^sO8;mQa_vZ4|KcC`ou4kv4L5ZmaS8=2Yz>|2&Usr@4f3!t=fc#1Aec ztpLATP$Y+D>R!wRkV>YjcZE}uW=Y0T8zaJ#`%FBM`3y4j2;h{Zb|ciDME~YIFnQp( z9gZK_E8@j?qtsvLIBy#3m4}~sD*|3)2^R!!%-OIRu4&l}U`g!$ajb{iCHkA9&ymGC zaj;LO6efhr<0d&z(%@zGN9~~4p@kz&Kd?)4;ODtXSqH0q<`U2FMr?l+!T8&R>7i&i za>>E1B!7uKr*^RdQ!`2>NEV%JC}%_xEwZ!*=X7s$-S)GrL&kp}jyJZUEtsrBKrXsm_7P z2wJTzpH{>hOPWLH5U%am=g4^;EBgsOAx&yg>I&YUz4K3gQvx_O@%wxGA>M!Pwu`#d zl1IEc3Qtv0^T;k1g67|Meps=UzrCf2Z1D1YiU377RYXz#veWF6-u{%6&w0T_=Zs!o z*Yv+=n6JOOZw(=D2j+ZR++lAP@2fGoh>u>*KBw!w!vPiH5&8!e{BRK=AY6GN zApQ#x|2r^+@&TqCOdM@2E!^DMT+J>2B>iy=<-iC#7Y?@=Uj(qj!n!hywAkkS=8e#S zWxvY6TJMZY(o!n~ALq>ORLO8}#5H7|nve{|)|GaHc+41SN93#rC~yO~~i{ z;EiqKop8Il!Bff}FkWEwlyJ~+MXo7Z{g3+^?yaX`At4|_q5qTnkU!jK=H%%9&%WgY zjGkt#IqfuK{@!@jCSY@RieVAB)J`H5JRL#a=W6oa5$=^to+{3!&inBF`D9|e@O@_| z=j_p6g_^5ZSg)T(O{EXJ=>5+9NNQYNyzngoq&2KS!yQMHQV-KFH4ft0VU!=Kt9Ke5 ztMk0RksHbLJ!DT&u&Qb^s&<<1C1%4rTi7s2I$*fUUsd;|ayq3Hv8W^I)bnVl+SDIT)%CXo@N0!B&f9!E z8Nd4Y*I2S{C0L2%>e~<^W-#fy?M`b-{wQ`(Bwn&zA}HGn;mmH%@vywReAHmf*|AvB zgET7=8GmdN(4a_TkUg0H({=7^xiTGVKK|2gc?T3qRQD>gAggbP431jN$e7wa;_#;P zva=4Emu=fcHK5j*q7ekkhv_s_`qO7r!HkAE?_$X)4HliOd9NHlvf8#ZE)zs->2RXdtfzN+<5OR=Y?nT9iaE3Q;*cqq`NnUi z!G{gjn`~mGE?;xdRAY%)?0mq`9*qH!PF;`V((FlST0JUG&1yA|7yl&2@4Yk-5?1C_ zMOO-VY_DlLz25#k8v{#!GEoefopJTz_#$Yn1Daw7!VoS^;&ADM^zA z%LFyl=)5zX%T0lxsh-ozw8hC-L$JDvK^GA};&qVg(~vgCkt6oLpp5fA0~Hz_W>Wlp zi|NX)(c9YL<>dQ`Q^#kq+qdY_{2afom&%P9=j41&i_FM3bcVevc0X4i)2@54E%iBz zT)t3oK-Q|9^tLt?qvm&`ob2zpONAcsCwJJ7(63f=&tI^7fYmWd^nwBT88IkjJHDsG zONULx>YYUyenSJ&f_jWqp%6p@x+3~JRyoyzST0U0I(a^yXdA7J%lVJeXv?Ejz?U=< zYE*u1qR6n~sw#2~%+t6T#iK;Xm%ROSWK$^UgTHu&1%XosKU@Q~M^GKgjox1K4X1F5k2h`GqxkWX8%; zwM?b|-fEQ^opW){SLGnkOCxV5XWPYTDV=(Nr}O-8dqKr04oCqp%St!kUM%{3orS)z zf*!a#`bbM&lZr97>*hDjhT)OsCd+aAsms8Qz3!?TE&W3(w(Mp}kpJWD z`TNsy>HV|HLokusz;|DV{K&V-@&#u6bujNu#?nro+U6E{$eXIuGa{OLpS$&xbN1rh zM%6Q_pHs-%%{xh32&reNN-Z)zU@Zl>AcY!q{IF9cK7&k3DUU%V4`rCSF4A$&fg&*> zB<6|@s6uP`zO&)^?Pp6!Fjc)I+)?sxgIOWTg`PT8?G={nopv1ur{5L4jiTA zu!iy3gfq&?X}F0uT7eic4m_rUn$%n|De`kC>1AL^*8R({#-d8(Db!)aB{v1CTo-Q3 z>bGSHqV}6M$_`}I31m;|I^n@{&n~f>iD1`yV=o9Y!V3|N*n*X_OW1Sd2P2U4>V+P3 z^XJX`e5$SDg^}Qm>tDJ*kw=eE3FC>@QN-pjRY;wtZDMt0F~d54pTS$;ZyOok7t|4p zk0JjJ1bV7=iNRl(YDR}A2m*b@hbmGe8qPiBUJZs$6=NFp zp_rl@OywI7G2477jI^}I>Z92=onagIjC>@@-h=+>5dTzR^X0L%2={*6ak%5e7b~-s z!Wb^2I@mI?0iIiG>>SPw&DHrSH2OB8qp{xp7rINZ8C+_0xP|lR!q=-KS(jr}yI=TM ztpPQNt6VkytSq7WoeIgf^VPJO%ZB#1tpN&%!d%AwumqvpoeD9%{YQ4b9m=fVmQPoH z{w9PT&<^J0-(fC#xZ&+sFZQe|JGwM*9th?%Mj3xlV01hp8=4&{)^P5J-~^z=5fB(P zxG$KQTFf|-J3z^zU<7$bbz^>}=`Cp=@UZ9oyVlvdJh*3aZ#*t7<^1-fwRzUlEVSgM zj$6RPAscbK0Xpz_R~j5Tx+|=)Z2NnX0k+tDOfnvZeP!npBPKLnzoEBRnT20ZhqC|G z-xS2>>(tck1ifNqGqF;)#@TaWoAPPE<6f}4Uc-RcVwp7l{LZ~SNVISZ46_+vL*giTfXeTr3zUP9wA{*&zCt|i~-#Ecn(N@jd8b2 zqj{gr3MFm-8r0t=G@^zV_WGNnPuH5loC)B;cx9cU9j@edxJT-UxYrJ`F>cjacln*5 zlK_zUl0YK_mmk8B##|o?eF_k%`_j5~;@3rfY^$JK8$u|wON+aIaO6X!Je-fOPXDjC zWN12cX?c%&ZH=2GWh0GsNu{(%7D5k524N@si&V_kFq6Quc9wbsJj-Uc@glV)=@v3Q-UergNacAI$ddKQHg_jepA?TClVTvPpLAB{bX;%>p zX4ulQYn9ArPRYqb!NI9X>WmHbh!8ux8)SZU_(aT=%?3JH4d++l+%q(#F$c5%t$o@+ zxIP=exPHzIuLic*dkyK~nVBBtA&EfWuY@LQWE-~*FgF!|fb(K5iC1B@=2`f=G5J7s z{{*h@`=QAP$u6mvg%rpRuCPUtL6fqyjvS8CgM0*sgLm-e7{I)I&W(8}pAz1dsQ z`k~=QfBk)`(aFdL|I)1aCjd3Vb?t*7yoT+C^IM&(NQy6$h&sP7q(}VJQGl3iJoe?%U3|@?M$b7$PB+2JORDM>eed%>@6=n) zS0VcYrYiqOnEDUOce8MJw{f)kSJCuTPv32sA183*^Y<+e`u+quR;8XeN&-1~hz*FY z;U)-N-K!Cd{)q}!3H-Ct z=7Abq?t3qGN7V!53+|sJ(JI{nyCBJZUX2EzwCK6&{OUNAKz%Z0zZ+7R7 zX9?a?E-9CbksGQ;6{*e>eBV>H>2CSVo>su{gk$KRZ*NpJYuo60<;j<<{=~yEpdz3M zr%ik0CeRT?l(pKCR(r~XJK z%T&^=LgfCd@SqfA9cMBjjNq+eTbln)Y+BTXx@0R`7tAB=rs&U0(}cmHy;#Y~6n6Za zE;7`OaO+O~A>UgG@cepg&>7@A2_}A3UYjbiOr#fMIyFy(!+J00SKl+TOh_vUScPvo zluTWHHYlxwC1_z%>!vpcjLT3GMm~{nO%iy(Plb@%JrHz}g#q84=!0ak$!3#mMXi^% ztYLY{sjHy4BA68~^Ww*2EgZ=_DliiN9Fc$M?XJALRScRZw zVfa^Nl~ew5RipeS85nqFdeT&Jb`UQ>lY9>BEyj@qD4>o@{Fvd7q7FkeB&~V`z%&sS ztC5d~)DP$LLh}#Fv%t=WRrl7!BY+sTFfB%*?YlOMOWwN#6}S|wKy79o)NhRzs1>8i49--C-n&Oh1N6t-p^8ICgk z^;grSeGD|JrOY?8D4O|M+`8^s|F9j0q(%AzL;@%5*xno!O^m1q5JU)$P6cCSKT<(% zPw+NJ{aGofYxkz``GT;2(B(qUy3IQLi_)DcOyG~lc5qV9s2z?q@plF;g-Nrf)IjsR z_iGmS6+QuBir|;m%Gw+8A%J~C*EDrfEifx@$l{T-Q9qa;13$N_GoH^>afx*^7vdpd z2Jd%VRG#om&VHm5D+F{LFbVJ0A1-v!;iqtcqu(d@JqSoDTgct(#UZ4q1;li3Qi7^j z`WvP@%)89s`|zrFI1^O4{8oAcMbA+X&6iKEhH}}Uvj;d^@&q3QBhSmGTV@&`5f6fy z2>@@y&sUq@VX*CT!5XtyE7zIMadIRDuG9_D&&MxjBCq;4$Sh`3v=j-F@ufV+5}O0d z9#2lSD#A`wubWEQ6S6x?D za#i^|#g?5S?kuDCt`BCUBD%FGmt76Abrn9mg`PMz;NcTIo0!JW3iE~98s(PO%4aV} z#R9IvB(0=wGgr3T9gJ1Dh+pltChe<8@*RuKSQ^_SQ)apseUj%d8`pH%SL4*`g=w9^ z1AKlJ_7zNMC1$ryURM-Loa2=^w@!PTXKH9?7Vm9_hSemhL+$0$dFx3qV)34X6?!|R zbnCKOgL-<{qd8jNmR}DCy%%~nSPf(Jng!HXzBDcJEo!dJM7Ls0;AzdxcJ~d2?h&=E zxvQ>R?^m*E%1~=$mBk25uk5>1X-3$t5R5o9ZoAJDG%+)mn;D>{@=j#P*5$3P9qDMI z7!ZH`8skdL+cy7zh5YD$&Zt^<68o58si(S=p{Ait>qfCpOfHfgPd4G^j9QV~i6D{* z0Rj|}^kyC%a6wP^@GwYty{&wE`=SbnvA@9&SQx0x`pw&QCUS8r_H?ra*P$k}#U$^6GJFWgsc9m$*s2cDMM&3ZON^fps zWRyw5hj^!)@H+qc>eq*KiMt9iz>1mymVSxC!o=YI!L02UkPp@q{|3M0=dmTlwkt-$F$xdT2Ce>qX&j!z-$u$TM&HZ*+mHIOs)TYv8cwNeQ+L z6XIx2fsXXIeup$_G*p@MvT|;R5--G(4Zw*)W z)0PEFQXqbcH~GGR) z{nb%>8RGV*bSYBR&{Ojq>k;}6pT5UxiuJX*c`Oj|_H}pXgWiLfFakyZtDOS42*mSV z{Z_p$9(=PH!1%DOtO?}4FZbdhi?Zb*x)ix31=AR;7zW8Lg{Sc28|)bNl-3I9`%_3y zp6mv+7*pZU0P&SuqGL?JBpXvd^lXt$n;S zGAXBPh(hPuY`>hh3P0MAJ@@G*mXB!V?M-7l9-|ybRgZSQPQEhn3!{u<~$S; z)w#plq4Ecal$Ez>&v@hPu(EzuJ~kL8=9M~>zN6uG#K5L|<}rdvl-NnXvsEXLg1vP! zA>TGOYb{VDk+m7oMddWi)FFvv!HBC|CNz?;L9j+wx@l6i?m*FPB4P1He8%T7AJV+9 z*uk+|N-=|kDpjELAuGASo=8ITjC2`K`%`;%Hxt6C2={&8n>bst7jIH+P*^Y<^>(cs zE9kh8A<1*h?>4y@<5;Uc8Uk&L(J(QZ(y`S)IY1OZYjSot-E7$CvVIFzrv-uKT^Ygr zRqP$sQz-JEj!A{q{RF?>57;9553>-*9Kl}jc)wq*#a6Hr_x`rcE4f`@E4zYHSQl<2 zYX{PvuivaU`bL}j>3h01Fh*uucB>cZkf*=vJSi+RvA(PX{px)``+Zqm$+n~F=sGyj z5oqA`s(0B2l^(G#KGgAd|GwkJAtcI>!p{dp;V*nD`Xy+yfh)Vxt+M3uu5(Y*ub~hp z4X@Ym6({886HW{T>*bb+@JeI5ICw#?w^Q$wWYZ5=e2dj z_yI<~@b{iTj`jwU{m-&Il+fqaOKzU}5NUmp9nQV9PGmT8vd5juF0sTtsEou9o9Z47 z790o2P)R=76NKNGE_w1YKBqcqlaVq?DKqKvkE`kD1FBS5$8|O2oy}*-$B^%m ze~_NdjLrw>P!#xv>&bwSuv2{OH{UPI(rLLJF5>)-Xz+d>^^e5OI{CIAJ2UUm)RD}T zEnG!$@yqSZqRQx!?Q+4)+F6ixGm7J5!Uf%1^Y?gmUM57MlKoMm5U`Nj;aaF3d)$+r zg&8`OlCWV)2Hf;RUNTw!4t14DiiDw}K~0YEE}qw8rNclV$vJf+)k1-x0@w+)+)ig$ zTct;0Dzq&R(Q;qm-Mmkxwcy^SbIK8HLNOsH#?B8tL!cImCnaeu2j+BNeAiJ%?)_{6 zV%7igiEj=335!tI!H({sVt~PSBjg#!%vzIcPFp0OlFL|uLYN1Ie(yRVMS(dhQm!6b zhP6Y^W@sK(osgziPZjDY_m9PPsZR|xl`c$TUPrccT_dtpatEgZyOh59$_|Bb8g$6p z5ge9m+ekesBjDk4mqmSt-leUq6G z1qyw3F+XHIzx7w5x`=P+x|6lbJ>UZu!=dRgu}v}l$gk!~sYiTNvw1R6<}fPfd6b4T z9){{3#efi=he&d{?dB@8uV`{U9B4#P*j^SR*{x&d3>X0}!_inn)}`f7ez07nZ#j7Y zn4Edc*Hu&~*$y>iJyglyr0|?vN|F>&b3Mr#vAM-63Jj7dc#NTZ%I~AkkZHG_gz`m{ zDk!pH_H-cx&K>891rovZwXtZsaum+m%wOPJ9x9HujqDI|s#-ND;O5tV!KfTpV$ly- z|BzRW1fx^iR>Dj#J3{fUJ{6*6u&5oF{4kwSZ`3H4dx0vYyvn6i@HjRqt_r?{CC1uC z&`1681xOL@tHGS`39Lmc@UuY$88pC_QrHr5w)q@>AhBDZS;S5@x-!YNdiHH?m1;iDz;MlN9{b@ZT}M z2sD+duiuG{`1Fiqvg9dhP->=sqihZ`@JeD>BJiE1;f7?=)UfNgxw-41TIhg8^xUbMC=)V|ULR8oD_cMX`e_rNmWNLPvGD_`YV%2hu#wyK( z>5-N`GcnCUX|g<@F_2n>RvS@zJ8lIOkibE%GHHvfmmTH$_$qfT^OG_M64b>Y1n$_( z4%eO82^g|u)+G6$l!wu@Iz7M1I_OgR#ZhIP65Hz%Q}U|v{jGZYDsR}Tl(Mxt8qLi2 z`@IQdsK{L2G0(FdQv94r#D(H&EyUn(57gc{-mco4i1cZD3^236ADW3AphuMY%i5rc ziuoCG?x7{vw#xUh#;lt$tF>k`2retJB$(+3E8)xP%0FNy1r6Qxpx2+d3NP+ z)>#G~-Cg1E`C{KCL@Agl19EN%exU=c^a%-aE@I9S4n(#Sn!eU-)*P+!ZQ$cv)?lV= zOI(ol91^s400~yg{Z8$b{Jmh7fzh?V@;2ZiM&;IPnvtyfFxgM^HkYol!`!xO8WhOImZbOlnn)Hc74GBSWxHMTG&WsKgGo8^T^8~ znrRT!gpyOaacvL?9fsdZ3tBwJsOWaHx-)r^-dF}Es0JtWpOCi?Qn5_sqavu~!J!Soegitov7MToG@I49-+{Zv(VZKZxmCx;p7@NzPue!lA zJO76oHqdJ&qr_CPcLl~(EO+^fP*3pIOS_!u2K&vvU!UZNCv8iB`D!{OZv3h@Y$?}B z4<c02{u+G0*coTA1Xxr#8jV*cIr5v zFaZNXAsR(2)CnKHngh=14_=p196)HsGZXF@# zVvV_TQ#z31qGtJ+hF9dLHdomR90MDFR<#G_epg7#50bH24veH^o-3;lv{YZ4w8jw< z6&*x)GhRX2L#G7GFxOY4`QLqMQsM9O0fb-Hv7mP+{ea0Q1sh`!N04bv zDp2g3r1W1>Cx1_tXuUwLhMwk~DbrB9|DxtLjlnp7@bJrKXf0U55gMo_B0%3rBzqIg zSsCPKCX72I0MKlFc#2U-d7FKjjGE)$R>*Tu;VJB%Oi+n2_NGW5nmeSsFm9{Z!cgCu zQ=q6J*3Ft(t)RSz0y`)j^Ii(ao33vDShAh%FKx^o+%7YWkF0PDX5!wSz1%Z?s{LN3 z!d{jHJIQFq9c`jMC>beG_(}M|WjBHxK6{QIB=B*lO+-#rgJzkqA{uxSOhom!z-^Op z+b_hQDVHdQWmE&@9KXn46s9_{o6C)VO(WKJY#ajoDNeD`AZ}RzO(E~Q7HO}9#qJ5B(LxQJRJagMvr3x<1^6M0VxGmV6U2e*7plCW9?5U zrrN7mL7x@PMHAmm-ED(5gqL9HEUcH=r#5J?KAh~g!m8|byZDMXSfaYLZ>YL{OJM*} z_iPMw^m%fgvLKRNterH;&m)ZrnXU*_%JgQii#=AX0ld$qf6CM#T&!?Ju zjCv=!8LqSwcL+A9)M*>GO8U#b@yBIP(Ve>6%U~wZU2)k8DFm+a&&x#&-LNG1IU?X~ zCvhB?32u>f>2>LXJlTRHHFCr_@EAsPI#djlPCwc?tqf}a{kR(K$*c6rDaLQglJ?Ca zA{r5T+%?X#(e#w|AFtObJkVc$EQ;V@rn^Bi)%2w`#+sEz_!L5~vG0uX%ac+mCuSN5 zvBe~j5F@+RW!PG8z-Uaih+Re_3PA&P!I>(?p$${WB93vK9=DA7J2>INYpp7+m;4BS zmfSDJ<4D{F5q2=3%KpeCN;^2vTp2v>L|*0PH#mXA7PtZ7J!5s^k#YzXhE(eSE^ckf z)cCtG%mtjrmSV%{pJ|9p4HPe53aE%A&<~WVw9Zi>pv;;XnRgL*mJAa`&_rXwLhfGc_JhLlGh;L{DQV zW$jWGP16r~G<4oV0T9DbAhTod-b8=>aBn1@1A!SEHfa)$e)i8DxBp}MbiIJec0W&o zwY5mH^!JRVCx;1Wrkh-%Ql%7tcVS(p2UZGVnWJ^--QO~zx@0ks4-Ljwd<1p3VmI`L z6O6C^fN5CL04zBle%WI{uodra5~L;*{7kR?=fydF;X>^+dbc4T0IeZkqd#t#35}WL zHiheAu%woKVW7Rt4C3uybllR^EPcN?e1roT%S6KHe8XjUJGR<%X$lZ$6o}H4sQ=`b2^P^Dy`=Rw#EltmD%81r8k?i$B3fCUPo~ zl9i#LWnDhij)WH|VN_ccwf52p1v0!hRz2bQ+DbRx|K}L-zgFD42c{EAcz--hogdJ_ zOZ~lC;w7ulFOUU(E-!iE?lXNiL@ct3K7E`{<0oX5lOt6)(1L=Q&|&OUTN7@D-;9?) z>3DS}n%4!g$7>75FcZP%lN*(9H-sNn9ilRMBt{51N@>C}HLDU(4bf zIYh^mO`gGx78T75379~R+*Yk8rIl7LQLhd~fzsAOOGg2f&R`TEEk zzLYsIc!E(wa?h^z7?KGe?zv$ZbE&kK|$ekgAXKu^zCA4wWvqqAwD^I%}igD*(aL`Tk4Kz*q4 z-0sFBP2JQze(R>eP^4p{`k}JMdjl)bV|x5ur72qy?#ttA^LYSIZYvWycj^qh{xbZ5 zIvRKx$JQqd^x;oki~O==I%U(VItgxm`IHE2da}P2iquEX&z*^kNMW|9Q&8V`5n_2Y z+#BYY6z?%NaLG|it8hEqk813|{vd9K3|AoOegYo68=uxc{JwY2gKT5&N}7{~T{OP8 z!~&FX&)E%Ord7KkYCR;LbttlV;%k1ykvAil_ZUpd0yF-Z4QCrs=RQ2Wvv!bb)uJK! z(Tsl><6-1veWV#t)7x|Cn0UVzM>G0(X8fkuF09wqRD78fqcO2)QqY9`gWVE`7gdX} zOAz~qi0coGTZ*w!B>1g_^3C_AP{Lc&=A2F-G+z=8>@!rCtep%5*(y;Rq$`7L73jm2 z5C_{B9A{O6?!ZLXPZ-=X!HuHB|&q` z#5Krm4J(S_lBpgmEndd8@9Uw*6?U_f@G06l8?t*s&;4v(4hyzzuu_JOv^tne85y*pk){qEXxoU*b(GyIatE)?IyEy7xnowiMXZGNj4Ipx5Db{%TwG( znk0AHo)(VfPQu(j7APk9sbp2V7{R&dp35E)EEh_6F-)Bj)#MXFBvNQl$nGXNayH@_E0kdZe6+bUR7SzB@=$I6;DnkbY{TTIh`4CeYgbq?qOV z;gB4|Nq#IolX--!n5>XPVWCdJcph4VdVNk-0;x&8Ip^9j#8%fXr>MJ)E)bcShn%!r zc3`&PsW!q#op6s&^tx#(mhkT8L!7Jk2>DR>hfcV!T37)F>8;ebCbsK4AS9}d>{&Ux zw0`60jTQB15Ek7~#EFWZUMSn;#qJ|neN-18nG#6Sb;nTBIOYdK$|Bk&!^<$FrH#bkPr2>JbRQP&Xtm&bi z{~h@!wzJ@4QT=(v3ip!W5h3)_d2L}*VDwCee+0nD$P>`mNNLC31`t)H@Qdm?RXJvd z2KCl=S&3?}2X?z0|Ilgl#n;*_b4OqU7{OcL>CF2aA!#o;jFLJ{efj-7k6T3*LAc)6;2e~V(k|5oNQ zYyA~h;A<4imq=|adK8bhyt4GVvg!zl5QuW_2klUIt0Z7nFd3+J!7R9fF?#kB?`}#k zQ$4yH0t)>Sq;&L<2^*#cx$H@~g;`*6l^*PJ2qhvsg`a2;9zu@quU6lbP=z8aqHjB* z)XRF0yM+v;ed?G8`!wqM*h4}PxS|rsg*Ms@Oc6F^+E8kb9C=HB{QF*%_G}G4K8uM^ zN0iE`E-!WkngGpc6I?&s<_=EMQ%2kVt;oTtZXU;go473?*t)(CbL9&N?}aL%zVkKl zTns!7%g6Y*)avyFLsEBvz#gw{E`HeLDj=2xmf2x1=gSIJ;C1(h;m4s;WYy6gBY&f? zD^a5LUilf(4hhsPle`t}vxmcyfpmkj-~34UW1x+ZzP)_2kOgix@`HI;_!4ds~l+~DvLxZrD& zN(_>KCLJdMc}1A!@2<7x<}@J(T|SbQE`MUVb32w!-aNs#OR&c590@3sFW*s5avU2h zRzm5w1Y4I@4fO~F&^ijqW#LB5w5Gh~n70E{` zT~36}xr1$+k%8Y4flKfJ3j-(+S8arv0OV~k=UDf{F9nn!Xy6<=Bv@h|9+0vISy~ug zY%JVkTJjk}vkn?e2qe^o(0um?%;_s1ntfIzMjw~C6wGcHb`*qqinFkDpaZ9Y~5^XMMA+G zdoa@)Dt%AWZ<83#7@v<;%DF<~*H(v>54r*y)CWNL#Sjb%i*==DbG#ELU=}91$$r8Ye7nL&(4L(W$ zdJG=YA(Tdt0#Ek>KjP1r2R`Int>g@*jW2BOax3FUWNQQ5)gkXvY2utFd#$Q1GNo(bTQ5gPNh^)XQ)Kx1370;Rhf`)*LG^SrmZb12ueCzrdvGj@kV}H?=9eS(}Na>pXmoDmm z1xoL(5?D`YwVH_`=a5B8i$C`bBAN?Xz=Qs^29TjX7<_CBgD`W!Lb^Xy;O8#Brtzma z#{^XY)?rJH8>LjedN=fLs_w#;dtR`qHGE2nSqoFZl2yF}k>}4RLbvx2On5;emhXLi z8O=xpTqBW7G+C{mMk0LGz5iKv|uKkTkqp_>MW zO;HGuPC}u_Lx-RkG*Z!Z{3_Q}WTt5}FC!8#;N0%A^kTtKqM>ac{>19;>4a9F9l#Tr z>(g3i9(y~0C|1k`SvitxnqIHOGL-nulbYyC0g$rns~&kbfkJ=CJ3VAC3~Wm#q@13e zH!5Cc*DV>qtO8hU(nDd^)sqnE89;*NDcBI0TvgL>$V6eorqo81;GokiPg?4ysbz-Y zV6M;rv>57}F#XIM1TyV@$H60ViHI+3;>*e;l>^O56&eRHR1FaTbgaoo-duGk!izI# z0X=nmwr%Jau()0=KrbI=y8F#cnc%1D=ub-z;j?ua3_MzHsB=lL*&>) z79tX$P@7(8>1q&}u&Zttby8>~cPuT5x2}`KAJw_^yIq{(90DZ#t+lED+v5+JBMQIz z(R`KD*+?2PbF>MstJ@}r@Ip{3xOTSt5lt1(M+?X1TL|p*;}5|?&e@|O?%juPlDwKS zsmkbS+g;p9p^@`yY>^w%nqB4w!q-zE$Mb!jM;U;J!EB`9$x8gJy)%xfRl@91<8ktZ z>73&b@Bgany5pK!nsyRO=mDfi3%wYcbm>7rqz5>3X-Y5Bq!&S?SSTV91i{clC`wgS zdKE=Q2ndKE-3Uq%r28Gd_jf<q&u43=%u8R-;2txxe69Vo%e{zwi@%n9OxMbP3<9_Stx9N`g^ zP>hRZhg!-&3EyaOvbRo=8jJsAzB>g z9VieyH$9dS215_R_|W_K5z*RJam5bZ1BxmaS%B^i_H-Vh^A{o2tKx@NOAx?sM-%O;1xwOT2^4Y zr{h=TNTH%znzVRDfsCIpRA9r%eF!mO;~awE0V7D>W&t;{swEg9)w4eeYN-@~;eiqk zIB|qFGGMhHf?bJGnxACsk5b2*Kv^qdVfZ}{DC%-APq1PU|0D%~qT=I~CPV$QjidS1 zOIyoCAHj>G`3?v8GL*tGiN1ot{drJU3C70}rclfL>y55H&o*Y&)G3CTewJB zTxWMo1aNeevBkB(jjNk?jAg>)Q@K(CS3q~i0zA>n#IJy`XEL=b7+e5tOA$xdn`S$n z@r>BQHUw+JwE0SPiw9F(*#M1y!GVdGh7q>2A(qCr&9WyC%Fs=e_=#xLIQ0FJlf4tl(9`BV>JrXLX9cxXrAuO zXrxCFrr|(wkNMl3Ew}H<;g}XAM!(8%`e4uGPziiQ-)h~aTs*=XF8ny&=%m%d65#5lJfPwD$x~MGy0mE5xIGe=>Vjqw|&bR#mlSE)ORdEV_}dtA&s;Ms|3TU(Fh+M4#$od^EOwHWh}sv06cE z8g~;lsNp!VcbNgEj~a((bzGY34LcDJW1^~>`TX=Q04g;N!4=Gw1-fZ2`V2i{n{kw zvLzRF+5H<;O$ruXS$!4a$B^2C_vPn0rXTT7!T{)q&~+EFi@59^kkuq<3QfU4-{NJS zL;gN*PS#Y+=g}AUL=K+8B~Q+P7;5huquNsB6Py-Y3gkYJ82%*DcfPG?YBopqb1$7Z z>J%5X5(jS%GNfL=BxZz+O`#vbc86TJTdkne-)}8@0m9nXtD)GIT~?rtJ2V{LR%NuS zRG}F$fQp-HvHNh;`o6ZM{sr~({dE7SUojFmqxc4P%8a>sk=wx~wX1zWOB<79P;V7y zK)wDU*49P(r#g3C2|jS8D-h95&TQUJGbR`DS(5$Cd5#rsg_4LTNxttAXv*DJe)2KO z3pVM@s1W!a(uv-S$zSykmn|ZBHG|Ml-m4++=T7=io^f4>?12-tE>po%D6Jm*I>Qas zW-k~O{^Wv$2@W`4{!XvCjQVk%mc< zw~ixrx#n6N_zqxzim$snn!;4sw&JFm@7}r5mU-I;d^DFgs0twPa}-qsuW})g&z(c& zT<~7fFD0oX4IJ}gc8jjxk#}s4q<&p@73?9sOWk~6lfA=tW{dGQkefm0kD599X+sSd-C}4 z(MGU!n*ks1jF#|XsdvnkZ%~Y~5`<7(UUg(LWFzXzCZ(A&98BwTv>PB%_Gq%=su<2d z1WgRn{=tfgII89m-gT6FyK?8^wo1J3n*BjzxFYVNH5B`l2_r0& z@j6Y_B$Hn;@w9x=MXOKEbVG*prj2KfAK#0K_6)YFuWo3~qyg^IEnTnl%=Ik9IZHd1cMm#T=fv?BYj;4OHkfs+f;|#ceoav<$pu>EBkvr$Modi!!)e z&3grmHHp<`(Pa1rGhTa5>&qgvMY$Ab6|ypOod=7H1T+&Xme$z%9IPuPJX4l}uuo9y zd*#BKwm;UhCz(g*b$c}a^fS`ym5dn1g$6-!jN(Bdn3GP{#F$hK3_&4jr zc*qdgNF&+95B;UO^=qdq%Fi*Yc$@!Rk&h3&Zi^{oM0B4c>7B8rD7jjMR$SKFqQ|}W zm)@xAA4>41z~8(TXQ0R-b%WUSA}wfqsfEZwV-|ukmDezE>fPLBifo{LRco%9ZT4}d zJjB?BL|8Br&O6)xBOa{O{AS6SGJp4yA91JI|-z5z6u6Y!ON@$;VVL7$lYy5*U-WQ2! zoQ9#)doMr$iEVI&b>1PkdRk|M*e(u5x!rn7dTr&;GTFtkgvX^&#g#`5C z23lsdgr%Vb;qHe|*=FAk-&LWKR(#-@rBJ|dNJ(z4k`8+#W@jmQ84y>~qmhu)*0GX;{kRQ|B zFH0G(j7dY!cq%{W-8N`6G^-#lquthi0tm{c9 z?#x-FXg<*HybWdsxy8$LVouRXGpKn&cNU1RJ|Q-7D0T#DK`Nv-fuKT*zmlS<7V;t| z!JB3s&b7&ZLzY3+)KWEiQ_AGrov?S3dDgV^N(0H)cU0#bPCozs#I2>n=;sLp!Ek!{ zlf$I{*#qhdYg@go&??8Hbj~^`g3#FNIG`oms>2c)akBm21^~kkc;uGM=xz+>qhIo# z=zqA5Q#o}cH_Rtt`>qH=6gbV5Y!rMtF^P-J z)i`HSpxWX}L9Y1yk*rYqV!myDG`eT0GU&#Z>zbbVuKtx3SxXD6{EL!Lq8WV-SrI6g zIuSv~d~MU97p+3{?^9n!c%vk#pSM#@w%_}BbB~TPk!Z7q}k9X)62)brZW=E)7#hc?e{Dl||0EUO#6S2;Sr zMiLMi2`sdFWtmE%z8i&ja}u6?Dz$OIqG~}ssZnDnfuIS7IiA6>c8D2)L4uw+YLvVs zwdRFk16qYq(DVGt1`-Hs9`NPVPSNK(+ib+MvS+b~(r#A37&atr0P$o9=W4`HoC+EK zO%$eL%_g~30>EsLwnBdQaHpX zrVNf{SPz5YYa%ff{DxlV6>7^0MCvz3*y%ZhgXr%~warkV(E{)mZdS>lh{lDgRq-2)(As4+3hFORi)=*UQ8h-CJ=UR?Ks`N|7Xkj?< z7HnHxCJMh^D)afhSF@Hpb~Fr z$Dt&{eCp0q7neY~?xiqUox2Pog3qKYL#4b>W0Z%(+`tLdOnH+jX8=~A&Y5P<)b9gj zrTid{o^7&hunKTipd!*_y1Ws(v*=GHNs`DtJME|X zL*>`$9NqWGWho1mDv6Qtq20I5j5ame&7?8=bD7QZUEh-%gUuCWcX~EI99qbJ>tSNa zx7}7D+VNgxz*D*{4B`k4&+v<+1Rj~^qbaFWC4&0*%*~BdLn=LKx{`|n3lcqjX!XtA z?g^GqJO@u4nGzyq_ZZTt=kLAe)b!2oG-Kl(dsW}?{Ha8NhF6sQ^3E&rYl7#so{`~5 znJ@W;n=CKFf6Qw;aU2|(#JH<*hEdADQ+0n{)oE~5jNk{^S$8jNT2ojV>0>s_^6iAt zUwdrv_BvYAl>GBxj2|1l(JnoA>*)urd;|fL68UbI z$j9##XFg`V&a<$y=ZQ66*1aqFwLoKKJi>YVeXU?aqGX(R+$E5+&~rPdG>PD3m?=~v z{B$sgWB`v94rqtJla<5a9GeKD6huV(IUW^>4Kna*+ z>A}Sii?2!xQq9=DxYP6u$uh@;ELvks>7YA}I- zAxhgj?>R+l&4dXY-f&oeBo|q~p$CLdlTZ)PXsOh+WMUw&d)?2oKbHbloh{2OfUDyf zo`c8Fuzw6lV(zaGKfhvVXTg!^1jR1$>wCP!t?li7<5QzKfoqY~c{$qs6ETo&ii=YJ?)u3H7tY5R9(H}gHCMq!2ide<$aFJIg|F@x`!)1%sNPTvnba&D1jXD)|z)?wmC_yg)Z-QA;n z+jlzm>?JCPs+E;Sg4%;8Cf-xET(^DgW$_lz$$TX-?bWLbTl-duNl3~%i*Y?&gHs89 zGmKJGr5hKl@TEd;(vaJhu}g5HOZ{p&nsX{ag~KLsGCY{UMFDtU1x62i8X6-PxF|vOn=k?Ln)5-204oF=?tX`z%0wgFzH|S z(V^`@4t%qu8S&V1Xpd9Y;d6!g>&9FDe${~o1HF_QzZy8=U&k4Up@n73*ycGg72k&q zbgWFF0JEU|jV`!~DF3WUuJn%n+2M|dT@Q3`m65lWj2n2}dy!w?mG=ZWBx8Sex$%9!p;AB9?loGR_wdWk5IQ_ezBv*B zK^?3C7G*V9a)@kyFE;6(*{bAjI=sop4?xzbbf!(vb)Z~JxTOwmH+h)O;7eqP~<>_VzGC;^^G%>9BhaQwGL{>JK4cN0EPUvdedLF-@s(bHXHFwiG=@$4v@k6}iOwItJeIQ%xynSv-4X9vhOhX(Rm@(+ZuKcW zNrD^*IeGmx)j4kN!0R*^zLq^Ribfs!b=Gd3a?|;o$$(oLFn|GudIkfSZWvJDf#sw= zh^4YcnOi@z7-f>ZSP7dK=Vl#hx9C|RM39Cp=m;+}KK3>IL0_5Lf!%A*H;l zG}jZid^uqaj}zjT&lFS7ok^R;U+tSy^bG&_An6LHgIib4e=uM}SaaGhO)ktTXHdvt z)Qby8Zeyk6354kqI1!2&8iNup-F=ppbfr)sNtBE*w}k(-OO7A}tm6#ooG5^WO(=0p zp=lcg0Y7W$N0yUC>_<>_ikHW;z;ydf(Naboe{hwxYlcRBP%YaHakxXq~0!L#VH^4eN z3}qeVf?{*ENiVfYch&H-peKanOA<%3w$futeU9VIgnA^vzb}M9t+Rl^S{OhC1KeQe z+i=!Y(4{8P#}Sk%fNzFSqA-H6EvSxPPFKgXp)El)d&nBidTd6Nra0tE(}WFyz$g-^M@x~R78P%-uL&+PUNv#xet*vCmDW}>67Yusl9ZN_YLv;8 zKruqPu#-j-2}~&?P`kBA;5!*`zxee=dfR=~JO_Lx$qhkR8wMlQP(1k_TrBIyQ?$9C zPJQ%>@~?XE*bX^<*JqZOR)?5Ze&<&445-}}3eAcQc_(oRII8FOp<44kx;yd#s*peH4-9b=ZCrHxT-@6JvgO6&!)U|Iic& zYDvxq?Yc+Mpd7KN7WpZGFoFcW9N#7`iVe{7XQKQSl?%r{Eg*OMiQ8*hLH-9~^{1%Q z1s{hfHSwEP>1a1N%?RHZ6#MfMy2m<>lnKXvRL5Jvb}2FHFLv^wSoU8blABfhp(Emlpr?&$)}dA(A@ zO?D`1ee*A!lRz zV7}LM?FNoCUy$G(dbe(ckl^()YBxgx1eeHlb1Tf2XSCeF|An>N8HIVk1-;8cpZFNb z0gYD?GhLKc>Z?U?%f@XS%@OEPvk$aPOTe4bqHn0vqa-sx|a}UT^i*ruNoMg8ToF&|3F&vk9y>|3ax^_H2(wc`~ z4+?hpyJ2PFwIvKkGclLWPX;Cdq^sT~{Qz?g<(^$~0!SMChA~$3R9} z2i5srwrvIh&#dIgzOP&hMVg#Y3JrpBJTwzW4YTu~p-2h!JSEy9(?p>xc-vsoGQ_^a z1|GtZzLW>On_meg{&sacI6YxR+glDRqq_jAH%-O-fjWbIbUV=@1!u`ihoNyfHWwF$ zi)$Uy1l3z^z$Hs=zYuD#UjW&0jnsz5wK&mXt`#NXN!tP|SNe(-5i1sDj%+?z)m~Ib zUp~c;3>Q+T4ERUIzTemWb`@irO>V4*eEeMSViYm{w()1%HOLf{8`RFwJG_37+Q3d~k{!h-dzefIv`sVk1 uOFVxj!ud-JP-&I_~QmHI^-_LpZ*8Q5_!)6 literal 0 HcmV?d00001 diff --git a/registers/parse_bits.py b/registers/parse_bits.py new file mode 100644 index 0000000..ef01bcf --- /dev/null +++ b/registers/parse_bits.py @@ -0,0 +1,126 @@ +from collections import defaultdict +from dataclasses import dataclass +from typing import Union + +def aggregate_registers(d): + aggregated = defaultdict(list) + for row in d: + #assert row["register_name"] != "" + aggregated[row["register_name"]].append(row) + return dict(aggregated) + +def parse_bit_number(s): + assert '-' not in s + return int(s, 10) + +def parse_bit_set(s, split_char): + assert len(list(c for c in s if c == split_char)) == 1 + left, right = map(parse_bit_number, s.split(split_char, maxsplit=1)) + assert left > right, (left, right) + return left, right + +def parse_bit_range(s): + if '-' in s: + left, right = parse_bit_set(s, '-') + return set(range(right, left+1)) + elif ',' in s: + left, right = parse_bit_set(s, ',') + return set([right, left]) + else: + num = parse_bit_number(s) + return set([num]) + +def parse_bit_range(s): + if '-' in s: + left, right = parse_bit_set(s, '-') + return set(range(right, left+1)) + elif ',' in s: + left, right = parse_bit_set(s, ',') + return set([right, left]) + else: + num = parse_bit_number(s) + return set([num]) + +def parse_value(value): + return eval(value) + +@dataclass +class Bit: + name: str + bits: set[int] + mask: Union[None, int] + value: Union[None, int] + +@dataclass +class Enum: + name: str + defs: list[dict] + +@dataclass +class Register: + block: Union[None, str] + name: str + defs: list[Union[dict, Enum]] + +def parse_row(row): + return Bit( + name=row['bit_name'], + bits=parse_bit_range(row['bits']), + mask=parse_value(row['mask']) if row['mask'].strip() else None, + value=parse_value(row['value']) if row['value'].strip() else None, + ) + +def aggregate_enums(aggregated_rows): + non_enum = [] + enum_aggregated = defaultdict(list) + all_bits = set() + enum_bits = dict() + + def assert_unique_ordered(bits, row): + nonlocal all_bits + assert all(bit not in all_bits for bit in bits), (bits, row) + assert max(all_bits, default=32) > max(bits), (all_bits, bits) + all_bits |= bits + + for row in aggregated_rows: + bit = parse_row(row) + assert row["bit_name"] != "", row + if row["enum_name"] == "": + assert_unique_ordered(bit.bits, row) + non_enum.append(bit) + else: + if row["enum_name"] not in enum_bits: + assert_unique_ordered(bit.bits, row) + non_enum.append(row["enum_name"]) + else: + assert enum_bits[row["enum_name"]] == bit.bits, row + + enum_bits[row["enum_name"]] = bit.bits + enum_aggregated[row["enum_name"]].append(bit) + + return non_enum, dict(enum_aggregated) + +def aggregate_all_enums(aggregated): + out = [] + for register_name, rows in aggregated.items(): + non_enum, enum_aggregated = aggregate_enums(rows) + def resolve(row_or_string): + if type(row_or_string) == str: + return Enum(row_or_string, + enum_aggregated[row_or_string]) + elif type(row_or_string) == Bit: + return row_or_string + else: + assert False, (row_or_string, type(row_or_string)) + + defs = [resolve(aggregate) for aggregate in non_enum] + if 'block' in rows[0]: + blocks = set(row['block'] for row in rows) + assert len(blocks) == 1, blocks + block_name = next(iter(blocks)) + out.append( + Register(block_name, register_name, defs)) + else: + out.append( + Register(None, register_name, defs)) + return out