From 34d8b4106b0f65a127437e432ccfbf53cae94f81 Mon Sep 17 00:00:00 2001 From: Joerg Deckert Date: Mon, 23 Jun 2014 15:12:41 +0200 Subject: [PATCH] initial commit --- .gitattributes | 5 + GUI-Demo.md | 106 + README.md | 46 + make.bat | 58 + make.sh | 58 + release.sh | 37 + source/boing/boing-bel-background.spin | 224 ++ source/boing/boing-bel-corecon.spin | 42 + source/boing/boing-bel-driver.spin | 553 +++++ source/boing/boing-bel-feeder.spin | 762 ++++++ source/boing/boing-bel-foreground.spin | 505 ++++ source/boing/boing-bel.spin | 286 +++ source/boing/boing-reg.spin | 272 +++ source/gui-demo/GUIBase.spin | 938 +++++++ source/gui-demo/GUIDemo.spin | 582 +++++ source/gui-demo/InputField.spin | 194 ++ source/gui-demo/Keyboard.spin | 736 ++++++ source/gui-demo/MenuItem.spin | 125 + source/gui-demo/Mouse.spin | 492 ++++ source/gui-demo/PushButton.spin | 117 + source/gui-demo/RadioCheck.spin | 153 ++ source/gui-demo/SimpleBox.spin | 93 + source/gui-demo/Simple_Numbers.spin | 197 ++ source/gui-demo/SpinBox.spin | 225 ++ source/gui-demo/StatusLamp.spin | 115 + source/gui-demo/TextBox.spin | 195 ++ source/gui-demo/Timer.spin | 128 + source/gui-demo/VGA_HiRes_Text.spin | 542 +++++ source/gui-demo/vga-vid.spin | 545 +++++ source/plexbus/joystick.spin | 62 + source/plexbus/map.spin | 91 + source/plexbus/paddle.spin | 72 + source/plexbus/port-io.spin | 83 + source/plexbus/port-test.spin | 112 + source/sid/1startrk.dmp | Bin 0 -> 57500 bytes source/sid/2startrk.dmp | Bin 0 -> 448750 bytes source/sid/Yie_Ar_Kung_Fu.dmp | Bin 0 -> 658775 bytes source/sid/boing.sid | Bin 0 -> 289000 bytes source/sid/triborg.sid | Bin 0 -> 73750 bytes source/triborg/triborg-bel.spin | 3098 ++++++++++++++++++++++++ source/triborg/triborg-reg.spin | 405 ++++ 41 files changed, 12254 insertions(+) create mode 100644 .gitattributes create mode 100644 GUI-Demo.md create mode 100644 README.md create mode 100644 make.bat create mode 100755 make.sh create mode 100755 release.sh create mode 100644 source/boing/boing-bel-background.spin create mode 100644 source/boing/boing-bel-corecon.spin create mode 100644 source/boing/boing-bel-driver.spin create mode 100644 source/boing/boing-bel-feeder.spin create mode 100644 source/boing/boing-bel-foreground.spin create mode 100644 source/boing/boing-bel.spin create mode 100644 source/boing/boing-reg.spin create mode 100644 source/gui-demo/GUIBase.spin create mode 100644 source/gui-demo/GUIDemo.spin create mode 100644 source/gui-demo/InputField.spin create mode 100644 source/gui-demo/Keyboard.spin create mode 100644 source/gui-demo/MenuItem.spin create mode 100644 source/gui-demo/Mouse.spin create mode 100644 source/gui-demo/PushButton.spin create mode 100644 source/gui-demo/RadioCheck.spin create mode 100644 source/gui-demo/SimpleBox.spin create mode 100644 source/gui-demo/Simple_Numbers.spin create mode 100644 source/gui-demo/SpinBox.spin create mode 100644 source/gui-demo/StatusLamp.spin create mode 100644 source/gui-demo/TextBox.spin create mode 100644 source/gui-demo/Timer.spin create mode 100644 source/gui-demo/VGA_HiRes_Text.spin create mode 100644 source/gui-demo/vga-vid.spin create mode 100644 source/plexbus/joystick.spin create mode 100644 source/plexbus/map.spin create mode 100644 source/plexbus/paddle.spin create mode 100644 source/plexbus/port-io.spin create mode 100644 source/plexbus/port-test.spin create mode 100644 source/sid/1startrk.dmp create mode 100644 source/sid/2startrk.dmp create mode 100644 source/sid/Yie_Ar_Kung_Fu.dmp create mode 100644 source/sid/boing.sid create mode 100644 source/sid/triborg.sid create mode 100644 source/triborg/triborg-bel.spin create mode 100644 source/triborg/triborg-reg.spin diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f128240 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Auto detect text files and perform LF normalization + +* text=auto + +*.bat text eol=crlf diff --git a/GUI-Demo.md b/GUI-Demo.md new file mode 100644 index 0000000..f2bd687 --- /dev/null +++ b/GUI-Demo.md @@ -0,0 +1,106 @@ +GUI-DEMO +======== + +Text based UI Elements for Propeller for use with the VGA_HiRes_Text Driver + + +Demo / Text program +------------------- + + GUIDemo.spin - the demo app + + +Files that comprise the UI Elements +----------------------------------- + + FILE LONGS USED DESCRIPTION + PROG DATA + ---------------------- ------ ------ ----------------------------------- + GUIBase.spin 2,472 1,643 - UI control and infrastructure + InputField.spin 168 6 - Input Field object + MenuItem.spin 62 13 - Menu Item object + PushButton.spin 124 12 - Push Button object + RadioCheck.spin 78 13 - Radio Button and Check Box object + SimpleBox.spin 62 0 - Basic Box object + SpinBox.spin 191 12 - Spin Button object + TextBox.spin 206 4 - Text Box object + StatusLamp.spin 52 6 - Status Annunciator + +Required Drivers +---------------- + Vga_HiRes_Text.spin - VGA High Resolution Text Driver + Mouse.spin - PS2 Mouse driver + Keyboard.spin - PS2 Keyboard Driver + + +Description +----------- + +This text based UI for the High Res VGA Driver will work in all resolutions that +the driver supports, the demo itself is setup for 800x600 but will show OK at +1024x768 too. At 640x480 it will not look right (needs to be repositioned and +re-sized), It is very much a fist principles implementation with no bells and +whistles. It supports a static UI and only the basic functionality. To use the +UI Elements the user must decide on a screen layout and then place the elements +there. You only need to include the objects that you plan to use, you do not +have to declare them all. This is done at the top of GUIBase.spin in the +constants where you delcare how mane of each element type you will need. + + +There are 6 simple steps to using these objects: + + 1. Configure the numbers and types of GUI elements you need in the GUI Element + Inventory section of the CON section of the GUIBase.spin file. See the top + of that file for simple directions. + + 2. Declare the GUI object in your application's OBJ section. + + OBJ + GUI : "GUIBase" + + 3. Initialize the GUI object in your application's startup. You will need to + pass in the VGA, Mouse and Keyboard pin assignments you are using for each + of thise devices. This will initialise the GUI data structures and start the + VGA (High Res Text), Mouse, and Keyboard drivers. + + GUI.Init( VGABase, MouseDat, MouseCLk, KeyboardDat, KeyboardClk ) + + 4. Create the GUI Elements as required by your application by calling the + specific "Init" fucntion in GUIBase.spin. Note that all GUI interation and + control will be via the functions in GUIBase.spin, you will not need to + access the individual GUI Element files directly. For each element you + create you will need to have a variable in your VAR section to hold its + identifier. For example + + PUSH1 := GUI.PUSHInit( ... ) + + Creates a new pushbutton object identified by the variable PUSH1 which was + declared as a byte in the VAR section. This variable will be used to pass + in to calls to handle the object and will be returned to you for action in + the event that the mouse is clicked on it. + + 5. Create a main loop for your application that will service both your + application and run the processing for the UI. The UI is processed via the + ProcessUI function in GUIBase.spin. + + repeat + ProcessApplication + elem := GUI.ProcessUI + if elem <> -1 + case elem + PUSH1: CallYourFunctionForPUSH1() + PUSH2: CallYourFunctionForPUSH1() + etc... + + The ProcessUI function will handle the UI for you, highlighting elements as + the mouse passes over them, processing mouse clicks and keyboard entry, etc. + It will return -1 if no action is required (i.e. you did not click on + anything). If the user clicked on something (i.e. Push Button PUSH1 perhaps) + it will return the guid of the element that requires action (i.e. PUSH1). + In this case you will need to perform an action associated with the Push + Button. It is probably best to keep the main loop clean and call a function + to perform tha action rather than do it right in the loop. That makes it + easier to read and maintain. + + 6. Create your user action functions, one for each GUI Element that will do + something. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b1b70f --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +Toolbox 3 +========= + +Neues Futter für den Hive. Wie bei der ersten Toolbox genügt es den Inhalt des +Ordners "BIN/SDCARD" auf ein passendes Medium zu kopieren. + + +Inhalt: Ordner tbox-3 +===================== + +BOING +----- + +Eine Icone der Retrokultur auf dem Hive als Musikplayer! +Basis ist der geniale Grafikcode von kuroneko. + + - alle Dateien müssen sich in einem Verzeichis befinden + - weitere dmp-Dateien werden wie in einem Player abgespielt + +Tastencodes: + + [n] - nächster Titel + [p] - Pause + [esc] - Programmende + +Graphics-Code: kuroneko +SIDcog: Ahle2 +Hive-Code: drohne235 + + +GUI-DEMO +-------- + +Demo von einem textbasierten GUI-Code + + +PLEXBUS +------- + +Programmierbeispiele für den PlexBus und die Sepia-Karte + + +TRIBORG +------- + +Soundplayer diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..63bb99d --- /dev/null +++ b/make.bat @@ -0,0 +1,58 @@ +echo on +date /T +time /T + +REM # Definitionen +REM set D="-D __DEBUG -D __LANG_EN" +REM set D="-D __LANG_EN" +set D="-D __LANG_DE" + +REM # Pfade +set bin="..\Bin" +REM set bin="\home\ftp\hive" +set sdtbox="%bin%\sdcard\tbox-3" +set libpath="..\TriOS\lib" +set BSTC=bstc.exe + +REM ---------------------------------------------------------------- +REM Alte Versionen löschen + +rmdir %sdtbox% /S /Q +mkdir %sdtbox% +mkdir %sdtbox%\boing +mkdir %sdtbox%\gui-demo +mkdir %sdtbox%\plexbus +mkdir %sdtbox%\triborg + +REM del %sdsys%\xxxxxx +REM mkdir %sdsys% + +REM ---------------------------------------------------------------- +REM Bin-Dateien erzeugen, kopieren + +%BSTC% -L %libpath% %D% -b -O a .\source\boing\boing-reg.spin +move boing-reg.binary "%sdtbox%\boing\boing.bin" +%BSTC% -L %libpath% %D% -b -O a .\source\boing\boing-bel.spin +move boing-bel.binary "%sdtbox%\boing\boing.bel" +%BSTC% -L %libpath% %D% -D __ADM_FAT -D __ADM_SID -b -O a ..\TriOS\flash\administra\admflash.spin +move admflash.binary "%sdtbox%\boing\boing.adm" +copy .\source\sid\boing.sid "%sdtbox%\boing\" +copy .\source\sid\*.dmp "%sdtbox%\boing\" + +%BSTC% -L %libpath% %D% -b -O a .\source\triborg\triborg-reg.spin +move triborg-reg.binary "%sdtbox%\triborg\triborg.bin" +%BSTC% -L %libpath% %D% -b -O a .\source\triborg\triborg-bel.spin +move triborg-bel.binary "%sdtbox%\triborg\triborg.bel" +%BSTC% -L %libpath% %D% -D __ADM_FAT -D __ADM_SID -b -O a ..\TriOS\flash\administra\admflash.spin +move admflash.binary "%sdtbox%\triborg\triborg.adm" +copy .\source\sid\triborg.sid "%sdtbox%\triborg\" +copy .\source\sid\*.dmp "%sdtbox%\triborg\" + +for %%x in (.\source\plexbus\*.spin) do %BSTC% -L %libpath% %D% -b -O a %%x +rename *.binary *.bin +move *.bin "%sdtbox%\plexbus\" + +%BSTC% -L %libpath% %D% -e -O a .\source\gui-demo\guidemo.spin +move guidemo.eeprom "%sdtbox%\gui-demo\guidemo.bel" + +echo off diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..26d5f74 --- /dev/null +++ b/make.sh @@ -0,0 +1,58 @@ +#! /bin/sh + +# Definitionen +##D="-D __DEBUG -D __LANG_EN" +##D="-D __LANG_EN" +D="-D __LANG_DE" + +# Pfade +bin="../Bin" +##bin="/home/ftp/hive" +sdtbox="${bin}/sdcard/tbox-3" +libpath="../TriOS/lib" +BSTC="bstc" + +# ---------------------------------------------------------------- +# Alte Versionen löschen + +rm -rf ${sdtbox} +mkdir -p ${sdtbox} +mkdir ${sdtbox}/boing +mkdir ${sdtbox}/gui-demo +mkdir ${sdtbox}/plexbus +mkdir ${sdtbox}/triborg + +##rm -f ${sdsys}/xxxxxx +##mkdir -p ${sdsys} + + +# ---------------------------------------------------------------- +# Bin-Dateien erzeugen, kopieren + + +${BSTC} -L ${libpath} ${D} -b -O a source/boing/boing-reg.spin +mv boing-reg.binary "${sdtbox}/boing/boing.bin" +${BSTC} -L ${libpath} ${D} -b -O a source/boing/boing-bel.spin +mv boing-bel.binary "${sdtbox}/boing/boing.bel" +${BSTC} -L ${libpath} ${D} -D __ADM_FAT -D __ADM_SID -b -O a ../TriOS/flash/administra/admflash.spin +mv admflash.binary "${sdtbox}/boing/boing.adm" +cp source/sid/boing.sid "${sdtbox}/boing/" +cp source/sid/*.dmp "${sdtbox}/boing/" + +${BSTC} -L ${libpath} ${D} -b -O a source/triborg/triborg-reg.spin +mv triborg-reg.binary "${sdtbox}/triborg/triborg.bin" +${BSTC} -L ${libpath} ${D} -b -O a source/triborg/triborg-bel.spin +mv triborg-bel.binary "${sdtbox}/triborg/triborg.bel" +${BSTC} -L ${libpath} ${D} -D __ADM_FAT -D __ADM_SID -b -O a ../TriOS/flash/administra/admflash.spin +mv admflash.binary "${sdtbox}/triborg/triborg.adm" +cp source/sid/triborg.sid "${sdtbox}/triborg/" +cp source/sid/*.dmp "${sdtbox}/triborg/" + +for FILE in source/plexbus/*.spin ; do + ${BSTC} -L ${libpath} ${D} -b -O a ${FILE} + BASE="`basename ${FILE} .spin`" + mv "${BASE}.binary" "${sdtbox}/plexbus/${BASE}.bin" +done + +${BSTC} -L ${libpath} ${D} -e -O a source/gui-demo/guidemo.spin +mv guidemo.eeprom "${sdtbox}/gui-demo/guidemo.bel" diff --git a/release.sh b/release.sh new file mode 100755 index 0000000..482f6e8 --- /dev/null +++ b/release.sh @@ -0,0 +1,37 @@ +#! /bin/sh + +# Definitionen +VERSION="1.1" + +# Pfade +ARCHIV="HIVE-Toolbox3-${VERSION}" +MAKE="./make.sh" +BIN="Bin" + +# ---------------------------------------------------------------- +# Alte Versionen löschen + +rm -rf ../${BIN} +rm -f ../${ARCHIV}-bin.zip +rm -f ../${ARCHIV}-src.zip + +# ---------------------------------------------------------------- +# Binaries erstellen + +${MAKE} + +# ---------------------------------------------------------------- +# Archive erstellen + +mkdir .tmp +for file in *.md ; do cp "$file" .tmp/"${file/.md}".txt ; done +cd .tmp +zip -r9 ../../${ARCHIV}-bin.zip * +zip -r9 ../../${ARCHIV}-src.zip * +cd .. +rm -rf .tmp + +zip -r9 ../${ARCHIV}-src.zip sounds source make* + +cd .. +zip -r9 ${ARCHIV}-bin.zip ${BIN} diff --git a/source/boing/boing-bel-background.spin b/source/boing/boing-bel-background.spin new file mode 100644 index 0000000..69cee18 --- /dev/null +++ b/source/boing/boing-bel-background.spin @@ -0,0 +1,224 @@ +'' +'' VGA scanline driver 400x300 - background renderer +'' +'' Based on "Ball" demo for Gameduino +'' Copyright (c) 2011 by James Bowman +'' +'' Author: Marko Lukat +'' Last modified: 2012/12/24 +'' Version: 0.9 +'' +OBJ + system: "boing-bel-corecon" + +PUB null +'' This is not a top level object. + +PUB init(ID, mailbox) + + return system.launch(ID, @entry, mailbox) + +DAT org 0 ' background renderer + +entry jmpret $, #setup ' once + + rdlong indx, blnk ' | + cmpsub indx, scry wz ' | + if_ne jmp #$-2 ' waiting for last line to be fetched + +' Skip 26 sync lines and advance by a further 161 hub windows. This pushes the +' first background renderer wrlong beyond the first video renderer line rdlong +' if it were to render scanline -1. +' After the waitcnt we have another 9 insn (36 + 18 + 10 = 64, 4 hub windows). + + mov cnt, cnt + add cnt, $+1 + long 13{18} + 10 + 132*16*26 + 16*(161 - 4) + +loop waitcnt cnt, one ' initial sync point + + call #fill ' fill background + + add indx, #1 ' line done, advance + cmpsub indx, scry wz ' optionally wrap line index + if_z add cnt, two + + +one long 132*16*2 ' skip two scan lines +two long 132*16*28 ' skip all sync lines + + jmp #loop + +' support code + +fill + mov addr, indx + shr addr, #1 wc + add read, addr +fill_ret long 0-0 ' pipeline +read mov addr, $+3 + if_c shr addr, #16 + jmpret read, addr ' render background + + word eins[5], zwei[3] + word drei[21], zwei[3], drei[21], zwei[3], drei[21], zwei[3], drei[21], zwei[3] + word drei[21], zwei[3], drei[21], zwei[3], drei[21], zwei[3], drei[21], zwei[3] + word drei[21], zwei[3], drei[21], zwei[3], drei[21], zwei[3], drei[21], zwei[3] + word eins[4] + +eins mov addr, base ' 100(A) + + mov gindx, gindx0 + mov gflag, #0 + + mov qcnt, #100 +:loop wrlong ty_A, addr + add addr, #4 + djnz qcnt, #:loop + + jmp fill_ret + +zwei mov addr, base ' AB(48(CC))DA + + cmp gflag, #0 wz + if_z mov gflag, #1 + if_z rdword gline0, gindx + if_z add gindx, #4 + + wrlong ty_A, addr + add addr, #4 + wrlong ty_B, addr + add addr, #4 + + mov qcnt, #96/2 +:loop wrlong ty_C, addr + add addr, #4 + cmp qcnt, #1 wz ' last run? + wrlong ty_C, addr + add addr, #4 + if_ne djnz qcnt, #:loop + + wrlong ty_D, addr + add addr, #4 + wrlong ty_A, addr + + jmp fill_ret + +drei mov addr, base ' AB(16(DAAAAB))DA + + mov gline, gline0 + mov gflag, #0 + + wrlong ty_A, addr + add addr, #4 + wrlong ty_B, addr + add addr, #4 + + mov qcnt, #16 + +:loop mov ty_x1, ty_D ' leere punkte + mov ty_X2, ty_A + mov ty_X3, ty_B + shr gline,#1 wc + if_nc jmp #:empty + mov ty_X1, ty_C ' gefüllte punkte + mov ty_X2, ty_C + mov ty_X3, ty_C +:empty + wrlong ty_X1, addr + add addr, #4 + cmp qcnt, #1 wz ' last run? + '--------------------- + wrlong ty_X2, addr + add addr, #4 + wrlong ty_X2, addr + add addr, #4 + wrlong ty_X2, addr + add addr, #4 + wrlong ty_X2, addr + add addr, #4 + '--------------------- +:end wrlong ty_X3, addr + add addr, #4 + if_ne djnz qcnt, #:loop + + wrlong ty_D, addr + add addr, #4 + wrlong ty_A, addr + + jmp fill_ret + + + +' initialised data and/or presets + +blnk long -4 +base long NEGX + +ty_A {----} long $01010101 * C0 +ty_B {---#} long $00010101 * C0 + $01000000 * C1 +ty_C {####} long $01010101 * C1 +ty_D {##--} long $01010000 * C0 + $00000101 * C1 +ty_X1 long 0 +ty_X2 long 0 +ty_X3 long 0 + +gindx0 long 400 +gindx long 0 +gline0 long 0 +gline long 0 +gflag long 0 + +' Stuff below is re-purposed for temporary storage. + +setup add base, par ' scanline buffer (%%) + add blnk, base wc ' frame indicator + + rdword indx, blnk wz ' (%%) + if_nz mov scry, indx wc ' (%%) + if_c_or_nz jmp #$-2 ' auto-detect res_y + +' The loop is only left once a non-zero value has been written to scry +' and indx transitions to zero afterwards. + + add gindx0, par + jmp %%0 ' return + + fit + +' uninitialised data and/or temporaries + +scry res 1 ' must be 1st/2nd (%%) +indx res 1 ' | + +addr res 1 +qcnt res 1 + +tail fit + +CON + C0 = %%2220 ' background + C1 = %%1110 ' grid + +DAT +{{ + + TERMS OF USE: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + associated documentation files (the "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +}} +DAT diff --git a/source/boing/boing-bel-corecon.spin b/source/boing/boing-bel-corecon.spin new file mode 100644 index 0000000..125188b --- /dev/null +++ b/source/boing/boing-bel-corecon.spin @@ -0,0 +1,42 @@ +'' +'' prerequisites, dependencies & Co +'' +'' Author: Marko Lukat +'' Last modified: 2011/10/22 +'' Version: 0.9 +'' +CON + _clkmode = XTAL1|PLL16X + _xinfreq = 5_000_000 + +CON + ID_0 = $30B3309C + ID_1 = $9ED2732B + ID_2 = $38343032 ' cog binary magic number (2048) + + OVERLAY = %00000000_00000001 ' cog binary is an overlay + MAPPING = %00000000_00000010 ' translation table is present + +PUB null +'' This is not a top level object. + +PUB launch(ID, code, data) +'' PASM quick launch using a specific or the next available ID. +'' +'' parameters +'' ID: cog ID +'' 0..7: coginit, otherwise cognew (may fail) +'' code: address of code fragment (4n) +'' data: cognew/coginit parameter (4n) +'' +'' result +'' == 0: [ABORT] thread creation failed (cognew only) +'' <> 0: thread/cog ID + 1 + + ifnot (ID >> 3) + coginit(ID++, code, data) + elseifnot ID := cognew(code, data) + 1 + abort + return ID + +DAT diff --git a/source/boing/boing-bel-driver.spin b/source/boing/boing-bel-driver.spin new file mode 100644 index 0000000..622fb5a --- /dev/null +++ b/source/boing/boing-bel-driver.spin @@ -0,0 +1,553 @@ +'' +'' VGA scanline driver 400x300 (single cog) - video driver and pixel generator +'' +'' Author: Marko Lukat +'' Last modified: 2013/01/11 +'' Version: 0.10 +'' +'' - timing signalled as SVGA 800x600 +'' - vertical blank start sets frame indicator (FI) to 0 +'' - once the Nth scanline has been fetched the FI is set to N+1 +'' +'' 20121007: added minimal translation table +'' 20121229: now capable of using 64/256 colours (RRGGBBHV / RRGGBBgr + xxxxxxHV) +'' - 64c: $FC/2/2 (vpin/vgrp/sgrp) +'' - 256c: $FF/2/3 +'' 20121230: simplified vertical blanking code +'' +'' Note: With idle state being %00 {%hv} the switch code can be further simplified. +'' As this was the first converted driver I left the generic solution intact +'' so it can easily be adapted for other drivers (with non-zero idle state). +'' +OBJ + system: "boing-bel-corecon" + +PUB null +'' This is not a top level object. + +PUB init(ID, mailbox) + + return system.launch(ID, @driver, mailbox) + +DAT org 0 ' cog binary header + +header_2048 long system#ID_2 ' magic number for a cog binary + word header_size ' header size + word system#MAPPING ' flags + word 0, 0 ' start register, register count + + word @__table - @header_2048 ' translation table byte offset + +header_size fit 16 + +DAT org 0 ' video driver + +driver jmpret $, #setup ' -4 once + + mov dira, mask ' drive outputs + +' horizontal timing 400(800) 20(40) 64(128) 44(88) +' vertical timing 300(600) 1(1) 4(4) 23(23) + +vsync mov lcnt, #0 ' | + wrlong lcnt, blnk ' reset line counter (once) + + mov vscl, full ' 32/528 + +' mov ecnt, #1 + waitvid sync, #%%0011 ' front porch +' djnz ecnt, #$-1 + + xor sync, #$0101 ' active + + mov ecnt, #4 + waitvid sync, #%%0011 ' vertical sync + djnz ecnt, #$-1 + + xor sync, #$0101 ' inactive + + mov ecnt, #23 + waitvid sync, #%%0011 ' back porch + djnz ecnt, #$-1 + +' Vertical sync chain done, do visible area. + + mov scnt, #res_y + +:loop call #prefix ' sync and back porch + jmpret suffix_ret, #emit_0 ' visible line and front porch + + call #prefix ' sync and back porch + jmpret suffix_ret, #emit_1 ' visible line and front porch + + djnz scnt, #:loop ' repeat for all lines + + jmp #vsync ' next frame + + +prefix mov vscl, slow ' 32/108 + waitvid sync, #%%2011 ' latch sync and back porch + + mov cnt, cnt ' cover sync pulse period (%%) + add cnt, #9{14}+(64 * 4) ' because we drive sync lines + waitcnt cnt, #135 ' manually next (%%) + + mov outa, idle ' take over sync lines (##) +prefix_ret ret + + +suffix mov vscl, hs_f ' 1/20 + waitvid sync, #%%02 ' latch front porch + +' waitvid +' | S D e . . . R | mov vcfg | mov outa | +' clock  +' │ │ │ │ +' PLL  +' pixel 1:4 │+4 +' │ │ pixel %%2 │ pixel %%0 | +' outa  + + mov vcfg, vcfg_sync ' drive/change sync lines (##) + mov outa, #0 ' stop interfering + + if_nc wrlong lcnt, blnk ' report current line +suffix_ret ret + +' Even line emitter (fetch & emit). + +emit_0 waitcnt cnt, #0 ' re-sync after back porch (%%) + +' At this point the video h/w is driving the sync lines low (colour %%2). +' outa has taken over long ago so we can switch configuration here. + + mov vcfg, vcfg_norm ' -12 disconnect sync from video h/w (##) + mov addr, base ' -8 working copy + mov vscl, hvis ' -4 1/4 + + rdlong pal+$00, addr ' +0 = + cmp pal+$00, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$01, addr ' +0 = + cmp pal+$01, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$02, addr ' +0 = + cmp pal+$02, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$03, addr ' +0 = + cmp pal+$03, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$04, addr ' +0 = + cmp pal+$04, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$05, addr ' +0 = + cmp pal+$05, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$06, addr ' +0 = + cmp pal+$06, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$07, addr ' +0 = + cmp pal+$07, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$08, addr ' +0 = + cmp pal+$08, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$09, addr ' +0 = + cmp pal+$09, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$0A, addr ' +0 = + cmp pal+$0A, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$0B, addr ' +0 = + cmp pal+$0B, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$0C, addr ' +0 = + cmp pal+$0C, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$0D, addr ' +0 = + cmp pal+$0D, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$0E, addr ' +0 = + cmp pal+$0E, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$0F, addr ' +0 = + cmp pal+$0F, #%%3210 ' +8 WHOP + add addr, #4 ' -4 0..63 + + rdlong pal+$10, addr ' +0 = + cmp pal+$10, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$11, addr ' +0 = + cmp pal+$11, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$12, addr ' +0 = + cmp pal+$12, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$13, addr ' +0 = + cmp pal+$13, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$14, addr ' +0 = + cmp pal+$14, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$15, addr ' +0 = + cmp pal+$15, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$16, addr ' +0 = + cmp pal+$16, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$17, addr ' +0 = + cmp pal+$17, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$18, addr ' +0 = + cmp pal+$18, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$19, addr ' +0 = + cmp pal+$19, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$1A, addr ' +0 = + cmp pal+$1A, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$1B, addr ' +0 = + cmp pal+$1B, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$1C, addr ' +0 = + cmp pal+$1C, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$1D, addr ' +0 = + cmp pal+$1D, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$1E, addr ' +0 = + cmp pal+$1E, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$1F, addr ' +0 = + cmp pal+$1F, #%%3210 ' +8 WHOP + add addr, #4 ' -4 64..127 + + rdlong pal+$20, addr ' +0 = + cmp pal+$20, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$21, addr ' +0 = + cmp pal+$21, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$22, addr ' +0 = + cmp pal+$22, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$23, addr ' +0 = + cmp pal+$23, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$24, addr ' +0 = + cmp pal+$24, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$25, addr ' +0 = + cmp pal+$25, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$26, addr ' +0 = + cmp pal+$26, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$27, addr ' +0 = + cmp pal+$27, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$28, addr ' +0 = + cmp pal+$28, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$29, addr ' +0 = + cmp pal+$29, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$2A, addr ' +0 = + cmp pal+$2A, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$2B, addr ' +0 = + cmp pal+$2B, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$2C, addr ' +0 = + cmp pal+$2C, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$2D, addr ' +0 = + cmp pal+$2D, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$2E, addr ' +0 = + cmp pal+$2E, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$2F, addr ' +0 = + cmp pal+$2F, #%%3210 ' +8 WHOP + add addr, #4 ' -4 128..191 + + rdlong pal+$30, addr ' +0 = + cmp pal+$30, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$31, addr ' +0 = + cmp pal+$31, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$32, addr ' +0 = + cmp pal+$32, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$33, addr ' +0 = + cmp pal+$33, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$34, addr ' +0 = + cmp pal+$34, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$35, addr ' +0 = + cmp pal+$35, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$36, addr ' +0 = + cmp pal+$36, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$37, addr ' +0 = + cmp pal+$37, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$38, addr ' +0 = + cmp pal+$38, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$39, addr ' +0 = + cmp pal+$39, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$3A, addr ' +0 = + cmp pal+$3A, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$3B, addr ' +0 = + cmp pal+$3B, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$3C, addr ' +0 = + cmp pal+$3C, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$3D, addr ' +0 = + cmp pal+$3D, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$3E, addr ' +0 = + cmp pal+$3E, #%%3210 ' +8 WHOP + add addr,#4 ' -4 + rdlong pal+$3F, addr ' +0 = + cmp pal+$3F, #%%3210 ' +8 WHOP + add addr, #4 ' -4 192..255 + + rdlong pal+$40, addr ' +0 = + cmp pal+$40, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$41, addr ' +0 = + cmp pal+$41, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$42, addr ' +0 = + cmp pal+$42, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$43, addr ' +0 = + cmp pal+$43, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$44, addr ' +0 = + cmp pal+$44, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$45, addr ' +0 = + cmp pal+$45, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$46, addr ' +0 = + cmp pal+$46, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$47, addr ' +0 = + cmp pal+$47, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$48, addr ' +0 = + cmp pal+$48, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$49, addr ' +0 = + cmp pal+$49, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$4A, addr ' +0 = + cmp pal+$4A, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$4B, addr ' +0 = + cmp pal+$4B, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$4C, addr ' +0 = + cmp pal+$4C, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$4D, addr ' +0 = + cmp pal+$4D, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$4E, addr ' +0 = + cmp pal+$4E, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$4F, addr ' +0 = + cmp pal+$4F, #%%3210 ' +8 WHOP + add addr, #4 ' -4 256..319 + + rdlong pal+$50, addr ' +0 = + cmp pal+$50, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$51, addr ' +0 = + cmp pal+$51, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$52, addr ' +0 = + cmp pal+$52, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$53, addr ' +0 = + cmp pal+$53, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$54, addr ' +0 = + cmp pal+$54, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$55, addr ' +0 = + cmp pal+$55, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$56, addr ' +0 = + cmp pal+$56, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$57, addr ' +0 = + cmp pal+$57, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$58, addr ' +0 = + cmp pal+$58, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$59, addr ' +0 = + cmp pal+$59, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$5A, addr ' +0 = + cmp pal+$5A, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$5B, addr ' +0 = + cmp pal+$5B, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$5C, addr ' +0 = + cmp pal+$5C, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$5D, addr ' +0 = + cmp pal+$5D, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$5E, addr ' +0 = + cmp pal+$5E, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$5F, addr ' +0 = + cmp pal+$5F, #%%3210 ' +8 WHOP + add addr, #4 ' -4 320..383 + + rdlong pal+$60, addr ' +0 = + cmp pal+$60, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$61, addr ' +0 = + cmp pal+$61, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$62, addr ' +0 = + cmp pal+$62, #%%3210 ' +8 WHOP + add addr, #4 ' -4 + rdlong pal+$63, addr ' +0 = + cmp pal+$63, #%%3210 ' +8 WHOP + add lcnt, #1 ' -4 384..399, line has been fetched + + jmpret $, #suffix wc,nr ' +0 = chain call (carry clear) + +' Odd line emitter (emit only). + +emit_1 waitcnt cnt, #0 ' re-sync after back porch (%%) + +' At this point the video h/w is driving the sync lines low (colour %%2). +' outa has taken over long ago so we can switch configuration here. + + mov vcfg, vcfg_norm ' -12 disconnect sync from video h/w (##) + movd :vid, #pal+$00 ' -8 colour buffer + mov vscl, hvis ' -4 1/4 + + mov ecnt, #100 -1 ' +0 = quad pixel count (last one separate) + test $, #1 wc ' +4 set carry +:vid cmp 0-0, #%%3210 ' +8 WHOP + addx $-1, #511{+C} ' -4 + djnz ecnt, #:vid -1 ' +0 = maintain 16 cycle loop + + cmp pal+$63, #%%3210 ' +8 WHOP + + jmpret zero, #suffix wc,nr ' -4 chain call (carry set) + +' initialised data and/or presets + +idle long (hv_idle & $00FF) << (sgrp * 8) +sync long (hv_idle ^ $0200) & $FFFF + +hvis long 1 << 12 | 4 ' 1/4 +hs_f long 1 << 12 | 20 ' 1/20 +slow long 32 << 12 | 108 ' 32/108 +full long 32 << 12 | 528 ' 32/528 + +vcfg_norm long %0_01_1_00_000 << 23 | vgrp << 9 | vpin +vcfg_sync long %0_01_1_00_000 << 23 | sgrp << 9 | %11 + +mask long vpin << (vgrp * 8) | %11 << (sgrp * 8) + +blnk long -4 +base long +0 + +' Stuff below is re-purposed for temporary storage. + +setup rdlong cnt, #0 ' +0 = clkfreq + + add blnk, par ' +8 frame indicator + neg href, cnt ' -4 hub window reference + add base, par ' +0 = scanline buffer + +' Upset video h/w and relatives. + + movi ctra, #%0_00001_101 ' PLL, VCO/4 + movi frqa, #%0001_00000 ' 5MHz * 16 / 4 = 20MHz + + mov vscl, hvis ' 1/4 + mov vcfg, vcfg_sync ' VGA, 4 colour mode + + shr cnt, #10 ' ~1ms + add cnt, cnt + waitcnt cnt, #0 ' PLL needs to settle + +' The first issued waitvid is a bit of a gamble if we don't know where the WHOP +' is located. We could do some fancy math or simply issue a dummy waitvid. + + waitvid zero, #0 ' dummy (first one is unpredictable) + waitvid zero, #0 ' point of reference + + add href, cnt ' get current sync slot + shr href, #2 ' 4 system clocks per pixel + neg href, href ' | + and href, #%11 ' calculate adjustment + +' WHOP is reasonably far away so we can update vscl without re-sync. + + add vscl, href ' | + waitvid zero, #0 ' stretch frame + sub vscl, href ' | + waitvid zero, #0 ' restore frame + +' Setup complete, do the heavy lifting upstairs ... + + jmp %%0 ' return + +' uninitialised data and/or temporaries + + org setup + +href res 1 ' hub window reference + +ecnt res 1 ' element count +lcnt res 1 ' line counter +scnt res 1 ' scanlines +addr res 1 ' colour buffer reference + +pal res 100 ' colour buffer + +tail fit + +DAT ' translation table + +__table word (@__names - @__table)/2 + + word res_x + word res_y + +__names byte "res_x", 0 + byte "res_y", 0 + +CON + zero = $1F0 ' par (dst only) + vpin = $0FC ' pin group mask + vgrp = 1 ' pin group + sgrp = 1 ' pin group sync + hv_idle = $01010101 * %00 {%hv} ' h/v sync inactive + + res_x = 400 ' | + res_y = 300 ' UI support + +DAT diff --git a/source/boing/boing-bel-feeder.spin b/source/boing/boing-bel-feeder.spin new file mode 100644 index 0000000..a33a474 --- /dev/null +++ b/source/boing/boing-bel-feeder.spin @@ -0,0 +1,762 @@ +'' +'' VGA scanline driver 400x300 - image feeder +'' +'' Based on "Ball" demo for Gameduino +'' Copyright (c) 2011 by James Bowman +'' +'' Author: Marko Lukat +'' Last modified: 2013/01/03 +'' Version: 0.6 +'' +'' 20121225: adjusted palette (brighter) +'' 20121226: 256 colour setup (RRGGBBgr) +'' 20130103: minor tweak to palette table +'' +OBJ + system: "boing-bel-corecon" + +VAR + long guard_before, buffer[BSIZE/4], guard_after + byte image[BSIZE*BSIZE] + + long GD_srcp, GD_mask, GD_stack[32] + +PUB null +'' This is not a top level object. + +PUB init(ID, mailbox) + + long[mailbox][-3] := @image{0} >< 32 | @buffer{0} + return system.launch(ID, @entry, mailbox) + +PUB uncompress(wait{boolean}) + + ifnot wait + if cognew(GD_uncompress(256, @ball), @GD_stack{0}) +1 + return + + GD_uncompress(256, @ball) + +PRI GD_uncompress(offs, srcp) | b_off, b_len, minlen, offset + + GD_srcp := srcp + GD_mask := $01010101 + + b_off := GD_getn(4) + b_len := GD_getn(4) + minlen := GD_getn(2) + + repeat GD_getn(16) + ifnot GD_get1 + image[GD_translate(offs++)] := !GD_getn(8) + next + offset := offs - GD_getn(b_off) - 1 + repeat GD_getn(b_len) + minlen + image[GD_translate(offs++)] := image[GD_translate(offset++)] + +PRI GD_getn(n) : r + + repeat n + r := r << 1 | GD_get1 + +PRI GD_get1 : r + + if byte[GD_srcp] & GD_mask + r := 1 + + if (GD_mask <-= 1) == $01010101 + GD_srcp++ + +PRI GD_translate(offs) : r + + if offs => constant(256*(5+1)) + offs += 256 + if offs => constant(256*(40+2)) + offs += 256 + + r := offs.byte[1] + return (r / 7)*constant(16*112) + (r // 7) << 4 + offs & 15 + (offs & $F0)*7 + +DAT org 0 ' image feeder + +entry jmpret $, #setup ' once + + rdlong indx, blnk ' | + cmpsub indx, scry wz ' | + if_ne jmp #$-2 ' waiting for last line to be fetched + +' The foreground renderer starts reading the colour buffer after 26 sync lines +' and 161+5 hub windows. We have to be done before that happens. Using the assumed +' hubop (161 hub windows) as reference we simply go back by 112*2+4 hub windows. + + mov cnt, cnt + add cnt, $+1 + long 13{18} + 14 + 132*16*26 + 16*(161 - 2) - 228*16 + +loop waitcnt cnt, one ' initial sync point +' cogid $ nr ' assumed hubop + + call #transfer ' palette translation + + add indx, #1 ' line done, advance + cmpsub indx, scry wz ' optionally wrap line index + if_nz jmp #loop + +' per frame updates (during the first frame mask is off-screen) + + rdlong temp, blnk ' | + cmp temp, scry wz ' | + if_ne jmp #$-2 ' wait for ?/scry transition + + rdword msky, crdy ' | + shl msky, #16 ' | + sar msky, #16 ' update and sign-extend mask coordinate + + maxs msky, scry ' reasonable limit (off-screen) + mov mskc, msky ' | + add mskc, #BSIZE -1 ' bounding box setup + +one long 132*16*2 ' skip two scan lines +two long 132*16*28 ' skip all sync lines + + call #update ' update palette + + add cnt, two + jmp #loop + +' support code + +transfer cmps indx, msky wc ' | + if_nc cmps mskc, indx wc ' | + if_c jmp transfer_ret ' vertical bounds check + + rdlong addr, feed ' crs/dst + mov arg0, addr + rev arg0, #{32-}0 ' tsd/src + + mov temp, indx ' + sub temp, msky ' active row + + mov arg1, temp + shl arg1, #3 ' *8 + sub arg1, temp ' *7 + shl arg1, #4 ' *112 + + add arg0, arg1 ' apply offset + + mov ecnt, #BSIZE + + rdbyte phsb, arg0 + movd $+2, phsb + add arg0, #1 + wrbyte phsb, addr + add addr, #1 + djnz ecnt, #$-5 ' translate index to colour + +transfer_ret ret + +update mov arg0, #32*7 ' slot 7 + + call #cycle ' | + cmpsub arg0, #32*1 wz ' | + if_nz jmp #$-2 ' for all slots except 0 + +update_ret ret + +cycle mov arg1, arg0 ' | + add arg1, #palette ' apply base + + movd :cy2, arg1 ' head + add arg1, #30 ' | + movs :cy1, arg1 ' | + add arg1, #1 ' | + movd :cy1, arg1 ' body + movs :cy0, arg1 ' tail + + mov ecnt, #32-1 + +:cy0 mov temp, 1-1 ' preserve last entry +:cy1 mov 1-1, 0-0 ' long rotate + sub $-1, d1s1 + djnz ecnt, #:cy1 + +:cy2 mov 0-0, temp ' close rotate +cycle_ret ret + +' initialised data and/or presets + +feed long -12 ' | +crdy long -6 ' | + ' quick access relative to par +blnk long -4 ' | +base long NEGX ' | + +d1s1 long |< 9 | 1 ' dst/src +/- 1 + + long 0[$ & 1] +palette long $00[32] ' unused/transparent + long $FF[16], $C1[16] ' 1: $F0 - %111_1 + long $FF[16], $C1[16] ' 2: $E0 %111_0 + long $FC[16], $C0[16] ' 3: $D0 %110_1 + long $FC[16], $C0[16] ' 4: $C0 %110_0 + long $AB[16], $81[16] ' 5: $B0 %101_1 + long $AB[16], $81[16] ' 6: $A0 %101_0 + long $A8[16], $80[16] ' 7: $90 - %100_1 + +' Stuff below is re-purposed for temporary storage. + +setup add crdy, par ' mask coordinate (%%) + add feed, par ' mask buffer location + + add base, par ' scanline buffer + add blnk, base wc ' frame indicator + + rdword indx, blnk wz ' (%%) + if_nz mov scry, indx wc ' (%%) + if_c_or_nz jmp #$-2 ' auto-detect res_y + +' The loop is only left once a non-zero value has been written to scry +' and indx transitions to zero afterwards. + + mov msky, scry ' move off-screen + + movi ctrb, #%0_11111_000 ' LOGIC always (relocation support) + mov frqb, #palette >> 1 ' | + + jmp %%0 ' return + + fit + +' uninitialised data and/or temporaries + + org setup + +scry res 1 ' must be 1st..4th (%%) +indx res 1 ' | + +addr res 1 ' scanline reference +ecnt res 1 ' element count + +mskc res 1 ' upper limit (inclusive) +mskx res 1 ' mask coordinates +msky res 1 ' signed 16bit + +arg0 res 1 +arg1 res 1 + +temp res 1 + +tail fit + +CON + zero = $1F0 ' par (dst only) + + BSIZE = 112 ' mask width/height (4n) + +DAT + +ball byte $c9, $a1, $dc, $f9, $0f, $e0, $41, $3c, $88, $07, $f1, $20, $1e, $c4, $83, $78 + byte $10, $0f, $e2, $41, $3c, $88, $07, $71, $ff, $06, $ed, $85, $f2, $20, $68, $d8 + byte $be, $5d, $5b, $c1, $df, $bf, $d1, $70, $60, $c7, $94, $8d, $e0, $6f, $d9, $bc + byte $59, $d3, $26, $4d, $18, $0b, $be, $d6, $2c, $99, $37, $63, $ca, $a4, $71, $63 + byte $46, $82, $8f, $15, $8b, $e6, $82, $83, $89, $e0, $68, $38, $78, $5b, $b1, $10 + byte $9c, $8d, $1a, $31, $6c, $28, $78, $da, $b4, $1c, $dc, $0d, $19, $0c, $1e, $36 + byte $82, $7b, $b8, $30, $68, $c0, $bf, $f5, $e0, $9a, $14, $82, $93, $fd, $6a, $1f + byte $c4, $83, $b8, $1d, $9b, $d6, $ad, $5a, $0e, $b6, $06, $82, $a1, $35, $2b, $96 + byte $2c, $5a, $08, $5e, $86, $ec, $d9, $0e, $8b, $c1, $ca, $9c, $d9, $e0, $61, $d0 + byte $6e, $b0, $ab, $11, $66, $4c, $07, $eb, $28, $61, $39, $4a, $98, $0f, $07, $c1 + byte $c1, $94, $1d, $5b, $36, $ac, $25, $8a, $38, $18, $ac, $5b, $40, $1c, $b2, $6c + byte $c9, $42, $38, $09, $0e, $c6, $c3, $41, $e2, $0a, $ee, $c6, $8c, $96, $6a, $7b + byte $76, $e3, $62, $b8, $0c, $ee, $76, $6c, $db, $b2, $69, $c3, $7a, $70, $30, $d2 + byte $22, $0c, $84, $03, $3b, $c1, $25, $6e, $5a, $69, $11, $c1, $3d, $38, $58, $b5 + byte $62, $d9, $80, $fd, $e0, $19, $8d, $83, $b3, $a5, $e0, $6e, $2d, $5a, $5a, $d6 + byte $a4, $29, $dc, $59, $0f, $4e, $9a, $4b, $87, $ad, $8e, $59, $1c, $22, $45, $8a + + byte $ca, $90, $d6, $cb, $b2, $e9, $ad, $b0, $0e, $97, $43, $15, $d8, $1c, $66, $8b + byte $dc, $3a, $cc, $06, $f6, $83, $7d, $e5, $8d, $b2, $c1, $32, $1c, $a6, $c3, $5a + byte $1c, $55, $ec, $05, $47, $dd, $17, $a3, $16, $46, $9d, $e0, $a0, $a7, $0c, $04 + byte $cb, $70, $a7, $37, $67, $ea, $cc, $04, $d1, $3c, $5c, $e8, $b2, $a9, $d3, $48 + byte $70, $29, $01, $7a, $83, $a3, $2d, $9d, $36, $82, $b3, $7e, $fb, $fa, $ec, $da + byte $d1, $6d, $3b, $3c, $e8, $b8, $17, $03, $50, $41, $70, $d4, $13, $ad, $74, $e8 + byte $d0, $de, $20, $34, $5a, $50, $6f, $4f, $6f, $78, $d4, $15, $9e, $b4, $59, $0c + byte $8e, $e6, $d4, $99, $55, $a3, $3a, $5a, $85, $87, $e0, $a1, $c1, $bc, $7a, $75 + byte $6a, $cd, $04, $0f, $53, $2a, $55, $da, $d0, $6e, $4d, $9b, $86, $68, $61, $4e + byte $6d, $78, $30, $ad, $4a, $95, $4a, $15, $ca, $95, $2b, $d3, $fa, $db, $83, $e8 + byte $99, $7c, $45, $a1, $dd, $5e, $ff, $6e, $14, $46, $43, $ec, $9e, $56, $6e, $4c + byte $a9, $62, $43, $0a, $ed, $87, $a4, $52, $bc, $34, $15, $19, $54, $50, $28, $74 + byte $87, $88, $cb, $50, $31, $ac, $69, $d5, $b2, $26, $34, $9a, $af, $19, $b6, $75 + byte $6a, $6f, $10, $b9, $e0, $34, $6a, $b2, $a8, $a1, $30, $a8, $09, $1e, $3a, $0a + byte $86, $75, $6d, $e1, $b9, $74, $54, $42, $93, $2a, $4a, $a6, $55, $2d, $a7, $23 + byte $3c, $55, $0a, $d5, $25, $43, $7b, $b8, $d0, $6a, $45, $b3, $26, $8d, $e1, $a1 + + byte $56, $d4, $82, $e0, $11, $2d, $c2, $4b, $7c, $a8, $0d, $6a, $6a, $a6, $b6, $e0 + byte $90, $0e, $c2, $63, $2b, $0c, $0f, $c1, $2b, $19, $bc, $ca, $e8, $10, $fc, $c2 + byte $73, $b2, $0c, $3e, $22, $55, $64, $cd, $5f, $10, $9e, $d5, $aa, $11, $1d, $3d + byte $44, $68, $89, $57, $c1, $5b, $8c, $6a, $d5, $f1, $b3, $ff, $07, $f1, $20, $1e + byte $c4, $83, $78, $10, $0f, $e2, $41, $3c, $88, $07, $f1, $20, $a2, $4d, $73, $08 + byte $0f, $c2, $d6, $b4, $68, $52, $1f, $e2, $83, $a0, $72, $a5, $9a, $35, $aa, $53 + byte $15, $e2, $fd, $07, $0f, $25, $8a, $34, $88, $0d, $e1, $c1, $43, $99, $12, $c5 + byte $0a, $f5, $ab, $51, $a9, $24, $a2, $de, $04, $25, $82, $15, $2a, $90, $27, $5b + byte $44, $08, $15, $1e, $3e, $05, $29, $d0, $a7, $57, $96, $0e, $21, $11, $3a, $7c + byte $08, $55, $ac, $48, $a0, $7e, $b9, $ba, $65, $6a, $13, $2c, $27, $7d, $29, $0b + byte $de, $f2, $f4, $c8, $92, $a1, $55, $52, $08, $54, $2e, $4c, $a9, $60, $41, $0a + byte $05, $c8, $8b, $80, $d2, $a5, $48, $94, $5d, $ff, $83, $78, $10, $0f, $c2, $36 + byte $82, $7b, $10, $b6, $69, $25, $f8, $07, $21, $5b, $56, $2c, $06, $ff, $20, $64 + byte $d5, $92, $f9, $e0, $1f, $04, $ad, $59, $b2, $60, $36, $f8, $07, $01, $e3, $8a + byte $0d, $d8, $b3, $13, $fc, $fd, $9b, $54, $62, $d0, $be, $5d, $dd, $c1, $df, $bf + byte $51, $43, $fa, $f5, $da, $d6, $15, $fc, $a3, $85, $82, $68, $a1, $cb, $66, $f0 + + byte $53, $65, $c4, $60, $78, $d0, $ad, $b3, $f8, $1b, $53, $d4, $0b, $ec, $84, $07 + byte $ed, $e1, $57, $85, $11, $85, $f6, $e3, $85, $4e, $1d, $da, $82, $6f, $78, $d4 + byte $ab, $47, $97, $0d, $ed, $c1, $57, $45, $d6, $d0, $67, $27, $5e, $e8, $b0, $a6 + byte $35, $fc, $18, $55, $94, $0c, $c2, $83, $8e, $f0, $a0, $c5, $86, $65, $0b, $e6 + byte $cc, $98, $32, $61, $dc, $98, $11, $c3, $86, $0c, $1a, $b0, $6f, $2f, $38, $9b + byte $0c, $0e, $46, $83, $8b, $5d, $8b, $e1, $32, $38, $19, $09, $d7, $76, $ed, $98 + byte $37, $6b, $da, $a4, $09, $63, $46, $a3, $65, $34, $b4, $17, $3c, $82, $43, $3c + byte $0c, $4e, $f1, $28, $38, $d8, $4e, $fa, $e1, $d1, $ae, $9e, $e6, $61, $cb, $a6 + byte $75, $6b, $e1, $21, $1e, $5d, $0c, $3b, $7a, $74, $d7, $88, $82, $61, $55, $8b + byte $65, $4d, $1a, $15, $e8, $d7, $17, $1e, $6d, $b5, $80, $82, $61, $45, $b3, $25 + byte $8b, $1a, $cc, $9b, $53, $ab, $26, $5c, $b4, $40, $6d, $e1, $41, $b3, $46, $0d + byte $ea, $d5, $a9, $55, $6b, $46, $b5, $2a, $95, $2a, $b4, $6b, $0d, $8f, $1a, $2d + byte $a8, $0f, $8f, $aa, $4d, $a9, $0c, $1e, $0d, $22, $5a, $84, $67, $35, $aa, $c3 + byte $93, $f2, $68, $99, $0c, $a2, $53, $f0, $88, $16, $e1, $41, $b9, $16, $cd, $c1 + byte $d1, $7c, $bc, $08, $af, $c1, $23, $6b, $86, $37, $15, $2a, $84, $2b, $0b, $4f + byte $e9, $30, $5a, $46, $07, $11, $ca, $95, $29, $ab, $89, $15, $c1, $86, $8d, $4a + + byte $a0, $55, $4b, $69, $d2, $68, $c7, $76, $70, $b0, $19, $1c, $0a, $85, $55, $2b + byte $71, $42, $53, $e9, $b0, $1d, $3c, $c3, $a5, $b6, $f0, $90, $3b, $d2, $84, $05 + byte $0d, $c1, $49, $7b, $cd, $68, $98, $49, $c2, $62, $11, $d4, $b5, $33, $36, $45 + byte $29, $28, $4c, $85, $4b, $7d, $8b, $b2, $16, $ad, $72, $67, $b4, $c8, $12, $36 + byte $37, $41, $b4, $48, $9b, $b9, $b3, $36, $a9, $d3, $b1, $0b, $ad, $b6, $4c, $4d + byte $d1, $ba, $72, $e8, $b0, $ae, $2d, $5a, $95, $a6, $ca, $18, $3c, $d2, $66, $b4 + byte $0e, $af, $6a, $2b, $82, $52, $a5, $2d, $23, $3c, $8b, $57, $27, $6e, $62, $04 + byte $4f, $25, $4a, $14, $2b, $d2, $a8, $51, $82, $fa, $89, $25, $46, $59, $78, $08 + byte $8e, $82, $14, $2a, $50, $a0, $5e, $9c, $3a, $b1, $6a, $82, $a7, $10, $25, $82 + byte $15, $29, $54, $28, $40, $bf, $7c, $7d, $72, $d5, $8a, $51, $1d, $de, $82, $87 + byte $be, $03, $a0, $e7, $00, $08, $0d, $17, $8a, $a3, $45, $78, $d4, $2f, $4f, $ae + byte $5c, $39, $b2, $65, $ef, $01, $75, $6b, $69, $6d, $8e, $8b, $e1, $a8, $d4, $fc + byte $38, $5a, $5a, $26, $55, $28, $cf, $5a, $c1, $c1, $6c, $2e, $30, $bd, $95, $82 + byte $c7, $b4, $50, $aa, $c4, $dc, $5a, $dc, $aa, $45, $f9, $72, $2e, $ed, $78, $25 + byte $4c, $a9, $10, $c5, $6a, $a2, $c5, $19, $12, $19, $af, $a3, $83, $e0, $e0, $10 + byte $5e, $b6, $86, $30, $65, $79, $23, $1f, $84, $27, $51, $8f, $33, $1d, $85, $c7 + + byte $e8, $a1, $46, $b4, $2a, $55, $d1, $22, $3c, $0a, $0b, $0f, $f1, $41, $50, $8d + byte $c8, $9a, $2a, $83, $c3, $95, $a8, $13, $c1, $a1, $49, $c4, $0b, $51, $57, $20 + byte $1d, $c6, $ab, $f0, $a8, $48, $75, $74, $10, $29, $32, $3c, $09, $cd, $86, $e1 + byte $41, $a0, $ea, $f0, $94, $8e, $85, $08, $11, $2c, $48, $91, $c0, $f0, $a0, $32 + byte $bc, $8d, $0e, $41, $5d, $42, $54, $36, $50, $21, $42, $78, $bc, $14, $1a, $1e + byte $c2, $87, $40, $81, $ba, $64, $ca, $0c, $0e, $ca, $85, $55, $c5, $64, $19, $1e + byte $04, $44, $82, $b5, $a5, $7e, $6d, $94, $c1, $a8, $10, $17, $96, $e1, $3c, $58 + byte $1b, $ea, $96, $66, $96, $6c, $14, $8a, $33, $e6, $da, $98, $06, $4b, $a1, $0a + byte $b6, $ca, $bd, $b0, $0e, $14, $ac, $4d, $bd, $7a, $3a, $53, $52, $58, $86, $02 + byte $83, $87, $bc, $65, $b0, $34, $64, $c9, $52, $19, $3d, $14, $15, $04, $05, $02 + byte $f6, $82, $de, $f0, $a1, $3b, $2b, $ca, $10, $1e, $2d, $c2, $e3, $51, $94, $13 + byte $3e, $75, $ca, $d0, $21, $3d, $3c, $2a, $08, $0e, $f2, $f4, $ca, $d1, $2d, $3b + byte $7c, $c8, $90, $ae, $3d, $78, $5c, $e9, $e0, $21, $3b, $3e, $84, $0f, $e9, $d2 + byte $2a, $44, $78, $90, $9f, $2c, $c3, $87, $ac, $2a, $11, $3e, $82, $87, $80, $e8 + byte $72, $25, $82, $87, $8c, $e0, $15, $9e, $e5, $ea, $09, $1e, $f1, $31, $7c, $97 + byte $8d, $e8, $14, $1f, $65, $06, $87, $f8, $21, $20, $1f, $06, $9f, $f8, $2c, $2d + + byte $38, $24, $cb, $2f, $14, $5f, $b8, $05, $8f, $aa, $55, $8d, $a2, $83, $0c, $ee + byte $d2, $a4, $0e, $05, $a5, $0d, $51, $41, $4c, $d0, $2d, $4b, $86, $36, $c9, $12 + byte $44, $4d, $89, $c9, $51, $b2, $62, $42, $0c, $90, $2e, $45, $92, $b8, $29, $70 + byte $33, $26, $63, $22, $c8, $11, $32, $a5, $4b, $8d, $00, $97, $62, $76, $b6, $84 + byte $e0, $91, $10, $65, $48, $8b, $00, $4b, $70, $35, $a6, $4a, $b9, $05, $22, $7f + byte $48, $e5, $fc, $28, $83, $87, $c2, $e0, $25, $27, $01, $a4, $0f, $29, $19, $e2 + byte $34, $0a, $0c, $be, $32, $b8, $05, $4f, $29, $92, $05, $05, $1f, $d9, $3c, $13 + byte $00, $d7, $f0, $11, $3c, $24, $49, $0c, $5f, $7c, $64, $cb, $e4, $91, $21, $a4 + byte $4a, $0d, $5e, $12, $c4, $f3, $ab, $99, $bc, $2a, $00, $b7, $e0, $27, $8e, $6d + byte $f8, $08, $de, $52, $c2, $27, $47, $0e, $e2, $d9, $89, $15, $c3, $5a, $16, $4f + byte $e9, $e1, $23, $38, $70, $8e, $1f, $d1, $83, $1d, $db, $e0, $21, $8a, $25, $f7 + byte $e0, $c1, $45, $b2, $24, $4e, $12, $39, $b0, $17, $27, $96, $0d, $6b, $56, $2c + byte $45, $30, $4b, $0e, $e1, $21, $78, $48, $60, $1f, $3d, $d8, $88, $0e, $1e, $2c + byte $98, $71, $8d, $1e, $c1, $21, $7e, $b1, $0d, $5f, $2c, $98, $fb, $0f, $e0, $41 + byte $44, $44, $f0, $0f, $22, $62, $85, $85, $f0, $20, $2c, $9e, $b5, $e0, $10, $1e + byte $84, $24, $88, $65, $19, $fc, $83, $90, $24, $f6, $62, $52, $7e, $f0, $e6, $20 + + byte $96, $15, $d3, $08, $1f, $04, $38, $4b, $64, $cf, $46, $04, $a3, $10, $ee, $9f + byte $8b, $24, $09, $6c, $59, $31, $0b, $fe, $fe, $a5, $70, $e2, $c0, $8e, $3d, $0b + byte $c6, $21, $9c, $0b, $67, $8e, $ec, $d9, $92, $62, $c6, $3f, $84, $6d, $88, $e0 + byte $c9, $9a, $05, $e1, $e0, $c7, $95, $73, $0c, $20, $c7, $86, $25, $33, $02, $21 + byte $94, $2b, $97, $e0, $91, $21, $48, $11, $8f, $60, $4c, $99, $50, $e2, $48, $9e + byte $6d, $0c, $60, $1a, $41, $83, $07, $63, $86, $ec, $c9, $91, $26, $49, $8c, $50 + byte $29, $0f, $02, $2a, $94, $28, $d4, $a7, $47, $b7, $4e, $1d, $da, $b4, $6a, $09 + byte $d5, $ca, $14, $29, $d0, $ab, $47, $97, $0e, $ed, $da, $b4, $68, $0e, $5e, $2a + byte $c3, $83, $7e, $bd, $e1, $49, $6a, $f0, $51, $ae, $58, $41, $74, $d0, $a5, $33 + byte $3c, $68, $d5, $ac, $29, $7c, $8a, $56, $aa, $28, $3c, $c8, $d6, $29, $43, $bb + byte $56, $29, $9a, $25, $05, $4f, $1d, $5a, $14, $ca, $8b, $17, $e1, $41, $5a, $b2 + byte $90, $a4, $31, $78, $4a, $d3, $a4, $41, $ac, $1a, $59, $c9, $42, $5a, $bc, $b8 + byte $12, $c1, $43, $b7, $56, $8d, $e2, $d5, $8a, $56, $29, $42, $58, $78, $90, $1c + byte $26, $82, $87, $0c, $29, $1a, $d5, $a9, $51, $a5, $52, $b9, $30, $a1, $8a, $9f + byte $60, $f0, $d0, $26, $39, $3a, $88, $11, $25, $42, $b8, $52, $25, $82, $15, $29 + byte $14, $10, $3c, $a4, $4a, $52, $2f, $56, $b5, $4a, $15, $e1, $41, $88, $60, $81 + + byte $0a, $e4, $fb, $d7, $15, $1d, $c4, $a9, $11, $25, $52, $b8, $32, $a5, $e1, $41 + byte $a0, $00, $fd, $fe, $65, $48, $96, $20, $4e, $8c, $aa, $e8, $20, $2c, $3c, $08 + byte $0a, $1e, $e1, $43, $7b, $7c, $14, $2d, $4a, $85, $70, $a1, $f1, $21, $5e, $c8 + byte $97, $e7, $5f, $9a, $24, $f1, $62, $45, $8b, $cc, $16, $42, $85, $84, $a7, $e0 + byte $21, $55, $a2, $f8, $74, $11, $1f, $a2, $ab, $bc, $06, $a1, $51, $83, $7a, $75 + byte $6a, $d5, $88, $2e, $10, $2a, $62, $41, $2a, $d0, $14, $1c, $c4, $07, $07, $31 + byte $aa, $55, $a9, $12, $19, $1c, $95, $09, $6d, $10, $e1, $41, $5c, $78, $16, $11 + byte $9e, $94, $46, $db, $f0, $11, $1e, $94, $d7, $44, $21, $1a, $25, $88, $0f, $2f + byte $d1, $49, $69, $cd, $48, $c6, $f1, $a1, $02, $44, $8b, $f0, $24, $44, $82, $7a + byte $71, $fd, $20, $1d, $86, $47, $61, $c9, $42, $89, $e2, $e0, $a0, $56, $6d, $78 + byte $69, $10, $25, $a1, $66, $84, $67, $51, $d9, $40, $44, $b4, $10, $a6, $34, $38 + byte $08, $ce, $07, $62, $c3, $73, $0b, $14, $da, $10, $05, $c9, $93, $ab, $47, $4c + byte $73, $a8, $1f, $c2, $93, $b5, $e0, $e0, $29, $47, $b7, $2c, $99, $f3, $42, $f8 + byte $f0, $88, $0f, $82, $83, $87, $3e, $b9, $72, $64, $07, $0f, $19, $32, $82, $a7 + byte $90, $01, $29, $28, $38, $06, $2f, $e9, $d2, $a4, $49, $4d, $96, $3d, $21, $3c + byte $cb, $94, $11, $3c, $a4, $4a, $95, $22, $59, $52, $f0, $8d, $0e, $c1, $53, $4a + + byte $f0, $e4, $28, $31, $63, $2a, $12, $a4, $50, $81, $00, $f9, $fa, $f4, $fd, $84 + byte $b4, $a8, $44, $88, $e2, $5d, $b1, $3a, $f5, $e7, $81, $b8, $a0, $5b, $b6, $ae + byte $45, $21, $28, $bc, $c8, $d3, $2b, $27, $4b, $ca, $0a, $af, $6b, $40, $6f, $78 + byte $96, $55, $10, $37, $c3, $61, $90, $57, $39, $c3, $43, $9e, $3c, $85, $97, $32 + byte $3c, $47, $47, $5d, $32, $2b, $84, $c2, $53, $a9, $2f, $59, $c9, $0e, $8f, $32 + byte $b3, $41, $78, $c8, $06, $d1, $31, $59, $97, $81, $12, $11, $1e, $a3, $83, $dc + byte $da, $13, $bc, $04, $84, $f7, $d6, $11, $3c, $e2, $55, $78, $eb, $5c, $d1, $22 + byte $bc, $25, $4b, $9e, $3c, $a4, $07, $7f, $d9, $bc, $c2, $13, $f7, $64, $cb, $47 + byte $36, $ef, $e0, $10, $1e, $c2, $47, $b2, $e0, $2f, $8f, $5f, $3a, $08, $1e, $b2 + byte $78, $c9, $4c, $16, $dc, $b9, $73, $90, $9f, $2c, $f9, $86, $c7, $e8, $10, $9e + byte $b8, $57, $08, $1d, $3a, $a4, $4b, $3f, $13, $83, $b3, $2a, $08, $8c, $17, $91 + byte $70, $1a, $ce, $03, $29, $cb, $28, $78, $ea, $94, $19, $1e, $83, $c3, $3c, $9c + byte $57, $c9, $22, $61, $84, $07, $6d, $e1, $71, $1e, $04, $0f, $89, $12, $25, $c8 + byte $5f, $8b, $6b, $7b, $19, $4a, $0a, $9e, $e2, $d7, $52, $74, $0d, $1e, $12, $38 + byte $04, $8f, $74, $70, $14, $b7, $66, $74, $d8, $06, $12, $d8, $07, $ef, $a3, $28 + byte $85, $b3, $24, $4e, $f1, $22, $3c, $04, $cf, $f0, $c8, $25, $59, $85, $07, $0e + + byte $e1, $43, $5c, $74, $e4, $9a, $0d, $b8, $84, $27, $8e, $c1, $81, $3d, $3b, $ee + byte $dc, $92, $05, $d7, $f0, $10, $bc, $c2, $83, $f8, $e0, $f1, $06, $83, $43, $78 + byte $90, $1c, $2f, $38, $72, $e0, $10, $1c, $c4, $85, $0f, $a9, $c1, $23, $59, $47 + byte $47, $f1, $ec, $d8, $26, $8b, $f8, $10, $9e, $38, $85, $e7, $e0, $11, $1f, $a2 + byte $13, $27, $49, $e9, $92, $7d, $74, $08, $9f, $5c, $b9, $86, $f7, $e0, $c1, $96 + byte $ed, $36, $97, $c5, $b3, $0d, $70, $cf, $1a, $e3, $69, $3a, $f0, $5e, $16, $d3 + byte $4a, $3a, $37, $ae, $d3, $e2, $2e, $5c, $a5, $79, $c1, $43, $46, $78, $90, $16 + byte $bc, $f8, $ca, $99, $0f, $f3, $52, $c6, $3a, $08, $0f, $52, $d9, $8b, $bb, $8b + byte $d1, $29, $3e, $70, $8f, $8e, $52, $c5, $65, $04, $31, $fb, $88, $a7, $4c, $1e + byte $75, $31, $6b, $88, $13, $c7, $56, $0c, $1b, $d1, $ac, $d2, $65, $f0, $8c, $1e + byte $5c, $c2, $83, $d8, $e0, $c0, $5a, $14, $4b, $96, $2c, $a4, $27, $87, $a2, $19 + byte $9e, $58, $b3, $66, $25, $32, $78, $30, $67, $1e, $1e, $a2, $07, $bb, $f0, $c0 + byte $26, $3c, $b0, $0a, $0e, $2c, $98, $33, $63, $c6, $94, $09, $17, $b6, $d1, $89 + byte $55, $f0, $23, $c2, $98, $2d, $9b, $f0, $10, $1d, $e2, $23, $73, $e2, $cc, $88 + byte $86, $8f, $e0, $1d, $5e, $a2, $07, $53, $a2, $e1, $2b, $59, $b2, $22, $c9, $82 + byte $04, $73, $62, $e1, $83, $09, $13, $c2, $d1, $81, $74, $74, $09, $1e, $e1, $21 + + byte $38, $04, $4f, $d6, $a4, $c2, $73, $7c, $8a, $0e, $8c, $b9, $70, $59, $8a, $4b + byte $62, $4e, $64, $8e, $c8, $99, $0b, $ae, $c4, $56, $d8, $c3, $59, $20, $78, $4f + byte $03, $ce, $4b, $71, $2e, $06, $4f, $52, $2c, $53, $45, $74, $5c, $1a, $7b, $b0 + byte $71, $90, $09, $5f, $c4, $c3, $07, $a7, $f0, $15, $1c, $c2, $37, $8b, $e0, $c5 + byte $71, $2f, $ac, $09, $b2, $79, $30, $73, $44, $0f, $ce, $f1, $89, $03, $f9, $e8 + byte $44, $ba, $51, $90, $20, $1e, $7c, $c2, $57, $7c, $20, $55, $19, $82, $07, $25 + byte $4e, $14, $c3, $43, $f0, $24, $13, $9e, $89, $45, $87, $e4, $40, $21, $38, $90 + byte $63, $9b, $1e, $c2, $43, $74, $60, $c6, $98, $52, $72, $a0, $10, $1f, $c8, $06 + byte $8f, $f0, $11, $1d, $89, $11, $66, $c4, $90, $40, $7e, $20, $47, $36, $7d, $90 + byte $0e, $5f, $c4, $08, $11, $0a, $1e, $04, $f8, $47, $c7, $f8, $11, $bc, $19, $31 + byte $22, $18, $1c, $e8, $d3, $a3, $1b, $1c, $c2, $2f, $c3, $e0, $41, $9f, $5f, $f0 + byte $a0, $43, $9b, $56, $f4, $21, $c8, $80, $40, $78, $a4, $1b, $3c, $69, $d2, $44 + byte $41, $1c, $19, $d1, $91, $c1, $90, $01, $3b, $36, $a4, $88, $13, $a1, $e7, $3f + byte $00, $ad, $c4, $18, $40, $8f, $0e, $29, $12, $44, $09, $06, $ef, $95, $c8, $88 + byte $01, $7d, $be, $b4, $49, $66, $09, $ba, $21, $88, $59, $f4, $20, $94, $00, $e8 + byte $d2, $a6, $49, $1c, $31, $fd, $e0, $45, $2c, $78, $30, $ca, $01, $c1, $93, $3a + + byte $15, $84, $38, $43, $20, $33, $22, $84, $09, $11, $a4, $9f, $1e, $68, $d1, $c0 + byte $4a, $09, $6f, $f0, $0c, $9f, $c1, $33, $05, $e0, $a2, $41, $0d, $33, $ba, $e0 + byte $c9, $34, $7c, $07, $0f, $5a, $38, $82, $07, $86, $18, $98, $3e, $a2, $17, $3d + byte $3c, $b4, $71, $62, $a3, $82, $11, $75, $04, $08, $de, $f8, $c3, $07, $6e, $9a + byte $d8, $b1, $60, $44, $1b, $7c, $e9, $e3, $a3, $83, $1b, $27, $76, $ac, $98, $c0 + byte $05, $6f, $44, $04, $c3, $07, $de, $e8, $11, $3c, $d1, $83, $f0, $1f, $bd, $10 + byte $86, $0f, $bc, $e1, $0b, $32, $7a, $50, $fe, $91, $12, $41, $4c, $08, $61, $f8 + byte $0c, $1e, $38, $b0, $62, $c6, $00, $3a, $7c, $20, $21, $8c, $88, $00, $7e, $7c + byte $e1, $13, $07, $34, $c8, $10, $c0, $04, $6f, $04, $c1, $07, $1b, $66, $8c, $60 + byte $fd, $cb, $96, $22, $51, $9c, $18, $51, $22, $84, $0b, $15, $22, $58, $90, $40 + byte $f9, $f2, $e4, $ca, $94, $22, $21, $f8, $09, $14, $10, $3c, $65, $48, $96, $20 + byte $56, $b4, $48, $11, $c2, $c2, $47, $78, $08, $5e, $d2, $25, $8b, $0f, $9e, $c2 + byte $83, $47, $74, $60, $28, $80, $bf, $3c, $be, $dc, $39, $89, $67, $2b, $9a, $a5 + byte $70, $66, $42, $18, $c7, $47, $7e, $72, $f9, $48, $93, $c4, $5e, $0c, $2b, $91 + byte $c9, $81, $89, $60, $46, $e1, $49, $0e, $b3, $e0, $25, $82, $b9, $d0, $e0, $11 + byte $9d, $e5, $08, $63, $28, $8e, $8d, $28, $16, $cc, $99, $0a, $61, $24, $90, $81 + + byte $fc, $e8, $c9, $8c, $21, $3f, $3e, $c1, $83, $19, $53, $c6, $af, $83, $01, $7f + byte $7e, $7c, $a3, $47, $f0, $e4, $c5, $42, $18, $d3, $f8, $10, $3e, $d2, $05, $1f + byte $de, $e1, $8b, $17, $0f, $6e, $e1, $2b, $3c, $06, $1f, $9e, $c1, $83, $0b, $e7 + byte $53, $11, $fc, $78, $f3, $e4, $ce, $2d, $78, $70, $a2, $10, $3c, $e8, $86, $0f + byte $e2, $0c, $e9, $f1, $a6, $c9, $9d, $1a, $17, $ca, $1c, $39, $b0, $67, $c7, $37 + byte $78, $32, $0a, $5e, $e1, $93, $23, $05, $f6, $e4, $d8, $92, $e1, $45, $92, $50 + byte $f6, $88, $1e, $d4, $a8, $70, $a6, $c8, $81, $3c, $3b, $32, $ac, $59, $c9, $91 + byte $23, $5b, $96, $4c, $19, $d2, $a5, $49, $93, $ca, $45, $32, $67, $49, $12, $25 + byte $06, $a7, $e0, $c1, $5d, $1a, $57, $a9, $52, $82, $83, $a4, $44, $20, $87, $b7 + byte $2c, $9e, $32, $c2, $27, $57, $29, $c1, $93, $a3, $44, $0e, $e1, $83, $57, $78 + byte $e4, $c6, $2d, $3a, $70, $96, $cc, $29, $3a, $70, $90, $2d, $9b, $57, $f8, $88 + byte $0f, $d1, $99, $53, $74, $e0, $c0, $3b, $3c, $c8, $e4, $51, $3c, $c2, $43, $72 + byte $08, $0e, $d1, $41, $7c, $78, $e2, $2e, $9d, $1b, $d7, $0a, $c0, $39, $78, $94 + byte $cc, $f0, $51, $2f, $c2, $43, $7c, $cc, $16, $f1, $81, $43, $c5, $48, $4f, $c1 + byte $83, $6b, $f8, $e4, $14, $1f, $d9, $b3, $4f, $16, $e1, $2b, $38, $b4, $40, $8e + byte $d1, $31, $78, $26, $8b, $f0, $19, $1f, $29, $05, $87, $86, $c0, $2e, $7c, $a4 + + byte $cb, $e4, $50, $3f, $a2, $53, $43, $0c, $1e, $34, $ba, $45, $7c, $a4, $cc, $89 + byte $62, $74, $6c, $09, $b4, $e2, $4b, $f8, $88, $0e, $14, $29, $04, $07, $72, $c1 + byte $83, $06, $75, $6e, $54, $e3, $4b, $74, $24, $c7, $92, $46, $fc, $a0, $c6, $95 + byte $0a, $65, $4a, $d1, $23, $3c, $8c, $c8, $02, $21, $fe, $50, $dc, $86, $c6, $28 + byte $b3, $71, $54, $41, $09, $e2, $d9, $89, $63, $cb, $a6, $57, $f0, $6a, $8d, $79 + byte $8a, $7d, $f0, $10, $1b, $3c, $44, $b3, $16, $95, $07, $f3, $54, $78, $16, $c3 + byte $7a, $0e, $88, $64, $c9, $42, $7a, $13, $dc, $04, $ec, $c4, $b2, $61, $13, $1c + byte $58, $05, $87, $57, $c1, $bc, $0c, $b0, $8f, $17, $d1, $21, $3c, $b2, $0c, $5e + byte $cc, $5e, $07, $3b, $76, $c1, $21, $3c, $84, $8f, $e0, $41, $82, $39, $73, $62 + byte $c1, $8f, $15, $a9, $e8, $cc, $8c, $28, $d3, $f0, $10, $5d, $48, $b2, $08, $1f + byte $d1, $81, $28, $13, $76, $64, $d9, $9c, $0a, $d2, $c9, $82, $65, $70, $20, $4e + byte $8c, $69, $f0, $8c, $4f, $a4, $d2, $65, $7c, $0a, $9e, $64, $1f, $06, $eb, $f0 + byte $40, $92, $24, $09, $16, $e1, $21, $3e, $30, $21, $12, $7c, $d2, $0d, $51, $a2 + byte $44, $88, $84, $07, $36, $e1, $81, $54, $78, $20, $41, $9c, $78, $ba, $24, $42 + byte $38, $ba, $24, $8b, $e8, $48, $2c, $3c, $12, $26, $4b, $86, $34, $e9, $f0, $10 + byte $3e, $c2, $43, $70, $08, $5e, $47, $f2, $ac, $0e, $89, $aa, $91, $2c, $96, $e2 + + byte $5e, $bc, $ca, $e3, $a2, $6e, $9e, $a9, $3b, $31, $0d, $ae, $02, $f9, $ae, $10 + byte $1e, $d8, $9c, $a9, $27, $45, $21, $3a, $92, $63, $9b, $2c, $82, $e7, $5e, $d8 + byte $1a, $7b, $23, $3c, $46, $47, $32, $4c, $f7, $44, $74, $e4, $18, $1e, $82, $07 + byte $39, $b2, $e1, $81, $4d, $85, $60, $4c, $f8, $2c, $06, $cf, $21, $19, $3c, $58 + byte $13, $61, $42, $98, $11, $21, $86, $04, $8f, $42, $ba, $08, $1f, $5d, $81, $34 + byte $93, $f0, $c0, $88, $20, $43, $02, $0c, $e8, $93, $eb, $13, $e1, $91, $74, $8d + byte $08, $4f, $04, $e8, $f3, $a7, $47, $8f, $2f, $99, $aa, $19, $1e, $08, $45, $87 + byte $f0, $48, $9f, $1e, $5d, $ba, $74, $e8, $d0, $26, $45, $38, $3e, $10, $24, $48 + byte $20, $7c, $84, $87, $e0, $49, $9b, $36, $2d, $c6, $e1, $21, $78, $d2, $0f, $0f + byte $e1, $23, $3c, $06, $8f, $74, $49, $90, $41, $78, $88, $ae, $b4, $70, $11, $26 + byte $14, $dd, $e3, $05, $6e, $5a, $b4, $a2, $3b, $5e, $3a, $78, $e2, $23, $ce, $b3 + byte $b9, $2e, $f6, $d1, $d9, $d8, $06, $f9, $80, $d0, $3e, $67, $36, $1d, $ab, $46 + byte $f0, $24, $75, $35, $48, $1c, $c3, $65, $9d, $2e, $f6, $21, $ab, $e8, $b8, $0e + byte $96, $75, $b2, $08, $1e, $e1, $61, $59, $30, $3f, $06, $c1, $77, $1f, $3d, $85 + byte $75, $79, $35, $92, $45, $3e, $dc, $06, $ac, $8f, $11, $b1, $f0, $1c, $bc, $ef + byte $66, $78, $0c, $de, $88, $08, $21, $4c, $17, $57, $fb, $18, $b5, $85, $aa, $79 + + byte $b6, $ee, $46, $74, $42, $42, $18, $31, $22, $82, $e1, $23, $3a, $23, $23, $86 + byte $94, $48, $f0, $20, $14, $3c, $10, $d0, $a2, $59, $0c, $90, $67, $8b, $af, $10 + byte $9e, $10, $22, $24, $40, $93, $26, $0d, $1a, $c4, $91, $21, $0b, $4f, $88, $93 + byte $05, $42, $04, $34, $71, $e2, $a0, $8e, $9d, $1a, $55, $a2, $48, $90, $c4, $0b + byte $44, $c1, $01, $01, $ce, $f0, $80, $1d, $1b, $36, $ac, $58, $a9, $60, $26, $1c + byte $1c, $82, $07, $7e, $9a, $38, $c2, $07, $36, $6a, $58, $b1, $60, $c1, $8c, $09 + byte $13, $46, $04, $08, $f0, $ef, $83, $65, $b4, $b4, $68, $45, $85, $38, $41, $6c + byte $99, $6c, $a5, $2c, $41, $23, $09, $60, $0d, $5e, $5b, $93, $0e, $6e, $da, $b8 + byte $c2, $47, $f0, $8a, $4e, $f4, $e7, $41, $70, $08, $1f, $38, $d2, $44, $f8, $d8 + byte $3a, $4b, $23, $3c, $a4, $85, $e0, $8d, $9f, $3e, $3e, $ba, $78, $a2, $47, $72 + byte $c0, $11, $3f, $f2, $40, $74, $c4, $47, $0f, $6f, $78, $c0, $1d, $3e, $a2, $63 + byte $f0, $40, $40, $00, $7f, $70, $08, $1f, $47, $22, $78, $17, $0e, $04, $f4, $f1 + byte $47, $47, $3c, $e1, $b1, $0e, $c4, $47, $28, $e1, $11, $5f, $74, $08, $5e, $55 + byte $00, $3b, $b4, $f0, $19, $1e, $82, $27, $ee, $f8, $0a, $15, $0b, $fe, $f8, $18 + byte $3e, $82, $43, $7c, $80, $11, $1e, $a1, $04, $0f, $7c, $d1, $05, $66, $74, $80 + byte $8e, $0d, $2a, $14, $cc, $d1, $05, $76, $ba, $84, $1e, $be, $20, $07, $0f, $bc + + byte $78, $e1, $44, $07, $58, $30, $a3, $63, $72, $80, $02, $39, $3a, $c0, $c5, $93 + byte $2e, $60, $e1, $c4, $09, $03, $3a, $b4, $e8, $00, $19, $32, $d2, $c5, $31, $2a + byte $f1, $85, $84, $04, $00, $0d, $32, $44, $b0, $c0, $a8, $ce, $86, $e0, $15, $13 + byte $22, $00, $36, $28, $c1, $03, $c4, $e4, $0c, $df, $b0, $83, $07, $b4, $e0, $01 + byte $0e, $c4, $e2, $98, $0d, $18, $f0, $a7, $c4, $e8, $19, $3c, $41, $86, $4f, $8c + byte $18, $c0, $a3, $c5, $03, $1b, $66, $f2, $88, $1f, $e0, $82, $27, $a4, $e0, $01 + byte $0e, $2d, $18, $50, $38, $a1, $43, $0d, $3e, $98, $20, $a2, $0f, $5e, $20, $81 + byte $07, $6f, $cc, $e1, $11, $3c, $3a, $b0, $e1, $23, $78, $00, $0d, $5f, $c1, $13 + byte $02, $f8, $f0, $01, $1a, $14, $08, $e0, $40, $01, $03, $8c, $1f, $90, $a3, $03 + byte $84, $e8, $01, $26, $f8, $00, $80, $0b, $32, $78, $81, $8f, $0e, $c1, $13, $58 + byte $f8, $08, $1e, $30, $21, $c7, $0f, $08, $e0, $82, $27, $48, $10, $c0, $80, $00 + byte $0a, $1e, $30, $20, $41, $8c, $8f, $c9, $29, $78, $c3, $09, $be, $d1, $21, $3d + byte $84, $4f, $f8, $70, $a0, $87, $ef, $e4, $0a, $10, $3e, $6c, $68, $c1, $33, $3e + byte $65, $17, $78, $b0, $a1, $f8, $27, $44, $8f, $0e, $4d, $ee, $dc, $a8, $70, $a2 + byte $c8, $81, $3c, $5b, $32, $a4, $59, $f9, $27, $4c, $9f, $0f, $4d, $ea, $d4, $b8 + byte $50, $e2, $48, $81, $1d, $59, $36, $a4, $49, $f9, $27, $42, $9f, $0e, $af, $e0 + + byte $41, $85, $12, $c5, $e0, $49, $86, $75, $f0, $20, $4a, $80, $0e, $2d, $1a, $d1 + byte $8b, $02, $b9, $e8, $4d, $9c, $00, $dd, $e0, $41, $2d, $7c, $91, $23, $1b, $3f + byte $c2, $87, $7f, $82, $c1, $9b, $72, $f4, $08, $be, $88, $e8, $d1, $a6, $11, $3e + byte $82, $7f, $0a, $c0, $87, $9b, $26, $36, $2a, $c1, $df, $3f, $36, $fa, $78, $68 + byte $62, $a7, $8a, $99, $22, $06, $72, $68, $c9, $a0, $8e, $9f, $fe, $11, $e2, $c1 + byte $49, $1d, $2b, $65, $8c, $14, $d0, $91, $45, $43, $1a, $a5, $ff, $00, $80, $09 + byte $2f, $2e, $ec, $58, $31, $63, $0a, $fe, $d8, $d0, $e1, $c6, $41, $0d, $13, $26 + byte $0c, $e4, $d1, $a2, $41, $4d, $32, $7c, $fa, $c7, $80, $06, $05, $36, $2c, $c1 + byte $03, $7d, $f0, $8c, $5e, $fe, $21, $a1, $45, $89, $2c, $f8, $a2, $0a, $5f, $fe + byte $c1, $a1, $42, $8e, $14, $33, $46, $f4, $e8, $a2, $47, $f0, $81, $08, $1a, $05 + byte $50, $c4, $00, $83, $37, $49, $16, $c4, $b9, $51, $e3, $ca, $85, $33, $a5, $01 + byte $c4, $be, $26, $b0, $24, $41, $9c, $18, $51, $aa, $e5, $a1, $7b, $34, $00, $f2 + byte $e4, $82, $47, $f8, $64, $46, $94, $09, $11, $c2, $e1, $91, $42, $f0, $20, $4b + byte $96, $64, $f8, $0a, $0e, $84, $09, $11, $0c, $0e, $c1, $3f, $78, $10, $0a, $1e + byte $04, $e8, $07, $4f, $32, $e1, $37, $78, $d3, $a3, $47, $37, $f8, $13, $24, $18 + byte $3e, $82, $03, $5d, $3c, $e1, $17, $51, $70, $40, $40, $1f, $1f, $5d, $bc, $74 + + byte $48, $20, $27, $86, $94, $28, $92, $e4, $80, $90, $00, $fe, $e0, $89, $07, $4f + byte $7a, $40, $16, $1c, $88, $20, $0e, $9f, $c1, $9b, $0e, $0a, $e2, $c1, $93, $30 + byte $61, $44, $08, $11, $e0, $c7, $17, $3d, $f0, $e0, $46, $81, $1c, $19, $52, $24 + byte $e1, $21, $7c, $c4, $47, $bc, $78, $83, $57, $fc, $08, $9e, $08, $c3, $57, $f0 + byte $09, $df, $c1, $21, $7a, $86, $87, $e8, $1f, $7c, $71, $c7, $bf, $e8, $98, $3c + byte $e2, $23, $6c, $36, $2f, $93, $54, $2f, $c8, $0d, $bd, $91, $68, $eb, $24, $f3 + byte $30, $49, $55, $82, $4e, $39, $18, $88, $04, $2f, $d2, $a4, $83, $43, $2d, $0c + byte $cf, $a5, $83, $50, $78, $0b, $5e, $b5, $a1, $74, $44, $f7, $ca, $10, $1d, $10 + byte $37, $8e, $e0, $49, $12, $65, $2b, $a8, $95, $8d, $21, $7c, $20, $22, $44, $fb + byte $25, $04, $5f, $a4, $f1, $a2, $31, $d2, $c6, $45, $0b, $27, $89, $56, $58, $2b + byte $6b, $43, $67, $0c, $5e, $34, $70, $b4, $82, $5a, $41, $a4, $35, $21, $c4, $8d + byte $0b, $27, $4e, $1c, $d4, $b1, $63, $a3, $da, $29, $c3, $43, $6b, $c0, $1d, $1d + byte $82, $47, $70, $c0, $86, $15, $0b, $16, $cc, $c1, $0b, $57, $f8, $c0, $81, $23 + byte $f8, $62, $c2, $d4, $18, $82, $17, $f6, $e0, $81, $35, $7c, $60, $c6, $8c, $09 + byte $22, $46, $08, $f1, $29, $78, $43, $c1, $0c, $19, $13, $c6, $e0, $85, $03, $46 + byte $78, $80, $16, $9f, $20, $85, $0f, $0c, $e1, $03, $66, $7c, $0c, $0e, $50, $a1 + + byte $40, $0e, $0f, $10, $21, $62, $00, $7f, $85, $ce, $04, $fd, $b3, $44, $27, $6f + byte $64, $83, $c7, $71, $24, $d6, $e2, $59, $c2, $5d, $36, $8a, $c6, $16, $b8, $12 + byte $47, $82, $1e, $de, $3b, $11, $9e, $69, $86, $0f, $04, $e1, $65, $2d, $44, $87 + byte $a2, $59, $21, $ee, $10, $be, $a5, $10, $1e, $ea, $72, $bc, $c4, $bf, $26, $de + byte $c0, $93, $08, $4f, $75, $a1, $42, $84, $0f, $7a, $e1, $61, $2b, $46, $67, $1c + byte $7b, $38, $3a, $c6, $27, $9c, $45, $03, $7a, $b2, $88, $8e, $77, $28, $bc, $04 + byte $af, $cf, $71, $07, $61, $e3, $86, $15, $1e, $eb, $c2, $63, $c0, $0f, $0f, $2f + byte $dc, $64, $15, $bc, $a0, $63, $47, $00, $3f, $3c, $86, $0f, $d8, $b0, $61, $e1 + byte $8a, $17, $30, $a0, $43, $c3, $10, $1e, $f0, $c1, $85, $83, $07, $36, $2e, $58 + byte $30, $61, $06, $07, $ec, $d0, $d0, $83, $47, $07, $36, $3a, $c0, $8d, $17, $e1 + byte $25, $78, $80, $0b, $1e, $60, $d1, $80, $8e, $0e, $e1, $29, $3a, $40, $03, $0f + byte $0e, $1c, $5a, $30, $60, $40, $83, $02, $05, $12, $46, $74, $08, $1e, $50, $f7 + byte $06, $76, $ea, $eb, $60, $3c, $2d, $0b, $8c, $18, $92, $05, $0e, $1a, $eb, $68 + byte $59, $cf, $8b, $f1, $80, $7e, $6b, $6c, $f3, $69, $81, $01, $3d, $b8, $e0, $b5 + byte $8d, $21, $85, $a7, $6c, $70, $74, $b6, $c1, $3e, $5c, $96, $e1, $63, $6f, $42 + byte $4d, $96, $eb, $20, $7c, $40, $88, $0f, $f9, $20, $5e, $41, $c5, $02, $19, $73 + + byte $70, $80, $08, $01, $7c, $78, $00, $5b, $21, $b0, $05, $87, $e8, $00, $29, $bc + byte $80, $0b, $9e, $58, $c3, $e3, $d1, $0a, $0f, $65, $03, $2c, $58, $68, $e1, $01 + byte $4a, $78, $3a, $1b, $c1, $a3, $2a, $80, $05, $03, $2d, $59, $5c, $c5, $e8, $10 + byte $2f, $82, $4f, $78, $4c, $16, $90, $21, $85, $a7, $e8, $d8, $24, $a0, $3e, $89 + byte $f0, $11, $de, $db, $40, $f0, $7c, $13, $e1, $2d, $3e, $80, $0e, $ef, $7d, $30 + byte $38, $06, $cf, $f0, $9e, $0d, $06, $e7, $3a, $c0, $0b, $67, $6b, $1e, $c9, $32 + byte $11, $35, $d2, $c0, $b4, $08, $1e, $f3, $68, $2d, $e6, $85, $34, $19, $3c, $d0 + byte $80, $41, $bd, $b6, $83, $63, $f0, $04, $3b, $2f, $40, $83, $46, $05, $12, $04 + byte $f4, $b9, $19, $1c, $21, $a6, $8c, $75, $b0, $0c, $41, $00, $07, $16, $9d, $83 + byte $67, $78, $58, $1b, $20, $82, $27, $50, $20, $80, $2b, $67, $ce, $0c, $cf, $e1 + byte $23, $78, $02, $02, $08, $60, $6f, $81, $8c, $0f, $c1, $31, $f8, $46, $f7, $e8 + byte $00, $28, $7c, $9d, $85, $f0, $0c, $2c, $bc, $c0, $0f, $ef, $e1, $01, $70, $78 + byte $0c, $1e, $d9, $24, $bc, $c1, $8b, $8e, $d9, $24, $3e, $a6, $0b, $f8, $f0, $b2 + byte $79, $b4, $86, $07, $b7, $68, $64, $63, $20, $e1, $35, $78, $96, $85, $a2, $17 + byte $2d, $e0, $c2, $39, $95, $87, $60, $70, $44, $82, $14, $01, $18, $00, $3c, $58 + byte $fe, $83, $77, $2a, $8c, $18, $40, $00, $01, $80, $0b, $33, $78, $4f, $c6, $58 + + byte $50, $3c, $00, $c1, $87, $03, $c3, $ff, $a1, $6c, $89, $29, $13, $28, $60, $80 + byte $c1, $03, $7a, $f0, $e9, $81, $f1, $11, $20, $3c, $d8, $50, $83, $57, $74, $04 + byte $19, $5f, $92, $83, $ff, $4b, $94, $1c, $e2, $63, $74, $8a, $0f, $fe, $73, $26 + byte $98, $e8, $1e, $1f, $82, $47, $41, $80, $0d, $2b, $ba, $e4, $0b, $28, $fe, $fd + byte $d7, $88, $e3, $00, $73, $1f, $40, $ad, $0f, $f0, $f3, $65, $f0, $0a, $0f, $c1 + byte $63, $6d, $40, $86, $08, $0e, $76, $74, $f4, $1f, $9d, $ce, $26, $14, $48, $10 + byte $c0, $82, $02, $12, $1e, $82, $e7, $39, $80, $11, $9e, $c0, $83, $01, $11, $1c + byte $fd, $c7, $8b, $e0, $71, $16, $c2, $23, $38, $d0, $c0, $82, $4f, $b2, $3a, $02 + byte $e0, $c1, $82, $0c, $4e, $fe, $93, $65, $78, $4c, $96, $a0, $81, $07, $ef, $40 + byte $12, $0e, $90, $01, $41, $08, $00, $dd, $97, $00, $15, $7c, $c0, $83, $02, $8e + byte $04, $10, $02, $78, $78, $bf, $44, $24, $4d, $07, $dd, $03, $00, $7c, $bc, $f1 + byte $22, $f8, $82, $07, $09, $ac, $05, $16, $03, $5c, $89, $0c, $0c, $f0, $e9, $09 + byte $0f, $4e, $8f, $0c, $31, $10, $40, $02, $9d, $1f, $c1, $03, $76, $26, $8f, $00 + byte $31, $28, $f8, $87, $8e, $08, $d8, $11, $82, $7f, $f0, $00, $0b, $fc, $00, $c2 + byte $29, $e5, $41, $00, $54, $82, $88, $00, $c1, $3f, $08, $82, $88, $00, $70, $83 + byte $7f, $10, $02, $76, $12, $82, $7f, $10, $02, $03, $04, $40, $f0, $0f, $c2, $20 + + byte $01, $05, $ff, $20, $d2, $cb, $43, $1f, $0a, $e0, $c8, $80, $22, $01, $8c, $08 + byte $20, $02, $f8, $f8, $f6, $02, $1e, $d8, $b8, $50, $06, $2f, $c4, $80, $86, $01 + byte $fe, $7e, $81, $17, $0e, $ee, $b3, $80, $1c, $18, $52, $20, $e1, $3b, $3c, $04 + byte $af, $65, $f1, $25, $80, $84, $0f, $80, $c3, $00, $5f, $bf, $d8, $46, $38, $f6 + byte $41, $f8, $8b, $0f, $57, $0b, $3a, $54, $28, $90, $01, $85, $cf, $27, $05, $eb + byte $1c, $06, $2f, $48, $10, $a3, $47, $f0, $81, $75, $8f, $8e, $03, $44, $08, $e0 + byte $83, $37, $cc, $6b, $1d, $bc, $c1, $81, $4d, $4e, $b0, $61, $9e, $07, $a8, $c1 + byte $17, $0c, $e8, $70, $11, $3c, $de, $55, $fc, $88, $1e, $e1, $23, $78, $80, $02 + byte $b9, $21, $12, $40, $06, $08, $fe, $20, $e1, $c6, $c0, $1c, $98, $3c, $c3, $0f + byte $fc, $08, $08, $ad, $00, $04, $7f, $80, $11, $20, $01, $c7, $1f, $90, $bb, $20 + byte $02, $64, $88, $04, $04, $3e, $7a, $26, $07, $10, $b8, $5a, $00, $74, $ec, $d0 + byte $b0, $42, $c5, $82, $39, $17, $60, $8c, $95, $30, $71, $c2, $c0, $5e, $20, $b0 + byte $42, $81, $82, $19, $52, $26, $c0, $10, $2b, $70, $c2, $c4, $01, $1d, $5b, $47 + byte $48, $11, $25, $33, $3c, $82, $8f, $0e, $0d, $10, $6a, $8e, $0c, $3e, $e0, $e2 + byte $63, $0d, $8c, $0e, $c1, $a1, $64, $66, $c2, $e0, $1f, $1c, $a2, $53, $f8, $82 + byte $16, $5f, $21, $84, $c7, $e0, $1d, $9f, $d2, $65, $f0, $02, $0b, $23, $39, $c5 + + byte $27, $88, $d1, $39, $78, $40, $4f, $4e, $90, $07, $54, $74, $02, $0b, $22, $38 + byte $c4, $a7, $e8, $04, $be, $26, $06, $0f, $e0, $c0, $e2, $73, $74, $02, $d7, $10 + byte $41, $04, $0f, $a0, $40, $80, $44, $57, $b0, $60, $c1, $04, $6f, $c0, $80, $01 + byte $25, $1b, $30, $c1, $f7, $78, $9c, $c0, $e8, $04, $3a, $3a, $dc, $cf, $f0, $10 + byte $1c, $4e, $f0, $8a, $40, $9f, $30, $62, $01, $68, $d0, $43, $02, $24, $88, $b8 + byte $50, $25, $23, $d1, $e6, $0c, $5e, $c0, $d3, $22, $d0, $f0, $3a, $25, $d3, $64 + byte $5c, $8c, $ee, $c1, $21, $3c, $01, $29, $88, $d1, $39, $5e, $02, $0b, $8f, $40 + byte $0a, $62, $b2, $89, $d7, $40, $00, $a7, $83, $f0, $1e, $9e, $82, $67, $78, $07 + byte $5a, $07, $aa, $40, $85, $c8, $1b, $e9, $3c, $3a, $00, $8a, $16, $45, $3b, $de + byte $56, $81, $78, $1f, $af, $1b, $47, $f0, $7c, $04, $79, $37, $5c, $04, $8f, $64 + byte $95, $77, $7b, $25, $40, $80, $f1, $7e, $14, $84, $27, $00, $c1, $33, $3c, $01 + byte $cf, $5a, $d1, $02, $00, $80, $36, $16, $9e, $a2, $c5, $20, $b8, $f3, $7d, $08 + byte $6c, $b4, $f8, $e2, $5d, $18, $14, $a8, $92, $f8, $46, $ba, $60, $d6, $ca, $c4 + byte $00, $8e, $72, $59, $ce, $c4, $4b, $13, $de, $a2, $01, $b1, $16, $b7, $22, $7c + byte $78, $70, $e1, $c0, $81, $1d, $2f, $56, $01, $a0, $5c, $14, $1c, $83, $07, $6c + byte $d8, $b0, $60, $7e, $8b, $23, $1d, $5e, $82, $43, $f0, $80, $1e, $de, $c3, $45 + + byte $78, $fc, $07, $25, $0b, $5e, $bc, $8c, $36, $ff, $20, $99, $47, $6b, $68, $50 + byte $1b, $44, $32, $81, $15, $ad, $83, $67, $70, $c8, $da, $69, $2b, $5a, $40, $85 + byte $02, $3f, $ed, $c3, $08, $4e, $c1, $23, $5c, $e4, $c5, $68, $fd, $2e, $db, $45 + byte $da, $cf, $0a, $f1, $20, $38, $40, $5a, $46, $6b, $61, $5c, $16, $c1, $b8, $54 + byte $23, $f6, $52, $af, $cc, $83, $85, $69, $1e, $85, $8b, $e0, $0f, $7b, $2f, $7c + byte $a6, $4e, $d9, $33, $d3, $50, $f0, $0c, $0f, $40, $83, $5f, $a1, $00, $1e, $de + byte $e3, $15, $2c, $18, $c1, $23, $6d, $a5, $e3, $11, $50, $09, $73, $41, $c5, $88 + byte $f7, $fd, $b3, $10, $43, $84, $00, $3e, $5a, $c3, $ac, $49, $90, $21, $ef, $40 + byte $f0, $db, $e1, $68, $84, $87, $68, $15, $5c, $b7, $45, $59, $cc, $02, $35, $31 + byte $5a, $1e, $23, $50, $79, $21, $2d, $00, $0a, $97, $c1, $73, $1f, $be, $81, $ac + byte $00, $04, $10, $80, $78, $1e, $2d, $5a, $03, $60, $80, $f0, $92, $0e, $98, $70 + byte $01, $22, $38, $c1, $87, $13, $4f, $93, $36, $c0, $e0, $00, $1d, $76, $c1, $84 + byte $1a, $0b, $20, $80, $05, $e5, $3f, $00, $57, $84, $82, $91, $31, $20, $82, $03 + byte $0d, $3c, $f8, $84, $17, $f0, $60, $41, $06, $17, $ff, $15, $11, $0a, $24, $08 + byte $e1, $dd, $3f, $8c, $8a, $00, $19, $62, $78, $8f, $04, $f1, $32, $3c, $01, $8d + byte $ae, $fe, $c3, $33, $18, $10, $c1, $cd, $7f, $bf, $08, $0f, $59, $2f, $14, $25 + + byte $63, $60, $e1, $dd, $7f, $32, $8c, $f6, $a1, $0a, $10, $7c, $b8, $30, $c3, $7d + byte $10, $04, $00, $17, $56, $70, $0f, $42, $f0, $60, $43, $0d, $fe, $41, $08, $36 + byte $b4, $e0, $1e, $84, $a1, $07, $f7, $20, $1e, $c4, $83, $30, $94, $04, $d1, $30 + byte $0a, $45, $c3, $58, $99, $93, $32, $64, $40, $ce, $10, $d0, $47, $c4, $e4, $08 + byte $7e, $25, $01, $0c, $4c, $68, $95, $69, $13, $84, $ef, $58, $09, $3a, $02, $ac + byte $8e, $5e, $70, $92, $92, $c9, $b4, $80, $92, $00, $83, $7f, $28, $00, $0d, $0c + byte $f0, $24, $08, $fe, $91, $08, $68, $93, $00, $fb, $12, $86, $78, $48, $00, $7a + byte $0e, $e0, $3c, $8c, $b6, $df, $b6, $8f, $45, $39, $0f, $c2, $70, $fa, $7e, $63 + byte $0f, $e2, $41, $3c, $88, $07, $f1, $20, $1e, $c4, $83, $78, $10, $0f, $a2, $c0 + byte $81, $01, $05, $02, $04, $30, $20, $80, $00, $e0, $c3, $83, $07, $17, $0e, $6c + byte $d8, $c1, $2b, $38, $04, $6f, $b8, $c1, $13, $56, $f0, $09, $0f, $e1, $33, $3c + byte $05, $ff, $e8, $10, $9e, $61, $86, $bf, $e0, $19, $2f, $61, $05, $af, $64, $1d + byte $bc, $83, $03, $8c, $f0, $91, $6e, $93, $51, $f0, $4f, $36, $c1, $11, $7a, $f8 + byte $0e, $ee, $30, $a0, $43, $df, $0a, $80, $8b, $51, $74, $82, $19, $1c, $a0, $9d + byte $6d, $00, $65, $29, $5a, $41, $fb, $86, $57, $07, $76, $70, $81, $d2, $98, $b6 + byte $c4, $59, $8e, $0b, $50, $a1, $40, $6e, $ad, $57, $b2, $0d, $16, $09, $88, $dd + + byte $3f, $08, $b4, $82, $34, $d8, $ff, $83, $28, $2c, $90, $20, $40, $34, $cc, $41 + byte $d0, $4b, $e0, $c3, $cc, $85, $c0, $e2, $52, $a3, $2a, $9d, $c1, $31, $75, $35 + byte $a2, $89, $5d, $b2, $a0, $66, $4e, $48, $90, $20, $ce, $e4, $8a, $58, $0a, $72 + byte $03, $e4, $e0, $00, $01, $3c, $78, $70, $60, $e1, $04, $cf, $70, $11, $3c, $82 + byte $63, $f0, $08, $0e, $60, $40, $c7, $c3, $68, $05, $11, $42, $70, $01, $0d, $2d + byte $5a, $85, $8b, $e0, $11, $0d, $c3, $05, $68, $50, $c1, $3d, $1a, $05, $07, $90 + byte $d1, $38, $dc, $36, $82, $72, $90, $94, $82, $7b, $95, $cc, $c2, $e0, $a2, $51 + byte $47, $ac, $83, $b9, $2b, $d8, $37, $ca, $cc, $12, $76, $6a, $d1, $9c, $4c, $95 + byte $1d, $09, $c8, $2b, $82, $0b, $a3, $86, $a6, $0c, $f8, $1b, $01, $16, $0c, $a8 + byte $e1, $f3, $76, $2c, $cd, $da, $9d, $0e, $5f, $4d, $a7, $6e, $d4, $b7, $75, $6d + byte $67, $ad, $68, $b8, $67, $21, $47, $cb, $46, $00, $fb, $88, $05, $17, $b0, $d9 + byte $30, $a9, $15, $e5, $22, $57, $79, $cb, $f8, $c7, $8e, $9b, $65, $7a, $6b, $15 + byte $47, $d8, $b9, $01, $5a, $4b, $ac, $4d, $f0, $4b, $67, $e6, $80, $0e, $15, $f2 + byte $19, $8a, $5a, $b9, $23, $1c, $fe, $05, $70, $e1, $70, $77, $c2, $8d, $13, $30 + byte $a0, $05, $cb, $57, $14, $15, $83, $35, $44, $b0, $2f, $29, $58, $45, $49, $c8 + byte $e0, $df, $2f, $9c, $8e, $01, $0d, $f2, $2f, $0f, $e2, $41, $7c, $fa, $6c, $0e + + byte $e0, $40, $01, $03, $84, $7f, $2f, $fc, $9f, $66, $50, $a5, $22, $38, $43, $8f + byte $0c, $6b, $3b, $b8, $42, $8b, $95, $60, $6f, $41, $b8, $85, $9d, $b0, $83, $43 + byte $dc, $81, $17, $5c, $fc, $83, $0e, $0e, $c0, $e2, $06, $00, $b8, $30, $33, $2c + byte $c8, $16, $b0, $07, $00, $c0, $c9, $fe, $d0, $81, $42, $86, $08, $0e, $54, $e1 + byte $f7, $0f, $15, $12, $04, $30, $a5, $3e, $08, $86, $83, $6a, $1e, $c4, $83, $78 + byte $10, $0f, $e2, $41, $3c, $88, $07, $f1, $20, $1e, $c4, $83, $78, $10, $0f, $02 + +DAT +{{ + + TERMS OF USE: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + associated documentation files (the "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +}} +DAT diff --git a/source/boing/boing-bel-foreground.spin b/source/boing/boing-bel-foreground.spin new file mode 100644 index 0000000..f561831 --- /dev/null +++ b/source/boing/boing-bel-foreground.spin @@ -0,0 +1,505 @@ +'' +'' VGA scanline driver 400x300 - foreground renderer +'' +'' Based on "Ball" demo for Gameduino +'' Copyright (c) 2011 by James Bowman +'' +'' Author: Marko Lukat +'' Last modified: 2012/12/24 +'' Version: 0.11 +'' +OBJ + system: "boing-bel-corecon" + +PUB null +'' This is not a top level object. + +PUB init(ID, mailbox) + + return system.launch(ID, @entry, mailbox) + +DAT org 0 ' foreground renderer + +entry jmpret $, #setup ' once + + rdlong indx, blnk ' | + cmpsub indx, scry wz ' | + if_ne jmp #$-2 ' waiting for last line to be fetched + +' Skip 26 sync lines and advance by a further 161 hub windows. This pushes an +' assumed foreground renderer hubop beyond the first video renderer line rdlong +' if it were to render scanline -1. +' After the waitcnt we have no further insns (18 + 14 = 32, 2 hub windows). + + mov cnt, cnt + add cnt, $+1 + long 13{18} + 14 + 132*16*26 + 16*(161 - 2) + +loop waitcnt cnt, eins ' initial sync point +' wrbyte indx, base ' assumed hubop + + call #pixels ' fetch foreground data (~92 hub windows) + + waitcnt cnt, zwei ' block until line is available + + rdlong bflag, bfadr wz + if_nz call #shadow ' draw shadow + cmp bflag, #0 wz + if_nz call #solid ' draw ball + + rol mask, #8 ' update shadow mask, it's unused + ' during 1st frame (msky == scry) + add indx, #1 ' line done, advance + cmpsub indx, scry wz ' optionally wrap line index + if_nz jmp #loop + +' per frame updates (during the first frame mask/shadow are off-screen) + + rdlong temp, blnk ' | + cmp temp, scry wz ' | + if_ne jmp #$-2 ' wait for ?/scry transition + + rdword mskx, crdx ' | + shl mskx, #16 ' | + sar mskx, #16 ' | + rdword msky, crdy ' | + shl msky, #16 ' | + sar msky, #16 ' update and sign-extend mask coordinates + + maxs mskx, scrx ' | + maxs msky, scry ' reasonable limit(s) (off-screen) + + mov shdx, mskx ' | + add shdx, #SOFFX ' | + mov shdy, msky ' | + add shdy, #SOFFY ' apply shadow offset(s) + + mov mskc, msky ' | + add mskc, #BSIZE -1 ' | + mov shdc, shdy ' | + add shdc, #BSIZE -1 ' bounding box setup + + long $00FF00FF ' | + mov mask, $-1 ' reset shadow mask (scry == 2n+?) + +eins long 100*16 ' initial line fetch +zwei long 164*16 ' remainder of two scan lines +drei long 132*16*28 ' skip all sync lines + + add cnt, drei + jmp #loop + +' support code + +pixels rdlong addr, feed wz ' get current buffer address + + if_z mov msky, scry ' move off-screen (disabled) + if_z jmp pixels_ret ' early exit + + mov ecnt, #BSIZE/4 + movd :set, #data + + mov temp, mskx ' | + and temp, #%11 wz ' observe alignment + + shl temp, #3 ' [4..0]: 24/16/8/0 + movs :two, temp + neg temp, temp ' [4..0]: 8/16/24/0 + movs :one, temp + + if_nz add ecnt, #1 ' need to process one more long + if_nz sub addr, #4 ' starting with addr[-1] + +:loop rdlong arg0, addr ' +0 = 00: DDCCBBAA, 01: ------DD + add addr, #4 ' +8 10: ----DDCC, 11: --DDCCBB + if_z jmp #:set ' -4 faster if aligned + + rdlong arg1, addr ' +0 = 00: n/a, 01: CCBBAA-- +:two shl arg1, #0-0 ' +8 10: BBAA----, 11: AA------ +:one shr arg0, #0-0 ' -4 + or arg0, arg1 ' +0 = + +:set mov 0-0, arg0 ' +4 + add $-1, dst1 ' +8 + + djnz ecnt, #:loop ' -4 + +pixels_ret ret + +solid cmps indx, msky wc ' | + if_nc cmps mskc, indx wc ' | + if_c jmp solid_ret ' vertical bounds check + + mov temp, indx + sub temp, msky + cmp temp, #BSIZE/2 wc + if_nc neg temp, temp + if_nc add temp, #BSIZE-1 + add temp, #table ' offset into table + + movs $+2, temp + neg arg2, #1 ' pipeline + mov arg0, 0-0 ' fetch offset/length pair + + mov arg1, arg0 + shr arg1, #16 wz ' length + + and arg0, #511 ' relative offset + add arg0, mskx ' absolute offset + + add arg2, arg0 ' | + add arg2, arg1 ' right side inclusive + + cmps arg2, #0 wc + if_nc cmps scrc, arg0 wc ' | + if_z_or_c jmp solid_ret ' horizontal bounds check + + cmps arg0, #0 wc ' | + if_c subabs arg1, arg0 ' | + if_c mov arg0, #0 ' clipped left + + cmps scrc, arg2 wc ' | + if_c mov arg1, scrx ' | + if_c sub arg1, arg0 ' clipped right side + +' arg0..(arg0+arg1-1) fits into the scanline buffer, draw the line + + mov addr, base + add addr, arg0 ' @byte[base][arg0] + + mov arg2, mskx ' | + and arg2, #%11 ' | + add arg2, arg0 ' | + sub arg2, mskx ' byte offset into data array + + shr arg1, #2 wz,nr ' special code for 1..3 + if_nz jmpret zero, #:full wc,nr ' carry set if taken (##) + + ' do an unaligned load + + mov arg0, arg2 ' remember for byte alignment + + shr arg2, #2 ' long offset + add arg2, #data ' long address + + movs $+2, arg2 + add arg2, #1 ' pipeline + mov arg3, 0-0 ' load 1st long + movs $+2, arg2 + test arg0, #%11 wz ' pipeline + mov arg2, 0-0 ' load 2nd long + + if_nz shl arg0, #3 ' 00: DDCCBBAA, 01: --DDCCBB + if_nz shr arg3, arg0 ' 10: ----DDCC, 11: ------DD + if_nz neg arg0, arg0 ' 00: n/a, 01: AA------ + if_nz shl arg2, arg0 ' 10: BBAA----, 11: CCBBAA-- + if_nz or arg3, arg2 ' combine both longs + + wrbyte arg3, addr + add addr, #1 + shr arg3, #8 + djnz arg1, #$-3 ' relaxed @ 2 hub windows/byte + + jmp solid_ret + +' length is prefix + 4n + suffix, split up and deal with it + +:full shr arg2, #2 ' long offset + add arg2, #data ' long address + + neg ecnt, arg0 ' 0123 >> 0321 + and ecnt, #%11 wz ' pixels in prefix + sub arg1, ecnt ' update length + if_z jmp #:core + + ' handle prefix + + shl arg0, #3 ' 1..3 >> 8..24 + rcr ecnt, arg0 ' create reverse mask (##) + + movs $+2, arg2 ' prefix data + rev ecnt, #0 ' adjust + + mov arg3, 0-0 ' | + andn arg3, ecnt ' only keep masked data + + rdlong quad, addr + and quad, ecnt + or quad, arg3 ' combine with background + wrlong quad, addr + + add arg2, #1 ' advance src + add addr, #4 ' advance dst + andn addr, #%11 ' | + + ' handle 4 + +:core test arg1, #%100 wz + if_nz movd $+2, arg2 + if_nz sub arg1, #4 ' update length + if_nz wrlong 0-0, addr + if_nz add addr, #4 ' advance dst + if_nz add arg2, #1 ' advance src + + ' handle 8n + + mov ecnt, arg1 ' remember for tail (%-??) + shr arg1, #3 wz ' check 8n count + if_z jmp #:suffix ' skip body + + mov frqb, addr + shr frqb, #1{/2} + + add arg2, arg1 ' | + add arg2, arg1 ' advance src + + movd :one, arg2 + sub :one, dst1 ' data[n][-1] + movd :two, arg2 + sub :two, dst2 ' data[n][-2] + + mov phsb, arg1 + shl phsb, #3 + mov addr, phsb ' advance dst + sub phsb, #1 ' 8n - 1 + +:one wrlong 0-0, phsb + sub $-1, dst2 + sub phsb, #7 wz +:two wrlong 0-0, phsb + sub $-1, dst2 + if_nz djnz phsb, #:one + + ' handle suffix + +:suffix and ecnt, #%11 wz ' suffix (unaligned) + if_z jmp solid_ret ' early exit + + shl ecnt, #3 ' 1..3 >> 8..24 + neg arg1, #1 ' create mask + + movs $+2, arg2 ' suffix data + shl arg1, ecnt ' adjust + + mov arg3, 0-0 ' | + andn arg3, arg1 ' only keep masked data + + rdlong quad, addr + and quad, arg1 + or quad, arg3 ' combine with background + wrlong quad, addr + +solid_ret ret + +shadow cmps indx, shdy wc ' | + if_nc cmps shdc, indx wc ' | + if_c jmp shadow_ret ' vertical bounds check + + mov temp, indx + sub temp, shdy + cmp temp, #BSIZE/2 wc + if_nc neg temp, temp + if_nc add temp, #BSIZE-1 + add temp, #table ' offset into table + + movs $+2, temp + neg arg2, #1 ' pipeline + mov arg0, 0-0 ' fetch offset/length pair + + mov arg1, arg0 + shr arg1, #16 wz ' length + + and arg0, #511 ' relative offset + add arg0, shdx ' absolute offset + + add arg2, arg0 ' | + add arg2, arg1 ' right side inclusive + + cmps arg2, #0 wc + if_nc cmps scrc, arg0 wc ' | + if_z_or_c jmp shadow_ret ' horizontal bounds check + + cmps arg0, #0 wc ' | + if_c subabs arg1, arg0 ' | + if_c mov arg0, #0 ' clipped left + + cmps scrc, arg2 wc ' | + if_c mov arg1, scrx ' | + if_c sub arg1, arg0 ' clipped right side + +' arg0..(arg0+arg1-1) fits into the scanline buffer, draw the line + + mov addr, base + add addr, arg0 ' @byte[base][arg0] + + shr arg1, #2 wz,wc,nr ' special code for 1..3 + if_nz jmpret zero, #:full wc,nr ' carry set if taken (##) + + test arg0, #1 wz + if_nz rol mask, #8 ' odd bytes only + + rdbyte quad, addr + and quad, mask + rol mask, #8 + wrbyte quad, addr + add addr, #1 + djnz arg1, #$-5 + + if_c_eq_z rol mask, #8 ' restore mask + + jmp shadow_ret + +:full neg ecnt, arg0 ' 0123 >> 0321 + and ecnt, #%11 wz ' pixels in prefix + sub arg1, ecnt ' update length + if_z jmp #:core + + shl arg0, #3 ' 1..3 >> 8..24 + rcr ecnt, arg0 ' create reverse mask (##) + rev ecnt, #0 ' adjust + + rdlong quad, addr + or ecnt, mask ' add shadow mask + and quad, ecnt + wrlong quad, addr + + add addr, #4 +'{rd/wrlong} andn addr, #%11 + +:core mov ecnt, arg1 ' remember for tail (%-??) + shr arg1, #2 wz ' check long count + if_z jmp #:tail ' skip body + + rdlong quad, addr + and quad, mask + cmp arg1, #1 wz + wrlong quad, addr + add addr, #4 + if_nz djnz arg1, #$-5 + +:tail and ecnt, #%11 wz ' suffix (unaligned) + if_z jmp shadow_ret ' early exit + + shl ecnt, #3 ' 1..3 >> 8..24 + neg arg1, #1 ' | + shl arg1, ecnt ' protect unused pixels + rdlong quad, addr + or arg1, mask ' add shadow mask + and quad, arg1 + wrlong quad, addr + +shadow_ret ret + +' initialised data and/or presets + +table word 48, 16, 43, 26, 39, 34, 36, 40, 34, 44, 32, 48, 30, 52, 28, 56 + word 26, 60, 25, 62, 23, 66, 22, 68, 21, 70, 19, 74, 18, 76, 17, 78 + word 16, 80, 15, 82, 14, 84, 13, 86, 13, 86, 12, 88, 11, 90, 10, 92 + word 10, 92, 9, 94, 8, 96, 8, 96, 7, 98, 7, 98, 6, 100, 6, 100 + word 5, 102, 5, 102, 4, 104, 4, 104, 3, 106, 3, 106, 3, 106, 2, 108 + word 2, 108, 2, 108, 2, 108, 1, 110, 1, 110, 1, 110, 1, 110, 1, 110 + word 0, 112, 0, 112, 0, 112, 0, 112, 0, 112, 0, 112, 0, 112, 0, 112 + +feed long -12 ' | +crdx long -8 ' | +crdy long -6 ' | + ' quick access relative to par +blnk long -4 ' | +base long NEGX ' | + +dst1 long 1 << 9 ' dst +/-= 1 +dst2 long 2 << 9 ' dst +/-= 2 + +scrc long 399 ' upper limit (inclusive) +scrx long 400 + +bfadr long 448 +bflag long 0 + +' Stuff below is re-purposed for temporary storage. + +setup add crdx, par ' mask coordinates (%%) + add crdy, par ' | + add feed, par ' mask buffer location + + add base, par ' scanline buffer + add blnk, base wc ' frame indicator + + rdword indx, blnk wz ' (%%) + if_nz mov scry, indx wc ' (%%) + if_c_or_nz jmp #$-2 ' auto-detect res_y + +' The loop is only left once a non-zero value has been written to scry +' and indx transitions to zero afterwards. + + mov msky, scry ' move off-screen + mov shdy, scry ' | + + movi ctrb, #%0_11111_000 ' LOGIC always (loader support) + + add bfadr, par + jmp %%0 ' return + + fit + +' uninitialised data and/or temporaries + + org setup + +scry res 1 ' must be 1st..5th (%%) +indx res 1 ' | + +addr res 1 ' scanline reference +ecnt res 1 ' element count + +mask res 1 ' shadow mask + +mskc res 1 ' upper limit (inclusive) +mskx res 1 ' mask coordinates +msky res 1 ' signed 16bit + +shdc res 1 ' upper limit (inclusive) +shdx res 1 ' shadow coordinates +shdy res 1 ' signed 16bit + +arg0 res 1 +arg1 res 1 +arg2 res 1 +arg3 res 1 + +temp res 1 +quad res 1 + +data res BSIZE/4 +1{unaligned} + +tail fit + +CON + zero = $1F0 ' par (dst only) + + BSIZE = 112 ' mask width/height (4n) + SOFFX = 15 ' | + SOFFY = 15 ' shadow offset + +DAT +{{ + + TERMS OF USE: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + associated documentation files (the "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +}} +DAT diff --git a/source/boing/boing-bel.spin b/source/boing/boing-bel.spin new file mode 100644 index 0000000..cdb4586 --- /dev/null +++ b/source/boing/boing-bel.spin @@ -0,0 +1,286 @@ +'' +'' VGA scanline driver 400x300 - demo +'' +'' Based on "Ball" demo for Gameduino +'' Copyright (c) 2011 by James Bowman +'' +'' Author: Marko Lukat +'' Last modified: 2012/12/24 +'' Version: 0.10 +'' +'' A few notes on timing. A (double) scan line lasts 2*(100+32) hub windows. +'' The background renderer fills the scan line as soon as the video driver +'' has read the relevant quad. At the same time the foreground renderer +'' fetches its data line. +'' +'' +-- video driver reads line N +'' +-- background renderer fills line N+1 +'' +-- foreground renderer fetches data for line N+1 +'' | +'' | +-- scanline N completely fetched +'' | +-- background renderer idle +'' | visible line area +-- foreground renderer draws shadow and ball +'' | | +'' ----------- 100 -----------#---32--- +'' ----------- 100 -----------+---32--- +'' ----------- 100 -----------#---32--- +'' | +'' +-- video driver reads line N+1 +'' +CON + _clkmode = XTAL1|PLL16X + _xinfreq = 5_000_000 + +CON + res_x = driver#res_x + res_y = driver#res_y + +OBJ + driver: "boing-bel-driver" + + back: "boing-bel-background" + ball: "boing-bel-foreground" + anim: "boing-bel-feeder" + + keyb: "bel-keyb" + gcon: "glob-con" + +VAR + long feeder ' @scan[-3] + long coordinates ' @scan[-2] + long frame ' @scan[-1] + long scan[res_x / 4] + long grid[12] + long bflag + +PUB main | cmd + + init +' boing + + repeat + case gc + 1: pc(keyb.gotkey) 'tastaturstatus senden + 2: pc(keyb.key) 'tastaturzeichen senden + 3: coordinates.word[0] := gw + coordinates.word[1] := gw + 4: waitVBL + 5: boing + 6: bars{gc,gc,gc} + 7: grid_on + 8: grid_off + 9: show_on + 10: bar_on + 11: bflag := gc + 12: grid[gc] := gw + 99: reboot + +PRI init + + dira := db_in ' datenbus auf eingabe schalten + outa[gcon#bus_hs] := 1 ' handshake inaktiv + keyb.start(gcon#b_keybd, gcon#b_keybc) ' tastaturport starten + driver.init(-1, @scan{0}) ' scanline driver + back.init(-1, @scan{0}) ' background + ball.init(-1, @scan{0}) ' foreground + anim.init(-1, @scan{0}) ' image feeder + grid_on + bflag := 0 + anim.uncompress(FALSE) ' uncompress image + + +CON ''------------------------------------------------- SUBPROTOKOLL-FUNKTIONEN + +' hbeat --------+ +' clk -------+| +' /wr ------+|| +' /hs -----+||| +------------------------- /cs +' |||| | -------- d0..d7 +DB_IN = %00001001_00000000_00000000_00000000 'maske: dbus-eingabe +DB_OUT = %00001001_00000000_00000000_11111111 'maske: dbus-ausgabe + +M1 = %00000010_00000000_00000000_00000000 +M2 = %00000010_10000000_00000000_00000000 'busclk=1? & /cs=0? + +M3 = %00000000_00000000_00000000_00000000 +M4 = %00000010_00000000_00000000_00000000 'busclk=0? + +PUB pc(zeichen) 'chip: ein byte an regnatix senden +''funktionsgruppe : chip +''funktion : ein byte an regnatix senden +''eingabe : byte +''ausgabe : - + + waitpeq(M1,M2,0) 'busclk=1? & prop2=0? + dira := db_out 'datenbus auf ausgabe stellen + outa[7..0] := zeichen 'daten ausgeben + outa[gcon#bus_hs] := 0 'daten gültig + waitpeq(M3,M4,0) 'busclk=0? + dira := db_in 'bus freigeben + outa[gcon#bus_hs] := 1 'daten ungültig + +PUB gc:zeichen 'chip: ein byte von regnatix empfangen +''funktionsgruppe : chip +''funktion : ein byte von regnatix empfangen +''eingabe : - +''ausgabe : byte + + waitpeq(M1,M2,0) 'busclk=1? & prop2=0? + zeichen := ina[7..0] 'daten einlesen + outa[gcon#bus_hs] := 0 'daten quittieren + waitpeq(M3,M4,0) 'busclk=0? + outa[gcon#bus_hs] := 1 + + +PUB pw(wert) 'sub: word senden +''funktionsgruppe : sub +''funktion : subprotokoll um einen long-wert an regnatix zu senden +''eingabe : 16bit wert der gesendet werden soll +''ausgabe : - +''busprotokoll : [put.byte1][put.byte2] +'' : [ hsb ][ ] + + pc(wert >> 8) + pc(wert) + +PUB gw:wert 'sub: word empfangen +''funktionsgruppe : sub +''funktion : subprotokoll um einen 16bit-wert von regnatix zu empfangen +''eingabe : - +''ausgabe : 16bit-wert der empfangen wurde +''busprotokoll : [get.byte1][get.byte2] +'' : [ hsb ][ lsb ] + + wert := gc << 8 + wert := wert + gc + +PUB gl:wert 'sub: long empfangen +''funktionsgruppe : sub +''funktion : subprotokoll um einen long-wert von regnatix zu empfangen +''eingabe : - +''ausgabe : 32bit-wert der empfangen wurde +''busprotokoll : [get.byte1][get.byte2][get.byte3][get.byte4] +'' : [ hsb ][ ][ ][ lsb ] + + wert := gc << 24 '32 bit empfangen hsb/lsb + wert := wert + gc << 16 + wert := wert + gc << 8 + wert := wert + gc + +CON ''------------------------------------------------- DEMO-FUNKTIONEN + LBASE = 0 + RBASE = res_x - ball#BSIZE + YBASE = res_y - ball#BSIZE + + MBAR = %00000000_11111111 + BLINE = 1 + +PRI boing : r | bx, by, bxv, byv + + bx := 0 + by := 0 + bxv := 2 + byv := 1 + + repeat + bx += bxv + by += byv + + if bx < LBASE + bx := constant(2*LBASE) - bx + -bxv + + if bx > RBASE + bx := constant(2*RBASE) - bx + -bxv + + if by > YBASE + by := constant(2*YBASE) - by + -byv + + ifnot ++r & 7 ' add some gravity + byv++ + + repeat 1 + waitVBL + coordinates.word{0} := bx ' | + coordinates.word[1] := by ' update coordinates + +PRI bars + + grid[BLINE ] := bar(gc) + grid[BLINE+2] := bar(gc) + grid[BLINE+4] := bar(gc) + +PRI bar(level):b + + b := MBAR + b := !(b >> (level & 7)) & MBAR + b := b | b >< 16 + +PRI grid_on|i + + repeat i from 0 to 11 + grid[i] := 0 + +PRI grid_off|i + + repeat i from 0 to 11 + grid[i] := $FFFF + +PRI show_on|i,n + + n := %00000000_11111111 + + repeat 19 + repeat i from 11 to 0 + grid[i] := grid[i-1] + n := n >> 1 + grid[0] := n | n >< 16 + waitcnt(cnt + clkfreq/30) + +PRI bar_on + + bar_up(BLINE ) + waitcnt(cnt + clkfreq/3) + bar_up(BLINE+2) + waitcnt(cnt + clkfreq/3) + bar_up(BLINE+4) + waitcnt(cnt + clkfreq/3) + +PRI bar_up(upline)|i + + repeat i from 11 to upline + grid[(i+1) <# 11] := 0 + grid[i] := %00000111_11100000 + waitcnt(cnt+clkfreq/20) + +PRI waitVBL + + repeat + until frame == res_y ' last line has been fetched + repeat + until frame <> res_y ' vertical blank starts (res_y/0 transition) + +DAT +{{ + + TERMS OF USE: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + associated documentation files (the "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +}} +DAT diff --git a/source/boing/boing-reg.spin b/source/boing/boing-reg.spin new file mode 100644 index 0000000..f1fe188 --- /dev/null +++ b/source/boing/boing-reg.spin @@ -0,0 +1,272 @@ +{{ + +}} + +OBJ + ios: "reg-ios" + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + +SIDMASK = %00000000_00000000_00000000_00010000 + +VAR + + long dmpreg + long fcnt + long fpos + long datcnt 'zeiger für dateiliste + byte fl_bye 'flag player beenden + byte fn[12] 'puffer für dateinamen + long dmu 'marker des aktuellen userverzeichnisses + long bx,by,bxv,byv + +PUB main | key + + ios.start + + ios.belload(string("boing.bel")) + ios.sdclose + + ifnot (ios.admgetspec & SIDMASK) + ios.sddmset(ios#DM_USER) 'u-marker setzen + dmu := ios.sddmget(ios#DM_USER) 'usermarker von administra holen + ios.admload(string("boing.adm")) + ios.sddmput(ios#DM_USER,dmu) 'usermarker wieder in administra setzen + ios.sddmact(ios#DM_USER) 'u-marker aktivieren + + play_count + repeat + intro + \boing(string("boing.sid")) + play_dir + + ios.belreset + ios.admreset + waitcnt(cnt+clkfreq*2) + ios.stop + +PRI esc_key + + if ios.g0_keystat + case ios.g0_keycode + 27: + 'ESC - geordneter rücksturz zur erde... :) + ios.sid_dmpstop 'ok, erstmal die mucke aus + ios.sid_mute(3) + ios.belreset + ios.sddmset(ios#DM_USER) 'u-marker setzen + dmu := ios.sddmget(ios#DM_USER) 'usermarker von administra holen + ios.admreset + waitcnt(cnt+clkfreq*2) + ios.sddmput(ios#DM_USER,dmu) 'usermarker wieder in administra setzen + ios.sddmact(ios#DM_USER) 'u-marker aktivieren + credits + ios.stop + "n": + ios.sid_dmpstop + ios.sid_mute(3) + abort + "p": + ios.sid_dmppause + +PRI credits + + ios.curoff + ios.printcls + ios.printnl + ios.print(string("Bouncing Ball for Hive - 2013 ",$0d,$0d)) + ios.print(string("Graphics-Code : kuroneko",$0d)) + ios.print(string("SidCog : Ahle2 ",$0d)) + ios.print(string("Hive-Code : drohne235",$0d,$0d)) + + ios.curon + +CON 'sd-player +PRI play_dir|stradr,len,i 'alle songs auf der sd-card abspielen + + datcnt := 0 'zum listenanfang + repeat i from 0 to fcnt-1 'dateiliste abspielen + fpos := i + 1 + play_dir_rdlst(@fn) + \boing(@fn) + +PRI play_count|stradr,i + + ios.sddir 'kommando: verzeichnis öffnen + datcnt := 0 'zum listenanfang + fcnt := 0 'zähler für dateianzahl + fl_bye := 0 + repeat while (stradr := ios.sdnext) 'dateiliste einlesen + if str_find(stradr,string(".DMP")) + fcnt++ + play_dir_wrlst(stradr) + +PRI play_dir_wrlst(stradr)|len,i 'kopiert dateinamen in liste + len := strsize(stradr) + repeat i from 0 to len-1 + ios.ram_wrbyte(ios#usrmod,byte[stradr][i],datcnt++) + ios.ram_wrbyte(ios#usrmod,0,datcnt++) + +PRI play_dir_rdlst(stradr)|i,n 'liest dateinamen aus list + i := 0 + repeat + n := ios.ram_rdbyte(ios#usrmod,datcnt++) + byte[stradr][i++] := n + while n <> 0 + +PRI str_find(string_1, string_2) : buffer | counter 'sys: string suchen + +'' ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +'' │ Searches a string of characters for the first occurence of the specified string of characters. │ +'' │ │ +'' │ Returns the address of that string of characters if found and zero if not found. │ +'' │ │ +'' │ string1 - A pointer to the string of characters to search. │ +'' │ string2 - A pointer to the string of characters to find in the string of characters to search. │ +'' └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + repeat strsize(string_1--) + + if(byte[++string_1] == byte[string_2]) + + repeat counter from 0 to (strsize(string_2) - 1) + + if(byte[string_1][counter] <> byte[string_2][counter]) + buffer~~ + + ifnot(buffer~) + return string_1 + +CON + + res_x = 400 + res_y = 300 + + LBASE = 0 + RBASE = res_x - 112 + YBASE = res_y - 112 + + LSHIFT = 8 + +PRI intro + + ball_on(0) + grid_on + pause(3) + bar_on + pause(3) + +PRI boing(stradr)|r,b1,b2,b3,status + + bx := 0 + by := 0 + bxv := 2 + byv := 1 + ball_on(1) + ios.sid_mdmpplay(stradr) ' läuft auf sid2 + repeat + bx += bxv + by += byv + if bx < LBASE + bx := constant(2*LBASE) - bx + -bxv + if bx > RBASE + bx := constant(2*RBASE) - bx + -bxv + if by > YBASE + by := constant(2*YBASE) - by + -byv + ifnot ++r & 7 ' add some gravity + byv++ + status := ios.sid_dmpstatus 'playerstatus abfragen + dmpreg := ios.sid_dmpreg + b1 := (word[dmpreg + 0]>>LSHIFT) #> 1 + b2 := (word[dmpreg + 2]>>LSHIFT) #> 1 + b3 := (word[dmpreg + 4]>>LSHIFT) #> 1 + bars(b1,b2,b3) + setBall(bx,by) + esc_key + waitVBL + while status + ios.sid_dmpstop + +PRI keystat : char + + ios.bus_putchar2(1) + return ios.bus_getchar2 + +PRI waitVBL + + ios.bus_putchar2(4) + +PRI setBall(x,y) + + ios.bus_putchar2(3) + ios.bus_putword2(x) + ios.bus_putword2(y) + +PRI boing_test + + ios.bus_putchar2(5) + +PRI bars(b1,b2,b3) + + ios.bus_putchar2(6) + ios.bus_putchar2(b1) + ios.bus_putchar2(b2) + ios.bus_putchar2(b3) + +PRI grid_on + + ios.bus_putchar2(7) + +PRI grid_off + + ios.bus_putchar2(8) + +PRI show_on + + ios.bus_putchar2(9) + +PRI bar_on + + ios.bus_putchar2(10) + +PRI ball_on(bflag) + + ios.bus_putchar2(11) + ios.bus_putchar2(bflag) + +PRI grid(line,val) + + ios.bus_putchar2(3) + ios.bus_putword2(line) + ios.bus_putword2(val) + +PRI pause(sec) + + waitcnt(cnt + clkfreq * sec) + +DAT + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/gui-demo/GUIBase.spin b/source/gui-demo/GUIBase.spin new file mode 100644 index 0000000..521cf1b --- /dev/null +++ b/source/gui-demo/GUIBase.spin @@ -0,0 +1,938 @@ +CON ''=====< GUI Base >======================================================= +'' +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: GUIBase.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== + +CON + '--------------------------------------------------------------------------- + ' GUI Element Inventory + '--------------------------------------------------------------------------- + ' User modifies these as required by the GUI application being written. + ' These constants describe how many elements of each type are required. Set + ' any you do not need to 1. You can't set it to 0 because there is no + ' conditional compilation for SPIN so I can't avoid NULL references. + '-------------------------------------------------------------------------- + + GZ_SPIN = 2 'number of SPIN controls + GZ_CHKB = 4 'number of Check Boxes + GZ_RADB = 6 'number of Radio Buttons + GZ_TBOX = 3 'number of Text Boxes + GZ_MENU = 6 'number of Menu Items + GZ_INPF = 3 'number of Input Fields + GZ_PUSH = 3 'number of pushbuttons + GZ_STAT = 2 'number of Status Lamps + + + '--------------------------------------------------------------------------- + ' Do not modify any of the constants below + '--------------------------------------------------------------------------- + + GZ_TOTAL = GZ_SPIN+GZ_CHKB+GZ_RADB+GZ_TBOX+GZ_MENU+GZ_INPF+GZ_PUSH+GZ_STAT + + GID_SPIN = $0100 'types + GID_CHKB = $0200 + GID_RADB = $0300 + GID_TBOX = $0400 + GID_MENU = $0500 + GID_INPF = $0600 + GID_PUSH = $0700 + GID_STAT = $0800 + + G_INIT = $8000 + G_IMSK = $7F00 + G_OMSK = $00FF + + VGACOLS = SVGA#cols + VGAROWS = SVGA#rows + + +OBJ + '--------------------------------------------------------------------------- + ' required driver objects, keep these + '--------------------------------------------------------------------------- + SVGA : "vga_hires_text" + MOUS : "mouse" + KEYB : "Keyboard" 'only needed if using the Input Field Object + + '--------------------------------------------------------------------------- + ' UI element objects + ' + ' You will need to comment out elements for which you set the number to 0, + ' the compiler will issue an error for 0 sized arrays. + ' + ' Note that the SBOX object (Simple Box) is used by several other objects so + ' do not comment out that one (it is miniscule anyway) + '--------------------------------------------------------------------------- + SPIN[GZ_SPIN] : "SpinBox" + CHKB[GZ_CHKB] : "RadioCheck" + RADB[GZ_RADB] : "RadioCheck" + TBOX[GZ_TBOX] : "TextBox" + MENU[GZ_MENU] : "MenuItem" + INPF[GZ_INPF] : "InputField" + PUSH[GZ_PUSH] : "PushButton" + STAT[GZ_STAT] : "StatusLamp" + SBOX : "SimpleBox" + + +VAR + long scrn[VGACOLS*VGAROWS/4] 'screen buffer - could be bytes, but longs allow more efficient scrolling + word colors[VGAROWS] 'row colors + long sync 'sync long - written to -1 by VGA driver after each screen refresh + byte cx0,cy0,cm0,cx1,cy1,cm1 'cursor control bytes + long liveINPF 'currently active Input Field (has keyboard focus) + word gz_elem[GZ_TOTAL] 'element management array + byte gz_groups[GZ_RADB] 'radio button group control + +CON ''=====< GUI Base Functions >============================================= + ' + ' NOTE Do not modify anything below unless you wish ot add or + ' make changes to the GUI functionality + '------------------------------------------------------------------------- +PUB Init( vgaBasePin, MouseDatPin, MouseClkPin, KeyboardDatPin, KeyboardClkPin ) | idx, gdx +'Initializes the VGA, Mouse and Keyboard Drivers as well as basic GUI parameters +' +' vgaBasePin - start of 8 consecutive pins where the VGA H/W interface is +' MouseDatPin - the pin driving the mouse data line +' MouseClkPin - the pin driving the mouse clock line +' KeyboardDatPin - the pin driving the keyboard data line +' KeyboardClkPin - the pin driving the keyboard clock line +' +'Returns the screen geometry in a WORD +' high byte = number of character rows +' low byte = number of cgaracter columns + + '--------------------------------------------------------------------------- + ' Start VGA, Mouse and Keyboard Drivers + '--------------------------------------------------------------------------- + + cx1 := 0 'text cursor starting position + cy1 := 3 ' (but hidden at start) + + SVGA.start(vgaBasePin,@scrn,@colors,@cx0,@sync) 'start VGA HI RES TEXT driver + + + MOUS.start(MouseDatPin, MouseClkPin) 'start mouse and set bound parameters + MOUS.bound_limits(0, 0, 0, VGACOLS - 1, VGAROWS - 1, 0) + MOUS.bound_scales(4, -7, 0) 'adjust speed/sensitivity to be a touch slower + MOUS.bound_preset(2, 6, 0) 'mouse starting position + + KEYB.start( KeyboardDatPin, KeyboardClkPin ) 'start keyboard driver + + cm0 := %001 'set mouse cursor to be a solid block + cm1 := %000 'set text cursor to be off for the moment + + ClearScreen( %%020, %%000 ) 'green on black each is %%RGB 4 levels per R-G-B + + + '--------------------------------------------------------------------------- + 'Prepare management and control array. This array has an entry for each + 'element declared it maintains the status of each element and indices into + 'the individual element arrays + ' + 'WORD format: aabb + ' + ' where aa = type with msb 0 = free 1 = set (initialized) + ' bb = index to element array + '--------------------------------------------------------------------------- + + gdx := 0 + + if GZ_SPIN + repeat idx from 0 to GZ_SPIN - 1 + gz_elem[gdx] := GID_SPIN + idx + SPIN[idx].set_gzidx( gdx ) + gdx++ + + if GZ_CHKB + repeat idx from 0 to GZ_CHKB - 1 + gz_elem[gdx] := GID_CHKB + idx + CHKB[idx].set_gzidx( gdx ) + gdx++ + + if GZ_RADB + repeat idx from 0 to GZ_RADB - 1 + gz_elem[gdx] := GID_RADB + idx + RADB[idx].set_gzidx( gdx ) + gdx++ + + if GZ_TBOX + repeat idx from 0 to GZ_TBOX - 1 + gz_elem[gdx] := GID_TBOX + idx + TBOX[idx].set_gzidx( gdx ) + gdx++ + + if GZ_MENU + repeat idx from 0 to GZ_MENU - 1 + gz_elem[gdx] := GID_MENU + idx + MENU[idx].set_gzidx( gdx ) + gdx++ + + if GZ_INPF + repeat idx from 0 to GZ_INPF - 1 + gz_elem[gdx] := GID_INPF + idx + INPF[idx].set_gzidx( gdx ) + gdx++ + + if GZ_PUSH + repeat idx from 0 to GZ_PUSH - 1 + gz_elem[gdx] := GID_PUSH + idx + PUSH[idx].set_gzidx( gdx ) + gdx++ + + if GZ_STAT + repeat idx from 0 to GZ_STAT - 1 + gz_elem[gdx] := GID_STAT + idx + STAT[idx].set_gzidx( gdx ) + gdx++ + + liveINPF := 0 + + return ( VGAROWS << 8 ) + VGACOLS + + +PUB ProcessUI | retVal, gdx_in, odx, idx, tmp +'This function is the UI processing and control funtion. It must be executed +'often, regularly, and quickly. It should be placed in the application main +'loop and be allowed to run as often as possible to ensure a responsive UI +' +'This function will manage the UI and will return the unique id of the GUI +'element that requires an action (i.e. user clicked on an item or pressed +'enter in an Input Field). The value returned ( a GUID ) is the same value +'that was returned when the element was created via "Init" function (i.e. +'from GUI.SPINInit() or GUI.PUSHInit(), etc) + + retVal := -1 + + cx0 := MOUS.bound_x 'get mouse position to set cursor 0 position + cy0 := MOUS.bound_y 'ALWAYS do this first + + '--------------------------------------------------------------------------- + 'check if mouse over a control + '--------------------------------------------------------------------------- + gdx_in := IsIn( cx0, cy0 ) + + '--------------------------------------------------------------------------- + 'on mouse left click perform UI action for selected element (if any) + '--------------------------------------------------------------------------- + if gdx_in <> -1 'if we are in a gui element + if MOUS.button(0) 'if mouse left-click + + odx := gz_elem[gdx_in] & G_OMSK 'index to object array + + case gz_elem[gdx_in] & G_IMSK + GID_SPIN: 'in Spin Control + if SPIN[odx].Clicked(cx0,cy0)<>-1 ' - execute click on spin + retVal := gdx_in + + GID_CHKB: 'in Check Box + CHKB[odx].Select( -1 ) ' - toggle checkbox + retVal := gdx_in + + GID_RADB: 'in Radio Button + idx := 0 + repeat GZ_RADB 'for each radio button + if odx == idx + RADB[idx].Select( 1 ) ' - toggle ON the selected one + RADB[idx].DrawText( 1 ) + else + if gz_groups[odx] == gz_groups[idx] + RADB[idx].Select( 0 ) ' - toggle OFF the others in this group + RADB[idx].DrawText( 0 ) + idx++ + retVal := gdx_in + + GID_TBOX: 'in Text Box + retVal := gdx_in + + GID_MENU: 'in Menu Item + retVal := gdx_in + + GID_INPF: 'in Input Field + idx := 0 + repeat GZ_INPF 'for each input field + if idx == odx + INPF[idx].Select(1,@cx1,@cy1) + liveINPF := idx + cm1 := %111 'turn text cursor on, underscore slow blink + else + INPF[idx].Select(0,@cx1,@cy1) + idx++ + 'note no return for selecting INPF + + GID_PUSH: 'in Push Button + retVal := gdx_in + + GID_STAT: 'in Status Lamp + retVal := gdx_in + + repeat while MOUS.button(0) ' - wait for mouse release + + '--------------------------------------------------------------------------- + 'Handle Keyboard input + '--------------------------------------------------------------------------- + + if KEYB.gotkey + idx := INPF[liveINPF].get_gzidx + if gz_elem[idx] & G_INIT == G_INIT 'is it set + tmp := INPF[liveINPF].Handler(KEYB.key) + if tmp & $80000000 + retVal := INPF[liveINPF].get_gzidx 'if enter was pressed + + return retVal + + +PUB GetMouseXY +'Returns the mouse location in a WORD +' high byte = x location +' low byte = y location + return ( cx0 << 8 ) + cy0 + + +PRI IsIn( cx, cy ) | idx, gdx, retVal +'returns -1 if not in an element else a gui token (gz_elem[] array index) + + retVal := -1 + + if GZ_SPIN + repeat idx from 0 to GZ_SPIN - 1 + gdx := SPIN[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if SPIN[idx].IsIn( cx, cy ) + retVal := SPIN[idx].get_gzidx + + if GZ_CHKB + repeat idx from 0 to GZ_CHKB - 1 + gdx := CHKB[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if CHKB[idx].IsIn( cx, cy ) + retVal := CHKB[idx].get_gzidx + CHKB[idx].DrawText( 1 ) + else + CHKB[idx].DrawText( 0 ) + + if GZ_RADB + repeat idx from 0 to GZ_RADB - 1 + gdx := RADB[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if RADB[idx].IsIn( cx, cy ) + retVal := RADB[idx].get_gzidx + RADB[idx].DrawText( 1 ) + else + RADB[idx].DrawText( 0 ) + + if GZ_TBOX + repeat idx from 0 to GZ_TBOX - 1 + gdx := TBOX[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if TBOX[idx].IsIn( cx, cy ) + retVal := TBOX[idx].get_gzidx + + if GZ_MENU + repeat idx from 0 to GZ_MENU - 1 + gdx := MENU[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if MENU[idx].IsIn( cx, cy ) + retVal := MENU[idx].get_gzidx + MENU[idx].DrawText( 1 ) + else + MENU[idx].DrawText( 0 ) + + if GZ_INPF + repeat idx from 0 to GZ_INPF - 1 + gdx := INPF[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if INPF[idx].IsIn( cx, cy ) + retVal := INPF[idx].get_gzidx + + if GZ_PUSH + repeat idx from 0 to GZ_PUSH - 1 + gdx := PUSH[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if PUSH[idx].IsIn( cx, cy ) + retVal := PUSH[idx].get_gzidx + PUSH[idx].DrawText( 1 ) + else + PUSH[idx].DrawText( 0 ) + + if GZ_STAT + repeat idx from 0 to GZ_STAT - 1 + gdx := STAT[idx].get_gzidx + if gz_elem[gdx] & G_INIT == G_INIT 'is it set + if STAT[idx].IsIn( cx, cy ) + retVal := STAT[idx].get_gzidx + + return retVal + + +CON ''=====< SIMPLE BOX INTERFACE FUNCTIONS >================================= + +PUB SBOXInit( pRow, pCol, pWidth, pHeight, pTitlePtr ) +'Creates box with specified width and text. Something like: +' ------------- +' | | - title area ( non-existant if no title supplied) +' ------------- +' | | +' | | - text area with optional scolling print capability +' | | +' ------------- +' +' pRow = row upper location +' pCol = col left location +' pWidth = width +' pHeight = height +' pTitlePtr = title text (text must be 2 less than width) +' +' NOTE: Columns must be LONG aligned to take advantage of the most efficient +' scrolling (via LONGMOVE). The start column must be on a long boundary +' (multiple of 4) and the width must be a multiple of 4. If print scrolling +' is not required this restriction can be ignored. +' +' NOTE: There is NO RETURN value from this call. Simple Boxes are not tracked, +' they are viaual element but are not active elements + SBOX.DrawBox( pRow, pCol, pWidth, pHeight, pTitlePtr, @scrn, VGACOLS ) + + +CON ''=====< SPIN BOX INTERFACE FUNCTIONS >=================================== + +PUB SPINInit( pRow, pCol, pWidth, pType, pNum, pDataPtr ) | idx, gdx, retVal +'Creates a spin button box with specified width and text. Something like: +' --------------------- +' | spin text |↑|↓| +' --------------------- +' +' pRow = row upper location +' pCol = col left location +' pWidth = width of the control in columns +' pType = 0 = text 1 = numeric +' pNum = number of data elements +' pDataPtr = pointer to data for spin control +' +' The Data for the spin control is stored in a DAT section. For text type +' the section will be a number of byte aligned strings, each with a null +' terminator. For numeric type the section will be a number of long aligned +' longs. +' +'Returns -1 if there are no more free elements in the SPIN object array +' guid on success which is unique and used to identify the specific +' instance of the control. + + retVal := -1 + + repeat idx from 0 to GZ_TBOX - 1 + gdx := SPIN[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + SPIN[idx].Init( pRow, pCol, pWidth, pType, pNum, pDataPtr, @scrn, VGACOLS ) + quit + + return retVal + +PUB SPINIsIn( guid ) | odx +' returns true if the mouse is inside the spin control +' false otherwise + + odx := gz_elem[guid] & G_OMSK + return SPIN[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB SPINClicked( guid ) | odx +'checks where the mouse click ocurred and returns -1 if not on an UP or DOWN +'arrow otherwise it returns the zero based data index of the currently +'displayed data + + odx := gz_elem[guid] & G_OMSK + return SPIN[odx].Clicked( MOUS.bound_x, MOUS.bound_y ) + +PUB SPINGetDataIndex( guid ) | odx +'returns the zero based index of the currently displayed data item + + odx := gz_elem[guid] & G_OMSK + return SPIN[odx].GetDataIndex + + +CON ''=====< CHECK BOX INTERFACE FUNCTIONS >================================== + +PUB CHKBInit( pRow, pCol, pTextWidth, pTextPtr ) | idx, gdx, retVal +'Creates a checkbox box at the specified location. +'Something like: +' +' X Text here +' +' where X is a check box symbol +' +' pRow = row upper location +' pCol = col left location +' pTextWidth = width of button text (MAX 15 characters) +' pTextPtr = checkbox text (MAX 15 characters) +' +'Returns -1 if there are no more free elements in the CHKB object array +' guid on success which is unique and used to identify the specific +' instance of the control. + + retVal := -1 + + repeat idx from 0 to GZ_CHKB - 1 + gdx := CHKB[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + CHKB[idx].Init( pRow, pCol, pTextWidth, 0, pTextPtr, @scrn, VGACOLS ) + quit + + return retVal + +PUB CHKBIsIn( guid ) | odx +' returns true if the mouse is inside the check box +' false otherwise + odx := gz_elem[guid] & G_OMSK + return CHKB[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB CHKBDrawText( guid, pMode ) | odx +' pMode 0 = 0 for normal +' = 1 for inverted + odx := gz_elem[guid] & G_OMSK + CHKB[odx].DrawText( pMode ) + +PUB CHKBSelect( guid, pSel ) | odx +'sel = 1 to select 0 to deselect -1 to toggle it + odx := gz_elem[guid] & G_OMSK + CHKB[odx].Select( pSel ) + + +PUB CHKBIsSet( guid ) | odx +'returns the status of the button (set=true not set=false) + odx := gz_elem[guid] & G_OMSK + return CHKB[odx].isSet + + +CON ''=====< RADIO BUTTON INTERFACE FUNCTIONS >=============================== + +PUB RADBInit( pRow, pCol, pTextWidth, pTextPtr, pGroupID ) | idx, gdx, retVal +'Creates a radio button at the specified location. +'Something like: +' +' X Text here +' +' where X is a radio button symbol +' +' pRow = row upper location +' pCol = col left location +' pTextWidth = width of button text (MAX 15 characters) +' pTextPtr = checkbox text (MAX 15 characters) +' pGroupID = the id of the radio button group that this button belongs to +' +'Returns -1 if there are no more free elements in the RADB object array +' guid on success which is unique and used to identify the specific +' instance of the control. + + retVal := -1 + + repeat idx from 0 to GZ_RADB - 1 + gdx := RADB[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + RADB[idx].Init( pRow, pCol, pTextWidth, 1, pTextPtr, @scrn, VGACOLS ) + gz_groups[idx] := pGroupID 'remember group button belongs to RB_GRP + quit + + return retVal + +PUB RADBIsIn( guid ) | odx +' returns true if the mouse inside the radio button +' false otherwise + odx := gz_elem[guid] & G_OMSK + return RADB[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB RADBDrawText( guid, pMode ) | odx +' pMode 0 = 0 for normal +' = 1 for inverted + odx := gz_elem[guid] & G_OMSK + RADB[odx].DrawText( pMode ) + +PUB RADBSelect( guid, pSel ) | odx +'sel = 1 to select 0 to deselect -1 to toggle it + odx := gz_elem[guid] & G_OMSK + RADB[odx].Select( pSel ) + +PUB RADBIsSet( guid ) | odx +'returns the status of the button (set=true not set=false) + odx := gz_elem[guid] & G_OMSK + return RADB[odx].isSet + + +CON ''=====< PUSH BUTTON INTERFACE FUNCTIONS >================================ + +PUB PUSHInit( pRow, pCol, pTextPtr ) | idx, gdx, retVal +'Creates a pushbutton at the specified location. +'Something like: +' --------------- +' |ttttttttttttttt| +' --------------- +' +' where t = button text (MAX 15 characters) +' +' pRow = row upper location +' pCol = col left location +' pTextPtr = push button text (MAX 15 characters + null terminator) +' +'Returns -1 if there are no more free elements in the PUSH object array +' guid on success which is unique and used to identify the specific +' instance of the control. + + retVal := -1 + + repeat idx from 0 to GZ_PUSH - 1 + gdx := PUSH[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + PUSH[idx].Init( pRow, pCol, pTextPtr, @scrn, VGACOLS ) + quit + + return retVal + +PUB PUSHIsIn( guid ) | odx +' returns true if the mouse is inside the push button +' false otherwise + odx := gz_elem[guid] & G_OMSK + return PUSH[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB PUSHDrawText( guid, pMode ) | odx +'draws the pusbutton text in normal or inverted video. +' mode bit 0 = 0 for normal +' = 1 for inverted + odx := gz_elem[guid] & G_OMSK + PUSH[odx].DrawText( pMode ) + +PUB PUSHSetText( guid, pPtr ) | odx +'sets new text for the pushbutton +' pPtr points to the text, it MUST be the same size (or less) as the text +' it is replacing + odx := gz_elem[guid] & G_OMSK + PUSH[odx].SetText( pPtr ) + + +CON ''=====< TEXT BOX INTERFACE FUNCTIONS >=================================== + +PUB TBOXInit( pRow, pCol, pWidth, pHeight, pWrap, pTitlePtr ) | idx, gdx, retVal +'Creates a box with specified width and text. Something like: +' ------------- +' | | - title area ( non-existant if no title supplied) +' ------------- +' | | +' | | - text area with optional scolling print capability +' | | +' ------------- +' +' pRow = row upper location +' pCol = col left location +' pWidth = width +' pHeight = height +' pWrap = 0 = truncate 1 = wrap lines +' pTitlePtr = title text text (text must be 2 less than width ore 32 MAX) +' +'Returns -1 if there are no more free elements in the TBOX object array +' guid on success which is unique and used to identify the specific +' instance of the control. +' +' NOTE: Columns must be LONG aligned to take advantage of the most efficient +' scrolling (via LONGMOVE). The start column must be on a long boundary +' (multiple of 4) and the width must be a multiple of 4. If print scrolling +' is not required this restriction can be ignored. + + retVal := -1 + + repeat idx from 0 to GZ_TBOX - 1 + gdx := TBOX[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + TBOX[idx].Init( pRow, pCol, pWidth, pHeight, pWrap, pTitlePtr, @scrn, VGACOLS ) + quit + + return retVal + +PUB TBOXIsIn( guid ) | odx +' returns true if the mouse is inside the text box text area +' (i.e. excluding title area if there is one) +' false otherwise + odx := gz_elem[guid] & G_OMSK + return TBOX[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB TBOXClear( guid ) | odx +'clears the text box. + odx := gz_elem[guid] & G_OMSK + TBOX[odx].Clear + +PUB TBOXPrint( guid, pTxtPtr, pSize ) | odx +'Prints a line of text. Iif already at the last row the existing text is +'scrolled up one line first and the top line is removed. Inverted text is +'written with the high bit of each character set. If line wrap is enabled the +'lines that are wrapped are marked with a preceding inverted right arrow +'character (non-printable Ascii). Leading and trailing CRLF characters are +'stripped out. Any intra line CRLFs are ignored and will display as 'funny' +'characters. +' +' pTxtPtr = pointer to null terminated string +' size = 0 for null terminated strings +' = the string length for non-null terminated strings + odx := gz_elem[guid] & G_OMSK + TBOX[odx].Print( pTxtPtr, pSize ) + +PUB TBOXScroll( guid ) | odx +'scroll the test area up one line + odx := gz_elem[guid] & G_OMSK + TBOX[odx].Scroll + +PUB TBOXTitle( guid, pTxtPtr ) | strIdx, vgaIdx, odx +'replace the original titlebar caption with new text. If no title was declared +'on initialization then this method will do nothing +' +' pTxtPtr = pointer to text to place in titlebar + odx := gz_elem[guid] & G_OMSK + TBOX[odx].Title( pTxtPtr ) + + +CON ''=====< MENU ITEM INTERFACE FUNCTIONS >================================== + +PUB MENUInit( pRow, pCol, pTextPtr ) | idx, gdx, retVal +'Creates a menu item at the specified location. +'Something like: +' StttttttttttttttS +' +' where S = space character +' t = character position (MAX 15 characters) +' +' pRow = row upper location +' pCol = col left location +' pTextPtr = menu item text (MAX 15 characters + null terminator) +' +'Returns -1 if there are no more free elements in the MENU object array +' guid on success which is unique and used to identify the specific +' instance of the control. + + retVal := -1 + + repeat idx from 0 to GZ_MENU - 1 + gdx := MENU[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + MENU[idx].Init( pRow, pCol, pTextPtr, @scrn, VGACOLS ) + quit + + return retVal + +PUB MENUIsIn( guid ) | odx +' returns true if the mouse is inside the menu item +' false otherwise + odx := gz_elem[guid] & G_OMSK + return MENU[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB MENUDrawText( guid, pMode ) | odx +'draws the menu item text in normal or inverted video. +' mode bit 0 = 0 for normal +' = 1 for inverted + odx := gz_elem[guid] & G_OMSK + MENU[odx].DrawText( pMode ) + +PUB MENUSetText( guid, pPtr ) | odx +'Place new text on the menu item. +' pPtr points to the text, it MUST be the same size (or less) as the text +' it is replacing + odx := gz_elem[guid] & G_OMSK + MENU[odx].SetText( pPtr ) + +PUB MENUSetStatus( guid, pStat ) | odx +'set a user defined status BYTE (any value fro 0 to 255) + odx := gz_elem[guid] & G_OMSK + MENU[odx].SetStatus( pStat ) + +PUB MENUGetStatus( guid ) | odx +'get the use defined status + odx := gz_elem[guid] & G_OMSK + return MENU[odx].GetStatus + + +CON ''=====< INPUT FIELD INTERFACE FUNCTIONS >================================ + +PUB INPFInit(pRow, pCol, pWidth, pType, pTitlePtr ) | idx, gdx, retVal +'Creates an input field box with specified width and text. Something like: +' ----------------------- +' |Title| input field | +' ----------------------- +' +' pRow = row upper location +' pCol = col left location +' pWidth = width of the control in columns +' pType = 0 = standalone 1=tacked on to above +' pHeight = height of the control in rows +' pTitlePtr = title text ( remainder is used for the input field ) +' +'Returns -1 if there are no more free elements in the SPIN object array +' guid on success which is unique and used to identify the specific +' instance of the control. + + retVal := -1 + + repeat idx from 0 to GZ_INPF - 1 + gdx := INPF[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + INPF[idx].Init( pRow, pCol, pWidth, pType, pTitlePtr, @scrn, VGACOLS ) + quit + + return retVal + +PUB INPFIsIn( guid ) | odx +' returns true if the mouse is inside the input field +' false otherwise + odx := gz_elem[guid] & G_OMSK + return INPF[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB INPFClear( guid ) | odx +'clear the input field + odx := gz_elem[guid] & G_OMSK + INPF[odx].clear + +PUB INPFSelect( guid, pSel ) | odx +'pSel = 1 to select 0 to deselect -1 to toggle it + odx := gz_elem[guid] & G_OMSK + INPF[odx].Select( pSel, @cx1, @cy1 ) + liveINPF := odx + cm1 := %111 'turn text cursor on, underscore slow blink + +PUB INPFGetString( guid, pBuf ) | odx, tmp, str, len +'places the string in the input field into the buffer provided then clears +'the input field. + odx := gz_elem[guid] & G_OMSK + tmp := INPF[odx].GetStringCode + str := ( tmp & $1FFF0000 ) >> 16 + len := tmp & $000000FF + bytemove( pBuf, @scrn.byte[str], len ) + byte[pBuf][len] := 0 + INPFClear( guid ) + +CON ''=====< STATUS LAMP INTERFACE FUNCTIONS >================================ + +PUB STATInit( pRow, pCol, pWidth, pTitlePtr ) | idx, gdx, retVal +'Creates a status lamp with specified width and text. Something like: +' +' Title : XXXX +' +' where XXXX is user supplied condition status +' +' pRow = row upper location +' pCol = col left location +' pWidth = width of the control in columns +' pHeight = height of the control in rows +' pTitlePtr = title text text (text must be 7 less than width ) +' +'Returns -1 if there are no more free elements in the SPIN object array +' guid on success which is unique and used to identify the specific +' instance of the control. + + retVal := -1 + + repeat idx from 0 to GZ_STAT - 1 + gdx := STAT[idx].get_gzidx + if gz_elem[gdx] & G_INIT == 0 'is it free + retVal := gdx + gz_elem[gdx] |= G_INIT 'mark it used + STAT[idx].Init( pRow, pCol, pWidth, pTitlePtr, @scrn, VGACOLS ) + quit + + return retVal + +PUB STATIsIn( guid ) | odx +' returns true if the mouse is inside the menu item +' false otherwise + odx := gz_elem[guid] & G_OMSK + return STAT[odx].IsIn( MOUS.bound_x, MOUS.bound_y ) + +PUB STATSet( guid, pSet, pStrPtr ) | odx +'pSet = 0 = off mode (uses normal video) +' 1 = on mode (uses inverted video) +'pStr = pointer to status string (4 chars max) + odx := gz_elem[guid] & G_OMSK + STAT[odx].Set( pSet, pStrPtr ) + +PUB STATGetStatus( guid ) | odx +'returns the current status of the object (1=on 0=off) + odx := gz_elem[guid] & G_OMSK + return STAT[odx].GetStatus + + +CON ''===== START OF VGA HIGH RES TEXT SCREEN FUNCTIONS ====================== + +PUB PrintStr( prRow, prCol, strPtr, inv ) | strLen, vgaIdx, idx +'this places text anywhere on the screen and can overwrite UI elements +' +' prRow = row +' prCol = column +' strPtr = pointer to null terminated string +' inv = 0 for normal 1 for inverted video + + if ( prRow < VGAROWS ) AND ( prCol < VGACOLS ) + strLen := strsize( strPtr ) + vgaIdx := prRow * VGACOLS + prCol + bytemove( @scrn.byte[vgaIdx], strPtr, strLen ) + if inv + repeat idx from 1 to strLen + byte[@scrn][vgaIdx] += 128 + vgaIdx++ + +PUB ClearScreen( ForeClr, BackClr ) | wdClr +' This clears the whole screen and sets all rows to the given colours +' ForeClr and BackClr are best represented as quaternary numbers (base 4) +' - these are represented as %%RGB where there are 4 levels for each ( R, G, B) +' - thus entering %%003 is brightest Green + + wdClr := BackClr << 10 + ForeClr << 2 + LONGFILL( @scrn, $20202020, VGACOLS*VGAROWS/4 ) '4 space characters in long + WORDFILL( @colors, wdClr, VGAROWS ) + + +PUB SetLineColor( line, ForeClr, BackClr ) | wdClr +' This sets a single row to the given colours +' ForeClr and BackClr are best represented as quaternary numbers (base 4) +' - these are represented as %%RGB where there are 4 levels for each ( R, G, B) +' - thus entering %%003 is brightest Green + + if line < VGAROWS + wdClr := BackClr << 10 + ForeClr << 2 + colors[line] := wdClr + + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/gui-demo/GUIDemo.spin b/source/gui-demo/GUIDemo.spin new file mode 100644 index 0000000..6159e4a --- /dev/null +++ b/source/gui-demo/GUIDemo.spin @@ -0,0 +1,582 @@ +CON 'Dokumentation, Lizenz +''***********************************/********* +''* VGA High-Res Text UI Elements Demo v1.2 * +''* * +''* File: GUIDemo.spin * +''* Author: Allen Marincak * +''* Copyright (c) 2009 Allen Marincak. * +''* See end of file for terms of use. * +''* * +''* Parts are from a demo by Chip Gracey * +''* Copyright (c) 2006 Parallax, Inc. * +''* Under the same MIT license * +''* ( see end of file ) * +''********************************************* +' +' Notes on the VGA High Res Driver from Chip Gracey's Demo program. +' ---------------------------------------------------------------------------- +' +' 3 June 2006 +' +' This program (a quick piece of junk) demonstrates the VGA_HIRES_TEXT +' object. It is meant for use on the Propeller Demo Board Rev C. You can +' plug in a mouse for screen action. The mouse driver has been upgraded +' to provided bounded mouse coordinates. This makes constrained and +' scaled mouse movement mindless. +' +' The VGA_HIRES_TEXT went through much metamorphosis before completion. +' Initially, it ran on five COGs! I thought this was a miracle, since I +' didn't think we'd be able to get such high-res displays on the current +' Propeller chip. It used four COGs to build scan lines, and a fifth COG +' to display them. I kept looking at the problem and realized that it +' all came down to how little monkeying could be done with the data in +' order to display it. The scan line building was reorganized so that a +' single RDLONG picks up four lines worth of pixels for a character, and +' then buffers them within the COG, to later output them with back-to- +' back 'WAITVID color,pixels' and 'SHR pixels,#8' instruction sequences. +' This was so much faster that only two COGs were required for the job! +' They had to be synchronized so that they could step seamlessly into +' eachother's shoes as they traded the tasks of building scan lines and +' then displaying them. Anyway, it all came together nicely. + +' Note that the driver has different VGA mode settings which you can de- +' comment and try. Also, the driver contains its own font. You will see +' the character set printed out when you run the program. There are some +' characters within the font that provide quarter-character-cell block +' pixels (for 128x64 characters, you can get 256x128 'pixels'). They can +' be used for graphing or crude picture drawing, where text can be inter- +' mingled. +' +' If you have a 15" LCD monitor, you must see the 1024x768 mode on it. +' At least on my little Acer AL1511 monitor, every pixel locks perfectly. +' +'----------------------------------------------------------------------------- +' +' Allen Marincak Feb 2009 Demo program Notes +' +' Chip states above that this demo is for use on the Propeller Demo Board. It +' will however work on any Propeller board with VGA and Mouse driver +' functionality. +' +' The info above was from the VGA_HiRes_Text demo program Chip Gracey wrote. +' There is nothing of consequence left here of that file but the VGA Driver +' driver itself remains intact. *This* demo is setup to show how to use some +' very simple text based graphical user interface elements written for the VGA +' HiRes Text mode. It is an ultra trivial implementation, with virtually no +' error checking so heads up! I did this on purpose to keep it lean. After the +' VGA driver uses some 6K to 10K bytes of memory and 2 cogs to display text I +' did not want to use up most of the rest of the memory with a boatload of +' error checking and a fancier UI on its own COG. I opted to leave as much +' memory as possible for applications that could benefit from cheap and simple +' buttons, menus, text panels and such. Especially since one needs the Mouse +' and Keyboard drivers as well, each of these take up 1 more COG. After loading +' those drivers and creating 10 or so UI elements you will have 2/3 to 1/2 of +' available memory left for your application (depending on VGA Resolution +' desired, I tend to use 800x600 as a good compromise between memory and real +' estate). +' +' There are several UI elements implemented, you stitch together what you need +' and lay it out how you want to use it. There are 9 objects (well 8, the Radio +' Button and Check Box objects are both from the RadioCheck.spin object), and +' some glue functions in the package (in GUIBase.spin). The UI elements are +' rudimentary and lean but provide adequate funtionality for many control and +' monitoring applications. I did not implement repositioning the items, any +' buffering of data in text boxes, pop-up windows or drop down lists, etc. +' These would just use up more memory and I was after a simple light-weight +' (thereby static) UI to provide simple control and feedback for some of my +' projects. +' +' +' SimpleBox.spin +' - draws boxes in the area specified, with or without a "title bar" +' - useful to visually group and organize other control +' +' RadioCheck.spin +' - creates Check Box and Radio Button controls +' +' TextBox.spin +' - creates simple text windows controls that are line based. +' - truncates or wraps lines into the window pane +' - optional (but useful) titlebar avaiable +' +' MenuItem.spin +' - simple text "button" that to create a rudimentary menubar. +' +' PushButton.spin +' - creates a straightforward pushbutton +' +' InputField.spin +' - creates a one line input field with a caption / title. There is only +' simple editing via backspace. +' - keyboard input focus can be switched between controls on the fly with +' mouse click +' +' SpinBox.spin +' - created straight forward spin button controls +' +' StatusLamp.spin +' - a simple annunciator 'lamp' for condition feedback +' +' GUIBase.spin +' - This is actually the main interface to the GUI objects +' - you call functions here, not in the object files themselves +' - GUI initialization and processing is done with functions here +' - Init() starts VGA, Mouse, and Keyboard drivers and initializes +' data structures used by the UI +' - ProcessUI() manages the UI infrastructure you must call this in +' your main loop regularly for a responsive UI. It +' returns ID of elements that require actionm (i.e. +' the mouse was clicked on a Push Button, etc). +' +' Memory requirements with UI +' - 1/3 to 1/2 of RAM depending on VGA resolution +' +' COGS used +' - the UI does not use a new COG it runs out of the main app's COG. +' - 2 COGS for the High Res VGA Text driver +' - 1 COG for the Mouse Driver +' - 1 COG for the Keyboard Driver (optional, for Input Field Object only) +' +'----------------------------------------------------------------------------- + +CON + + _clkmode = xtal1 + pll16x + _xinfreq = 5_000_000 + + vga_base = 8 'VGA - VSync + + mouse_dat = 19 'MOUSE data + mouse_clk = 18 'MOUSE clock + keyboard_dat = 17 'KEYBOARD data + keyboard_clk = 16 'KEYBOARD clock + + +OBJ + '--------------------------------------------------------------------------- + ' UI element objects YOU NEED THIS + '--------------------------------------------------------------------------- + GUI : "GUIBase" 'starts VGA, Mouse, & Keyboard drivers + + '--------------------------------------------------------------------------- + ' Auxiliary objects (these are just used in the demo app only) + '--------------------------------------------------------------------------- + TMRS : "Timer" + NUMS : "simple_numbers" + + +VAR + '--------------------------------------------------------------------------- + ' UI element IDs YOU MUST HAVE THESE to remember the GUI ID's (guid) of the + ' elements you create. In this demo APP there are 29 items, + ' your application will have more or fewer (as required). + ' These are returned by the element Init() calls + ' ( i.e. CHKB1 := GUI.CHKBInit(..) ) + '--------------------------------------------------------------------------- + byte CHKB1 + byte CHKB2 + byte CHKB3 + byte CHKB4 + byte RADB1 + byte RADB2 + byte RADB3 + byte RADB4 + byte RADB5 + byte RADB6 + byte TBOX1 + byte TBOX2 + byte TBOX3 + byte MENU1 + byte MENU2 + byte MENU3 + byte MENU4 + byte MENU5 + byte MENU6 + byte INPF1 + byte INPF2 + byte INPF3 + byte PUSH1 + byte PUSH2 + byte PUSH3 + byte STAT1 + byte STAT2 + byte SPIN1 + byte SPIN2 + + '--------------------------------------------------------------------------- + ' Screen Geometry returned by call to GUI.Init() (may be useful) + '--------------------------------------------------------------------------- + byte vga_rows, vga_cols + + '--------------------------------------------------------------------------- + ' variables used by local main loop processing, not required by library + ' functions. You can get rid of these when creating your own app. + '--------------------------------------------------------------------------- + long cnt1 'line counter for textbox 1 + long cnt2 'line counter for textbox 2 + long clrCnt 'clear textbox counter + long random 'random number seed + long logging 'logging flag + byte strBuf[36] + byte strBuf2[64] + + +PUB start | gx, tmr1, tmr2, idx, str, tmp + + '--------------------------------------------------------------------------- + 'Some houskeeping ... timers and "logging" status + 'This TIMER object started here is used for this demo only. It is used to + 'periodically trigger printing to text boxes to simulate "live text" comming + 'in. It is not needed by the UI stuff and you don't need it in your program + 'unless you want a timer object to do timed activity. Note that it uses a + 'separate COG (so it is not cheap). + '--------------------------------------------------------------------------- + TMRS.start( 10 ) 'start timer object (1/10 sec timer) + tmr1 := TMRS.register 'register 2 timers + tmr2 := TMRS.register + + logging := 1 'logging is on to start + + + '--------------------------------------------------------------------------- + ' Create the UI + '--------------------------------------------------------------------------- + CreateUI + + + '--------------------------------------------------------------------------- + 'some final non UI related houskeeping ... + '--------------------------------------------------------------------------- + TMRS.set( tmr1, 10 ) 'start 1 second timeout + TMRS.set( tmr2, 17 ) 'start 1.7 second timeout + + + '--------------------------------------------------------------------------- + ' Everything is setup, now the MAIN LOOP begins ... + '--------------------------------------------------------------------------- + repeat + gx := GUI.ProcessUI 'process the UI + + case gx 'handle GUI Events ( Mouse click or Enter Key in Input Field ) + + CHKB1: UserDoSomethingFunction( gx ) + + CHKB2: UserDoSomethingFunction( gx ) + + CHKB3: UserDoSomethingFunction( gx ) + + CHKB4: UserDoSomethingFunction( gx ) + + RADB1: UserDoSomethingFunction( gx ) + + RADB2: UserDoSomethingFunction( gx ) + + RADB3: UserDoSomethingFunction( gx ) + + RADB4: UserDoSomethingFunction( gx ) + + RADB5: UserDoSomethingFunction( gx ) + + RADB6: UserDoSomethingFunction( gx ) + + MENU1: DoMenu1Action + + MENU2: DoMenu2Action + + MENU3: DoMenu3Action + + MENU4: DoMenu4Action + + MENU5: DoMenu5Action + + MENU6: DoMenu6Action + + PUSH1: UserDoSomethingFunction( gx ) + + PUSH2: UserDoSomethingFunction( gx ) + + PUSH3: UserDoSomethingFunction( gx ) + + SPIN1: UserDoSomethingFunction( gx ) + + SPIN2: UserDoSomethingFunction( gx ) + + INPF1: DoInputFieldAction( INPF1 ) + + INPF2: DoInputFieldAction( INPF2 ) + + INPF3: DoInputFieldAction( INPF3 ) + + + '---------------------------------------------------------------------- + 'user application code goes here or after the ProcessUI call (but still + 'within the REPEAT loop!). + '---------------------------------------------------------------------- + idx := GUI.GetMouseXY + GUI.PrintStr( vga_rows-1, 15, NUMS.decf( idx >> 8, 2 ), 0 ) + GUI.PrintStr( vga_rows-1, 20, NUMS.decf( idx & $FF, 2 ), 0 ) + + if TMRS.isClr( tmr2 ) 'if timer 2 expired write line text into textbox 2 + TMRS.set( tmr2, 17 ) + cnt2++ + bytemove( @strBuf, string( "Text # " ), 7 ) + str := NUMS.dec( cnt2 ) + tmp := strsize( str ) + 1 + bytemove( @strBuf[7], str, tmp ) + GUI.TBOXPrint( TBOX2, @strBuf, 0 ) + + if logging == 1 + if TMRS.isClr( tmr1 ) 'if timer 1 expired write line text into textbox 1 + TMRS.set( tmr1, 10 ) + cnt1++ + bytemove( @strBuf, string( "This is line number " ), 20 ) + str := NUMS.dec( cnt1 ) + tmp := strsize( str ) + 1 + bytemove( @strBuf[20], str, tmp ) + GUI.TBOXPrint( TBOX1, @strBuf, 0 ) + + + +CON ''=====< START OF UI HELPER FUNTIONS >================================== +PRI CreateUI | tmp +'You create this function to create your GUI. +' +'This function is called during startup to create and position the UI elements +'that you will use. The total number of UI elements of each type must be +'declared in the OBJ section (i.e. if you need 5 pushbuttons, delcare an array +'of 5 pushbutton objects). + + '--------------------------------------------------------------------------- + 'Initialize GUI, starts VGA, Mouse, and Keyboard Drivers + ' + 'YOU MUST DO THIS FIRST (saving screen geometry is optional) + '--------------------------------------------------------------------------- + tmp := GUI.Init(vga_base, mouse_dat, mouse_clk, keyboard_dat, keyboard_clk ) + vga_rows := ( tmp & $0000FF00 ) >> 8 + vga_cols := tmp & $000000FF + + + '--------------------------------------------------------------------------- + 'setup screen colours + '--------------------------------------------------------------------------- + GUI.ClearScreen( %%020, %%000 ) 'green on black each is %%RGB 4 levels per R-G-B + GUI.SetLineColor( 0, %%000, %%333 ) 'Menu Area colour Line 1 + GUI.SetLineColor( 1, %%000, %%333 ) 'Menu Area colour Line 2 + GUI.SetLineColor( 2, %%000, %%333 ) 'Menu Area colour Line 3 + + repeat tmp from 3 to vga_rows-16 + GUI.SetLineColor( tmp, %%111, %%333 ) 'Console Window colour + + repeat tmp from vga_rows-15 to vga_rows-2 + GUI.SetLineColor( tmp, %%111, %%333 ) 'Console Window colour + + GUI.SetLineColor(vga_rows-1,%%111,%%333) 'Status Line colour + + + '--------------------------------------------------------------------------- + 'put up some menu items + '--------------------------------------------------------------------------- + GUI.SBOXInit( 0, 0, vga_cols, 3, 0 ) 'menu group box + MENU1 := GUI.MENUInit(1, 3,string(" Open Port ")) 'Menu Items setup + MENU2 := GUI.MENUInit(1,19,string("Stop Logging")) + MENU3 := GUI.MENUInit(1,35,string("New Text Box")) + MENU4 := GUI.MENUInit(1,51,string("Chk Battery ")) + MENU5 := GUI.MENUInit(1,67,string("Read Sensors")) + MENU6 := GUI.MENUInit(1,83,string(" Clear Log ")) + GUI.MENUSetStatus( MENU2, logging ) + + + '--------------------------------------------------------------------------- + 'put up some check boxes + '--------------------------------------------------------------------------- + GUI.SBOXInit( 11, 4, 18, 8, string("Terminal")) 'checkbox group box + CHKB1 := GUI.CHKBInit( 14, 6, 12, string( "7-bit ASCII" ) ) + CHKB2 := GUI.CHKBInit( 15, 6, 12, string( "Show Cursor" ) ) + CHKB3 := GUI.CHKBInit( 16, 6, 12, string( "Local Echo" ) ) + CHKB4 := GUI.CHKBInit( 17, 6, 12, string( "Append LF" ) ) + GUI.CHKBSelect( CHKB3, 1 ) 'select this one as the group's startup default + + + '--------------------------------------------------------------------------- + 'put up some radio buttons + '--------------------------------------------------------------------------- + GUI.SBOXInit( 20, 4, 18, 10, string("Baud Rate")) 'radio button group box + RADB1 := GUI.RADBInit( 23, 6, 11, string( "115200" ), 0 ) + RADB2 := GUI.RADBInit( 24, 6, 11, string( "57600 " ), 0 ) + RADB3 := GUI.RADBInit( 25, 6, 11, string( "19200 " ), 0 ) + RADB4 := GUI.RADBInit( 26, 6, 11, string( "9600 " ), 0 ) + RADB5 := GUI.RADBInit( 27, 6, 11, string( "4800 " ), 0 ) + RADB6 := GUI.RADBInit( 28, 6, 11, string( "1200 " ), 0 ) + GUI.RADBSelect( RADB4, 1 ) 'select this one as the group's startup default + + + '--------------------------------------------------------------------------- + 'put up some status lamps + '--------------------------------------------------------------------------- + GUI.SBOXInit( 4,4,18,6,string("Status") ) 'status lamp group box + STAT1 := GUI.STATInit( 7,6, 14, string( "Logging" ) ) + STAT2 := GUI.STATInit( 8,6, 14, string( " Port" ) ) + GUI.STATSet( STAT1, 1, string("ON") ) + GUI.STATSet( STAT2, 0, string("OFF") ) + + + '--------------------------------------------------------------------------- + 'put up some text boxes, one without a title + '--------------------------------------------------------------------------- + TBOX1 := GUI.TBOXInit( 4,28,36,26,1,string("Logging Window") ) + TBOX2 := GUI.TBOXInit(10,70,24,20,1,0 ) + TBOX3 := GUI.TBOXInit(vga_rows-25,4,90,22,1,string("Console Window") ) + cnt1 := 0 'line counter for textbox 1 + cnt2 := 0 'line counter for textbox 2 + clrCnt := 0 'clear textbox counter + random := 0 'random number seed + + + '--------------------------------------------------------------------------- + 'put up a couple of input fields + '--------------------------------------------------------------------------- + INPF1 := GUI.INPFInit( 4, 70, 24, 0, string("Speed") ) + INPF2 := GUI.INPFInit( 31, 4, 60, 0, string("Message") ) + INPF3 := GUI.INPFInit( vga_rows-4, 4, 90, 1, string("Command") ) + GUI.INPFSelect( INPF3, 1 ) + + '--------------------------------------------------------------------------- + 'put up some push buttons + '--------------------------------------------------------------------------- + PUSH1 := GUI.PUSHInit( 7, 70, string( "Slower" ) ) + PUSH2 := GUI.PUSHInit( 7, 78, string( " Stop " ) ) + PUSH3 := GUI.PUSHInit( 7, 86, string( "Faster" ) ) + + + '--------------------------------------------------------------------------- + 'put up some spin buttons + '--------------------------------------------------------------------------- + SPIN1 := GUI.SPINInit( 31, 70, 24, 0, 4, @SpinDat0 ) 'languages + SPIN2 := GUI.SPINInit( 34, 70, 24, 1, 10, @SpinDat1 ) 'log scale + + + '--------------------------------------------------------------------------- + 'Put something in the status bar (just the mouse coordinates in this demo) + '--------------------------------------------------------------------------- + GUI.PrintStr(vga_rows-1,0,string( "Status Line: X=xx Y=xx" ), 0 ) + + +PRI UserDoSomethingFunction( val ) ' - USER ACTION FUNCTION +'this is a dummy ... in a real app the user would have one of these for each +'UI action that required some activity. For this Demo where the user would +'normally call their own function I just call this dummy placeholder instead. + GUI.TBOXPrint( TBOX3, string( "Normally a user function would be called." ), 0 ) + return val + + +PRI DoMenu1Action | mStat + + mStat := GUI.MENUGetStatus( MENU1 ) 'get current user status of menu item + + if mStat == 0 + GUI.TBOXPrint( TBOX3, string( "Opened port."), 0 ) 'announce activity to console window + GUI.MENUSetText( MENU1, string( "Close Port" ) ) 'change the menu item text + GUI.MENUSetStatus( MENU1, 1 ) 'set new status of the menu item + GUI.STATSet( STAT2, 1, string("OPEN") ) 'set status lamp ON + else + GUI.TBOXPrint( TBOX3, string( "Closed port." ), 0 ) 'announce activity to console window + GUI.MENUSetText( MENU1, string( " Open Port" ) ) 'change the menu item text + GUI.MENUSetStatus( MENU1, 0 ) 'set new status of the menu item + GUI.STATSet( STAT2, 0, string("OFF") ) 'set status lamp OFF + + +PRI DoMenu2Action | mStat + + mStat := GUI.MENUGetStatus( MENU2 ) 'get current user status of menu item + + if mStat == 0 + GUI.TBOXPrint( TBOX3, string("Started Logging."),0) 'announce activity to console window + GUI.MENUSetText( MENU2, string( "Stop Logging" )) 'change the menu item text + GUI.MENUSetStatus( MENU2, 1 ) 'set new status of the menu item + GUI.STATSet( STAT1, 1, string("ON") ) 'set status lamp ON + logging := 1 + else + GUI.TBOXPrint(TBOX3,string("Stopped logging." ),0) 'announce activity to console window + GUI.MENUSetText( MENU2, string( " Log Data " ) ) 'change the menu item text + GUI.MENUSetStatus( MENU2, 0 ) 'set new status of the menu item + GUI.STATSet( STAT1, 0, string("OFF") ) 'set status lamp OFF + logging := 0 + + +PRI DoMenu3Action + GUI.TBOXPrint( TBOX1, @longstring, 0 ) + GUI.TBOXPrint( TBOX2, @longstring, 0 ) + GUI.SBOXInit( 12, 36, 14, 10, string("New Text Box")) 'create new simple box + + +PRI DoMenu4Action | rnd + rnd := ?random / 100_000_000 + rnd *= rnd 'formulate a random number + bytemove( @strbuf, string( "Battery = 9." ), 12 ) + bytemove( @strbuf[12], NUMS.decx( rnd, 3 ), 3 ) + bytemove( @strbuf[15], string( " vdc." ), 4 ) + strbuf[19] := 0 + GUI.TBOXPrint(TBOX3, @strbuf, 0 ) 'display 'voltage' in console window + + +PRI DoMenu5Action + GUI.TBOXPrint(TBOX3,string("Sensors are not ready."),0) 'announce activity to console window + + +PRI DoMenu6Action | tmp + GUI.TBOXClear( TBOX1 ) 'ACTION: clear the text box + bytemove(@strBuf,string("Logging Window Cleared "),24) 'create new text box title + clrCnt++ + bytemove(@strBuf[24],NUMS.decf(clrCnt,3),3) 'tack on cleared count + tmp := 16 + repeat while strBuf[tmp] <> 0 'make cleared count text inverted + strBuf[tmp++] += 128 + GUI.TBOXTitle( TBOX1, @strBuf ) 'put new title in text box + + +PRI DoInputFieldAction( guid ) + GUI.INPFGetString( guid, @strBuf2 ) + GUI.TBOXPrint(TBOX3, @strbuf2, 0 ) 'display 'voltage' in console window + + + +DAT + +longstring byte 13,10,"This is a long string. It will take up ",13,10,"several lines. The leading and trailing CRLF should be stripped off but not the one inside the string.",13,10,0 + + +'----------------------------------------------------------------------------- +'Data for the spin buttons. Text type spin buttons will have an array of null +'terminated stings here. Numeric type spin buttons will have an array of long +'values. +'----------------------------------------------------------------------------- +SpinDat0 byte "English",0 + byte "French",0 + byte "Spanish", 0 + byte "Slovak", 0 + +SpinDat1 long 1,2,5,10,20,50,100,200,500,1000 + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/gui-demo/InputField.spin b/source/gui-demo/InputField.spin new file mode 100644 index 0000000..aa26eec --- /dev/null +++ b/source/gui-demo/InputField.spin @@ -0,0 +1,194 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: InputField.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' Simple Keyboard Input Handler +''============================================================================ +'' +'' Creates a simple one line input field. The input field manages characters +'' from the keyboard. The contol was designed for simple commands or value +'' entry. The only editing is via the backspace key. The enter key signals +'' completion and it is up to the calling application to do as required with +'' the input. + + +OBJ + SBOX : "SimpleBox" + + +VAR + word varGdx 'GUI control variable + long varVgaPos 'screen location of start of input area + long varScreenPtr 'screen buffer pointer + long varCxPtr 'points to the text cursor x position variable + byte varRow 'top row location + byte varCol 'left col location + byte varCol2 'right col location + byte varWidth 'width ... text width is 2 less than total width + byte varTitleWidth 'number of rows used for title area ( 0 or 2 ) + byte varStatus '0 = unselected 1 = selected + byte varColIdx 'index to current col to be written + byte varCursorCol 'column the text cursor is in + byte varVgaCols 'width of screen in columns + + +PUB Init ( pRow, pCol, pWidth, pType, pTitlePtr, pVgaPtr, pVgaWidth ) | vgaIdx + + varVgaCols := pVgaWidth + varRow := pRow + varCol := pCol + varWidth := pWidth + varTitleWidth := strsize( pTitlePtr ) + varColIdx := 0 + varScreenPtr := pVgaPtr + varCol2 := varCol + varWidth + varCxPtr := 0 + varCursorCol := varCol + varTitleWidth + varColIdx + 2 + + SBOX.DrawBox( pRow, pCol, pWidth, 3, 0, pVgaPtr, pVgaWidth ) + + vgaIdx := varRow * varVgaCols + varCol + + if pType == 1 + byte[varScreenPtr][vgaIdx] := 18 'left 'tee' char + byte[varScreenPtr][vgaIdx+pWidth-1] := 19 'right 'tee' char + + vgaIdx += varTitleWidth + 1 + + byte[varScreenPtr][vgaIdx] := 16 'top 'tee' char + vgaIdx += varVgaCols + byte[varScreenPtr][vgaIdx] := 15 'vertical line char + vgaIdx += varVgaCols + byte[varScreenPtr][vgaIdx] := 17 'bottom 'tee' char + + vgaIdx -= ( varVgaCols + varTitleWidth ) + bytemove( @byte[varScreenPtr][vgaIdx], pTitlePtr, varTitleWidth ) + + varVgaPos := (varRow+1)*varVgaCols+varCol+2+varTitleWidth 'save input area start + + +PUB Handler( pKeyCode ) | retVal, vgaIdx +'Handles key codes for a basic one line input field intended for simple data +'or commands (it is not a text editor :-)). The control uses the screen as a +'buffer so no extra memory is required to pass back the data to the caller. +'When the user presses enter a return value is bitfield encoded containing +'the address and size of the text entered. The caller then must get or use the +'data entered and then call the CLEAR method to reset the input field. +' +' returns 0 = ok, no action required +' 1 = not selected +' MSB set = enter pressed, caller to read string and clear field +' Returned word is bitfield encoded to return the address +' and size of the string entered. Encoded as follows: +' %100a_aaaa_aaaa_aaaa_0000_0000_ssss_ssss +' a = 13bit address s = 8 bit size +' + retVal := 0 + vgaIdx := varVgaPos + varColIdx + + if varStatus == 0 + retVal := 1 + else + case pKeyCode + $20 .. $7E: 'standard ASCII characters + if varColIdx < varWidth - 3 - varTitleWidth + byte[varScreenPtr][vgaIdx] := pKeyCode + varColIdx++ + varCursorCol++ + byte[varCxPtr][0] := varCursorCol 'move text cursor + + $0D: 'carriage return + retval := $80000000 ' set MSB + + $C8: 'backspace + if ( varColIdx ) + vgaIdx-- + varColIdx-- + varCursorCol-- + byte[varScreenPtr][vgaIdx] := " " + byte[varCxPtr][0] := varCursorCol 'move text cursor + + return retVal + + +PUB IsIn( pCx, pCy ) | retVal + + retVal := false + + if ( pCx => varCol ) AND ( pCx =< varCol2 ) + if pCy == varRow + 1 + retVal := true + + return retVal + + +PUB clear + bytefill( @byte[varScreenPtr][varVgaPos], 32, varWidth - varTitleWidth - 3 ) + varColIdx := 0 + varCursorCol := varCol + varTitleWidth + varColIdx + 2 + byte[varCxPtr][0] := varCursorCol 'move text cursor + + +PUB Select( pSel, pCxPtr, pCyPtr ) + + if pSel <> -1 + varStatus := pSel + else + if varStatus == 0 + varStatus := 1 + else + varStatus := 0 + + if varStatus == 1 + varCxPtr := pCxPtr + byte[varCxPtr][0] := varCursorCol + byte[pCyPtr][0] := varRow + 1 + + +PUB GetStringCode | retval + + retval := varVgaPos << 16 ' encode address + retval |= $80000000 ' set MSB + retval += varColIdx ' set size + + return retval + + +PUB set_gzidx( gzidx ) + varGdx := gzidx + + +PUB get_gzidx + return varGdx + + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/Keyboard.spin b/source/gui-demo/Keyboard.spin new file mode 100644 index 0000000..5908c79 --- /dev/null +++ b/source/gui-demo/Keyboard.spin @@ -0,0 +1,736 @@ +''*************************************** +''* PS/2 Keyboard Driver v1.0.1 * +''* Author: Chip Gracey * +''* Copyright (c) 2004 Parallax, Inc. * +''* See end of file for terms of use. * +''*************************************** + +{-----------------REVISION HISTORY----------------- + v1.0.1 - Updated 6/15/2006 to work with Propeller Tool 0.96} + +VAR + + long cog + + long par_tail 'key buffer tail read/write (19 contiguous longs) + long par_head 'key buffer head read-only + long par_present 'keyboard present read-only + long par_states[8] 'key states (256 bits) read-only + long par_keys[8] 'key buffer (16 words) read-only (also used to pass initial parameters) + + +PUB start(dpin, cpin) : okay + +'' Start keyboard driver - starts a cog +'' returns false if no cog available +'' +'' dpin = data signal on PS/2 jack +'' cpin = clock signal on PS/2 jack +'' +'' use 100-ohm resistors between pins and jack +'' use 10K-ohm resistors to pull jack-side signals to VDD +'' connect jack-power to 5V, jack-gnd to VSS +'' +'' all lock-keys will be enabled, NumLock will be initially 'on', +'' and auto-repeat will be set to 15cps with a delay of .5s + + okay := startx(dpin, cpin, %0_000_100, %01_01000) + + +PUB startx(dpin, cpin, locks, auto) : okay + +'' Like start, but allows you to specify lock settings and auto-repeat +'' +'' locks = lock setup +'' bit 6 disallows shift-alphas (case set soley by CapsLock) +'' bits 5..3 disallow toggle of NumLock/CapsLock/ScrollLock state +'' bits 2..0 specify initial state of NumLock/CapsLock/ScrollLock +'' (eg. %0_001_100 = disallow ScrollLock, NumLock initially 'on') +'' +'' auto = auto-repeat setup +'' bits 6..5 specify delay (0=.25s, 1=.5s, 2=.75s, 3=1s) +'' bits 4..0 specify repeat rate (0=30cps..31=2cps) +'' (eg %01_00000 = .5s delay, 30cps repeat) + + stop + longmove(@par_keys, @dpin, 4) + okay := cog := cognew(@entry, @par_tail) + 1 + + +PUB stop + +'' Stop keyboard driver - frees a cog + + if cog + cogstop(cog~ - 1) + longfill(@par_tail, 0, 19) + + +PUB present : truefalse + +'' Check if keyboard present - valid ~2s after start +'' returns t|f + + truefalse := -par_present + + +PUB key : keycode + +'' Get key (never waits) +'' returns key (0 if buffer empty) + + if par_tail <> par_head + keycode := par_keys.word[par_tail] + par_tail := ++par_tail & $F + + +PUB getkey : keycode + +'' Get next key (may wait for keypress) +'' returns key + + repeat until (keycode := key) + + +PUB newkey : keycode + +'' Clear buffer and get new key (always waits for keypress) +'' returns key + + par_tail := par_head + keycode := getkey + + +PUB gotkey : truefalse + +'' Check if any key in buffer +'' returns t|f + + truefalse := par_tail <> par_head + + +PUB clearkeys + +'' Clear key buffer + + par_tail := par_head + + +PUB keystate(k) : state + +'' Get the state of a particular key +'' returns t|f + + state := -(par_states[k >> 5] >> k & 1) + + +DAT + +'****************************************** +'* Assembly language PS/2 keyboard driver * +'****************************************** + + org +' +' +' Entry +' +entry movd :par,#_dpin 'load input parameters _dpin/_cpin/_locks/_auto + mov x,par + add x,#11*4 + mov y,#4 +:par rdlong 0,x + add :par,dlsb + add x,#4 + djnz y,#:par + + mov dmask,#1 'set pin masks + shl dmask,_dpin + mov cmask,#1 + shl cmask,_cpin + + test _dpin,#$20 wc 'modify port registers within code + muxc _d1,dlsb + muxc _d2,dlsb + muxc _d3,#1 + muxc _d4,#1 + test _cpin,#$20 wc + muxc _c1,dlsb + muxc _c2,dlsb + muxc _c3,#1 + + mov _head,#0 'reset output parameter _head +' +' +' Reset keyboard +' +reset mov dira,#0 'reset directions + mov dirb,#0 + + movd :par,#_present 'reset output parameters _present/_states[8] + mov x,#1+8 +:par mov 0,#0 + add :par,dlsb + djnz x,#:par + + mov stat,#8 'set reset flag +' +' +' Update parameters +' +update movd :par,#_head 'update output parameters _head/_present/_states[8] + mov x,par + add x,#1*4 + mov y,#1+1+8 +:par wrlong 0,x + add :par,dlsb + add x,#4 + djnz y,#:par + + test stat,#8 wc 'if reset flag, transmit reset command + if_c mov data,#$FF + if_c call #transmit +' +' +' Get scancode +' +newcode mov stat,#0 'reset state + +:same call #receive 'receive byte from keyboard + + cmp data,#$83+1 wc 'scancode? + + if_nc cmp data,#$AA wz 'powerup/reset? + if_nc_and_z jmp #configure + + if_nc cmp data,#$E0 wz 'extended? + if_nc_and_z or stat,#1 + if_nc_and_z jmp #:same + + if_nc cmp data,#$F0 wz 'released? + if_nc_and_z or stat,#2 + if_nc_and_z jmp #:same + + if_nc jmp #newcode 'unknown, ignore +' +' +' Translate scancode and enter into buffer +' + test stat,#1 wc 'lookup code with extended flag + rcl data,#1 + call #look + + cmp data,#0 wz 'if unknown, ignore + if_z jmp #newcode + + mov t,_states+6 'remember lock keys in _states + + mov x,data 'set/clear key bit in _states + shr x,#5 + add x,#_states + movd :reg,x + mov y,#1 + shl y,data + test stat,#2 wc +:reg muxnc 0,y + + if_nc cmpsub data,#$F0 wc 'if released or shift/ctrl/alt/win, done + if_c jmp #update + + mov y,_states+7 'get shift/ctrl/alt/win bit pairs + shr y,#16 + + cmpsub data,#$E0 wc 'translate keypad, considering numlock + if_c test _locks,#%100 wz + if_c_and_z add data,#@keypad1-@table + if_c_and_nz add data,#@keypad2-@table + if_c call #look + if_c jmp #:flags + + cmpsub data,#$DD wc 'handle scrlock/capslock/numlock + if_c mov x,#%001_000 + if_c shl x,data + if_c andn x,_locks + if_c shr x,#3 + if_c shr t,#29 'ignore auto-repeat + if_c andn x,t wz + if_c xor _locks,x + if_c add data,#$DD + if_c_and_nz or stat,#4 'if change, set configure flag to update leds + + test y,#%11 wz 'get shift into nz + + if_nz cmp data,#$60+1 wc 'check shift1 + if_nz_and_c cmpsub data,#$5B wc + if_nz_and_c add data,#@shift1-@table + if_nz_and_c call #look + if_nz_and_c andn y,#%11 + + if_nz cmp data,#$3D+1 wc 'check shift2 + if_nz_and_c cmpsub data,#$27 wc + if_nz_and_c add data,#@shift2-@table + if_nz_and_c call #look + if_nz_and_c andn y,#%11 + + test _locks,#%010 wc 'check shift-alpha, considering capslock + muxnc :shift,#$20 + test _locks,#$40 wc + if_nz_and_nc xor :shift,#$20 + cmp data,#"z"+1 wc + if_c cmpsub data,#"a" wc +:shift if_c add data,#"A" + if_c andn y,#%11 + +:flags ror data,#8 'add shift/ctrl/alt/win flags + mov x,#4 '+$100 if shift +:loop test y,#%11 wz '+$200 if ctrl + shr y,#2 '+$400 if alt + if_nz or data,#1 '+$800 if win + ror data,#1 + djnz x,#:loop + rol data,#12 + + rdlong x,par 'if room in buffer and key valid, enter + sub x,#1 + and x,#$F + cmp x,_head wz + if_nz test data,#$FF wz + if_nz mov x,par + if_nz add x,#11*4 + if_nz add x,_head + if_nz add x,_head + if_nz wrword data,x + if_nz add _head,#1 + if_nz and _head,#$F + + test stat,#4 wc 'if not configure flag, done + if_nc jmp #update 'else configure to update leds +' +' +' Configure keyboard +' +configure mov data,#$F3 'set keyboard auto-repeat + call #transmit + mov data,_auto + and data,#%11_11111 + call #transmit + + mov data,#$ED 'set keyboard lock-leds + call #transmit + mov data,_locks + rev data,#-3 & $1F + test data,#%100 wc + rcl data,#1 + and data,#%111 + call #transmit + + mov x,_locks 'insert locks into _states + and x,#%111 + shl _states+7,#3 + or _states+7,x + ror _states+7,#3 + + mov _present,#1 'set _present + + jmp #update 'done +' +' +' Lookup byte in table +' +look ror data,#2 'perform lookup + movs :reg,data + add :reg,#table + shr data,#27 + mov x,data +:reg mov data,0 + shr data,x + + jmp #rand 'isolate byte +' +' +' Transmit byte to keyboard +' +transmit +_c1 or dira,cmask 'pull clock low + movs napshr,#13 'hold clock for ~128us (must be >100us) + call #nap +_d1 or dira,dmask 'pull data low + movs napshr,#18 'hold data for ~4us + call #nap +_c2 xor dira,cmask 'release clock + + test data,#$0FF wc 'append parity and stop bits to byte + muxnc data,#$100 + or data,dlsb + + mov x,#10 'ready 10 bits +transmit_bit call #wait_c0 'wait until clock low + shr data,#1 wc 'output data bit +_d2 muxnc dira,dmask + mov wcond,c1 'wait until clock high + call #wait + djnz x,#transmit_bit 'another bit? + + mov wcond,c0d0 'wait until clock and data low + call #wait + mov wcond,c1d1 'wait until clock and data high + call #wait + + call #receive_ack 'receive ack byte with timed wait + cmp data,#$FA wz 'if ack error, reset keyboard + if_nz jmp #reset + +transmit_ret ret +' +' +' Receive byte from keyboard +' +receive test _cpin,#$20 wc 'wait indefinitely for initial clock low + waitpne cmask,cmask +receive_ack + mov x,#11 'ready 11 bits +receive_bit call #wait_c0 'wait until clock low + movs napshr,#16 'pause ~16us + call #nap +_d3 test dmask,ina wc 'input data bit + rcr data,#1 + mov wcond,c1 'wait until clock high + call #wait + djnz x,#receive_bit 'another bit? + + shr data,#22 'align byte + test data,#$1FF wc 'if parity error, reset keyboard + if_nc jmp #reset +rand and data,#$FF 'isolate byte + +look_ret +receive_ack_ret +receive_ret ret +' +' +' Wait for clock/data to be in required state(s) +' +wait_c0 mov wcond,c0 '(wait until clock low) + +wait mov y,tenms 'set timeout to 10ms + +wloop movs napshr,#18 'nap ~4us + call #nap +_c3 test cmask,ina wc 'check required state(s) +_d4 test dmask,ina wz 'loop until got state(s) or timeout +wcond if_never djnz y,#wloop '(replaced with c0/c1/c0d0/c1d1) + + tjz y,#reset 'if timeout, reset keyboard +wait_ret +wait_c0_ret ret + + +c0 if_c djnz y,#wloop '(if_never replacements) +c1 if_nc djnz y,#wloop +c0d0 if_c_or_nz djnz y,#wloop +c1d1 if_nc_or_z djnz y,#wloop +' +' +' Nap +' +nap rdlong t,#0 'get clkfreq +napshr shr t,#18/16/13 'shr scales time + min t,#3 'ensure waitcnt won't snag + add t,cnt 'add cnt to time + waitcnt t,#0 'wait until time elapses (nap) + +nap_ret ret +' +' +' Initialized data +' +' +dlsb long 1 << 9 +tenms long 10_000 / 4 +' +' +' Lookup table +' ascii scan extkey regkey ()=keypad +' +table word $0000 '00 + word $00D8 '01 F9 + word $0000 '02 + word $00D4 '03 F5 + word $00D2 '04 F3 + word $00D0 '05 F1 + word $00D1 '06 F2 + word $00DB '07 F12 + word $0000 '08 + word $00D9 '09 F10 + word $00D7 '0A F8 + word $00D5 '0B F6 + word $00D3 '0C F4 + word $0009 '0D Tab + word $0060 '0E ` + word $0000 '0F + word $0000 '10 + word $F5F4 '11 Alt-R Alt-L + word $00F0 '12 Shift-L + word $0000 '13 + word $F3F2 '14 Ctrl-R Ctrl-L + word $0071 '15 q + word $0031 '16 1 + word $0000 '17 + word $0000 '18 + word $0000 '19 + word $007A '1A z + word $0073 '1B s + word $0061 '1C a + word $0077 '1D w + word $0032 '1E 2 + word $F600 '1F Win-L + word $0000 '20 + word $0063 '21 c + word $0078 '22 x + word $0064 '23 d + word $0065 '24 e + word $0034 '25 4 + word $0033 '26 3 + word $F700 '27 Win-R + word $0000 '28 + word $0020 '29 Space + word $0076 '2A v + word $0066 '2B f + word $0074 '2C t + word $0072 '2D r + word $0035 '2E 5 + word $CC00 '2F Apps + word $0000 '30 + word $006E '31 n + word $0062 '32 b + word $0068 '33 h + word $0067 '34 g + word $0079 '35 y + word $0036 '36 6 + word $CD00 '37 Power + word $0000 '38 + word $0000 '39 + word $006D '3A m + word $006A '3B j + word $0075 '3C u + word $0037 '3D 7 + word $0038 '3E 8 + word $CE00 '3F Sleep + word $0000 '40 + word $002C '41 , + word $006B '42 k + word $0069 '43 i + word $006F '44 o + word $0030 '45 0 + word $0039 '46 9 + word $0000 '47 + word $0000 '48 + word $002E '49 . + word $EF2F '4A (/) / + word $006C '4B l + word $003B '4C ; + word $0070 '4D p + word $002D '4E - + word $0000 '4F + word $0000 '50 + word $0000 '51 + word $0027 '52 ' + word $0000 '53 + word $005B '54 [ + word $003D '55 = + word $0000 '56 + word $0000 '57 + word $00DE '58 CapsLock + word $00F1 '59 Shift-R + word $EB0D '5A (Enter) Enter + word $005D '5B ] + word $0000 '5C + word $005C '5D \ + word $CF00 '5E WakeUp + word $0000 '5F + word $0000 '60 + word $0000 '61 + word $0000 '62 + word $0000 '63 + word $0000 '64 + word $0000 '65 + word $00C8 '66 BackSpace + word $0000 '67 + word $0000 '68 + word $C5E1 '69 End (1) + word $0000 '6A + word $C0E4 '6B Left (4) + word $C4E7 '6C Home (7) + word $0000 '6D + word $0000 '6E + word $0000 '6F + word $CAE0 '70 Insert (0) + word $C9EA '71 Delete (.) + word $C3E2 '72 Down (2) + word $00E5 '73 (5) + word $C1E6 '74 Right (6) + word $C2E8 '75 Up (8) + word $00CB '76 Esc + word $00DF '77 NumLock + word $00DA '78 F11 + word $00EC '79 (+) + word $C7E3 '7A PageDn (3) + word $00ED '7B (-) + word $DCEE '7C PrScr (*) + word $C6E9 '7D PageUp (9) + word $00DD '7E ScrLock + word $0000 '7F + word $0000 '80 + word $0000 '81 + word $0000 '82 + word $00D6 '83 F7 + +keypad1 byte $CA, $C5, $C3, $C7, $C0, 0, $C1, $C4, $C2, $C6, $C9, $0D, "+-*/" + +keypad2 byte "0123456789.", $0D, "+-*/" + +shift1 byte "{|}", 0, 0, "~" + +shift2 byte $22, 0, 0, 0, 0, "<_>?)!@#$%^&*(", 0, ":", 0, "+" +' +' +' Uninitialized data +' +dmask res 1 +cmask res 1 +stat res 1 +data res 1 +x res 1 +y res 1 +t res 1 + +_head res 1 'write-only +_present res 1 'write-only +_states res 8 'write-only +_dpin res 1 'read-only at start +_cpin res 1 'read-only at start +_locks res 1 'read-only at start +_auto res 1 'read-only at start + +'' +'' +'' _________ +'' Key Codes +'' +'' 00..DF = keypress and keystate +'' E0..FF = keystate only +'' +'' +'' 09 Tab +'' 0D Enter +'' 20 Space +'' 21 ! +'' 22 " +'' 23 # +'' 24 $ +'' 25 % +'' 26 & +'' 27 ' +'' 28 ( +'' 29 ) +'' 2A * +'' 2B + +'' 2C , +'' 2D - +'' 2E . +'' 2F / +'' 30 0..9 +'' 3A : +'' 3B ; +'' 3C < +'' 3D = +'' 3E > +'' 3F ? +'' 40 @ +'' 41..5A A..Z +'' 5B [ +'' 5C \ +'' 5D ] +'' 5E ^ +'' 5F _ +'' 60 ` +'' 61..7A a..z +'' 7B { +'' 7C | +'' 7D } +'' 7E ~ +'' +'' 80-BF (future international character support) +'' +'' C0 Left Arrow +'' C1 Right Arrow +'' C2 Up Arrow +'' C3 Down Arrow +'' C4 Home +'' C5 End +'' C6 Page Up +'' C7 Page Down +'' C8 Backspace +'' C9 Delete +'' CA Insert +'' CB Esc +'' CC Apps +'' CD Power +'' CE Sleep +'' CF Wakeup +'' +'' D0..DB F1..F12 +'' DC Print Screen +'' DD Scroll Lock +'' DE Caps Lock +'' DF Num Lock +'' +'' E0..E9 Keypad 0..9 +'' EA Keypad . +'' EB Keypad Enter +'' EC Keypad + +'' ED Keypad - +'' EE Keypad * +'' EF Keypad / +'' +'' F0 Left Shift +'' F1 Right Shift +'' F2 Left Ctrl +'' F3 Right Ctrl +'' F4 Left Alt +'' F5 Right Alt +'' F6 Left Win +'' F7 Right Win +'' +'' FD Scroll Lock State +'' FE Caps Lock State +'' FF Num Lock State +'' +'' +100 if Shift +'' +200 if Ctrl +'' +400 if Alt +'' +800 if Win +'' +'' eg. Ctrl-Alt-Delete = $6C9 +'' +'' +'' Note: Driver will buffer up to 15 keystrokes, then ignore overflow. + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/MenuItem.spin b/source/gui-demo/MenuItem.spin new file mode 100644 index 0000000..f71245d --- /dev/null +++ b/source/gui-demo/MenuItem.spin @@ -0,0 +1,125 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: MenuItem.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' MenuItem Control +''============================================================================ +'' +'' Creates a menu item. This is really like a text button, but when placed with +'' others and inside a simple box it can look like a menu bar. User determined +'' state information may be assigned to it. + + +VAR + word varGdx 'GUI control variable + long varScreenPtr 'screen buffer pointer + long varVgaPos 'starting position of the menu item + byte varTextN[18] 'normal text, 15 chars MAX, with room for terminating Null and bracketing spaces + byte varTextI[18] 'inverted text, 15 chars MAX, with room for terminating Null and bracketing spaces + byte varRow 'top row location + byte varCol 'left col location + byte varCol2 'right col location + byte varWidth 'width of text + byte varStatus '0=normal else user defined value + byte varVgaCols 'width of screen in columns + + +PUB Init( pRow, pCol, pTextPtr, pVgaPtr, pVgaWidth ) | strIdx + + varVgaCols := pVgaWidth + varRow := pRow + varCol := pCol + varScreenPtr := pVgaPtr + varStatus := 0 + varWidth := strsize( pTextPtr ) + 2 + varCol2 := varCol + varWidth - 1 + + varTextN[0] := 32 + bytemove( @varTextN[1], pTextPtr, varWidth - 2 ) 'copy menu item text string + varTextN[varWidth - 1] := 32 + varTextN[varWidth] := 0 + strIdx := 0 + repeat varWidth + varTextI[strIdx] := varTextN[strIdx]+128 'invert the string + strIdx++ + varTextI[strIdx] := 0 + + varVgaPos := varRow * varVgaCols + varCol 'now draw the menu item + bytemove( @byte[varScreenPtr][varVgaPos], @varTextI, varWidth ) + + +PUB DrawText( pMode ) + + if pMode & 1 + bytemove( @byte[varScreenPtr][varVgaPos], @varTextN, varWidth ) + else + bytemove( @byte[varScreenPtr][varVgaPos], @varTextI, varWidth ) + + +PUB IsIn( pCx, pCy ) : qq + + qq := false + + if ( pCx => varCol ) AND ( pCx =< varCol2 ) + if pCy == varRow + qq := true + + return qq + + +PUB SetText( pPtr ) | strIdx + + bytefill( @varTextN[1], 32, varWidth - 2 ) 'clear it first + bytemove( @varTextN[1], pPtr, strsize(pPtr) ) 'copy menu item text string + strIdx := 0 + repeat varWidth + varTextI[strIdx] := varTextN[strIdx]+128 'invert the string + strIdx++ + bytemove( @byte[varScreenPtr][varVgaPos], varTextI, varWidth ) + + +PUB SetStatus( pStat ) + varStatus := pStat 'user defined status value + + +Pub GetStatus + return varStatus + + +PUB set_gzidx( gzidx ) + varGdx := gzidx + + +PUB get_gzidx + return varGdx + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/Mouse.spin b/source/gui-demo/Mouse.spin new file mode 100644 index 0000000..fb7f470 --- /dev/null +++ b/source/gui-demo/Mouse.spin @@ -0,0 +1,492 @@ +''*************************************** +''* PS/2 Mouse Driver v1.1 * +''* Author: Chip Gracey * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''*************************************** + +' v1.0 - 01 May 2006 - original version +' v1.1 - 01 Jun 2006 - bound coordinates added to simplify upper objects + + +VAR + + long cog + + long oldx, oldy, oldz 'must be followed by parameters (10 contiguous longs) + + long par_x 'absolute x read-only (7 contiguous longs) + long par_y 'absolute y read-only + long par_z 'absolute z read-only + long par_buttons 'button states read-only + long par_present 'mouse present read-only + long par_dpin 'data pin write-only + long par_cpin 'clock pin write-only + + long bx_min, by_min, bz_min 'min/max must be contiguous + long bx_max, by_max, bz_max + long bx_div, by_div, bz_div + long bx_acc, by_acc, bz_acc + + +PUB start(dpin, cpin) : okay + +'' Start mouse driver - starts a cog +'' returns false if no cog available +'' +'' dpin = data signal on PS/2 jack +'' cpin = clock signal on PS/2 jack +'' +'' use 100-ohm resistors between pins and jack +'' use 10K-ohm resistors to pull jack-side signals to VDD +'' connect jack-power to 5V, jack-gnd to VSS + + stop + par_dpin := dpin + par_cpin := cpin + okay := cog := cognew(@entry, @par_x) + 1 + + +PUB stop + +'' Stop mouse driver - frees a cog + + if cog + cogstop(cog~ - 1) + longfill(@oldx, 0, 10) + + +PUB present : type + +'' Check if mouse present - valid ~2s after start +'' returns mouse type: +'' +'' 3 = five-button scrollwheel mouse +'' 2 = three-button scrollwheel mouse +'' 1 = two-button or three-button mouse +'' 0 = no mouse connected + + type := par_present + + +PUB button(b) : state + +'' Get the state of a particular button +'' returns t|f + + state := -(par_buttons >> b & 1) + + +PUB buttons : states + +'' Get the states of all buttons +'' returns buttons: +'' +'' bit4 = right-side button +'' bit3 = left-side button +'' bit2 = center/scrollwheel button +'' bit1 = right button +'' bit0 = left button + + states := par_buttons + + +PUB abs_x : x + +'' Get absolute-x + + x := par_x + + +PUB abs_y : y + +'' Get absolute-y + + y := par_y + + +PUB abs_z : z + +'' Get absolute-z (scrollwheel) + + z := par_z + + +PUB delta_reset + +'' Reset deltas + + oldx := par_x + oldy := par_y + oldz := par_z + + +PUB delta_x : x | newx + +'' Get delta-x + + newx := par_x + x := newx - oldx + oldx := newx + + +PUB delta_y : y | newy + +'' Get delta-y + + newy := par_y + y := newy - oldy + oldy := newy + + +PUB delta_z : z | newz + +'' Get delta-z (scrollwheel) + + newz := par_z + z := newz - oldz + oldz := newz + + +PUB bound_limits(xmin, ymin, zmin, xmax, ymax, zmax) | i + +'' Set bounding limits + + longmove(@bx_min, @xmin, 6) + + +PUB bound_scales(x_scale, y_scale, z_scale) + +'' Set bounding scales (usually +/-1's, bigger values divide) + + longmove(@bx_div, @x_scale, 3) + + +PUB bound_preset(x, y, z) | i, d + +'' Preset bound coordinates + + repeat i from 0 to 2 + d := ||bx_div[i] + bx_acc[i] := (x[i] - bx_min[i]) * d + d >> 1 + + +PUB bound_x : x + +'' Get bound-x + + x := bound(0, delta_x) + + +PUB bound_y : y + +'' Get bound-y + + y := bound(1, delta_y) + + +PUB bound_z : z + +'' Get bound-z + + z := bound(2, delta_z) + + +PRI bound(i, delta) : b | d + + d := bx_div[i] + b := bx_min[i] + (bx_acc[i] := bx_acc[i] + delta * (d < 0) | 1 #> 0 <# (bx_max[i] - bx_min[i] + 1) * ||d - 1) / ||d + + +DAT + +'*************************************** +'* Assembly language PS/2 mouse driver * +'*************************************** + + org +' +' +' Entry +' +entry mov p,par 'load input parameters: + add p,#5*4 '_dpin/_cpin + rdlong _dpin,p + add p,#4 + rdlong _cpin,p + + mov dmask,#1 'set pin masks + shl dmask,_dpin + mov cmask,#1 + shl cmask,_cpin + + test _dpin,#$20 wc 'modify port registers within code + muxc _d1,dlsb + muxc _d2,dlsb + muxc _d3,#1 + muxc _d4,#1 + test _cpin,#$20 wc + muxc _c1,dlsb + muxc _c2,dlsb + muxc _c3,#1 + + movd :par,#_x 'reset output parameters: + mov p,#5 '_x/_y/_z/_buttons/_present +:par mov 0,#0 + add :par,dlsb + djnz p,#:par +' +' +' Reset mouse +' +reset mov dira,#0 'reset directions + mov dirb,#0 + + mov stat,#1 'set reset flag +' +' +' Update parameters +' +update movd :par,#_x 'update output parameters: + mov p,par '_x/_y/_z/_buttons/_present + mov q,#5 +:par wrlong 0,p + add :par,dlsb + add p,#4 + djnz q,#:par + + test stat,#1 wc 'if reset flag, transmit reset command + if_c mov data,#$FF + if_c call #transmit +' +' +' Get data packet +' + mov stat,#0 'reset state + + call #receive 'receive first byte + + cmp data,#$AA wz 'powerup/reset? + if_z jmp #init + + mov _buttons,data 'data packet, save buttons + + call #receive 'receive second byte + + test _buttons,#$10 wc 'adjust _x + muxc data,signext + add _x,data + + call #receive 'receive third byte + + test _buttons,#$20 wc 'adjust _y + muxc data,signext + add _y,data + + and _buttons,#%111 'trim buttons + + cmp _present,#2 wc 'if not scrollwheel mouse, update parameters + if_c jmp #update + + + call #receive 'scrollwheel mouse, receive fourth byte + + cmp _present,#3 wz 'if 5-button mouse, handle two extra buttons + if_z test data,#$10 wc + if_z_and_c or _buttons,#%01000 + if_z test data,#$20 wc + if_z_and_c or _buttons,#%10000 + + shl data,#28 'adjust _z + sar data,#28 + sub _z,data + + jmp #update 'update parameters +' +' +' Initialize mouse +' +init call #receive '$AA received, receive id + + movs crate,#100 'try to enable 3-button scrollwheel type + call #checktype + movs crate,#200 'try to enable 5-button scrollwheel type + call #checktype + shr data,#1 'if neither, 3-button type + add data,#1 + mov _present,data + + movs srate,#200 'set 200 samples per second + call #setrate + + mov data,#$F4 'enable data reporting + call #transmit + + jmp #update +' +' +' Check mouse type +' +checktype movs srate,#200 'perform "knock" sequence to enable + call #setrate '..scrollwheel and extra buttons + +crate movs srate,#200/100 + call #setrate + + movs srate,#80 + call #setrate + + mov data,#$F2 'read type + call #transmit + call #receive + +checktype_ret ret +' +' +' Set sample rate +' +setrate mov data,#$F3 + call #transmit +srate mov data,#0 + call #transmit + +setrate_ret ret +' +' +' Transmit byte to mouse +' +transmit +_c1 or dira,cmask 'pull clock low + movs napshr,#13 'hold clock for ~128us (must be >100us) + call #nap +_d1 or dira,dmask 'pull data low + movs napshr,#18 'hold data for ~4us + call #nap +_c2 xor dira,cmask 'release clock + + test data,#$0FF wc 'append parity and stop bits to byte + muxnc data,#$100 + or data,dlsb + + mov p,#10 'ready 10 bits +transmit_bit call #wait_c0 'wait until clock low + shr data,#1 wc 'output data bit +_d2 muxnc dira,dmask + mov wcond,c1 'wait until clock high + call #wait + djnz p,#transmit_bit 'another bit? + + mov wcond,c0d0 'wait until clock and data low + call #wait + mov wcond,c1d1 'wait until clock and data high + call #wait + + call #receive_ack 'receive ack byte with timed wait + cmp data,#$FA wz 'if ack error, reset mouse + if_nz jmp #reset + +transmit_ret ret +' +' +' Receive byte from mouse +' +receive test _cpin,#$20 wc 'wait indefinitely for initial clock low + waitpne cmask,cmask +receive_ack + mov p,#11 'ready 11 bits +receive_bit call #wait_c0 'wait until clock low + movs napshr,#16 'pause ~16us + call #nap +_d3 test dmask,ina wc 'input data bit + rcr data,#1 + mov wcond,c1 'wait until clock high + call #wait + djnz p,#receive_bit 'another bit? + + shr data,#22 'align byte + test data,#$1FF wc 'if parity error, reset mouse + if_nc jmp #reset + and data,#$FF 'isolate byte + +receive_ack_ret +receive_ret ret +' +' +' Wait for clock/data to be in required state(s) +' +wait_c0 mov wcond,c0 '(wait until clock low) + +wait mov q,tenms 'set timeout to 10ms + +wloop movs napshr,#18 'nap ~4us + call #nap +_c3 test cmask,ina wc 'check required state(s) +_d4 test dmask,ina wz 'loop until got state(s) or timeout +wcond if_never djnz q,#wloop '(replaced with c0/c1/c0d0/c1d1) + + tjz q,#reset 'if timeout, reset mouse +wait_ret +wait_c0_ret ret + + +c0 if_c djnz q,#wloop '(if_never replacements) +c1 if_nc djnz q,#wloop +c0d0 if_c_or_nz djnz q,#wloop +c1d1 if_nc_or_z djnz q,#wloop +' +' +' Nap +' +nap rdlong t,#0 'get clkfreq +napshr shr t,#18/16/13 'shr scales time + min t,#3 'ensure waitcnt won't snag + add t,cnt 'add cnt to time + waitcnt t,#0 'wait until time elapses (nap) + +nap_ret ret +' +' +' Initialized data +' +dlsb long 1 << 9 +tenms long 10_000 / 4 +signext long $FFFFFF00 +' +' +' Uninitialized data +' +dmask res 1 +cmask res 1 +stat res 1 +data res 1 +p res 1 +q res 1 +t res 1 + +_x res 1 'write-only +_y res 1 'write-only +_z res 1 'write-only +_buttons res 1 'write-only +_present res 1 'write-only +_dpin res 1 'read-only +_cpin res 1 'read-only + + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/PushButton.spin b/source/gui-demo/PushButton.spin new file mode 100644 index 0000000..915e5b3 --- /dev/null +++ b/source/gui-demo/PushButton.spin @@ -0,0 +1,117 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: PushButton.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' Push Button Control +''============================================================================ +'' +'' Creates a straight forward pushbutton control. + + +OBJ + SBOX : "SimpleBox" + + +VAR + word varGdx 'GUI control variable + long varScreenPtr 'screen buffer pointer + long varVgaPos 'starting position of the menu item + byte varTextN[16] 'normal text, 15 chars MAX, with room for terminating Null + byte varTextI[16] 'inverted text, 15 chars MAX, with room for terminating Null + byte varRow 'top row location + byte varCol 'left col location + byte varCol2 'right col location + byte varWidth 'width of text + byte varVgaCols 'width of screen in columns + + +PUB Init( pRow, pCol, pTextPtr, pVgaPtr, pVgaWidth ) | strIdx + + varVgaCols := pVgaWidth + varRow := pRow + varCol := pCol + varScreenPtr := pVgaPtr + varWidth := strsize( pTextPtr )+ 2 + varCol2 := varCol + varWidth - 1 + + SBOX.DrawBox( pRow, pCol, varWidth, 3, 0, pVgaPtr, pVgaWidth ) + + bytemove( @varTextN[0], pTextPtr, varWidth - 2 ) 'copy menu item text string + + strIdx := 0 + repeat varWidth-2 + varTextI[strIdx] := varTextN[strIdx]+128 'invert the string + strIdx++ + + varVgaPos := varRow * varVgaCols + varCol + varVgaCols + 1 + bytemove( @byte[varScreenPtr][varVgaPos], varTextI, varWidth - 2 ) + + +PUB DrawText( pMode ) + + if pMode & 1 + bytemove( @byte[varScreenPtr][varVgaPos], @varTextI, varWidth-2 ) + else + bytemove( @byte[varScreenPtr][varVgaPos], @varTextN, varWidth-2 ) + + +PUB IsIn( pCx, pCy ) : qq + + qq := false + + if ( pCx > varCol ) AND ( pCx < varCol2 ) + if pCy == varRow+1 + qq := true + + return qq + + +PUB SetText( pPtr ) | strIdx + + bytefill( @varTextN[0], 32, varWidth - 2 ) 'clear it first + bytemove( @varTextN[0], pPtr, strsize(pPtr) ) 'copy menu item text string + + strIdx := 0 + repeat varWidth-2 + varTextI[strIdx] := varTextN[strIdx]+128 'invert the string + strIdx++ + bytemove( @byte[varScreenPtr][varVgaPos], varTextN, varWidth-2 ) + + +PUB set_gzidx( gzidx ) + varGdx := gzidx + + +PUB get_gzidx + return varGdx + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/RadioCheck.spin b/source/gui-demo/RadioCheck.spin new file mode 100644 index 0000000..89f1b5d --- /dev/null +++ b/source/gui-demo/RadioCheck.spin @@ -0,0 +1,153 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: RadioCheck.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' Button Control +''============================================================================ +'' +'' Creates Radio Buttons or Check Boxes +'' + +VAR + word varGdx 'GUI control variable + long varScreenPtr 'screen buffer pointer + long varVgaPos 'start location of the button + byte varTextN[16] 'normal text, 15 chars MAX, with room for terminating Null + byte varTextI[16] 'inverted text, 15 chars MAX, with room for terminating Null + byte varRow 'top row location + byte varCol 'left col location + byte varCol2 'right col location + byte varWidth 'width of text + byte varStatus 'status byte 0 = none 1 = active + byte varSelChar 'selected character + byte varUnselChar 'unselected character + byte varVgaCols 'width of screen in columns + + +PUB Init( pRow, pCol, pTextWidth, pStyle, pTextPtr, pVgaPtr, pVgaWidth ) | strIdx, vgaIdx + + varVgaCols := pVgaWidth + varStatus := 0 + varRow := pRow + varCol := pCol + varWidth := pTextWidth + varScreenPtr := pVgaPtr + varVgaPos := varRow * varVgaCols + varCol + varCol2 := varCol + varWidth + 1 + + if pStyle == 0 + varSelChar := 7 'CheckBox Style + varUnselChar := 6 + else + varSelChar := 5 'Radio Button Style + varUnselChar := 4 + + strIdx := strsize( pTextPtr ) + bytemove( @varTextN, pTextPtr, strIdx ) 'copy checkbox text string + bytefill(@varTextN[strIdx],32,pTextWidth-strIdx) 'pad out with spaces + varTextN[pTextWidth] := 0 + strIdx := 0 + repeat pTextWidth + varTextI[strIdx] := varTextN[strIdx]+128 'create inverted string + strIdx++ + varTextI[strIdx] := 0 + + vgaIdx := varVgaPos 'now draw the checkbox + + byte[varScreenPtr][vgaIdx++] := varUnselChar 'empty bullet + byte[varScreenPtr][vgaIdx++] := 32 + + strIdx := strsize( @varTextN ) + bytemove( @byte[varScreenPtr][vgaIdx], @varTextN, strIdx ) + + +PUB DrawText( pMode ) | strIdx, vgaIdx + + strIdx := strsize( @varTextN ) + vgaIdx := varVgaPos 'bullet location + + if varStatus == 1 + byte[varScreenPtr][vgaIdx] := varSelChar 'selected bullet + else + byte[varScreenPtr][vgaIdx] := varUnselChar 'non-selected bullet + + vgaIdx += 2 'checkbox text location + + if pMode & 1 + bytemove( @byte[varScreenPtr][vgaIdx], @varTextI, strIdx ) + else + bytemove( @byte[varScreenPtr][vgaIdx], @varTextN, strIdx ) + + + +PUB IsIn( pCx, pCy ) | retVal + + retVal := false + + if ( pCx => varCol ) AND ( pCx =< varCol2 ) + if pCy == varRow + retVal := true + + return retVal + + +PUB Select( pSel ) + + if pSel <> -1 + varStatus := pSel + else + if varStatus == 0 + varStatus := 1 + else + varStatus := 0 + + if varStatus == 1 + byte[varScreenPtr][varVgaPos] := varSelChar 'selected bullet + else + byte[varScreenPtr][varVgaPos] := varUnselChar 'non-selected + + +PUB IsSet + if varStatus == 1 + return true + else + return false + + +PUB set_gzidx( gzidx ) + varGdx := gzidx + + +PUB get_gzidx + return varGdx + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/SimpleBox.spin b/source/gui-demo/SimpleBox.spin new file mode 100644 index 0000000..3370a95 --- /dev/null +++ b/source/gui-demo/SimpleBox.spin @@ -0,0 +1,93 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: SimpleBox.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' SimpleBox Control +''============================================================================ +'' +'' Just draws a box with optional title, plain and simple. +'' + +PUB DrawBox( pRow, pCol, pWidth, pHeight, pTitlePtr, pVgaPtr, pVgaWidth ) | idx, vgaIdx, rowCnt, tbTitle, vgaStartIdx + vgaStartIdx := pRow * pVgaWidth + pCol + + vgaIdx := vgaStartIdx 'clear the area first + repeat pHeight + bytefill(@byte[pVgaPtr][vgaIdx],32,pWidth) + vgaIdx += pVgaWidth + + vgaIdx := vgaStartIdx 'goto top left row/col + + byte[pVgaPtr][vgaIdx++] := 10 'top left corner char + bytefill(@byte[pVgaPtr][vgaIdx],14,pWidth-2) 'horizontal line + vgaIdx += pWidth - 2 + byte[pVgaPtr][vgaIdx++] := 11 'top right corner char + + vgaIdx := vgaStartIdx + pVgaWidth 'move down to start of next row + + if pTitlePtr <> 0 'if there is a title + + byte[pVgaPtr][vgaIdx++] := 15 'vertical line char + + idx := strsize( pTitlePtr ) + bytemove( @byte[pVgaPtr][vgaIdx], pTitlePtr, idx )'place title + vgaIdx += pWidth - 2 + byte[pVgaPtr][vgaIdx++] := 15 'vertical line char + + vgaIdx := vgaStartIdx + 2 * pVgaWidth 'move down to start of next row + + byte[pVgaPtr][vgaIdx++] := 18 'left 'tee' char + bytefill(@byte[pVgaPtr][vgaIdx],14,pWidth-2)'horizontal line + vgaIdx += pWidth - 2 + byte[pVgaPtr][vgaIdx++] := 19 'right 'tee' char + + rowCnt := 3 'row counter + + else + + rowCnt := 1 'row counter + + vgaIdx := vgaStartIdx + pVgaWidth * rowCnt 'move down to start of next row + repeat pHeight - rowCnt - 1 + byte[pVgaPtr][vgaIdx++] := 15 'vertical line char + vgaIdx += pWidth - 2 + byte[pVgaPtr][vgaIdx++] := 15 'vertical line char + vgaIdx -= pWidth + vgaIdx += pVgaWidth + + 'the above left vgaIdx pointing to the start of the last line + + byte[pVgaPtr][vgaIdx++] := 12 'bottom left corner char + bytefill(@byte[pVgaPtr][vgaIdx],14,pWidth-2) 'horizontal line + vgaIdx += pWidth - 2 + byte[pVgaPtr][vgaIdx++] := 13 'bottom right corner char + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/Simple_Numbers.spin b/source/gui-demo/Simple_Numbers.spin new file mode 100644 index 0000000..ef3c13f --- /dev/null +++ b/source/gui-demo/Simple_Numbers.spin @@ -0,0 +1,197 @@ +''**************************************** +''* Simple_Numbers * +''* Authors: Chip Gracey, Jon Williams * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''**************************************** +'' +'' Provides simple numeric conversion methods; all methods return a pointer to +'' a string. +'' +'' Updated... 29 APR 2006 + + +CON + + MAX_LEN = 64 ' 63 chars + zero terminator + + +VAR + + long idx ' pointer into string + byte nstr[MAX_LEN] ' string for numeric data + + +PUB dec(value) + +'' Returns pointer to signed-decimal string + + clrstr(@nstr, MAX_LEN) ' clear output string + return decstr(value) ' return pointer to numeric string + + +PUB decf(value, width) | t_val, field + +'' Returns pointer to signed-decimal, fixed-width (space padded) string + + clrstr(@nstr, MAX_LEN) + width := 1 #> width <# constant(MAX_LEN - 1) ' qualify field width + + t_val := ||value ' work with absolute + field~ ' clear field + + repeat while t_val > 0 ' count number of digits + field++ + t_val /= 10 + + field #>= 1 ' min field width is 1 + if value < 0 ' if value is negative + field++ ' bump field for neg sign indicator + + if field < width ' need padding? + repeat (width - field) ' yes + nstr[idx++] := " " ' pad with space(s) + + return decstr(value) + + +PUB decx(value, digits) | div + +'' Returns pointer to zero-padded, signed-decimal string +'' -- if value is negative, field width is digits+1 + + clrstr(@nstr, MAX_LEN) + digits := 1 #> digits <# 10 + + if (value < 0) ' negative value? + -value ' yes, make positive + nstr[idx++] := "-" ' and print sign indicator + + div := 1_000_000_000 ' initialize divisor + if digits < 10 ' less than 10 digits? + repeat (10 - digits) ' yes, adjust divisor + div /= 10 + + value //= (div * 10) ' truncate unused digits + + repeat digits + nstr[idx++] := (value / div + "0") ' convert digit to ASCII + value //= div ' update value + div /= 10 ' update divisor + + return @nstr + + +PUB hex(value, digits) + +'' Returns pointer to a digits-wide hexadecimal string + + clrstr(@nstr, MAX_LEN) + return hexstr(value, digits) + + +PUB ihex(value, digits) + +'' Returns pointer to a digits-wide, indicated (with $) hexadecimal string + + clrstr(@nstr, MAX_LEN) + nstr[idx++] := "$" + return hexstr(value, digits) + + +PUB bin(value, digits) + +'' Returns pointer to a digits-wide binary string + + clrstr(@nstr, MAX_LEN) + return binstr(value, digits) + + +PUB ibin(value, digits) + +'' Returns pointer to a digits-wide, indicated (with %) binary string + + clrstr(@nstr, MAX_LEN) + nstr[idx++] := "%" ' preface with binary indicator + return binstr(value, digits) + + +PRI clrstr(strAddr, size) + +' Clears string at strAddr +' -- also resets global character pointer (idx) + + bytefill(strAddr, 0, size) ' clear string to zeros + idx~ ' reset index + + +PRI decstr(value) | div, z_pad + +' Converts value to signed-decimal string equivalent +' -- characters written to current position of idx +' -- returns pointer to nstr + + if (value < 0) ' negative value? + -value ' yes, make positive + nstr[idx++] := "-" ' and print sign indicator + + div := 1_000_000_000 ' initialize divisor + z_pad~ ' clear zero-pad flag + + repeat 10 + if (value => div) ' printable character? + nstr[idx++] := (value / div + "0") ' yes, print ASCII digit + value //= div ' update value + z_pad~~ ' set zflag + elseif z_pad or (div == 1) ' printing or last column? + nstr[idx++] := "0" + div /= 10 + + return @nstr + + +PRI hexstr(value, digits) + +' Converts value to digits-wide hexadecimal string equivalent +' -- characters written to current position of idx +' -- returns pointer to nstr + + digits := 1 #> digits <# 8 ' qualify digits + value <<= (8 - digits) << 2 ' prep most significant digit + repeat digits + nstr[idx++] := lookupz((value <-= 4) & $F : "0".."9", "A".."F") + + return @nstr + + +PRI binstr(value, digits) + +' Converts value to digits-wide binary string equivalent +' -- characters written to current position of idx +' -- returns pointer to nstr + + digits := 1 #> digits <# 32 ' qualify digits + value <<= 32 - digits ' prep MSB + repeat digits + nstr[idx++] := (value <-= 1) & 1 + "0" ' move digits (ASCII) to string + + return @nstr + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/SpinBox.spin b/source/gui-demo/SpinBox.spin new file mode 100644 index 0000000..183e444 --- /dev/null +++ b/source/gui-demo/SpinBox.spin @@ -0,0 +1,225 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: SpinBox.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' Simple SpinBox +''============================================================================ +'' +'' Creates a simple spin box control. Data may be text or numeric. + + +CON + MAX_LEN = 16 + + +OBJ + SBOX : "SimpleBox" + + +VAR + word varGdx 'GUI control variable + long varScreenPtr 'screen buffer pointer + long varDataPtr 'points to the display data (not the setup bytes) + long varDrawIdx 'vga index to start of display item area + long n_idx 'index into number string + word varUpIdx 'vga index of up arrow + word varDnIdx 'vga index of down arrow + byte n_str[MAX_LEN] 'string for numeric data + byte varRow 'top row location + byte varCol 'left col location + byte varCol2 'right col location + byte varWidth 'width ... info area is 6 less than total width + byte varVgaCols 'width of screen in columns + byte varNumData 'number of data items + byte varDataIdx 'current data item displayed + byte varType '0 = text 1 = numeric + + +PUB Init ( pRow, pCol, pWidth, pType, pNum, pDataPtr, pVgaPtr, pVgaWidth ) | vgaIdx + + varVgaCols := pVgaWidth + varRow := pRow + varCol := pCol + varWidth := pWidth + varScreenPtr := pVgaPtr + varCol2 := varCol + varWidth + varDataPtr := pDataPtr + varType := pType + varNumData := pNum + varDataIdx := 0 + varDrawIdx := varRow*varVgaCols+varVgaCols+varCol+1 'display item location + + SBOX.DrawBox( pRow, pCol, pWidth, 3, 0, pVgaPtr, pVgaWidth ) + + vgaIdx := varRow * varVgaCols + varCol + varWidth - 3 + byte[varScreenPtr][vgaIdx] := 16 'top 'tee' char + vgaIdx += varVgaCols + byte[varScreenPtr][vgaIdx] := 15 'vertical line char + varDnIdx := vgaIdx+1 'save position of DN arrow + byte[varScreenPtr][vgaIdx+1] := 3 'down arrow char + vgaIdx += varVgaCols + byte[varScreenPtr][vgaIdx] := 17 'bottom 'tee' char + + vgaIdx := varRow * varVgaCols + varCol + varWidth - 5 + byte[varScreenPtr][vgaIdx] := 16 'top 'tee' char + vgaIdx += varVgaCols + byte[varScreenPtr][vgaIdx] := 15 'vertical line char + varUpIdx := vgaIdx+1 'save position of UP arrow + byte[varScreenPtr][vgaIdx+1] := 2 'up arrow char + vgaIdx += varVgaCols + byte[varScreenPtr][vgaIdx] := 17 'bottom 'tee' char + + DrawData( varDataIdx ) + + +PUB IsIn( pCx, pCy ) | retVal + + retVal := false + + if ( pCx => varCol ) AND ( pCx =< varCol2 ) + if pCy == varRow + 1 + retVal := true + + return retVal + + +PUB Clicked( pCx, pCy ) | idx, retVal + + retVal := -1 'default return if neither arrow was pressed or at boundary + + idx := pCy*varVgaCols+pCx + + if idx == varUpIdx + if varDataIdx <> varNumData - 1 + varDataIdx++ + retVal := varDataIdx + + if idx == varDnIdx + if varDataIdx <> 0 + varDataIdx-- + retVal := varDataIdx + + if retVal <> -1 + DrawData( varDataIdx ) + + return retVal + + +PUB GetDataIndex +'returns the zero based index of the currently displaying data + return varDataIdx + + +PUB set_gzidx( gzidx ) + varGdx := gzidx + + +PUB get_gzidx + return varGdx + + +PRI DrawData( didx ) | tptr, strLen +'Draws the specified (by zero based index) data in the spin box display area. +' didx = index of data item to display + + tptr := varDataPtr + bytefill(@byte[varScreenPtr][varDrawIdx],32,varWidth-6) + + if varType == 0 '--------------- + + strLen := strsize( tptr ) + repeat didx + tptr += strLen + 1 + strLen := strsize( tptr ) + bytemove(@byte[varScreenPtr][varDrawIdx], tptr, strLen ) + + else '------------ + strLen := varWidth-6 + strLen := 1 #> strLen <# constant(MAX_LEN - 1) + bytemove(@byte[varScreenPtr][varDrawIdx], decf(long[tptr][didx],strLen), strLen ) + + +''============================================================================= +'' The routine that follows is taken from the Simple_Numbers.spin object. +'' However, I collapsed clrstr() and decstr() into this decf() function. +'' +''**************************************** +''* Simple_Numbers * +''* Authors: Chip Gracey, Jon Williams * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''**************************************** + +''============================================================================= +PRI decf(value, width) | t_val, field, div, z_pad + +'' Converts value to signed-decimal string equivalent +'' - characters written to current position of n_idx +'' - returns pointer to n_str: signed-decimal, fixed-width, space padded string + + n_idx~ ' reset index + t_val := ||value ' work with absolute + field~ ' clear field + + bytefill(@n_str, 0, MAX_LEN) ' clear string to zeros + + repeat while t_val > 0 ' count number of digits + field++ + t_val /= 10 + + field #>= 1 ' min field width is 1 + if value < 0 ' if value is negative + field++ ' - bump field for neg sign indicator + + if field < width ' need padding? + repeat (width - field) ' yes + n_str[n_idx++] := " " ' - pad with space(s) + + if (value < 0) ' negative value? + -value ' - yes, make positive + n_str[n_idx++] := "-" ' - and print sign indicator + + div := 1_000_000_000 ' initialize divisor + z_pad~ ' clear zero-pad flag + + repeat 10 + if (value => div) ' printable character? + n_str[n_idx++] := (value / div + "0") ' - yes, print ASCII digit + value //= div ' - update value + z_pad~~ ' - set zflag + elseif z_pad or (div == 1) ' printing or last column? + n_str[n_idx++] := "0" + div /= 10 + + return @n_str + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/StatusLamp.spin b/source/gui-demo/StatusLamp.spin new file mode 100644 index 0000000..a8bcad9 --- /dev/null +++ b/source/gui-demo/StatusLamp.spin @@ -0,0 +1,115 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: StatusLamp.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' Status Lamp +''============================================================================ +'' +'' Creates status lamp to provide visual feedback of conditions. +'' +'' You create this item with the Init() function then you must set a status +'' right away, on or off (or else it will not show anything). + + +VAR + word varGdx 'GUI control variable + long varVgaPos 'screen location of start of input area + long varScreenPtr 'screen buffer pointer + long varCxPtr 'points to the text cursor x position variable + byte varRow 'top row location + byte varCol 'left col location + byte varCol2 'right col location + byte varWidth 'width ... text width is 2 less than total width + byte varTitleWidth 'number of rows used for title area ( 0 or 2 ) + byte varStatus '0 = off 1 = on + byte varVgaCols 'width of screen in columns + + +PUB Init ( pRow, pCol, pWidth, pTitlePtr, pVgaPtr, pVgaWidth ) | vgaIdx + + varVgaCols := pVgaWidth + varRow := pRow + varCol := pCol + varWidth := pWidth + varTitleWidth := strsize( pTitlePtr ) + varScreenPtr := pVgaPtr + varCol2 := varCol + varWidth + + vgaIdx := varRow * varVgaCols + varCol + bytefill( @byte[varScreenPtr][vgaIdx], 32, varWidth ) + bytemove( @byte[varScreenPtr][vgaIdx], pTitlePtr, varTitleWidth ) + + varVgaPos := varRow*varVgaCols+varCol+varWidth-4 'save status text area start + byte[varScreenPtr][varVgaPos-2] := ":" + + +PUB IsIn( pCx, pCy ) : qq + + qq := false + + if ( pCx => varCol ) AND ( pCx =< varCol2 ) + if pCy == varRow + qq := true + + return qq + + +PUB Set( pSet, pStr ) | strLen + + varStatus := pSet + + strLen := strsize( pStr ) + strLen := 1 #> strLen <# 4 + + bytefill( @byte[varScreenPtr][varVgaPos], 32, 4 ) + bytemove( @byte[varScreenPtr][varVgaPos], pStr, strLen ) + + if varStatus == 1 + byte[varScreenPtr][varVgaPos+0] += 128 + byte[varScreenPtr][varVgaPos+1] += 128 + byte[varScreenPtr][varVgaPos+2] += 128 + byte[varScreenPtr][varVgaPos+3] += 128 + + +PUB GetStatus + return varStatus + + + +PUB set_gzidx( gzidx ) + varGdx := gzidx + + +PUB get_gzidx + return varGdx + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/TextBox.spin b/source/gui-demo/TextBox.spin new file mode 100644 index 0000000..9c24d69 --- /dev/null +++ b/source/gui-demo/TextBox.spin @@ -0,0 +1,195 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' File: TextBox.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +''============================================================================ +'' TextBox Control +''============================================================================ +'' +'' Creates a simple text box with auto scrolling text area +'' +'' This control is a text window that you can print ASCII strings to. You can +'' set it to truncate ot wrap long lines. If lines are wrapped all wrapped +'' lines will be prefixed with an inverted right arrow character to mark the +'' wrapping operation. Leading and trailing CRLF characters are stripped from +'' the string but CRLFs inside the string remain and will be print as "funny" +'' characters. A titlebar with a caption may optionally be applied. + +OBJ + SBOX : "SimpleBox" + + +VAR + word varGdx 'GUI control variable + long varScreenPtr 'screen buffer pointer + byte varRow 'top row location + byte varRow2 'bottom row location + byte varCol 'left col location + byte varCol2 'right col location + byte varWidth 'width ... text width is 2 less than total width + byte varHeight 'height ... text height is 2 or 4 less than total height + byte varTitle 'number of rows used for title area ( 0 or 2 ) + byte varRowIdx 'index to current row to be written + byte varVgaCols 'width of screen in columns + byte varWrap '0=truncate lines 1=wrap lines (marks wrapped lines) + + +PUB Init( pRow, pCol, pWidth, pHeight, pWrap, pTitlePtr, pVgaPtr, pVgaWidth ) + + varVgaCols := pVgaWidth + varRow := pRow + varCol := pCol + varWidth := pWidth + varHeight := pHeight + varWrap := pWrap + varRow2 := pRow + 2 + varCol2 := pCol + pWidth - 1 + varRowIdx := 0 + varScreenPtr := pVgaPtr + + if pTitlePtr == 0 'no title text ? + varTitle := 0 + else + varTitle := 2 + + SBOX.DrawBox( pRow, pCol, pWidth, pHeight, pTitlePtr, pVgaPtr, pVgaWidth ) + + +PUB IsIn( pCx, pCy ) : qq + + qq := false + + if ( pCx => varCol ) AND ( pCx =< varCol2 ) + if ( pCy => ( varRow + varTitle ) ) AND ( pCy =< varRow2 ) + qq := true + + return qq + + +PUB Clear | tbRows, idx, vgaIdx + + tbRows := varHeight - varTitle - 2 + + vgaIdx := ( varRow + varTitle + 1 ) * varVgaCols + varCol + 1 + + repeat tbRows + bytefill( @byte[varScreenPtr][vgaIdx], 32, varWidth - 2 ) + vgaIdx += varVgaCols + + varRowIdx := 0 + + +PUB Print( pTxtPtr, pSize ) | strLen, tbRows, vgaIdx + + tbRows := varHeight - varTitle - 2 + if varRowIdx == tbRows 'if full, scroll up + Scroll + + vgaIdx := (varRow+varTitle+varRowIdx+1)*varVgaCols+varCol+1 'index to screen + + if pSize <> 0 'determine string size + strLen := pSize + else + strLen := strsize( pTxtPtr ) + + if strlen > 0 'strip leading CRLF + repeat while byte[pTxtPtr][0] < 32 + strLen-- + pTxtPtr++ + if strLen == 0 + quit + + if strLen > 0 'strip trailing CRLF + repeat while byte[pTxtPtr][strLen-1] < 32 + strLen-- + if strLen == 0 + quit + + if varWrap == 0 'if not wrapping lines + strLen <#= (varWidth-2) ' - truncate line length to window width + + if strLen =< varWidth-2 + bytemove(@byte[varScreenPtr][vgaIdx],pTxtPtr,strLen)' - copy whole string + if varRowIdx < tbRows + varRowIdx++ + else + bytemove(@byte[varScreenPtr][vgaIdx],pTxtPtr,varWidth-2)' - copy part string + if varRowIdx < tbRows + varRowIdx++ + strlen -= varWidth-2 + pTxtPtr += varWidth-2 + + repeat 'repeat till all of string done + if varRowIdx == tbRows 'scroll if required + Scroll + vgaIdx := (varRow+varTitle+varRowIdx+1)*varVgaCols+varCol+1 'index to screen + byte[varScreenPtr][vgaIdx] := $81 'inverted right arrow to show wrap + if strLen =< varWidth-3 + bytemove(@byte[varScreenPtr][vgaIdx+1],pTxtPtr,strLen)' - copy remaining string + if varRowIdx < tbRows + varRowIdx++ + quit + else + bytemove(@byte[varScreenPtr][vgaIdx+1],pTxtPtr,varWidth-3)' copy part string + strlen -= varWidth-3 + pTxtPtr += varWidth-3 + if varRowIdx < tbRows + varRowIdx++ + + +PUB Scroll | vgaIdx, tbRows + + tbRows := varHeight - varTitle - 2 + vgaIdx := ( varRow + varTitle + 1 ) * varVgaCols + varCol + repeat tbRows - 1 + longmove( @byte[varScreenPtr][vgaIdx], @byte[varScreenPtr][vgaIdx+varVgaCols], varWidth / 4 ) + vgaIdx += varVgaCols + bytefill( @byte[varScreenPtr][vgaIdx+1], 32, varWidth - 2 ) + varRowIdx-- + + +PUB Title( pTxtPtr ) | strIdx, vgaIdx + + if varTitle <> 0 + strIdx := strSize( pTxtPtr ) + vgaIdx := varRow * varVgaCols + varCol + varVgaCols +1 'title location + bytefill( @byte[varScreenPtr][vgaIdx], 32, varWidth - 2 ) + bytemove( @byte[varScreenPtr][vgaIdx], pTxtPtr, strIdx ) + + +PUB set_gzidx( gzidx ) + varGdx := gzidx + + +PUB get_gzidx + return varGdx + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/Timer.spin b/source/gui-demo/Timer.spin new file mode 100644 index 0000000..7be4a59 --- /dev/null +++ b/source/gui-demo/Timer.spin @@ -0,0 +1,128 @@ +'' =========================================================================== +'' VGA High-Res Text UI Elements Base UI Support Functions v1.2 +'' +'' FILE: Timer.spin +'' Author: Allen Marincak +'' Copyright (c) 2009 Allen MArincak +'' See end of file for terms of use +'' =========================================================================== +'' +'' ============================================================================ +'' Timer function +'' ============================================================================ +'' +'' Starts a new cog to provide timing functionality. +'' +'' There are 8 timers available, the code running on the new cog simply +'' decrements each timer value at the rate specified in the start() call. +'' +'' Only the first object to create this needs to call the START method. That +'' will launch it on a new COG, other objects can reference it in the OBJ +'' section but do not need to start it, just call the public methods. +'' ============================================================================ + + +DAT +' stack size determined StackLengthAJM.spin + cog long 0 'cog execute is running on + stack long 0,0,0,0,0,0,0,0,0,0,0,0 'stack space 12 longs + tmrs_used byte 0,0,0,0,0,0,0,0 'bitfield signifying timers in use + timers word 0,0,0,0,0,0,0,0 'timer values (16 bit) + + +PUB start( period ) : okay1 +'' +'' Launches TIMER TASK on new cog, returns 0 on error, else the cog that was +'' started up. +'' - period is the fractional part of a second to operate at +'' example: 10 = 1/10th of a second +'' 30 = 1/30th of a second +'' 100 = 1/100th of a second + + wordfill( @timers, 0, 8 ) + bytefill( @tmrs_used, 0, 8 ) + + cog := cognew( execute( period, @timers ), @stack ) + 1 + okay1 := cog + + +PUB register : new_tmr | idx +'' Register a timer. Returns the first free timer (8 timers numbered 0 to 7) +'' or returns -1 if none are available. + + repeat idx from 0 to 7 + if tmrs_used[idx] == 0 + tmrs_used[idx] := 1 + new_tmr := idx + return + + new_tmr := -1 + + +PUB unregister( tmrid ) +'' Unregisters a timer, frees it up for reuse. + + if tmrs_used[tmrid] == 1 + timers[tmrid] := 0 + tmrs_used[tmrid] := 0 + + +PUB set( tmrid, val ) +'' Sets a registered timer with a 16 bit value. + + if tmrs_used[tmrid] == 1 + timers[tmrid] := val + + +PUB isClr( tmrid ): clr +'' Checks the status of the registered timer. +'' returns 1 if it has expired (zeroed) +'' 0 if is still running + + clr := 0 + if timers[tmrid] == 0 + clr := 1 + + +PUB read( tmrid ) +'' returns the current count of a registered timer. + + return timers[tmrid] + + +PRI execute( period, ptr_tmrs ) | idx, interval_cnt, time_base +'' new cog executes this, it just decrements the timers + + interval_cnt := clkfreq / period + time_base := cnt + + repeat + waitcnt( time_base += interval_cnt ) + repeat idx from 0 to 7 + if word[ptr_tmrs][idx] <> 0 + word[ptr_tmrs][idx] -= 1 + + +{{ +┌────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy│ +│of this software and associated documentation files (the "Software"), to │ +│deal in the Software without restriction, including without limitation the │ +│rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│sell copies of the Software, and to permit persons to whom the Software is │ +│furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in │ +│all copies or substantial portions of the Software. │ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS│ +│IN THE SOFTWARE. │ +└────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/source/gui-demo/VGA_HiRes_Text.spin b/source/gui-demo/VGA_HiRes_Text.spin new file mode 100644 index 0000000..e3f19d8 --- /dev/null +++ b/source/gui-demo/VGA_HiRes_Text.spin @@ -0,0 +1,542 @@ +''*************************************** +''* VGA High-Res Text Driver v1.0 * +''* Author: Chip Gracey * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''*************************************** +'' +'' This object generates a 1024x768 VGA signal which contains 128 columns x 64 +'' rows of 8x12 characters. Each row can have a unique forground/background +'' color combination and each character can be inversed. There are also two +'' cursors which can be independently controlled (ie. mouse and keyboard). A +'' sync indicator signals each time the screen is refreshed (you may ignore). +'' +'' You must provide buffers for the screen, colors, cursors, and sync. Once +'' started, all interfacing is done via memory. To this object, all buffers are +'' read-only, with the exception of the sync indicator which gets written with +'' -1. You may freely write all buffers to affect screen appearance. Have fun! +'' + +CON + +'{' 1024 x 768 @ 57Hz settings: 128 x 64 characters + + hp = 1024 'horizontal pixels + vp = 768 'vertical pixels + hf = 16 'horizontal front porch pixels + hs = 96 'horizontal sync pixels + hb = 176 'horizontal back porch pixels + vf = 1 'vertical front porch lines + vs = 3 'vertical sync lines + vb = 28 'vertical back porch lines + hn = 1 'horizontal normal sync state (0|1) + vn = 1 'vertical normal sync state (0|1) + pr = 60 'pixel rate in MHz at 80MHz system clock (5MHz granularity) +'} + +{' 800 x 600 @ 75Hz settings: 100 x 50 characters + + hp = 800 'horizontal pixels + vp = 600 'vertical pixels + hf = 40 'horizontal front porch pixels + hs = 128 'horizontal sync pixels + hb = 88 'horizontal back porch pixels + vf = 1 'vertical front porch lines + vs = 4 'vertical sync lines + vb = 23 'vertical back porch lines + hn = 0 'horizontal normal sync state (0|1) + vn = 0 'vertical normal sync state (0|1) + pr = 50 'pixel rate in MHz at 80MHz system clock (5MHz granularity) +} + +{' 640 x 480 @ 69Hz settings: 80 x 40 characters + + hp = 640 'horizontal pixels + vp = 480 'vertical pixels + hf = 24 'horizontal front porch pixels + hs = 40 'horizontal sync pixels + hb = 128 'horizontal back porch pixels + vf = 9 'vertical front porch lines + vs = 3 'vertical sync lines + vb = 28 'vertical back porch lines + hn = 1 'horizontal normal sync state (0|1) + vn = 1 'vertical normal sync state (0|1) + pr = 30 'pixel rate in MHz at 80MHz system clock (5MHz granularity) +} + +' columns and rows + + cols = hp / 8 + rows = vp / 12 + + +VAR long cog[2] + +PUB start(BasePin, ScreenPtr, ColorPtr, CursorPtr, SyncPtr) : okay | i, j + +'' Start VGA driver - starts two COGs +'' returns false if two COGs not available +'' +'' BasePin = VGA starting pin (0, 8, 16, 24, etc.) +'' +'' ScreenPtr = Pointer to 8,192 bytes containing ASCII codes for each of the +'' 128x64 screen characters. Each byte's top bit controls color +'' inversion while the lower seven bits provide the ASCII code. +'' Screen memory is arranged left-to-right, top-to-bottom. +'' +'' screen byte example: %1_1000001 = inverse "A" +'' +'' ColorPtr = Pointer to 64 words which define the foreground and background +'' colors for each row. The lower byte of each word contains the +'' foreground RGB data for that row, while the upper byte +'' contains the background RGB data. The RGB data in each byte is +'' arranged as %RRGGBB00 (4 levels each). +'' +'' color word example: %%0020_3300 = gold on blue +'' +'' CursorPtr = Pointer to 6 bytes which control the cursors: +'' +'' bytes 0,1,2: X, Y, and MODE of cursor 0 +'' bytes 3,4,5: X, Y, and MODE of cursor 1 +'' +'' X and Y are in terms of screen characters +'' (left-to-right, top-to-bottom) +'' +'' MODE uses three bottom bits: +'' +'' %x00 = cursor off +'' %x01 = cursor on +'' %x10 = cursor on, blink slow +'' %x11 = cursor on, blink fast +'' %0xx = cursor is solid block +'' %1xx = cursor is underscore +'' +'' cursor example: 127, 63, %010 = blinking block in lower-right +'' +'' SyncPtr = Pointer to long which gets written with -1 upon each screen +'' refresh. May be used to time writes/scrolls, so that chopiness +'' can be avoided. You must clear it each time if you want to see +'' it re-trigger. + + 'if driver is already running, stop it + stop + + 'implant pin settings + reg_vcfg := $200000FF + (BasePin & %111000) << 6 + i := $FF << (BasePin & %011000) + j := BasePin & %100000 == 0 + reg_dira := i & j + reg_dirb := i & !j + + 'implant CNT value to sync COGs to + sync_cnt := cnt + $10000 + + 'implant pointers + longmove(@screen_base, @ScreenPtr, 3) + font_base := @font + + 'implant unique settings and launch first COG + vf_lines.byte := vf + vb_lines.byte := vb + font_third := 1 + cog[1] := cognew(@d0, SyncPtr) + 1 + + 'allow time for first COG to launch + waitcnt($2000 + cnt) + + 'differentiate settings and launch second COG + vf_lines.byte := vf+4 + vb_lines.byte := vb-4 + font_third := 0 + cog[0] := cognew(@d0, SyncPtr) + 1 + + 'if both COGs launched, return true + if cog[0] and cog[1] + return true + + 'else, stop any launched COG and return false + else + stop + + +PUB stop | i + +'' Stop VGA driver - frees two COGs + + repeat i from 0 to 1 + if cog[i] + cogstop(cog[i]~ - 1) + + +CON + + #1, scanbuff[128], scancode[128*2-1+3], maincode 'enumerate COG RAM usage + + main_size = $1F0 - maincode 'size of main program + + hv_inactive = (hn << 1 + vn) * $0101 'H,V inactive states + + +DAT + +'***************************************************** +'* Assembly language VGA high-resolution text driver * +'***************************************************** + +' This program runs concurrently in two different COGs. +' +' Each COG's program has different values implanted for front-porch lines and +' back-porch lines which surround the vertical sync pulse lines. This allows +' timed interleaving of their active display signals during the visible portion +' of the field scan. Also, they are differentiated so that one COG displays +' even four-line groups while the other COG displays odd four-line groups. +' +' These COGs are launched in the PUB 'start' and are programmed to synchronize +' their PLL-driven video circuits so that they can alternately prepare sets of +' four scan lines and then display them. The COG-to-COG switchover is seemless +' due to two things: exact synchronization of the two video circuits and the +' fact that all COGs' driven output states get OR'd together, allowing one COG +' to output lows during its preparatory state while the other COG effectively +' drives the pins to create the visible and sync portions of its scan lines. +' During non-visible scan lines, both COGs output together in unison. +' +' COG RAM usage: $000 = d0 - used to inc destination fields for indirection +' $001-$080 = scanbuff - longs which hold 4 scan lines +' $081-$182 = scancode - stacked WAITVID/SHR for fast display +' $183-$1EF = maincode - main program loop which drives display + + org 'set origin to $000 for start of program + +d0 long 1 << 9 'd0 always resides here at $000, executes as NOP + + +' Initialization code and data - after execution, space gets reused as scanbuff + + 'Move main program into maincode area + +:move mov $1EF,main_begin+main_size-1 + sub :move,d0s0 '(do reverse move to avoid overwrite) + djnz main_ctr,#:move + + 'Build scanbuff display routine into scancode + +:waitvid mov scancode+0,i0 'org scancode +:shr mov scancode+1,i1 'waitvid color,scanbuff+0 + add :waitvid,d1 'shr scanbuff+0,#8 + add :shr,d1 'waitvid color,scanbuff+1 + add i0,#1 'shr scanbuff+1,#8 + add i1,d0 '... + djnz scan_ctr,#:waitvid 'waitvid color,scanbuff+cols-1 + + mov scancode+cols*2-1,i2 'mov vscl,#hf + mov scancode+cols*2+0,i3 'waitvid hvsync,#0 + mov scancode+cols*2+1,i4 'jmp #scanret + + 'Init I/O registers and sync COGs' video circuits + + mov dira,reg_dira 'set pin directions + mov dirb,reg_dirb + movi frqa,#(pr / 5) << 2 'set pixel rate + mov vcfg,reg_vcfg 'set video configuration + mov vscl,#1 'set video to reload on every pixel + waitcnt sync_cnt,colormask 'wait for start value in cnt, add ~1ms + movi ctra,#%00001_110 'COGs in sync! enable PLLs now - NCOs locked! + waitcnt sync_cnt,#0 'wait ~1ms for PLLs to stabilize - PLLs locked! + mov vscl,#100 'insure initial WAITVIDs lock cleanly + + 'Jump to main loop + + jmp #vsync 'jump to vsync - WAITVIDs will now be locked! + + 'Data + +d0s0 long 1 << 9 + 1 +d1 long 1 << 10 +main_ctr long main_size +scan_ctr long cols + +i0 waitvid x,scanbuff+0 +i1 shr scanbuff+0,#8 +i2 mov vscl,#hf +i3 waitvid hvsync,#0 +i4 jmp #scanret + +reg_dira long 0 'set at runtime +reg_dirb long 0 'set at runtime +reg_vcfg long 0 'set at runtime +sync_cnt long 0 'set at runtime + + 'Directives + + fit scancode 'make sure initialization code and data fit +main_begin org maincode 'main code follows (gets moved into maincode) + + +' Main loop, display field - each COG alternately builds and displays four scan lines + +vsync mov x,#vs 'do vertical sync lines + call #blank_vsync + +vb_lines mov x,#vb 'do vertical back porch lines (# set at runtime) + call #blank_vsync + + mov screen_ptr,screen_base 'reset screen pointer to upper-left character + mov color_ptr,color_base 'reset color pointer to first row + mov row,#0 'reset row counter for cursor insertion + mov fours,#rows * 3 / 2 'set number of 4-line builds for whole screen + + 'Build four scan lines into scanbuff + +fourline mov font_ptr,font_third 'get address of appropriate font section + shl font_ptr,#7+2 + add font_ptr,font_base + + movd :pixa,#scanbuff-1 'reset scanbuff address (pre-decremented) + movd :pixb,#scanbuff-1 + + mov y,#2 'must build scanbuff in two sections because + mov vscl,vscl_line2x '..pixel counter is limited to twelve bits + +:halfrow waitvid underscore,#0 'output lows to let other COG drive VGA pins + mov x,#cols/2 '..for 2 scan lines, ready for half a row + +:column rdbyte z,screen_ptr 'get character from screen memory + ror z,#7 'get inverse flag into bit 0, keep chr high + shr z,#32-7-2 wc 'get inverse flag into c, chr into bits 8..2 + add z,font_ptr 'add font section address to point to 8*4 pixels + add :pixa,d0 'increment scanbuff destination addresses + add :pixb,d0 + add screen_ptr,#1 'increment screen memory address +:pixa rdlong scanbuff,z 'read pixel long (8*4) into scanbuff +:pixb if_nc xor scanbuff,longmask 'invert pixels according to inverse flag + djnz x,#:column 'another character in this half-row? + + djnz y,#:halfrow 'loop to do 2nd half-row, time for 2nd WAITVID + + sub screen_ptr,#cols 'back up to start of same row in screen memory + + 'Insert cursors into scanbuff + + mov z,#2 'ready for two cursors + +:cursor rdbyte x,cursor_base 'x in range? + add cursor_base,#1 + cmp x,#cols wc + + rdbyte y,cursor_base 'y match? + add cursor_base,#1 + cmp y,row wz + + rdbyte y,cursor_base 'get cursor mode + add cursor_base,#1 + + if_nc_or_nz jmp #:nocursor 'if cursor not in scanbuff, no cursor + + add x,#scanbuff 'cursor in scanbuff, set scanbuff address + movd :xor,x + + test y,#%010 wc 'get mode bits into flags + test y,#%001 wz + if_nc_and_z jmp #:nocursor 'if cursor disabled, no cursor + + if_c_and_z test slowbit,cnt wc 'if blink mode, get blink state + if_c_and_nz test fastbit,cnt wc + + test y,#%100 wz 'get box or underscore cursor piece + if_z mov x,longmask + if_nz mov x,underscore + if_nz cmp font_third,#2 wz 'if underscore, must be last font section + +:xor if_nc_and_z xor scanbuff,x 'conditionally xor cursor into scanbuff + +:nocursor djnz z,#:cursor 'second cursor? + + sub cursor_base,#3*2 'restore cursor base + + 'Display four scan lines from scanbuff + + rdword x,color_ptr 'get color pattern for current row + and x,colormask 'mask away hsync and vsync signal states + or x,hv 'insert inactive hsync and vsync states + + mov y,#4 'ready for four scan lines + +scanline mov vscl,vscl_chr 'set pixel rate for characters + jmp #scancode 'jump to scanbuff display routine in scancode +scanret mov vscl,#hs 'do horizontal sync pixels + waitvid hvsync,#1 '#1 makes hsync active + mov vscl,#hb 'do horizontal back porch pixels + waitvid hvsync,#0 '#0 makes hsync inactive + shr scanbuff+cols-1,#8 'shift last column's pixels right by 8 + djnz y,#scanline 'another scan line? + + 'Next group of four scan lines + + add font_third,#2 'if font_third + 2 => 3, subtract 3 (new row) + cmpsub font_third,#3 wc 'c=0 for same row, c=1 for new row + if_c add screen_ptr,#cols 'if new row, advance screen pointer + if_c add color_ptr,#2 'if new row, advance color pointer + if_c add row,#1 'if new row, increment row counter + djnz fours,#fourline 'another 4-line build/display? + + 'Visible section done, do vertical sync front porch lines + + wrlong longmask,par 'write -1 to refresh indicator + +vf_lines mov x,#vf 'do vertical front porch lines (# set at runtime) + call #blank + + jmp #vsync 'new field, loop to vsync + + 'Subroutine - do blank lines + +blank_vsync xor hvsync,#$101 'flip vertical sync bits + +blank mov vscl,hx 'do blank pixels + waitvid hvsync,#0 + mov vscl,#hf 'do horizontal front porch pixels + waitvid hvsync,#0 + mov vscl,#hs 'do horizontal sync pixels + waitvid hvsync,#1 + mov vscl,#hb 'do horizontal back porch pixels + waitvid hvsync,#0 + djnz x,#blank 'another line? +blank_ret +blank_vsync_ret ret + + 'Data + +screen_base long 0 'set at runtime (3 contiguous longs) +color_base long 0 'set at runtime +cursor_base long 0 'set at runtime + +font_base long 0 'set at runtime +font_third long 0 'set at runtime + +hx long hp 'visible pixels per scan line +vscl_line2x long (hp + hf + hs + hb) * 2 'total number of pixels per 2 scan lines +vscl_chr long 1 << 12 + 8 '1 clock per pixel and 8 pixels per set +colormask long $FCFC 'mask to isolate R,G,B bits from H,V +longmask long $FFFFFFFF 'all bits set +slowbit long 1 << 25 'cnt mask for slow cursor blink +fastbit long 1 << 24 'cnt mask for fast cursor blink +underscore long $FFFF0000 'underscore cursor pattern +hv long hv_inactive '-H,-V states +hvsync long hv_inactive ^ $200 '+/-H,-V states + + 'Uninitialized data + +screen_ptr res 1 +color_ptr res 1 +font_ptr res 1 + +x res 1 +y res 1 +z res 1 + +row res 1 +fours res 1 + + +' 8 x 12 font - characters 0..127 +' +' Each long holds four scan lines of a single character. The longs are arranged into +' groups of 128 which represent all characters (0..127). There are three groups which +' each contain a vertical third of all characters. They are ordered top, middle, and +' bottom. +' +' NOTE: MSB set inverts the character. i.e. $31 = a 1 $B1 = an inverted 1 +' +' char 0 = ← left arrow +' char 1 = → right arrow +' char 2 = ↑ up arrow +' char 3 = ↓ down arrow +' char 4 = empty circle bullet (radio button) +' char 5 = filled circle bullet (radio button) +' char 6 = empty square bullet (check box) +' char 7 = filled square bullet (check box) +' char 8 = ▶ right triangle bullet +' char 9 = • small bullet +' char 10 = ┌ top left corner (but curved) +' char 11 = ┐ top right corner (but curved) +' char 12 = └ bottom left corner (but curved) +' char 13 = ┘ bottom right corner (but curved) +' char 14 = ─ horizontal line +' char 15 = │ vertical line +' char 16 = ┬ top 'tee' +' char 17 = ┴ bottom 'tee' +' char 18 = ├ left 'tee' +' char 19 = ┤ right 'tee' +' char 20 = ┼ cross point + +font long + +long $0C080000,$30100000,$7E3C1800,$18181800,$81423C00,$99423C00,$8181FF00,$E7C3FF00 'top +long $1E0E0602,$1C000000,$00000000,$00000000,$18181818,$18181818,$00000000,$18181818 +long $00000000,$18181818,$18181818,$18181818,$18181818,$00FFFF00,$CC993366,$66666666 +long $AA55AA55,$0F0F0F0F,$0F0F0F0F,$0F0F0F0F,$0F0F0F0F,$00000000,$00000000,$00000000 +long $00000000,$3C3C1800,$77666600,$7F363600,$667C1818,$46000000,$1B1B0E00,$1C181800 +long $0C183000,$180C0600,$66000000,$18000000,$00000000,$00000000,$00000000,$60400000 +long $73633E00,$1E181000,$66663C00,$60663C00,$3C383000,$06067E00,$060C3800,$63637F00 +long $66663C00,$66663C00,$1C000000,$00000000,$18306000,$00000000,$180C0600,$60663C00 +long $63673E00,$66663C00,$66663F00,$63663C00,$66361F00,$06467F00,$06467F00,$63663C00 +long $63636300,$18183C00,$30307800,$36666700,$06060F00,$7F776300,$67636300,$63361C00 +long $66663F00,$63361C00,$66663F00,$66663C00,$185A7E00,$66666600,$66666600,$63636300 +long $66666600,$66666600,$31637F00,$0C0C3C00,$03010000,$30303C00,$361C0800,$00000000 +long $0C000000,$00000000,$06060700,$00000000,$30303800,$00000000,$0C6C3800,$00000000 +long $06060700,$00181800,$00606000,$06060700,$18181E00,$00000000,$00000000,$00000000 +long $00000000,$00000000,$00000000,$00000000,$0C080000,$00000000,$00000000,$00000000 +long $00000000,$00000000,$00000000,$18187000,$18181800,$18180E00,$73DBCE00,$18180000 + +long $080C7E7E,$10307E7E,$18181818,$7E181818,$81818181,$99BDBDBD,$81818181,$E7BD99BD 'middle +long $1E3E7E3E,$1C3E3E3E,$30F0C000,$0C0F0300,$00C0F030,$00030F0C,$00FFFF00,$18181818 +long $18FFFF00,$00FFFF18,$18F8F818,$181F1F18,$18FFFF18,$00FFFF00,$CC993366,$66666666 +long $AA55AA55,$FFFF0F0F,$F0F00F0F,$0F0F0F0F,$00000F0F,$FFFF0000,$F0F00000,$0F0F0000 +long $00000000,$0018183C,$00000033,$7F363636,$66603C06,$0C183066,$337B5B0E,$0000000C +long $0C060606,$18303030,$663CFF3C,$18187E18,$00000000,$00007E00,$00000000,$060C1830 +long $676F6B7B,$18181818,$0C183060,$60603860,$307F3336,$60603E06,$66663E06,$0C183060 +long $66763C6E,$60607C66,$1C00001C,$00001C1C,$180C060C,$007E007E,$18306030,$00181830 +long $033B7B7B,$66667E66,$66663E66,$63030303,$66666666,$06263E26,$06263E26,$63730303 +long $63637F63,$18181818,$33333030,$36361E36,$66460606,$63636B7F,$737B7F6F,$63636363 +long $06063E66,$7B636363,$66363E66,$66301C06,$18181818,$66666666,$66666666,$366B6B63 +long $663C183C,$18183C66,$43060C18,$0C0C0C0C,$30180C06,$30303030,$00000063,$00000000 +long $0030381C,$333E301E,$6666663E,$0606663C,$3333333E,$067E663C,$0C0C3E0C,$3333336E +long $66666E36,$1818181C,$60606070,$361E3666,$18181818,$6B6B6B3F,$6666663E,$6666663C +long $6666663B,$3333336E,$066E7637,$300C663C,$0C0C0C7E,$33333333,$66666666,$6B6B6363 +long $1C1C3663,$66666666,$0C30627E,$180C060C,$18181818,$18306030,$00000000,$0018187E + +long $00000000,$00000000,$00001818,$0000183C,$00003C42,$00003C42,$0000FF81,$0000FFC3 'bottom +long $0002060E,$00000000,$18181818,$18181818,$00000000,$00000000,$00000000,$18181818 +long $18181818,$00000000,$18181818,$18181818,$18181818,$00FFFF00,$CC993366,$66666666 +long $AA55AA55,$FFFFFFFF,$F0F0F0F0,$0F0F0F0F,$00000000,$FFFFFFFF,$F0F0F0F0,$0F0F0F0F +long $00000000,$00001818,$00000000,$00003636,$0018183E,$00006266,$00006E3B,$00000000 +long $00003018,$0000060C,$00000000,$00000000,$0C181C1C,$00000000,$00001C1C,$00000103 +long $00003E63,$00007E18,$00007E66,$00003C66,$00007830,$00003C66,$00003C66,$00000C0C +long $00003C66,$00001C30,$0000001C,$0C181C1C,$00006030,$00000000,$0000060C,$00001818 +long $00003E07,$00006666,$00003F66,$00003C66,$00001F36,$00007F46,$00000F06,$00007C66 +long $00006363,$00003C18,$00001E33,$00006766,$00007F66,$00006363,$00006363,$00001C36 +long $00000F06,$00603C36,$00006766,$00003C66,$00003C18,$00003C66,$0000183C,$00003636 +long $00006666,$00003C18,$00007F63,$00003C0C,$00004060,$00003C30,$00000000,$FFFF0000 +long $00000000,$00006E33,$00003B66,$00003C66,$00006E33,$00003C66,$00001E0C,$1E33303E +long $00006766,$00007E18,$3C666660,$00006766,$00007E18,$00006B6B,$00006666,$00003C66 +long $0F063E66,$78303E33,$00000F06,$00003C66,$0000386C,$00006E33,$0000183C,$00003636 +long $00006336,$1C30607C,$00007E46,$00007018,$00001818,$00000E18,$00000000,$0000007E + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/gui-demo/vga-vid.spin b/source/gui-demo/vga-vid.spin new file mode 100644 index 0000000..6626d01 --- /dev/null +++ b/source/gui-demo/vga-vid.spin @@ -0,0 +1,545 @@ +{{ +┌────────────────────────────────────────┬────────────────┬────────────────────────┬──────────────────┐ +│ VGA 1024x768 Tile Driver v0.9 │ by Chip Gracey │ (C)2006 Parallax, Inc. │ 11 November 2006 │ +├────────────────────────────────────────┴────────────────┴────────────────────────┴──────────────────┤ +│ │ +│ This object generates a 1024x768 VGA display from a 64x48 array of 16x16-pixel 4-color tiles. │ +│ It requires two cogs (or three with optional cursor enabled) and at least 80 MHz. │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +}} +CON + +' 1024 x 768 @ 60Hz settings + + hp = 1024 'horizontal pixels + vp = 768 'vertical pixels + hf = 16 'horizontal front porch pixels + hs = 96 'horizontal sync pixels + hb = 176 'horizontal back porch pixels + vf = 1 'vertical front porch lines + vs = 3 'vertical sync lines + vb = 28 'vertical back porch lines + pr = 60 'pixel rate in MHz at 80MHz system clock (5MHz granularity) + + ht = hp + hf + hs + hb 'total scan line pixels + +' Tile array + + xtiles = hp / 16 + ytiles = vp / 16 + + COLS = 64 + ROWS = 48 + +VAR + + long cog[3] + + long dira_ '9 contiguous longs + long dirb_ + long vcfg_ + long cnt_ + long array_ptr_ + long color_ptr_ + long cursor_ptr_ + long sync_ptr_ + long mode_ + + +PUB start(base_pin, array_ptr, color_ptr, cursor_ptr, sync_ptr, mode) : okay | i, j + +'' Start driver - starts two or three cogs +'' returns false if cogs not available +'' +'' base_pin = First of eight VGA pins, must be a multiple of eight (0, 8, 16, 24, etc): +'' +'' 240Ω 240Ω 240Ω 240Ω +'' +7 ───┳─ Red +5 ───┳─ Green +3 ───┳─ Blue +1 ── H +'' 470Ω │ 470Ω │ 470Ω │ 240Ω +'' +6 ───┘ +4 ───┘ +2 ───┘ +0 ── V +'' +'' array_ptr = Pointer to 3,072 long-aligned words, organized as 64 across by 48 down, +'' which will serve as the tile array. Each word specifies a tile bitmap and +'' a color palette for its tile area. The top 10 bits of each word form the +'' base address of a 16-long tile bitmap, while the lower 6 bits select a +'' color palette for the bitmap. For example, $B2E5 would specify the tile +'' bitmap spanning $B2C0..$B2FF and color palette $25. +'' +'' color_ptr = Pointer to 64 longs which will define the 64 color palettes. The RGB data +'' in each long is arranged as %%RGBx_RGBx_RGBx_RGBx with the sub-bytes 3..0 +'' providing the color data for pixel values %11..%00, respectively: +'' +'' %%3330_0110_0020_3300: %11=white, %10=dark cyan, %01=blue, %00=gold +'' +'' cursor_ptr = Pointer to 4 longs which will control the cursor, or 0 to disable the +'' cursor. If a pointer is given, an extra cog will be started to generate +'' the cursor overlay. Here are the 4 longs that control the cursor: +'' +'' cursor_x - X position of cursor: ..0..1023.. (left to right) +'' cursor_y - Y position of cursor: ..0..767.. (bottom to top) +'' +'' cursor_color - Cursor color to be OR'd to background color as %%RGBx: +'' %%3330=white, %%2220 or %%1110=translucent, %%0000=off +'' +'' cursor_shape - 0 for arrow, 1 for crosshair, or pointer to a cursor +'' definition. A cursor definition consists of 32 longs +'' containing a 32x32 pixel cursor image, followed by two +'' bytes which define the X and Y center-pixel offsets +'' within the image. +'' +'' sync_ptr = Pointer to a long which will be set to -1 after each refresh, or 0 to +'' disable this function. This is useful in advanced applications where +'' awareness of display timing is important. +'' +'' mode = 0 for normal 16x16 pixel tiles or 1 for taller 16x32 pixel tiles. Mode 1 +'' is useful for displaying the internal font while requiring half the array +'' memory; however, the 3-D bevel characters will not be usable because of +'' the larger vertical tile granularity of this mode. + + 'If driver is already running, stop it + stop + + 'Ready i/o settings + i := $FF << (base_pin & %011000) + j := base_pin & %100000 == 0 + dira_ := i & j + dirb_ := i & !j + vcfg_ := $300000FF + (base_pin & %111000) << 6 + + 'Ready cnt value to sync cogs by + cnt_ := cnt + $100000 + + 'Ready pointers and mode + longmove(@array_ptr_, @array_ptr, 5) + + 'Launch cogs, abort if error + repeat i from 0 to 2 + if i == 2 'cursor cog? + ifnot cursor_ptr 'cursor enabled? + quit 'if not, quit loop + waitcnt($2000 + cnt) 'cursor cog, allow prior cog to launch + vcfg_ ^= $10000000 'set two-color mode + array_ptr_~ 'flag cursor function + ifnot cog[i] := cognew(@entry, @dira_ + i << 15) + 1 + stop + return {false} + + 'Successful + return true + + +PUB stop | i + +'' Stop driver - frees cogs + + 'If already running, stop any VGA cogs + repeat i from 0 to 2 + if cog[i] + cogstop(cog[i]~ - 1) + + +DAT + +' ┌─────────────────────────────┐ +' │ Initialization - all cogs │ +' └─────────────────────────────┘ + + org + +' Move field loop into position + +entry mov field,field_code + add entry,d0s0_ + djnz regs,#entry + +' Acquire settings + + mov regs,par 'dira_ ─ dira + cmpsub regs,bit15 wc 'dirb_ ─ dirb +:next movd :read,sprs 'vcfg_ ─ vcfg + or :read,d8_d4 'cnt_ ─ cnt + shr sprs,#4 'array_ptr_ ─ ctrb +:read rdlong dira,regs 'color_ptr_ ─ frqb + add regs,#4 'cursor_ptr_ ─ vscl + tjnz sprs,#:next 'sync_ptr_ ─ phsb + + sumc vf_lines,#2 'alter scan line settings by cog + sumnc vb_lines,#2 + sumnc tile_line,#2 * 4 + + rdlong regs,regs wz 'if mode not 0, set tile size to 16 x 32 pixels + if_nz movs tile_bytes,#32 * 4 + if_nz shr array_bytes,#1 + + mov regs,vscl 'save cursor pointer + +' Synchronize all cogs' video circuits so that waitvid's will be pixel-locked + + movi frqa,#(pr / 5) << 2 'set pixel rate (VCO runs at 2x) + mov vscl,#1 'set video shifter to reload on every pixel + waitcnt cnt,d8_d4 'wait for sync count, add ~3ms - cogs locked! + movi ctra,#%00001_110 'enable PLLs now - NCOs locked! + waitcnt cnt,#0 'wait ~3ms for PLLs to stabilize - PLLs locked! + mov vscl,#100 'subsequent WAITVIDs will now be pixel-locked! + +' Determine if this cog is to perform one of two field functions or the cursor function + + tjnz ctrb,#vsync 'if array ptr, jump to field function + 'else, cursor function follows + +' ┌─────────────────────────┐ +' │ Cursor Loop - one cog │ +' └─────────────────────────┘ + +' Do vertical sync lines minus three + +cursor mov par,#vf + vs + vb - 6 + +:loop mov vscl,vscl_line +:vsync waitvid ccolor,#0 + djnz par,#:vsync + +' Do three lines minus horizontal back porch pixels to buy a big block of time + + mov vscl,vscl_three_lines_mhb + waitvid ccolor,#0 + +' Get cursor data + + rdlong cx,regs 'get cursor x + add regs,#4 + rdlong cy,regs 'get cursor y + add regs,#4 + rdlong ccolor,regs 'get cursor color + add regs,#4 + rdlong cshape,regs 'get cursor shape + sub regs,#3 * 4 + + and ccolor,#$FC 'trim and justify cursor color + shl ccolor,#8 + +' Build cursor pixels + + mov par,#32 'ready for 32 cursor segments + movd :pix,#cpix + mov cnt,cshape + +:pixloop cmp cnt,#1 wc, wz 'arrow, crosshair, or custom cursor? + if_a jmp #:custom + if_e jmp #:crosshair + + cmp par,#32 wz 'arrow + cmp par,#32-21 wc + if_z mov cseg,h80000000 + if_nz_and_nc sar cseg,#1 + if_nz_and_c shl cseg,#2 + mov coff,#0 + jmp #:pix + +:crosshair cmp par,#32-15 wz 'crosshair + if_ne mov cseg,h00010000 + if_e neg cseg,#2 + cmp par,#1 wz + if_e mov cseg,#0 + mov coff,h00000F0F + jmp #:pix + +:custom rdlong cseg,cshape 'custom + add cshape,#4 + rdlong coff,cshape + +:pix mov cpix,cseg 'save segment into pixels + add :pix,d0 + + djnz par,#:pixloop 'another segment? + +' Compute cursor position + + mov cseg,coff 'apply cursor center-pixel offsets + and cseg,#$FF + sub cx,cseg + shr coff,#8 + and coff,#$FF + add cy,coff + + cmps cx,neg31 wc 'if x out of range, hide cursor via y + if_nc cmps pixels_m1,cx wc + if_c neg cy,#1 + + mov cshr,#0 'adjust for left-edge clipping + cmps cx,#0 wc + if_c neg cshr,cx + if_c mov cx,#0 + + mov cshl,#0 'adjust for right-edge clipping + cmpsub cx,pixels_m32 wc + if_c mov cshl,cx + if_c mov cx,pixels_m32 + + add cx,#hb 'bias x and y for display + sub cy,lines_m1 + +' Do visible lines with cursor + + mov par,lines 'ready for visible scan lines + +:line andn cy,#$1F wz, nr 'check if scan line in cursor range + + if_z movs :seg,cy 'if in range, get cursor pixels + if_z add :seg,#cpix + if_nz mov cseg,#0 'if out of range, use blank pixels +:seg if_z mov cseg,cpix + if_z rev cseg,#0 'reverse pixels so they map sensibly + if_z shr cseg,cshr 'perform any edge clipping on pixels + if_z shl cseg,cshl + + mov vscl,cx 'do left blank pixels (hb+cx) + waitvid ccolor,#0 + + mov vscl,vscl_cursor 'do cursor pixels (32) + waitvid ccolor,cseg + + mov vscl,vscl_line_m32 'do right blank pixels (hp+hf+hs-32-cx) + sub vscl,cx + waitvid ccolor,#0 + + add cy,#1 'another scan line? + djnz par,#:line + +' Do horizontal back porch pixels and loop + + mov vscl,#hb + waitvid ccolor,#0 + + mov par,#vf + vs + vb - 3 'ready to do vertical sync lines + jmp #:loop + +' Cursor data + +vscl_line long ht 'total pixels per scan line +vscl_three_lines_mhb long ht * 3 - hb 'total pixels per three scan lines minus hb +vscl_line_m32 long ht - 32 'total pixels per scan line minus 32 +vscl_cursor long 1 << 12 + 32 '32 pixels per cursor with 1 clock per pixel +lines long vp 'visible scan lines +lines_m1 long vp - 1 'visible scan lines minus 1 +pixels_m1 long hp - 1 'visible pixels minus 1 +pixels_m32 long hp - 32 'visible pixels minus 32 +neg31 long -31 + +h80000000 long $80000000 'arrow/crosshair cursor data +h00010000 long $00010000 +h00000F0F long $00000F0F + +' Initialization data + +d0s0_ long 1 << 9 + 1 'd and s field increments +regs long $1F0 - field 'number of registers in field loop space +sprs long $DFB91E76 'phsb/vscl/frqb/ctrb/cnt/vcfg/dirb/dira nibbles +bit15 long $8000 'bit15 mask used to differentiate cogs in par +d8_d4 long $0003E000 'bit8..bit4 mask for d field + +field_code 'field loop code begins at this offset + +' Undefined cursor data + +cx res 1 +cy res 1 +ccolor res 1 +cshape res 1 +coff res 1 +cseg res 1 +cshr res 1 +cshl res 1 +cpix res 32 + + +' ┌─────────────────────────┐ +' │ Field Loop - two cogs │ +' └─────────────────────────┘ + + org + +' Allocate buffers + +palettes res 64 'palettes of colors +colors res xtiles 'colors for tile row +pixels0 res xtiles 'pixels for tile row line +0 +pixels1 res xtiles 'pixels for tile row line +1 +pixels2 res xtiles 'pixels for tile row line +2 +pixels3 res xtiles 'pixels for tile row line +3 + +' Each cog alternately builds and displays four scan lines + +field mov cnt,#ytiles * 4 / 2 'ready number of four-scan-line builds/displays + +' Build four scan lines + +build_4y movd col0,#colors+0 'reset pointers for scan line buffers + movd col1,#colors+1 + movd pix0,#pixels0+0 + movd pix1,#pixels1+0 + movd pix2,#pixels2+0 + movd pix3,#pixels3+0 + movd pix4,#pixels0+1 + movd pix5,#pixels1+1 + movd pix6,#pixels2+1 + movd pix7,#pixels3+1 + + mov ina,#2 'four scan lines require two waitvid's + +build_32x mov vscl,vscl_two_lines 'output lows for two scan lines so other cog +:zero waitvid :zero,#0 '..can display while this cog builds (twice) + + mov inb,#xtiles / 2 / 2 'build four scan lines for half a row + +build_2x rdlong vscl,ctrb 'get pair of words from the tile array + + movs col0,vscl 'get color bits from even tile + andn col0,#$1C0 + + andn vscl,#$3F 'strip color bits and add tile line offset + add vscl,tile_line + +col0 mov colors+0,palettes 'get even tile color + add col0,d1 + +pix0 rdlong pixels0+0,vscl 'get line +0 even tile pixels + add pix0,d1 + add vscl,#4 + +pix1 rdlong pixels1+0,vscl 'get line +1 even tile pixels + add pix1,d1 + add vscl,#4 + +pix2 rdlong pixels2+0,vscl 'get line +2 even tile pixels + add pix2,d1 + add vscl,#4 + +pix3 rdlong pixels3+0,vscl 'get line +3 even tile pixels + add pix3,d1 + + add ctrb,#2 * 2 'point to next pair of tile words + shr vscl,#16 'shift odd tile word into position + + movs col1,vscl 'get color bits from odd tile + andn col1,#$1C0 + + andn vscl,#$3F 'strip color bits and add tile line offset + add vscl,tile_line + +col1 mov colors+1,palettes 'get odd tile color + add col1,d1 + +pix4 rdlong pixels0+1,vscl 'get line +0 odd tile pixels + add pix4,d1 + add vscl,#4 + +pix5 rdlong pixels1+1,vscl 'get line +1 odd tile pixels + add pix5,d1 + add vscl,#4 + +pix6 rdlong pixels2+1,vscl 'get line +2 odd tile pixels + add pix6,d1 + add vscl,#4 + +pix7 rdlong pixels3+1,vscl 'get line +3 odd tile pixels + add pix7,d1 + djnz inb,#build_2x 'loop for next tile pair (48 inst/loop) + + djnz ina,#build_32x 'if first half done, loop for 2nd waitvid + + sub ctrb,#xtiles * 2 'back up to start of same row + +' Display four scan lines + + mov inb,#4 'ready for four scan lines + movs :waitvid,#pixels0 'reset waitvid pixel pointer + +:line mov ina,#xtiles 'ready for tiles + movd :waitvid,#colors 'reset waitvid color pointer + mov vscl,vscl_tile 'set pixel rate for tiles + +:tile cmp ina,#1 wz 'check if last tile + add :waitvid,d0s0 'advance pointers (waitvid already read) +:waitvid waitvid colors,pixels0 'do tile slice + if_nz djnz ina,#:tile 'strange loop allows hsync timing and ina=1 + + call #hsync 'do horizontal sync (ina=1) + + djnz inb,#:line 'another scan line? + +' Another four scan lines? + + add tile_line,#8 * 4 'advance eight scan lines within tile row +tile_bytes cmpsub tile_line,#16 * 4 wc 'tile row done? (# doubled for mode 1) + if_c add ctrb,#xtiles * 2 'if done, advance array pointer to next row + + djnz cnt,#build_4y 'another four scan lines? + + sub ctrb,array_bytes 'display done, reset array pointer to top row + +' Visible section done, handle sync indicator + + cmp cnt,phsb wz 'sync enabled? (cnt=0) + if_nz wrlong neg1,phsb 'if so, write -1 to sync indicator + +' Do vertical sync lines and loop + +vf_lines mov ina,#vf + 2 'do vertical front porch lines (adjusted ±2) + call #blank + +vsync mov ina,#vs 'do vertical sync lines + call #blank_vsync + +vb_lines mov ina,#vb - 2 'do vertical back porch lines (adjusted ±2) + movs blank_vsync_ret,#field '(loop to field, blank_vsync follows) + +' Subroutine - do blank lines + +blank_vsync xor hv_sync,#$0101 'flip vertical sync bits + +blank mov vscl,vscl_blank 'do horizontal blank pixels + waitvid hv_sync,#0 + +hsync mov vscl,#hf 'do horizontal front porch pixels + waitvid hv_sync,#0 + + mov vscl,#hs 'do horizontal sync pixels + waitvid hv_sync,#1 + + rdlong vscl,frqb 'update another palette + and vscl,color_mask +:palette mov palettes,vscl + add :palette,d0 + add frqb,#4 + add par,count_64 wc + if_c movd :palette,#palettes + if_c sub frqb,#64 * 4 + + mov vscl,#hb 'do horizontal back porch pixels + waitvid hv_sync,#0 + + djnz ina,#blank 'another blank line? +hsync_ret +blank_ret +blank_vsync_ret ret + +' Data + +d0s0 long 1 << 9 + 1 'd and s field increments +d0 long 1 << 9 'd field increment +d1 long 2 << 9 'd field double increment + +tile_line long 2 * 4 'tile line offset (adjusted ±2 * 4) +array_bytes long xtiles * ytiles * 2 'number of bytes in tile array + +vscl_two_lines long ht * 2 'total pixels per two scan lines +vscl_tile long 1 << 12 + 16 '16 pixels per tile with 1 clock per pixel +vscl_blank long hp 'visible pixels per scan line + +hv_sync long $0200 '+/-H,-V states +count_64 long $04000000 'addend that sets carry every 64th addition +color_mask long $FCFCFCFC 'mask to isolate R,G,B bits from H,V +neg1 long $FFFFFFFF 'negative 1 to be written to sync indicator diff --git a/source/plexbus/joystick.spin b/source/plexbus/joystick.spin new file mode 100644 index 0000000..a715332 --- /dev/null +++ b/source/plexbus/joystick.spin @@ -0,0 +1,62 @@ +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle │ +│ Copyright (c) 2010 Ingo Kripahle │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : TriOS +Name : +Chip : Regnatix +Typ : Programm +Version : +Subversion : +Funktion : +Komponenten : - +COG's : - +Logbuch : +Kommandoliste : +Notizen : + +}} + +OBJ + ios: "reg-ios" + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + +PUB main | j + + ios.start + repeat + ios.printbin(ios.joy,8) + ios.printnl + until ios.key + ios.stop + +DAT + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/plexbus/map.spin b/source/plexbus/map.spin new file mode 100644 index 0000000..811fddc --- /dev/null +++ b/source/plexbus/map.spin @@ -0,0 +1,91 @@ +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle │ +│ Copyright (c) 2010 Ingo Kripahle │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : TriOS +Name : +Chip : Regnatix +Typ : Programm +Version : +Subversion : +Funktion : +Komponenten : - +COG's : - +Logbuch : +Kommandoliste : +Notizen : + +}} + +OBJ + ios: "reg-ios" + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + +PUB main | ack,adr,n + +' anzeige einer echtzeitkarte aller 128 slaves am bus +' praktisch um die bausteine zu korrekt zu jumpern + + ios.start + ios.plxHalt + + n := 0 + ios.curoff + ios.printcls + repeat + ios.curhome + ios.printnl + ios.print(string(" 0123456789ABCDEF")) + ios.printnl + repeat adr from 0 to 127 + + 'ios.plxstart + 'ack := ios.plxwrite(adr<<1) + 'ios.plxstop + ack := ios.plxping(adr) + + if n == 0 + ios.printhex(adr,2) + ios.printchar(" ") + if ack + ios.printqchar("┼") + else + ios.printqchar("•") + if n++ == 15 + ios.printnl + n := 0 + until ios.key + ios.stop + + + +DAT + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/plexbus/paddle.spin b/source/plexbus/paddle.spin new file mode 100644 index 0000000..e66b6dc --- /dev/null +++ b/source/plexbus/paddle.spin @@ -0,0 +1,72 @@ +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle │ +│ Copyright (c) 2010 Ingo Kripahle │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : TriOS +Name : +Chip : Regnatix +Typ : Programm +Version : +Subversion : +Funktion : +Komponenten : - +COG's : - +Logbuch : +Kommandoliste : +Notizen : + +}} + +OBJ + ios: "reg-ios" + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + +PUB main | n,p + + ios.start + repeat + ' paddle-wert einlesen + ' obere 8 bit = digtale kontakte + ' untere 8 bit = analogwert + n := ios.paddle + ' gesamtwert anzeigen + ios.printhex(n,4) + 'analogwert abtrenne + p := n & $0ff + 'balken ausgeben + repeat p/5 + ios.printchar("*") + ios.printnl + until ios.key + ios.stop + +DAT + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/plexbus/port-io.spin b/source/plexbus/port-io.spin new file mode 100644 index 0000000..9972f9d --- /dev/null +++ b/source/plexbus/port-io.spin @@ -0,0 +1,83 @@ +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle │ +│ Copyright (c) 2010 Ingo Kripahle │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : TriOS +Name : +Chip : Regnatix +Typ : Programm +Version : +Subversion : +Funktion : +Komponenten : - +COG's : - +Logbuch : +Kommandoliste : +Notizen : + +}} + +OBJ + ios: "reg-ios" + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + +PUB main | n + +' demo für die direkte ansteuerung der ports +' es wird port 1 eingelesen und an port 3 an den angeschlossenen +' led's ausgegeben/angezeigt + + ios.start + + 'poller in administra anhalten und i2c-bus anfordern + ios.plxHalt + + repeat + 'port auslesen + n := ios.plxIn($20) 'pcf8574 + + 'port aus led's ausgeben + ios.plxOut($22,n) 'pcf8574 + + 'zusätzlich den wert auf dem screen ausgeben + ios.printbin(n,8) + ios.printnl + + until ios.key + ios.stop + + 'wenn nach den i2c-operationen weiter automatisch joysticks etc. + 'abgefragt werden, muss der bus wieder freigegeben werden + ios.plxRun + + +DAT + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/plexbus/port-test.spin b/source/plexbus/port-test.spin new file mode 100644 index 0000000..7d54048 --- /dev/null +++ b/source/plexbus/port-test.spin @@ -0,0 +1,112 @@ +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle │ +│ Copyright (c) 2010 Ingo Kripahle │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : TriOS +Name : +Chip : Regnatix +Typ : Programm +Version : +Subversion : +Funktion : +Komponenten : - +COG's : - +Logbuch : +Kommandoliste : +Notizen : + +}} + +OBJ + ios: "reg-ios" + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + +PUB main | p1,p2,p3,a0,a1,a2,a3 + + ios.start + ios.plxHalt + + ios.curoff + ios.printcls + repeat + ios.curhome + ios.printnl + p1 := ios.plxIn($20) + p2 := ios.plxIn($21) + p3 := ios.plxIn($22) + a0 := ios.plxch($48,0) + a1 := ios.plxch($48,1) + a2 := ios.plxch($48,2) + a3 := ios.plxch($48,3) + print_port(1,p1) + print_port(2,p2) + print_port(3,p3) + ios.printnl + print_chan(0,a0) + print_chan(1,a1) + print_chan(2,a2) + print_chan(3,a3) + ios.plxout($3A,!(cnt>>23)) + until ios.key + ios.stop + +PRI print_chan(cnr,wert) + + ios.print(string("A/D ")) + ios.printdec(cnr) + ios.printchar(" ") + ios.printhex(wert,2) + ios.printchar(" ") + ios.printchar("[") + repeat wert>>3 + ios.printqchar("‣") + repeat (255-wert)>>3 + ios.printqchar(" ") + ios.printchar("]") + ios.printnl + +PRI print_port(pnr,wert) + + ios.print(string("Port ")) + ios.printdec(pnr) + ios.printchar(" ") + + repeat 8 + if wert & 1 + ios.printqchar("‣") + else + ios.printqchar(" ") + wert := wert >> 1 + ios.printnl + + +DAT + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/source/sid/1startrk.dmp b/source/sid/1startrk.dmp new file mode 100644 index 0000000000000000000000000000000000000000..21c103692bbaee52a549a556db6046ebd11d1944 GIT binary patch literal 57500 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 literal 0 HcmV?d00001 diff --git a/source/sid/2startrk.dmp b/source/sid/2startrk.dmp new file mode 100644 index 0000000000000000000000000000000000000000..683fc7bdb3d8f1a83780049b2528ab65d3f6c915 GIT binary patch literal 448750 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

fheR9dv4=EUL<1h5FT>+|~=f z5$ad}_BQ5~Pl?PG{M8@d=27t^uE08T{pzc?t=pgCx$^o|*7m;dzY6uMtnHzGm9;(e zi)L-VH0AZHtnK0bS7iooUcV|c==H0RL7oz?Uxf_nTxtC(WRT~|>sOjVIj>OJ2GEEKwQvC7)eEV6=loHq4**1^ zy&p~noI3+RR9CxhL*^CEodF;!?fuXZ=a0h406RbGRZLGUWC!N9A$ z1_Q718VtPBHK<;6kbO|sV7(9O8dP4{I|slLU4yziU)|^XpsqpPox?t;Z1)|fPhVfy z2d@qFOpuX}un&Gq*iO?9^|5yjAcHRm+a2(JhHnbn6`*~PgsjV;M)N5YEhw=N6tDrArW*Q=00o-5a@?5`|?H{w;9LFZM-Ak-&xS!90|x-3ElF(U9v zGpO{jsBXloat+FrSN_W8s`6KIn!&z3`>Vh!C|r0y!=Uwt#c?t_E(pt`sh{Y zzsml~)Ti=Sq5mrTtI&Uy{gr8Ar=l)PlEI2smO<=;c@0_yD}QC3nc!7kgY1woyU?q= z1}%fotL(2VgV2(^4_XERWPfED1d#oebsGZ6{>nNt0c3w=83dqgSG^h}gV|q&3}$~7 zIy1As3Z0qRU)34(s3e1xURegKeb6#k`72w4Sw$gBwg#(x&@u>KmC7J%5R=$9gQYUa z8pMcF8N^>nK1dnVA=?@Rpvs^QIb=}Rp!zGm9|RMtsKWC&x(0P04A0}_{`T-Zj;=wy z9}Lgq9tREI>B$%Gzsk=|gy(Va8{z&VJdcx~n+VV2=-Gtol|@zQ z6+Mqb5)Qq}y&uF90-#sLgMnJFfS*ocI^eXqa5Ar;YcKkZ8q_zYm zYR9kotMEKd?r#szR1jlBybbm3y`=D)1`zY+F>|Rer{VpTWX!fLHmMIExCr(zA)ER~XUOD*>KUdS6H{VMmjv-(I>k8E2Avc&g;0Klt8f4fBWj&18<#OZmjU+EgW z1zzPfXc+{r@*3njdi*$emDeEe&H%uxyap|U;8i{iw+uFTB^5{j`81q05x)T-pN7*q zSC-_{aMoQJkx#=<&r1gJr}9bMAQ@av708f5?cSkjXZ7(4aPaTt9Ay#_K?A>S0RI0uR;d1UX^oI>Xp{7Ohutrstn4+z^l-`!*hlHT}%mhrOKd_ z81yRl^##3p-AKG2M1Nwtesy}@>sQl~LEQ&AD%reVH?lx0O83Ew$NhY8PtvQ4$Gt9k z*T_1JP`#pEjkay@YJNLMr5U8BObMuzfp#?lpjU6%-b>5b?+53%b5uGd7mv%C7nI@9 z_7xj}NxXR6&j;tWb5uTw;!Cg(>RfqLeV%tFCTdjYk4ArGZV9}ambuc@UOevSgZK@L z3iP>n+|LL1$Xw|Nd!`yusfs$#r@x(7E#?ZmDpl0|RZvl$5}7OTDyS#}?yrK10q{PugVO%zX}=TDe?2ckU^a*`f=!0$RN*^pAY7Jkk=rVbA#=EKA1B|SFXEr$RNv- zC_}Xm(mjs)tB^q^ock-yp!2HN_E)S72p7D{YtW*?KA6`a=K~|atGos+D(r)K4PHDh zQGr*VPW=ovR}EgtTmd*eAN`d@1>m!CMz6f$duK!i0KY+NFy0ReuqtQts#g@1?wk$1 z!iYu&{d};TEB9CBT)Dq0=gR$6Ialtl4y5;sqEEklKFE57aKZLBZS9rn0|0t;VCzBv zxPAoyyt+HRUz8M1dG*Gv>tV#j+a>it>u`eK4=Vz^l9l1Fv)q zM&B+Xll3ap8FUT${h*)Y=o(aBoj>aPpsJ|q)%l~o59%6Jy$W>(WxMV;?D`N@#VgZY zWbm`;{i5N$ADD9l)m~-QhRQa1Q5{icP|gi7A}d3vMrCEN zxsu-q%5eUu+&crv%3$yP0H7x(I#-tlsJG|yL96xgpW-#Ba~0}W`Ft?cuk!hz)%x(K z^7)`uYb#z^y#%}(u-Uul5CGaYrE>X3%>H4g;gs95Ow%4!1=@Pqe z_gA5^&4Bx>P}v4R)mn`zpATA8Rh_}!i-UdePi~7UN++ZAr=VBibO}q%<_i1ZAK$iO zV#$E}t8lsmz|L#259aeh%OFyl*&eh+GpK7YoFYFd+FtW2pAQD7uNm}ett(ua!9MsRqNjF=BWv)=^{ULE z>s830?rIuU$RGpaud<2)IJL|5s+=ns(ecXruUxOn{wvojRR*6D*DF;92jt4ZD^&&s zY_7noZ{N;OgR8$bt&|Wy3W<=@OKyh zujsx0_{J?=Af|nFcwvNo1H7UU_;J1R)!`=@0Iz671U48z2FnqXx6z-%j#Eap%s?Zm z-(a8-)pZ7PR2=RDTj@7)R1B;zkfUPYD-0ki&7fcT=r;OO;FV_3f%hf-hMe{#6F@ zTrqHkfjn0X+SC^PRQ6R}WH67g_iSUX^b4t~SLr?5kyqh8+wYQZa_j7d_iU52 zbyH$r)kRb^A_87#$Romgw#&Ja{i88gvg0)7O7^HcS4b^=k6!n|Fjox7h&)#S9=}t5 zqvDl4saNqTy#I=Rqg#Xa^}32z)JaZJRlK53asnz|(UW=vfLA$#_C6WR70qs^4E82A z%vH{yy*&wYrGH8VAas)JpK`!682%KLfu7W(sJgjQUWF(1h>0<(J{b((AI#hho#Y{d z`0=azS{dZ1JcD_z7WejgTM%wFS6BDRnO8MeSNBP+4|5f`3zkrCEnRtLI|GtI@G6g> zchKov(X_%d=!&v;(1BOipX0ew;Oahcme8wT--x`rx=)-Xcts<+r55@{X+#9vU*!?@ z4m$8k?{U;$*<4k=-Fnpk=sk|t8N4o<`>(854I}g(NBx!cssYITS2uiL9~pYi;QE%2 zY+IvA#u2->^y%E|5;c2|!f&+&O%Y{_}E{>b&Z6J{am(xqBznuR`|@R+P;Z z^eS}kFyQs8(7gkI)Q7?auX6ttv7LYhul!6n_g{tjm1azY3jzx&MlGPW|yR zgL2Q=;FUxLWhgT!*O4>GQzBOmUWE*Xxyl*jxe_LVSDHap(Z#(|Pi#abOhi;=1|{c+ zDrAu9BTPh8A%mG$A%g&D=cKtpRJs3(l$rqOl~y2?a8|8_UX^Q5qJmzPYf!EOylSsO z?1SYRl(_=0w9CTvN|@N-mE;4!(+|hI>0Mk zgU%~igAHEETmi`aSGMB-&^73K~skk%>7rU zqR8O;x*dlnQGNp%%>7puRbwBN8d-x^;$#{sDozGbg$y!%{M0gJkOB9iA%m<}?nTSF zlDmmcuiP_;PXw=28C=N3S%O!&|H_``0IyUTXavudi~z54|CPN>x8biOss^v*IslaZ zO0FD0>91t20F?epW*0!|uOuG;O1(5e;66r*81d zqbilbtFWfZvZI$l{2yQSI)%JUpW&?f8|V6ZFjwr*==O7uQIeU$a5t*hhBvY zGT?V!A%i?u&a2X22@@Mp$%ux(lHUN^Lk3xvJgU-P$#sD3A%m<}a^;=B@*T%lZJ9xz z-70 zko8J|~$%Rk*v${)+1DT29FRD)cjCy|UBrY7L&9lzW3pug*@&y;`MLRBxvr zM+UQAS=FHOSK<9vSsAQzpb=G{`qkMngRBhTRp@8P{)(a^O|1Nt zeRZMoS7#?heJX!tXA_mbvRXKNd)S?!aGT#B%o$`GC3=-J$a5uYu+w&ReOi65>kM=Q zhBE;=OU(}XU2@h$cN5uPX)T@Dp8XY#K%JZFq9KFem5o4Dx(3yrTT~UVEGqCS-{V+R z;8m{4*l&PWc@0`rRsG6FH0oEq-WJ)<%YD%Gs#H+SS(e^amc{a3j1O0Pn_y{unJ6{iuE>s495a=ps+D|)(LqssLwdb*zg zL^W{zs*he-KQ(w&-le);1r^1LvNs`sSNR?%e0M4A&b&Bf=WNuke6KBe<-2p~uY5nx z{)%{|CN=x3nK^f_rN8nK>KXigP=2G4L3c9CMg2;y1Im!=SM(+}iVDh*>sK~c$Y5E& za=ps?peaKYl_>-CDreA?q1p#+oo5w=ESWMOs*u5~S9z{1s!p%ykLw6hQSd6)uPmyn zeq~XCSL&~{eq~Wr^(z}u)vs&>v?TM&MgYkEDvZeWt1u$_t1u$_t1u$#mCetf}-cy9%u!9i>2EKQ8 z6_p*Q5tZE6R{koyPD!ivC`zxz#SN@7l!<)W6?1Om?+P!n-uWYU=e??C_ z(Qj1#%DP@Ee}(3;w!gCO9q3i|S9W#>Ap5KEUNu!wJs%A3Rm=X$zI}#2mG#QLeO6`A zt^*m&b7gldm0nrzN2OQx^}0&0>^=$^%>HVSuh(UN6}oq_zY6bF%l<07S1tRi`s;Nn zOP0Y(uPlR=zp@O1SJ_|j`x!u=%3oQp0eBU5XDm+3VCAnYgGg=O2Q7mDvcIwn0?7W# z-opYQ`zu=?0J6Wb3(RUvJ7Sw zh4-;{u~z=d-o;w^E6X6*u5+c8qCqlPDuY+eL5<3~ z4KX6~D!jj4Gw9Dv_&%5`km0)knnBg8@Ld3P$lBi?z6+2~OzgV=RR+U*Yx5m_cyDdk zaj>H7y8wB|VL(pYk-@Oz0I=@@bQx4$g>J(*SKT`rp`zI#hxgWM232I}`-6Hn5galI zw=pd>WJIMI)DiUkK^lRm^7nBps!FfyJ*m*E{B@P^-rDeW6`d>k4d_+)x(Wln4~DO+ z@Lct|A3QPE+eY6Ks(5AJr~;7n>crR-xkL#; z{qpDr1BfbTaAfof1JJ9GLC|M)^g08GDr67<^0A$+15t$x0yui-ChtD_>kJ^O@B}F6 zLr=9RuRfaI&+rGYbzbQj)IWu%j93{eUYRn0SCxvgGJsb>MX{pr93)2tUgb52cjGbu zUIi895lalfU*+%PKoc3L^lD=FNdwC7!4F-_G{QbetO}s4rL=`d!`fNcsx{i)l zbo0+6PBH*q)!!ea5#W_7gU%Iwe~`F^?++4C-47l+baTC0PA9y_ z4!K@se|7B8tZk6lUmZKNU_ka)#||wSko9Wrfn^4&4DLI8l7ULE#`z7H4ZXq}vv@?M zSEuLCF;M9h-i5>fGN{U+yuy1X7^tH9mH7<@Dqj8W{1pZ&UVV7}Is=F*^XiTBJsc14 zJ{Xh%EP3|uCUD5Qw(&xQX%`J!?O%j{_6Dn0t4Vx*qyNkAAN9%fy!UK_sB8> z;8ob2c?6wiFt0R&>f1jwzrsMJS1X6l7?Azd=?Bgkknaaq4zC%I{ndx&*A2-2>d^-` z49FRL?~yAER2h8q!RriE8T`VoFXwFvUPt1^_FEXBp@+sJz1S zJU7*=?5}VtW4sC(1WRV-T(7dfnwfLG3K`@Pu2v_-3){m8z)n>Pz>nGf?qr>AnpEvcI}`?-c`7MYSX4 zXYRXhfU2naE98TB9Q;Q1S3~#9jsqa;)nD8{#UmK&4l&nOk50%8=)3&wWb_ zRC+aY?=l0GUf~3IkPCczY59h${Q5ADKPJ0HVtN>c`)_#sH$q{_2Bo zT4w-JWq%@j*C4zTJ-1iiJELgD{S3Gc^;cSx z`NMbeb9*!*SBhu^wJiY9eNfjRjZi@ML9NMz5m^}qi7G2Yh$<^X{*BN%5TeS;5Jr4+ zWF@?nYqS3fyvqHeoBdbNtGo|dEgX84_rcBnE9^K}QQ>^h<_f&ZXA}006zn*8cQ$3H z_CZ>;&HgJ>hP*q&E!jHHyE6b&21FGy7)Btf+<#?JRr{btg?%ued0AB02lE-dMFn2v zGkS{(yvl3PMpXBMgM4oyYkPRgG;4c!UtiYt@V>sR?cv>R`FzlxQLHi;-UFHED!d0W z&y~G{4jIgI72emE=gOWFMh5fw;P?RFo5<&b_VhA%mCpym_a^fBpuIsEyvpZ;_LM22 z(j7Q71E!+f+43e;8i{!q`9II;8k9O^lo(mz^l9ltq%aa%J+km zK^lRLKAuhNeBQq703hEFhR*(cKN!9@k?#lTeSP{H`F@bUZy-OO?+59elSVXn)u;c; z-s_AE=JUbuy$P*;=sk|j6*8Fnuk4(&%b?y5nle;TnKHn)=M0)MAgYkTpgvhevtES^ zh7pJ=&y_{h@k;Ag7FFfjEvm}5(+Dzmm2VGv6{;V&ceWAWm9kyEsEw$2Wg`IO{;Mz| z_g{q(x&JDR$o*GgMAj>ts~cr7>lOV5-k4H9KWM*E>6JwVUS++q5wBQY+34Eoa? z-~Ik8>#fbofTd<%83V6^G61l5q<~l1Us#eQ)m36&T{%R0jWq)N)tV77Mzp^LR0c3v_-Y1j&l|8YJ5m~PW z>FdjS75e(Jzp`GnO0Vo~)0JKYUS)qpyy~@f=fJD%uL7^KzY2YQ*tzq0O~%3lrAy_5Zw-A928*UG_&}DxWI_R>$dO7;9tb;DADD=uY=(36eunczoN_U(=GMJSiWH2j(IZGr;l|lQj zLI<5HgYGzW1~sb9{;Ry>U?03;{}nweQ|*J{eaM*9VH``$2p2Fo3LA^gd+0yUTh-eSHdKz1o=Wb3b@(!bjwLoNE((?guR@IGHko z{ya{|AWw-uj}tPebEQruWRT}d?zX!Os*0kYf$QxRuTZS9`c=;0PtW+>M93h^lH7Sg zuR;a^&eW-J+^|JB`r$!QlEZB^FiX+ie7Rl}`ci(nLN> zt$1a<9{}Vsb>QGE167^jYqJXsRCR`* zoLyo7dX?AU*WbL%0C<(x;Lp#VWB|O%{a1K1>rHu;&j;a4cuK&ld_H*jz^noJeDLyt z1q1T=;N=5L2ITWWI5O1X8X1I#V*nY<=Y#Uabmmn)AAJ7686JTQ=JUZF_ncz@`(QpF z{OW-<2D%J(dc|{9@yga_dBajaU5S6Y$RR-jP zfkp;T8jv%Hd>D{3hen7#Hz4!s*<%|9WPkPCLstyQ{_1N# zeBA(5QPrz&9PQx*>lr#L%Kqxl7e)=pdNr~z#X!ZY@gJIHpwg>-KS-x!4SzNFmL&!% zy*hSenSn~Le)PeU3?PH*uXL_{YJP=*DuX|J_>2MSue65ziw~SLK>d~Wsy%gR%>eaR zdPo0jKd^3q`YXMofA;?v*>2ByRT;GBya1@b z@~6*MunF@yr}`_+pxrw&skxs)GYDS-S>h4uuQY>JXTXS@L3^UA${^Jl=+?5z;CJUa zuX3&yKp(!2tPJ*?7p@~KgFWZPlej=F9$~KZPpLBaT*(?DgZcY|fme&^d#T~OOZoeQ)YqpOT#zqF{!RIwSNYwgKJSwW^{bh=*f|jDSLJt? zyna=GcZuc-%J7iPmAa+y{;T}`L3=A#<*!2h>Q8R#S%(m|)KI?)U$27$2;cMiD9~}o_EGqb)i=|gXfRp8D0SJS0RH) zEq8JNz>X6#2!PH~^*g*Fg8vG zy&trw;M+s>10yUd`1WrN#ePwX3PP4u)S?2AzdvYE0k}34Rg}*raOD8-8?-*Wza4rd zzzaj`1gKt7R0`xu5%*tVL?eT!?*c#u%elfn2ml!@=L&as0Fc3QuCNaR0I$lq!aK=o5_I@}S@VqksM0K_6He_Dm+!+9( z(%ujMQ`l<(ASzvh{-=8F?}l3j!K=ImErZ}yUW0*Gc?||$1r^P_D!)I7sM@=;zgJDy zpgS3Vjy`{X(B3%!Ug;Xt-Pztb0HFN-p!YK<+ckrI-vep>3K=ZFKPdhR8T^*8ou(aE zZtomG246_uAGCK4AcN)i2bXaF!6zoAii(q|coqCr&S3CYA%mb#_-aqcAOqgd5HbjW z_CbxRoGXc{(<`lCnZE+B%I^<)U!N*NF;~zlRR#w{+o4yg3<`ukWSuMZS9m(J&8tgO z&a3RN!dEJ@zhb=-ie`UhXQ|*-_E&T&qsA-i6@8_WfGUIbJ>g2P%mFm@it``zwp8;}zu`OuRHD z>jS*XyR$tp0=){mGw*}mFB*1d2Ao%6cLqS~oMspMVD?vo@Je?a)vLfO-Es78!n!O# zpX{%!%K||5SAkbq83y51nL*d9kU?Czbyn0VBVLsm6!ihG zLI#oZ&}9)a$bjor$RL2gE6t$N$D+CsuQY>BIM=J}8Emd9em~4K-sQ4&LA1g z{wicJ`>W8Inf+Dh%*_6(&Y(vn8Eo*%-+z@=)G}D@gSG~OA4OJxwx zfJw}EUiOY(0Hrc`21{j-eNgg2H(2VBZ4ClYWzhOn$e`Zis9y2?Al6_N6;9lHN*`T= zx(|luadZvpJ{X?I(KV=g6`se*{q3*#=W!bMgDt&6CkOXN!S$ixJ3aa0??cYdO@!xh z@Ef7F6rRTk-O=DxcpgX3CRDG+^(moBujqLk${_Sg&vA4Fv4jBVm7e1e0QIqFm!Ma< z_k*4>p%Kul-1}kNg)P_ z&JZ%FdZl%SkU;==8;RAgG=t77s3`tagI7=n0N_>b*|w;_tK734>J0grILyCyIAqwS{Z@g2=y!0b^)~aL;V#+r6Y2Gd*AvM zKR<{+g$$N+g%t$=J5D)QUcV~m%IjCWoZ)f$9s2rp^IpHwHFyiW$|`CZ1h4WM3PW@{!~7R8zh6v$svaf=H~}P z2H)51IFKcF$dbX_-(F`>{na3P)y^Q;9x|BqDr7M0Rmfo0t8%VNz0&%XsVMYHl|h*p zcon*LSgA!t!7Eh;-5`Trg|0A+2zvFpkyvj>e`33Sb$Z_Y)wE<#{S`+go7d|`cnzw* zx_I2*v%M$j)y3nex66pTM%HQ7s$PXJtj^0fi#3Dvlqrp;N$mhHVD0OkOY z!TIeRl}^dU<6t{RKp75gU$GIGl8eX1Wk4C`wsTZIiQ-GJ59(ZbRDI3|iHRE3`J>Ta znOj0s(=u0@+Kb2id=S53QGq@ek9&Xn9+@j0VIB@qsfs$#r@x(7Ev_8ADpl0|RZvl$ z5}7OTDyS#}?yrK10tk-4*GGGJ}2ml|=CTIy!akVS;Kkz-6?paO;YzPCqQNVfD*&hGqrbAK0DLy}Gw{8${3-kf zt-*LdD8Q*y8T9kPa<1H8m2>6(s+=qLSLIx}zdEoro)6M@6e-+B zRPL|dw6#~N4@N+*4s2Zr0N1YoKt=D~%5$YGA%#<3y>aV$7;*8q`zu|8x4^5s1}%f& z)pVz#yrO&`%xf_4DzCx7D_w)pw~NT+>PM(E=o+l+47vtYug)L!eNfk+>ecz9z7Oge zRJ{sy24%bMIPCfmRh2>0T`0q6hhqlAdp|xcY^Q0*T%A8E>Vpif3fmpn+ZC|y zG<5r*&z00gt2Ib_l6l?7bfVv|6ikb!mWldp;kuS|9%@UV}PU zp?;Om2SfcTpATBC4}U734_dXh;+54)z^hLrWf<3LEtKKOP8q-}tI2>@x!xY$CsWtk z$t{6bI#Ri#E%9U-NEB9CV ze9+Eo8@v)_KvZQ0MN7c;kU^%8C>+=xGN`=Ls6qyrSE6uWdp;kuv+V}2pat%sU z;8nQ>)%%2g(R@C5Y0CBLYPugJ zmS|M@d@%Hj>KfGF7^Gh`uffnSn%AJs6?m1`pmmFYS3yOw21CDSJ|DCUR(chB4Rj5v zUQxd&T{%YN^TEI?U4!bcLI*@XAEbU!{ZqZ4Z_mFiVKAEXWl0-y}}e2|oy z0PyMsVY}KWnkxdpt9(8f-q)8^^wchKWR+f7XHBJ|)@y*MLI!nL)2KoQ8Ss9FkU;>a zcDY`ab0s4>z4HDm*Q>Js%JoW>!KcLaN|nI@xpMGIl|ccUE9lj?Z|CQ()nBc`{chR$ zEn>+#Ud2y?b5uK@f8qmg8~TUkQr-}JXZ|deLMXI<_gbU zCtjUo08!C{<&jtC89-Dsczbu`Rqqb^Q;16cluyaq7(i6|ryRh;Sr~z+!k^-(zRo~{ zS9sb9BN}scf`P_dtuc`2idpg<2J&1nu<&Y)DzKe_CmHDG%2~3(0C*KHkVj1Jq(22- zg$rarMu1l|BJxUp1H7UU5s;|Bt8#=~2Y8h;Xz$ssGHCDFt}&PIDh=A7_@`&)B z?Q*VU|7gsW>^P0Nl07QV6;eyzqp#ndJRCpkq`@rpXh38;8QPwEi>UgZqh`(!XzG`pQL*qhieS2=_B_9V=e{wWoJ&`GX; z$^p+{_)|~@dQy*~>gGy$6`s_i*^N>4$zb^YV8{o4BXp8ywqM=X${aX}T9Fqtj_g~%ceSKu;IfLury8xknmHQb&{VMk}P`@bs zDP*wG&j0|*koy_J_vm%5lHrztRkD@q8@!UJpbTXOjeoOJ?1ugWzjQ9-ZDH7M5sUbWXC_Q7%u%3Oh0+GU~lgS4yl;g#eAgnRno zcs^)nEC6!Jv zK6kdcYD6Woi>UIsvnd0jdSu%|k|oI?qRQvadj1A*MV(i&1{-=Ma|IyxU)hcWK-Zw_l{2yI zzj95?{a0ZhRJOY^^wlfp)pDvp+UXK9nES6xMUlbxbvq7EqWlIjnES6RD)6dQQK^wN zcqLAzp`zks5LL(^%aWg3h72;`UNmHo^~$|yIahKw(diW#GmNkkMes_M!G%nmC3uzl zuk2|K@Jf||M(|w82=FTRU)kGq8~#e7YVb;~13>AoWfZvZI$l{2yQ zSI)%JUpW&?f8|Upl|g2=k-@&o(8?gsmE;_H6*9aT8|L7A&cuWYU=e`Rx3`74{NtfG)5n=7zAWRUer)*$pM&lSq@+%Jl0 z&w9mgLO|~zfQB*%kd@)=B-k#1DubW!Vy*;GWe6Q~3TU^XuE9Z~%Kh!U2IV($e|v~3 z_qS6&gZ_B#Z|ANT`BS-Hl;3p^pvoYhA_G7MbH6CvP0(*3gSlVy>?GD80A%n`>Tl<+ z7XZj$?ic00J^;v|&Xw+iD7bP4tEkQ&^*-eDM`M5c-Ea3k^G9(X1pxjk-=*IDcGLg>z+Z*CRMRT}@K@n3 z6~OtU&?^A&?cpvJz};_$UIBo=%Khzw@GAGW2VUj=_P{I6V9oa2-yV4N-N_)&m8?PV zDrAu7O4trwg$(jsK^ZVt;FV_3d4(MZK!aD5~Ua(}z6K}2Tfq~M^tZ2{q42}I~8@k5*4j@Wf=so z+A0cOO?N69)*yJ*UW4FOdkunD*^Bb?I6{WJJClmiQbW$O7o`j;PaX}Tyu~Yi zzo-C>{&oTK`>*(`Bm(6Ac7AiD01NVjfx*OX5%HE8JsBpF&-p^p#4qoNHK7OYY zejIw0`}+9JEfUp3GFM6l({}Jmqf+4V0aTqaqT`hU_|k^?E6t$)Dd&}DkO0gTpF87E zfmfPA1^CS^0KhBFpaP~0&@0X0E$~V+81KCB8%zeypzec#SGm7E@JcgS-%WgXGRSk~ z{wib;fW6xgyb2ivKp7-KMg}w6DT4%5Y^MwoP_f;#9lQ!`hhA9*!K=V_0G7eb_Ux}T zDrJc&L&Ymo2JD078gzdZqQaG12EnTk6#&a1cvY^!KK{zqAXt+3L3^qZz<~bB)*wb? zf5p$^2owAItL%_%4OSWC=ba^kwL_*=iwp*bj425j3=SCpJ&!|VKn8ZQscSklfQfOZA}gbd~iq-C(; zm1Piml`D|JUxf-JSYm4syb2Xa0Ja9f_FTUT{_3BNtb|S{TZ7FE>U|V$%xQPl9mo6? zM*MT}?YiTH4B|J)Oz0hb$l#~s>3-dD%wOS8sTbX>U*UNi)vNN}+3)E8y1w0%;U)0x z!C$Git6tTKYAlE72>vO9ihs>sRu)WAN>v`T_cE)~~=4 zt$u9QuQuyfehQ%XgLdx>mTcCqr0N_Ta^7)Xs_F-1sa^d5(5`*}2-T0x`qgn95A1wC zR6hpvS1(ltU!wZeEv!MUUwNfSD%)7KTEFs2k(>_#(E643KwA9@fYz_{%**^00MwRV zuKJZnB{doRxYn;cDyhi;*sNcPEN%8*ZPu?g>sOogtIhr^oPTJ^J9O`C)~`0}SKiO? z*FDDx=Y!K-2OV5EKh;>B0sycY^54Ar)tA!uQq3X5c;)*+>#_jw+)$6sKk7c2FAXga zP}dnQ4DpGHy1r1q`r^epzR0@RtE1fHi%I=-3bDYqBh5x7BfAyTqm4+MYSNIJ(T>`H{{py0u zm5vDKgBq3pshjs-X$JjIdH&&2*7$ja6XvXZoMB3UU~mjW_#$r z3Ty{`EQ2^73~Xm!`Du7yI{?dI7nQQal%eXAF=arXOpeNSXZZFI6|UT>AK+Dp3V>x0 zyb4hPu<8eRrPYsHxOd*{zuH1C2Ew%W+Wn%N{a2g)SK_I$)Y=uuum+(F`MHU{`(Wt5 zLf=K$2ZO(QQ`a5MdgcA?_>Hg+TKx+C>h7(+J7a{^uevS^R|eS!D<@+;kN|f7gFgLN z?~%8Wcva1xP(UXKMOF7-y>lB!wb_5Q`TSt3dxskSHtScL^{eQwHv6wO`>*_Y=gs=n zX8mfjeq~R-XgR?;=x{%npPR7$cKG&hr}wwpJr2xWet)~&r2@$BZ@0Tt9f6md)^!Gb z`*&Su(6@irbq2&i?_%}phdfb*-$09>zqfYh^Y;880DXVES3jhFr6Xcg?nU+e?H-k% zrP6O;gh%CPsTx&Xzlu>&$-=v%^IPRZf4lyMCMwjg@>}IY{VIbvC|W;gW4AS^BLF6PP6>3`)6TJ@uwt2%?_TnVolbLA>p&Xv2qeC1&cmUHFLnB);*4U!UfJC4xXgd9VuI6#P`sMGxYr% z^H)PJKKA!M*4xeie;CK!urd7NV}JJ_DKJG}v#~d9jJ){R(N8MCAI7l^-X^olQSpay z?EP0;Uwn-6fvEP3@P~2i{a4#$1phdH7-wAHf3;mkEYb-6Fpj2G598SR;2r!vGMTGo`pgXko#*ORFFv;Y2Q@1GFpj;s<<&BRqv8+an6~ef5gZkN z7{|_?Un3(pD*iBzojZ?)5&U5s)AljhSa`1Z!#MW*;GMDzc&_-vIQIPDE*Zg5oulvH zn6{702#$(BjAKu}Ovnh1YL>o#W4`^hVFZ5|$CTlF0O8nJ6 z`BNO#2?Ffdr8mk5j%tkn)As*JMsQT$kv}E=YQK!&s1{z$e@gBq?hYfKB)~E_6WG2% zfMxLe+Zvn?BY1sS1|JL=;6pNk=ZY21 zGI&fz@LaJb+8TUVM(|v@ijpZ>3?o=~&0oDW@QRh%p5c94;FW5-_^acASG*h88vKs~ zuXz8k4E`s9SG?m`27e^*iuWkX;D7q!WB>1u>s;}!W*L0@i;t1_;knX%Q0D3#GD4Jr z_FBu}l8oTF;=R@~_;+Ol&sEx;-x)^mer_3jSKt*p0L$Qy24205tbwWMBY{^v0?c3i zn5Yj+-}x;7Yz@9U2r`ebeegXpf-jJN!~E5IW!vUIrBTTmeBXa-yy*K)qn21x`3vtEjzyj z57_osc9+`us|!PY{MCgaz7FfM=={}%A-)dlvgrKPg`qzF>cSBFE9<}N{MCga=9Sg2 zI)8OxsE@z8FvQ%o_g{7X>cSAS-TJROe|2G~kH5Mw#4F0~ojZSZVTjkDRX;j^bz!KF zzq&BQ>)d>M=daN4MBf**`c>zzE)4bYR~Lp@eayFa{_4UI>y_27I)8OxsE@z8FvOZ@ zzPqGuj>94Rp$Sqk0JAZ}s(e_tZ9|Twi zJAZ}sLH^1z*!e3RVcOpLEBXznk7cm)R}>Wh%V6iP=sEzHws-zYKa^@2#EoLUAGCYt zPOtPsskS?J_k;SOR9l0cUg?KY?cTZ5t5x|>s`)G252DA5{FQzv)z%>Nif-H?$oipF zt6xE{XaodVKa^_z3ipFF0{ftTDAm>=^omAcA6y+~y|Q~}=#`IPy|Uf8yB}N~X1%fu zc6zls%z9vhgq*IgWdh$>M-k-Ww6sLyatQ(%BmloUabzZUReflKdAm{by)RE zb{yyx)ns_CSg$OD&?_3jbESGEYY_K?G=k?!^-5~U&?_3jbH#c^86m_Uzv}dAb(ry>2?_k&ytBPOa|Nd}=;T4%ueAoZ~fLa%59N5y(&{z~r$dEuyDZ4A+^4DJWv+Jp?O zSGERKulP@~UYWnb{UDdZczuvw*&2jislQqsX1%gC2)&{a{HIvq%wIvT)L&r@k}TO8 zgkHhnT2ufmgU~A)!GDVN%KTNQSDW=Keg9SGul9`e@mG6BSg*{tcY3vFWV3$7?{Dw) z3U4dftY49~cm8V6NFRT-XJoT}#rt6AuTZBZr8aGkdbL@<;`jA!*01OYVzYkLBVY0k z_g8yHxH4h>YO{VtEcySjezj+0VFZrM9P+Z?57N0B0J}^5d!a76L4alO1F0^`e@gBq zelpZWmv7@gC3h1)73!kDL4c{~e-X|HuM=SPt4GCha)IuFt^B8?TKm)Cd~k&TJ0CnD zBe*X56#}e&^+xLvTK!8<0 z{%iN`dq!r_;^C)De?L^upCG{Qogb4CT&-Ov!0J~g!-(xW_)o3z_i-Kl-zm!0~x_log~1jAOCF_ah?FX z^ZM_+E(+g%2LR*M6M)E`X2(Xrl`na)sGJcUY&^5 zkDm|a=rsZ?ga4!7F42Atz%uv?p&ZQ>7OQ@&$Oz_@-s4DAACVC}R~vNmWA&?#h7prH z0od8ZKa|vR26=?tJO85mDP}wWhSjfrNk(u6H7e=9`k0L1sQ5bU&g?Gd&=M^WdujXIk$V~(}C^G63gKK6h<(wOc{PT z@QRse89Wns#oV=f=l?nIO4%;ekN+j`ir0s&!G9ch#VgA0ou3N4N^9_+c>Rj*CYV=p zHt{Q7zuGgx>)h_0|5vYHxiZKa{8g_;sg_s<|2MB+sruNx^Zy=3uwGdP|7pk|E1YHU zQz3(_iB|piKSBmoMP;tehJ3K@S_c1F$RI1VRX@&!5lP#h_WBjwrSe=!_2Zv={fh2u z0aynAh1ajBlYsPv~S^etQ!w5ddu{HRA243;WkFCMq2)yF6 zBCB7m23}3kk&xA|o(a7A`Ve2BsObL{cy)#V%iyO2uii$0W$-t>enr&}0G7eec>QY6 z$oVY*EQ6m7{)$JKimrJzYR?G&hGp<`UcaJ#AO2IaJO8UNg0I8Y;BN(9@s-;ed^Ye( z=Sog3e>?DsXV>aiza#3y(#QF*HTd};$m~mO4W5?~EPd=ttbX;ovTcis5=(3!{DRl7 zoL6#2|3w+We~OuC^{e0W`W4kn_)kd&|8K8f?HOUVTLv$9{c6t$uMewV{l4=`JLv3$ zx97Z~f-3s@tjhubb!;K3x`R&pue5{CPIxgw`>*u<40c8jK>M%i_cKs;8|F%TwypmP zEYbcej;gm}{a0WKjX+e|4`==gETIw5b^`E}sr6rhB{Tv%jsn(y1(xIy=C70`T~t=T zLR4B8)u`;;8Bu9nRHL%p8Bx&)jSB6?cJ8d))u;g2xwCS&8)0V?h>9wyh)RFM`mexU z5TA;f3fQ@`a<`i+JDUJ^X+$?ycJ2)B(r@GDu{IypsMa@QVIajLP57KqF#Q z-rr6mI+j=l5fzPyypp>KM3qNa20OMZeM}j^EBaF%uS^-htIT%OcJPWubaQ3O0AA6E zZmvujz^gpMw7ue$Ww6pKTZ5He*%}0p^~%;DfUH-x1_5NfvNZ@m&j+1X;;)cFJs)&l z`7?;AS0RIXKB$1LL1ZwGs59s)Do&=-E6ZS|SC+v_uPlR=URee! zy|N5~S9(6ES8f>uuV|7wUReggD?K0dxso*qUeSn-SJr<8Ug`Ou&y}n}@G6h6HQ3;l ztU;vK&z;Nnx95G({1rx&ypr7+K*=jvg8)ii$sIiaJs+$ysON*qtB^rGAJn-D8PwB0 zosy72Js(scWH65i8KhMk^{NlAEQ1xV%wK_5q&{);lJi0Eio`OKVSShibgEaK^qIc` zuV~wje@fOMc%|oqIwfHrq!IB?$r=Q&^n6gK#MU5qrRReR*ct?{^n6eO^H&wG%poJS znOAm~3Lx{!G6*2^${aF)%qzP~1(11V83d61mDR7H3^XO(j${2-gZnG1Um=6pUs?YZ zGB}{WGT&b5l~q3~UYWL6dS$-7(krW9ReELG4qg!xJ6@S@ul$wOuLk#5cJEyIE31AC z?yp`cywc^YdKEIL%USg*WROO5YcOPxMs(V4_s+;5jp#gs)vu7jJi@dc86-2&>6P6( zgIDyYI<{N=3cR8bF{(cP$})(kXhi3)EQ6|7og=GvXO$((VCAn&83y-PrVN$8GG!Rt zUs(nr8CkEa`T-#8m1Ph>)+<|s0J2`$y)%HUSGEQLWPfFA5E;z=%Ia6h;DG)rWH9?H zyLUzg2lQ8#!Ah^J`cdhXX?vwtmcdG|tbSGLm1%qBuPlR=zp@Mt?yqbOR{qNBSA+X2 zTZ2e#$t!<;u;i7jL5wJQ)yH311{=KUjWHMqaB3|9WiGFa)At-;D)Sq3YAWovM7f3;b^LIwx)S0RJhUxf_b z%wN?i8vPX=zQ`J^c=flbe)TfG|Ehj}yS_cCJ|Bcrrs9)s~;e0ShWxgHvgE^{D{m4<-y>pI=-&5pu(GV5C zr^xG~Au0gDUugz)yR&;|=#{?zN`X+nYQO&qbdQ|Vn9F^6t;2H9Wum;O_I{En^-86T+3iYdZy?pP#aurQ?6J1oH`jO|#G6-IU zD`ztJX?U0`0HOMkM_2|cUfCL~cx4%^cx4%^cx7v_;#H_$g{ZUpeSOPE{5{*J=Y@%2`@h-2 zQTbfS{sFdsWyb>j1_5A+%@tJi;tq~Vfz$I+-v!(M^$w0ofs4mwSA#O>TzOR9CllwY zw{m)3p87>p`dv$pN*n;9((hU-V9J1~^t+Y{oSwh3g(eYE>31#tPl>`Is?Trdx$-|H zzrpu|JXijwBr5Rg3)}Nt$#sBNpWmM6O0FDw_3ZZ6T!B}g+n(o2v;=yk8T4wcS5o6# zQMFbw2*POwy;|!`)C@YWoF$q;e_x;TN;9bM>$`Z|nOJ5}Ml^UOztPB`M3pnhyprq4 z83bT!FlP|J#p5zpT?U<3!o)^Y?yuSzL{uSzEHaXFL=`d!z;+x&6*36m;&JDdX3%*h zOly@9%l)RGJ1+Q>6LEq+ZUO5w& z3%znC;y03BITPPEs9yEul`yfPS2CiZSHi@GUP)99y^`wyuO8W!RaCCL!7G_7MD?C+ zJrheNgUl|XdPKe$;$#peBC2=F7eicRB!h@bzgesR?Smv_or?OO>Z@1It5QXUiQrXG zQC@>G0=x<;%4<-51H1|<%4<-f0U9aRi8vCGJIRIUQ&MRl4u0hu;XQHk_*DGhD zu0hwU$VA$mT^S_jjSRY8Ez9og)9$?b31K?{JXgX*Wbl2$b_Gt)%Lruf-NJSSE*_WP z=rVZosN^~tDk@i=Rh0Eg<_b}T46>{d=L8hqW15v3mcvOW+@So$kxV(G7(iKV}C zCYJuHuQGTBOKtaku+;XxdgZ(-wOyFl;FXNH5!*{;@Th_^Ft6k~8oZJ#Z}3Xy3Q?8* zN@lm?mD0zae7O~WB{^^SE7z;AJF{LnuS$O#fe92fuF8!5^X!t8< zyRJc%CA)WS@Jg-&yed^xuDs!|WUdW+jRr)JuV(G7Z zMClpiHyRm~sF10=$(1+!mCRMcU&-t?_Cd)^Slp07_+$r~s78AlCsvm7#XXstoRsWv(!y^j9*w07|`*d;l2O zUpW&?f8|Up{gv~o)GJ|PBZD%cp;z)74ZV`68hRy61g}cHk}GfUO6IB&mCSB~SHeU@ zHL$<(eX!(}>s9HmgjWr{5+*kMm5hL1h3W@;2KkMKzmlk+S7CQ%&mc^MUX}hzuDro3 zXJYBEoQb8sawZP!ubhdczv`=3&Z|<}g^4+XJXbOzJ7fT%QWP@Cb0txA4%z+H&3EV0 zU&&k{s*pkU?J~QFs`OWq53oIC@W%d1a^B#Tu%zLyoLA*Njxe#|uVe&xRjxt#jfTH+ zO)T}wHL+AtXJVl zuY7kd{gqrfcvbo^62B$p?6~JgC2toFju}{YqGZ43_mP z=T)hqH{YF074_Y@+y_0XkU_}O>3QFsLk3x|Mmb-eQWmCP=9Ro1U0AK+D4 zzmg0>uT&YdOl9}Zjrx`AmCn`8U7s+q;jf&DW&O&TSo$kxV(G7(iKSl2l{a`LbJfr* zncarJa%Cv>$}?DMyX3r62Hyw!>Xk6D;jdb|Gw*}0q9w1K?Io`~s?uM{b#%PCdHqV} zs^PC>b{qam@^LHv%5z@sgU*uDUpcQzf8|Up{gsc<4Eo)K|3;|{5>y_jKK*=lDlG0x}6H9;PEGhk!^Q!b$&csr$ zWJDu_@*54klBgPbCD#F7m3k#t-tbp4SDnA|x$@a9=gRY;{>sn1JcH`5{LIU9uKvpJ z=$$3%uY3&(uaKy64ZbwHGgj2)0}@qZ4a#*4um*2FSI)%JUpW&8_E*lt(qBa;rtTeQ zVwpi_qWUY(ppVe{m1od@L;aO!(4$g+gS6SIo)KIpr1xevB7 z$o@)l-uWxfpfjeU2P zyjt8lx|4vc44}{AUKx><0l=wU@*9XM`zwlS0e`CUS7#@&J^)nptFx0>9{?(UMRP?! z<*yd^VtoLp{MD&lSRVkY`W0o6fNCE+I|(KVkay>^li;oZd3PpWDUf&P#l2V`G9vHJ zr*`>>tPH1i$!|0=C{Z;sDA$oQ2yI{7D_5Q~$bigM&LDtOyJU8|461L}?$Mf8bOVO2 zWa3r!SMQQuY2sD(SK13oyvqKHMxalV*q;5BjX+eo2GyQhR28o*s;YivQGr*ver3N= z)vqk7s(xi7s`}O9-adLYzTfpKt0?zu3rn(!&dj-9Wq-x*NRbiQUoGzKqgRW2`{>p9 z{yus|&*P{v!Vlnh7B4~>DA(1*Q;E=THM=5ug3SgUgi4L_01t6aZY-0OO!YfyPbQPHZcc*V~Z0jTO%j+ZOs(xirRrM>23cONf(7Cdxs`{0U0I!to>aT1> zwGY||0NG!K5!qjb5!qjb5!qjb5!qkaT-_*xx;yKtrQg6)U$wuo-#`ZQ{h&ov>6MLm z#quiqE4z-4S2`tj9hJYb5tYBP5tYBP5tYBP5tY9hWFO4_D(r*VU-5JFqHx(?9XaT} zJ^QP$4`zR5Dq8W1_CeCbN*QP$Bp@pTB%I$tCwi5&{m4Pt4YD#|1nqt z5f$z^C93SNXdl$5vcEcV5cjnbRrXg$4$25bmHid%gBn%#R~A*ptFRB|H5m56>=|sX zDt~q4pznj4K^3yF52}i)Ug6=#-WFV7rC0n+oB-KhkqakZf)UwY@v}<;sEX?IIF}DN zud=_QdjtJbS+DqgGWb(f2JJe)t2|eB#{ypEx#H(>@TV%hx_rQSmFLRtqrj`|ug*>` z+|g@C<-E%Nitg@|SJ_`(KH$8{{_64p=T-JsXD6Lkx&~F2EQ1xVEQ6K5vJ676vcDSN zFAll#SLA&tQP8XGuPlR=zp@NAcqPgJAp0xJAb{+z#`lZs1Cafdtq%a%Us(nLWX~{2 z2D85k8O;7_e7|Qf`zzBDC`0yFbq4i*&@$LyyWHbcDryf^)Nd~iu zVuWR|^H;76eH^l_LCkKc45B^&vNBxV=NVLG(E8QYeX<5IBG<3#3~E&T6gHTM5!tu% zQ`iD%2DJh?vD@`3S0Fd0{eDn0sCuA4^6Rua!$x}iO^tm6L*e&}x^y&}NQ$qY^3QQvO>bdSIA)PCdSLoH3 zx~GH~@O|*Yw)JqMcx}S#SDHcP)rqm*wk^G9YF<6@fzfRr5g_Z;iLoi%(esF`S0~11 zVR0D9dUaxK!GNq+C&rcx$Qe8_w#-14!4qRA833kL#;{qpDr1BfbTaAfof1BfbQ5bCo!dYu78 z6*33_`M|CIGgSQu83b_j&QS(B*QdOi+cm{NwGSS>bC!Y1x4(UCfq}}mzkhUzfy%d^ zc+D~cm2ZFlC@I`CR2Tim@Jcu_fnME|S9mE;cz*EOg!Agph8KvtdSZeV#d8Je!*39v zdWCgvz@H8?+g-1g3@G(#nSm;UcsmROm0n$&SYZGe%zE|hi8Bl!gITY3zxEsh$Y9p1 z%M)u1AcIeC<+)N`eQaW#fhwwri46u2)hD+0Y&nArr^c@^fT*6_%5$Y;c=N<{1`yRR zZRNRg^>JQ(bSux50)OyY=asHO1@PVw&S1qWQwH!VuR*9ND+73y*WkqNS&j<4%4=|9 z_W}b|odKH20C=TqQ1uF$$UtKsJZXTgLEi^g4A3>Gy!zhVXAICasJrvg-RBI@HK-?X zBfHlOP_}CZfAg+&2C57mxod*~Wbn_1V+KDrd4&OF@EgK*ns%tqkKJ{h0c7w`h3yK^ zdt6i*{=KkWfor?V3~E$(ODAWripnyGsB#83rX_=jDr69Z+nAoUs6qxASYRNlC<99j zfbDs%Hl~*u=y*jp|2*O(1K^b^gEDbrdW8Y-N|iyc{NGKVVF0{RWl-QRr_V6}Ua2xD z@cYwi41ib4cI6e`rpR+u@yg~3z_L^zl~>0Oxn5;|b?nfrZIIbt9XqsOK=xP14lNmw z^=j^cWd^DY?mK*vfl9B&@wNrpoh!Yx_2K#J3?Qn^t2fT~aGc5eVA!3(l4lQ(GJvSEznVEb#X!d^rOzK7 zo@Jo&SLo7Xpz>FbKDfj{<*(j*WSIf*D(uc!wU0hX?v?k!?5{pFzrsMptChoN49NcK z^aJM%$o^{O@R|YHUwvqP-GJ<`9({1bfSkei9=XCmmBB|Jyv{(C!4J*%a4OUAS1X4{ z8ORyLl$?HGih-;Q09FpqGSFpEc{MY4bG^#`3a2v0tL(34=4N@FBdYALX69V4LI%OB znYkq%fv7ZtN*|m@F;MXer!ovw{z^_7o~h5B@0~ltBPxIO$eim{_6(DAYdoUzS6{ks zoq>v1OZROUkp0!gd#@OvDysY7&)j$2fb6f358iR`8`)nC-7h;1fUH-4asL#LsCYGW z|11NQUcF{+fq_b|_T0C`K&4kR_bxL~>D8Y5PBMTDW`A|hy(!F{4c@=hNtf=3c8D*gIS6{t*ih;^sUD`j(K;^FhU|hXCixs$NxX}-Z#k4>pBlxE*1o+KQurB3WR{x5`s#?prr)~ znZydR7bXY^tt%mxWm~e{Ze&+RT{Q$Nr+P(sF3+DvbhR$Z`bxhO2Lm$5`s%Sevfl0)bj<+m zB=4Q-_s(c)*M0`f57$?&8RW&Ka=HYAXVB-MjF7ek7vMHKxY2(FTyed9qyMT~2cs4azT$OoqyGvk4rWx;06_W? zE8vRHCMNb@1z&Y6DzkR9|0uSy2X2&{vAJ{Vqg>tJ|=b&$`z z!Yk-@KJyB%fGa+u53hhLo`W%>+YjP95990Lk%PGoGPfT&nCl>O`*xcxV{pvWTEeHb>v{y+mU{$ zK>CO{aR;vWd{AQL^>&_v`ffu!1GwTj7<~Z174HY5ZwWc)bDSdwQ~U6KP`VT3;TXaD zLBW+9ct5D`Hnb7EACz-WAK^;IcV5xs)iFbO1%0(l{a5le5BXET6`v34+j?LCGkEnw zq6@x?SOHgjJ{aemLk8WeV1~}CUU4W}1zfRimk}cFI^ACQs#HG^#25iwvA&8C9j;;o4BUTJMsWXC8NvNmWd!$Ml@Z*} z5V5*m2AQwq8KeDI@eE{eoZCCTiV?3`TydQtqC4QqX(@h3S7(S3z!jfO#E4E`#fVN{ z#R%ZaGw8dCm$Cn<^dWQqRp~?a)!FOquSEY<^wu&nV5;et{y=2FRbd7gUWxuI=|dKJ zg;(Wt3ABWYmv+{`E7n)9ME_O9s?%4|TifZY=$YvB)ik(beO3DUSYMUCKGs*IuaEUr z!4>n>G<|){SEa9y^;Pt$b$k^nqvNZBE7n)iK{o?1bSx^`M^J)wko&Kq7YFMg>#Ne& z$Ng8OuaEUr!4>PP=-%o0>gM~eqJs{&Vto}Ibe+D6?ww9wy^Q@=(Lslmll4_}(80j^ zs_))$CG#@&UzH9z)>oy2j`h_m(SH>kbj+gQtLUI(7KI@)IOr>1ai+;2Gehizkq>5u zP?j(_Gx!N_>7a9F@D-=epnJ8^f5j^f*1_xcU&&sr<16$5#Qj0fpi3O-zw-THQOH1v z^k0bqxbh782%J>Mey}KHjFA2-83A1J`Ji-#iJ{{w>Aw;K^c9~jN&l4?ps&j55`KB~ zs)4V{>5>|3w++53r%M_u+n;xQrC&J0GXPQE5ANJOVLzzfO0f~VAC$g6Sq<=~n6K9N zPPmVw--56ayvMn^)ApPr8O-~GZ3c5cSTd*~$@_yPgB~k!7q}`J)L7YWd&r=(sD7su z+ojGc`6#E?uQ-E0yFYgmC4)*!w(|n6N(Nz&5q^KLWU!2YSFGE^s}5J;Rj1p-t4_De z2;UDD)rXLTSDkK;5x^Cn0^oBeH|%HNQvfIcJxc|A_!IzDoG|eD;E{t%Vc=5$R9M2m z=Y#j%zpREXgE+fW1C$K+i@pxGwnqCIaL-E($RNjRKh9FUbD$_>1aUvkQpJD_^7-Il zoTZAP^XkO?>uP{kd_MSrgICl5eZ}X4s1B+DUh(;$e168g;`6~r4sKHeyz)8d40r0_ zyc#-O9XPn4hOW-=m4ziWbajTGUO1@+@DY(;BaQ_v)+H*r( z@%bQhiG~EY;`71h4=jX%&j;~l)iCh+;PVGghJnuqp~$w#$!M2BXgD<>gM2>t{DBoU zAcK59_~L=HYCs10eDJpW&Zz->#pi=xK5$+QLk0)F(pYs~MNbjDVivt_|CQteUX=`j zme%)Lte8dBV6lQ%94qO+l0P-zs`Oup0l4b>ufzacIWq`EF{7maN({i2GlLtX|4IzN zRo{OlhHgK2YVTU4me;{=-@P7&W$M5B$lX`M!2MU-_g)KwE9C5Nw>bzTa{txpn{5ul zzjRau>Zk|&R!S3`%Zryp5ULx-zpkFBc#Ua`LV!oye8 zFyP9G?Ac@2)X?cG_^6FQoxXbVxb+lu`s$PKvYw(&Up;x;dWt%I_4Ff4`V6F&arNx6 zlVRX``xhP-)%Q$aZ-4gK=`gUqdis%-Fz|lx$>V3k;N0%afPAQ-!xi$OhAxB1hZ;D8 zn1jfN8aRV6ARlVr4C;t$Y8W!;ULha2SU7?!C9``HucG-?tV9*H^xy|NPt6!@zU!xkFdf(8cO24_s42HwV9V z{|w4)BR!DcI=D>@-5i`dIIo6o4(?cx%AIOa)>qTi8CYMHdtR)s$~`aESLL1;>#K6l zi_sqUyt)j=JuetsUzM(V**nAF`pPpHduJG2UwH02PeM&s?@LcAF7=LrG9l{xXl55b$LGPS9nHr@2DZ` zR}T-jIbbO7zxuPy+Ck?sD!l6SRjFV7`R1AOj8eb)v&~CkD6xXR`qRznl^Rwcowc9g z%bVBA291O3Z+j$O#SHKm|!SD*Yo#$Y91zde& zw)TsLSKvfu(eMg}UwCW1KNw!YaCKJE?kkR-P2iWqfM*D(U0>cUafsbPS=`ud#4DshF% zHVh+Pp%xAUyn2bccP<M|d=9!6on{^MIoPj*J_j9F@y-FD#OI)| z&hgFx7<>-;>Ri@AM|-X~6Z-nfI(T(-u)>m`@WUci-#eB7M_XJ#Bp3i>KPtncd%k!oH zM)^we(eYKBrFMK3`RH(kTfeH1J6v6!pMa~&^Am7Y-bTW>iVg^P#kdOW!YkHS;njdE zhtK7Cn;*axug-B}1h^`zGv;8NJ9qjjx~~Lm1ofTs^H339OtX( zvH*NoUqzP%46LsTt{m;o4AbC>nW5mSFav&hbXfpbC4*{6zA72iSS4SvzKRT94_9pl z6IUgJV4u=u!TPFnS(FT7M8Os7tMKZ2xN7I1{qjyccRq4ONGm5?{{a4PSuCFf7PvEP|^Aq^0ykC@KB{NE>uj8xIf5lp~ z^j~qTVxEH;SYKV9pTJk8|BCfhuurG2O8*t>tI~hP`YJeaV9}%{%V3AA$RPNN=U`;8 z(^t`%30(0URE3P_g0FZEMh3xGtgj-2;1XU3BZDxozKRUO!1^k>4Pjt?6`h$du)c~6 z!r*9kzM3Y3tglK2SzndTOx9PWGn4gIpTYFXGT8A|WU$j$k-<)1#T;Z71uexK?AF1^ zAaK<(gUvw%abgBrX0SPk5iK)h{mPla6>`i$jBsXfgRe06$mE6Y>3I|_t?}wm74B#t1OO3lr;4AL^kUJ(a0(`~2AHhE0EADTf z=01*h{3fofwge=4$FJ+Favz8L+sl0%?r+z9t%17D;Dq{BxsTIkFzZ((gHTK5K2FJ? z8f=dPW+)kiA=E3FflrsBCe!hi&Oy`wz*n3>`Q=_`C>d0gpjXZ63?+lkS6*i*8H53E zBZ>NzXE1RE7R8?$!4-C1FaTHFvmIUmSKPB*>I{6xMDJkXPXSkae=xiPuKaAG@)bsm z^OYH%ex%l~!YhnuyI4_gw-IsQ?0uIyu8y?@J6p(Wi9!hm&fq`%#~dheFC zGUCi))~|dH-UP0A4n_unE1rY8qsPO+S3C!Gb%p`B;yD-@1g`irJTf?fE2}`lz^CEL ziFgJKd>StE+)=`(;mW%4=>Xj{CZ<ntHPoh5{ngZRajIFsjmu) zD&Q<1;49CduY++<7^&smgd)*$?ioy6rM~hE7F zSH5zN@D)akWYAXUj;}7ANPX4DD)m(xtJGI*tWsYcp!bU^UyXZ}`s%H83ZU$R5#Xx> zw=NZ0()tw)&{y};`$dJ}T(`gF*0nO?(uvepJ_m0CS3CzJgTU3^fkkyjcY`n2Yn7YUtKtw>!8m;=c`g@aJ2i1qpA;H zb+{_;{rDWcU$ngUGPpW)q^Ke5?Z0Jc_Yv`(hG8AdSXo`Pn}f1O zIq!M~Ggek_#|Y10#>&nIVekz49K3*&YX4K7L7#(Bd$n0xD%+w(eMFl?RgP9@T`+*UDV&bv7JVm{o&j+K{r+-T4pvS7zulRhh)UWt_Flv4H zQ+z%c)!GhMQ7-{sO<8Y`nhbEo_4e{UnZDjGG77lj(ueU9V{7C_!z?h z?InYbEBC5oP;q4p2ek9~V4Q7_;L4r>T(xu1yaKK~gU-~V^yT5eRX+#C0DaZYL5me| zg$jLP(P{c*_!OY@A@eCf=@-Q_3X7J0Q9lK6ZZCZ@%vZ0Leo@o_Bv!sUM`vb-tI{v( zXA`b{O1~)gUzL7QKb!Cof)e2ppq;eS;0qdZQs3Cbt4d5%!UqTu}qZN)7kEMxKFK;oddj>a-f*m0T>ZxVoSQc(osI@2|)>Gkea zLAx3rSHlpiM9I1ufUEKYb;PdQ+yZyISiP{%&b+!I%9wergu?P{78o&;eK1p3_*l;e~ysEWua5wO(=c!ah@$z?F;`rdsJ2l@ZmD z`ie)yJLrHb-{ZKxidc2JJ$lt(@I6k}8L}?Q{a4Yeh7rEUaeWoNYA|sB)pg(3CxTux zxOU4Yww#wBBkZMq#r+IBhJI1*XOMnT`BTW?NIwG%$RPJKNUUT8(9Ziod0*cIukw75 z&z+^)P(}b(eC{mucJ~U;h)NNDpqz$_0bcRBbNL?q@7q~w;%dVAV5whm_fDx_mF^wP zsE8H#s&wzDA?sJAdj|$#A8{AB;{Gc^yBJ1rmGvv`zbf@B&!Fq}f-CO7DxHAbez7;0$U=?3V*qC4(haoI#D1ArZLp3_6P*zsu^0BVHL2;Z>VK z%Q?I%8C3Wf65&%wl#Q?tY9lZlCstbi-;vPix%B#z+9@&UuskJR(QIAei<`>#rOf_GW;e8v4&1y|gE6$)S^ zgN8(8u#J@=5gBY_Wk?+1D?{Q4U)e852Kn4s6o6!K#4C#~yyA1`U

g{w+&HOO`=+ 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

Q-?enZnZ+x&ND$$bf7deqlrbE<`u(jM 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

_}MTxw>vW+A8P1ug?y-?%OLWh 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 Y*;l1imy2$HtF9DXQwDRm+ON9*0!ECJn*aa+ literal 0 HcmV?d00001 diff --git a/source/sid/Yie_Ar_Kung_Fu.dmp b/source/sid/Yie_Ar_Kung_Fu.dmp new file mode 100644 index 0000000000000000000000000000000000000000..d0388a42351f2c7b10da19f9120df357e32cfddd GIT binary patch literal 658775 zcmeFaf3RiOSsmE!+kMqP^yGf6tJitD@N9&7P=u)h4K=hOEG$>G zc#J|ZX<43;(t*rO(Ky0tLP{?4quLp%#uJArinJ1Uh+4SHI4#PmN}fC^LbV5G0^L-B zppBV=K`xIq%>LG1ci*+nw@)LF7@R0i|D5W#wC_IO`PR4B{`R-eK09{w|9kyU?cA~F zZ9D$#mfd5fQvhSB!HjZt(O2kv-2YXJh@vh0sO0-|0IB)->sJbKD+ZX0sPb5 zdI{jycK&t%&+gVs0GD=tDS(T+^%B5u?!0kt0si%Fy#(-EJKq$*ukY4N0Kc>IuLtn& zcIzd8-`n~90sP)>y##P|=YJc(|G8T)0sPUO8`eFe=~spd8(HH-a7fQ0Df_*mjJ$F^3ec(X{wh1zH9Q&1NhWbF9F;&c_D!3 zr+NwCp2_b8@NcGi3E+Dtue-hgzcJNI04FCm2XJYsmjF&p{?h<1PxTVO>B(IIT%GEr zws@en&`a&(M{6H?31D;b(*bNv^%B5+lXC%Foa!ZjdA0LLbm12{L;OKtIg z*A{xIeau$Shh75MoP1LNTT{IRaB}jU0bH8uC4f_t_Xcozs+RyxPaX;2>Qpba#XqPm z^iuoyUuqwE31D;bF9X<`>Lq~tCchcL#i?EbI6nEm0=O{MO900vH{4Kwb5p(47T*xS zg{fX@A8!rd;#4mIY);-Cz}8eR0i2vX6u_maUII8Z`A`6tr+NwC^yFg!T%GErw)kXi zp_kgnuh%~G62Ru<3ju6R^%B5+lRd95z{ROv0ysYTx&SUr^%B6b$zKcL+*B{M#qrug zFSU;!s(t7sfX&J20Jf%j3E<@99|mw~s+RyxO@1kW%Tv7saC-9D0Ip8;a?KW_eT?4K zeG@g{;uQaiK8{b+TIZ~AaZGnf5lIon&_}| zd5V7paET_^DG9m9xuJ{40QK=%7j_1vFZWYVBrK=dG&Fyi_|N>+ zX8^neaIDT5&eb^sFSP}x7Z>U*gO}O|CL|Z@yo8qkHtS4gtIlM23E*U%`&_DXA6^1D zRcA|=>uiaa08ZEW)zvz`;-$90RP1t{k?~Udz~t>xox|}Gz-FEGZPi&HF9Fzw*hom2A?z^OV5zg%bGyaaH%&fBlnc{?w)1*Z6y>r9`Q+6R(?OEov(C4kKd(ul2^ zP4E)HeG?=e7pHm&;P?co%7v+30ysAL{=IJv9n?#0@z(>mFx5-#<4plvoa!Zj&B=`c zY)$nNz{$y%_7vdKR4)OXn*4SEm#2CO;Pm7(0bHHxrMCDdwS``4A0MfG=p}&7$&UoE zHPuT1_f1X&aB-@a0FF=I5x|A1UII8axg&scQ@zv{>$QbmY9BjlA9@L3bAtbMYpRz3 zPS!py)joI$;8eY<%k{2!3E*_K<7%~om)Zg*U#^z(Qv1+{bZLrz1+Y26W8IqSC4l>? zKQ2~(@DjlB>cI=ugS-TAtor<1^*JxK1?X|1vV@n~hZ2hP3Se^r3T{;<@)E$w%H2zq zySxN&s<6E{>T+TG@)E%55+CI+S4(^>FSW(b*Z);7wU18+5W%;+1h6^zjrhw}iQ45Q zfcr|Em%m&ralX6+aJ-BRgR2fCfUoMwXba@HjbQyQcU#^yMcX_ET{%!qV z^-}xz!vJDXUtR*(Ecto)%T`f?#oQDs+Zcw%>hJyzPtpmS@QGpm#vbYFE0VySMu}nmy0Do zUtR(@US=}oFBi%bW_bzVSeg5jznm+Rp5>*s`04t;>ZSH^E`Z3-mzMxGOMYJdvQ_f) z$%zoFSP}3=R#RE zC^#>*4;T=MIr_jQfX!<8R<)d$0Iu<@@K`U`r^`#UI9=9D%8y^I{@^8mQ)Pvv0GG=O z%konDfU_epF90tAY*v*X4?yhMxBWgV{k_|+QcyaaHntlSmga#^`s zUTPm0L?SUS051V-*3oCHjy}8uaE0 zl$Y8E2HZ%@3(iXbn{}k#sv|Wo0bFBjhYVb%4@Q08W+sya1O= ze!jfaJ}~Kt#Jm8!1h83WOIvle#7h9zm|tNkcA?J5c!?IrN{yxb__;cV<0XLOB|k5~ zg_55yFSQR$5F;@!051V-)|us2omuh{z%}Ngn6_T7vsYfC#pzOyDnEX;&UbkU;8e-a z3vjvQ=gUj&1C!~k>5l8(_PW0v`FX(u*sQbgt?5PpTXixX0O##n)1v`!`X0dX#Z3Qi zT=&-3{p|~N!XLn~36g@X=_3K0t0_Wlfiz-k`jOfK|Ehf;@z`Ec zHvw$b#3foV$2q$~JCLRaaH?iSXIEgy<(eD?aC(9~>g)>axLVWn+5(x^*%jDvxh7z> z4-jan3$7S_*daHMX$!hjtB$M!Z}VWh7un3$7S_*d-%iFq5Z>c4PTNX#3r z@ULhQYslqUHD2Lg0Z`Q%uOMy#PzT$11y!sqpq!0Y_*d-%iFxA{{uKb#x$z2VdI0y; z{JilBvK|0u0F76e2n4`61MRRm17;btLuMJAm(UKT>GiIVn9~lK$wX}e&x+$N{uM1a zTcRDDGzGx<745M36=r0#LuO>0!yTDZKai%^yFy}qWKLe;UjalNr`*+%Idu^K3V<`q zBXf>dm}dsSx#*EObr3Vt+5)rJBXjZ!|Ehf;F@JDQ{lLEh;0*cfoH~d!J%FfrmAiUy z4*ifhHD}>R=FksL#-j!2?bA8*!{+Un>Hpfz&<~mEGdGydsUJww>s=u+KQf1YNH!7m zLEIJeL-G%1ENAD?4<;`0Q_OK1uaNNsaJ=T{jaSH#0$?81c!e~*-W3w_#w+|QfT*P^ zukfz`nB6sAA;}AX`C#J}vcuW}8D!&C{TKQ`V%~U#e?<#ssf|}i(*uaQv+}CtwZ}{S z8~}NRe?<%C&W%^dqyu2K-FSsGz4n2`yzvVE3V<1X<5g=B^>aKc#z*VpRK2S96Em%D1?RrAO3nxD7vs(FxWGL2Ww=Ul~U zylO1D2Cu-q#;eA}sGs9malC5WJyrAb#w(=h@z}Y3)p&(y34kkJjaP`T02qUfR}zDW z+QuvVD_TVT9Q5IMg?|NbyyoX^yh55D0N45&uQ0OI78q|DuiEIt{Jil>#w(0zjaO}i zi~9MJS4GzMzU_5C5q+Gh`8oX|0M|vyD*?EY+Wc{J@lzbz$*Uskd*537;A$=X@u$;I zEP$w=FL||b-5)K0<2660Kfd$2H|^t3aSfUN*qk&wxav%Q2tXaAeh9#oZTe$#B6c7@ zr$06)&5lzwKc_$b{-oK#^>+H>mnO{)uF%sT|8COkV17>hkf+Nk0QEzF`)Ynp{Se@I z&CjVHW&XAB2Wu155Ag@98PpH+2WC_c&Y>UT4^~X5AL5UbH9u#ZZ(R3-3rC%*`8j#@ zr_+xu0M>CDuYPL*u<}D*{iEsE?5EE;7bUNLbo#CZ!0Hlt^7PG0?^>Gjt)USUq%;}yzdJzk-p*5egs`aNDDKkxAh`9qIa$T)huLXOho6>}Wu zhp>cI%N*zTS$-}pjxRDV<{!c<&CiWj$me=-jtr3gFkaQ1vF1_6D`c7El|=2SMShNX zq5!NrlUD+;QcYf2oFiK%uO!Y{4W}Iv=l3o0bIcPZ&W|thbIcPZ&RL`H@d_Dvk5|aZ zd%QwM-s2UXRgYJAx;%K zK4NDLfMI+vKc_z=K4SL{S|IWr>OiC^~0VnGIHvN0I_eW zw4iZMZaBQc3h(Au%`Z@JO{1N-Fa97X|@ki{~DggDv{Gs{z)s?0nu`4WE z)b+%RD@{MBgN*aDD@{L+*ZMi*{Na_RAF)>rKZSYXT`NsLVs{$=_~X_U(GO(g9Ix(L zY5Ea6=t>Ln>fx29AF=1I0E}~E3G(wEuTVH=oLdXz=ZtgFbLQtgUZIS^I5%Ele%0d@ z^79_AkdYU9#HzEfBz8^WSz+82fcbgzM+=JI7i0O)4`gf zbaW6|O^;W|gL=F|M&9EUa;+Y(ke&5-g?z5ZD`bE@Ueye;>W7V2$TH0z|69#*keAxL z1OW4M^9O*~Nj~%kfR;OpKLEu3_Mtxj#QydIm_M`yc~$MuKIjh#1Kt(=A%L$l2oU?* zOXu`Qv2&?|T+0~7N4!0N7T}ePyUfq&4;go3e|uP7{bA!Se4g`)z# z@QN*<28{E{EB2xKQF#R*_P3XxGF|~_Yslgct}}4eS^NPY_P57R)%u`}yYXfi0PzP} zP-{6)lyR5pZ2mwCUvD>F)iwDv?zTBY)0r31o;ym`ZmuHpZ zoH>rgIegBn$l{#bMg5%qFkYb+YH^Mh%+=@*n_nTjvp7c!?r+cfajMqOEza2oa?Y$D z+~1z{gLUVuAKc%b^@F){)(>Xez4a?p%zNuswc?)igZ1+suaKYjc!m7D$1CLLJzjAw zW2hh8-`?X@UM~URxWB!}E3C;Fuh@btk@1S$&AbA@l_KL60Pg)TUIF0#cH^H<_`d@pPN4baDTh`0|588Tb#26@^kYC`@nc*^F#n#fi!;r zXls^3{b;M4;t#a&by11)wvw9l17kbwkT_qg)+YVn{;RAXToI-nq90sC&icVs=bnB* z2eW>(mF=N^a4kIR2UpLte)xL3jJsT+@978f^Bm`_0vNBrM6aLMc?kek8jM!}Ser0j zu|=INRe#j^F8j#!4*;x~7_R_${oGih^>YDO$I1G^%8$i4xy!lea9+aw?H1=~;q@rV zKe)d=>j!IISwC8p4E1xF4sza}^@ID{vwnE}T;_?a_GSHGeK70CqN3Q*57r=MyrKtL zwUqfTeU3cJ#w!4=GCGWpR!be~2dl3(Ua=3Y*GhbFe|wKt%sFMe!cVb^Z1WNTtUDX8 z*h1^)#m+_M#r!<$2dm-6E83y;bICtgaW`I}1#9#c=b$L7`WENpE-L2c4*=XbU~vw> z>*vNRt)EL?>-!lb|KJV?^9TDteoi|ioVmZ9b_n40a{<~;j-h_Ey&uCkU+fm?=!dRf zNt|CtRHP>%`iT=7boinckg8V@bOCG<9N-_vwm<-5&a={ za2JyCirmFcCF2zU?r%3<0qE7w0i3FDJLNd{{a0cKcWfE2*avoX**uYK=c@B?UgGt0 z;}uu7Gq1FMF7p!ZZnHQ?=giM-p2)kZEA%=~WPD&vKkLV-S_R1Y2lw@5{oqc(o_-)d z&-n-Uw`cv}{3`3m@mev-^>gll?C}cud5>4f&wIQ=e%|91YDIZoa*EZZG%w-)cAM{# ziI^exc%}98$`Z8TF4n9coVREF;Qn@-m*A(kw>IkscjFqb(1Q87@rrk)_47L41>pPJ zW%}X!+Xe7_$O5#T%Q9Y(yCd~;-`_6!;rrV~KYV|?t&3{?+@h9QYL0WwYo!JEx06>@ z&uNF&&n3>eTb<*TtW&rB?WmuNezg7VsGm#z!TdbuAB$c0i~XW)L5S-NSwFbHoxG}b z9P&!*=T*-E@agvSL+j@d{$aNm-+|iSH>&0(E7RQ zht|)9C2iIxaSn=l{ajkKnWe-zTC};So`5<{E@o}nFcys>Ia?YWCv@BJ41&WT=&s!!eyh4kXI}5J>EV6Co75f;e zpEIL3UZF)>1IWDMs=-h{dg~0JXj_?p9i|^6^>eO%Wc?VepD$Kel74VaCg&eq#p&tC zNd25AA!Pj+t)Fu>D(lBJ)~`nD=giL;=b{E&RqN#+*wM~7mlj;P%lg5!zMOw7RtG!0 z%HtL2!xhLLuSV+UZIx4ag`euJivr;O_MU!>)X%wEYrMiwaesT(5AJX8tzYpSEQxcD z?IZQ`-a0i}w3Tg%bB^s?3zs-&L}7QEtup}Ndb`aN*1DW-MRhg_Gtm9<;VCAQ$ADD|~{a|$|>j(F@ zXZ_&*_N*VR<|VuWMO&3D>j&oTInG-pPI!f%I$7U8&vAaL?#4AG0Kon27UuwXR+z0bu*FFIocX!MIa(|#yh%S!*7wh|esF(# z)(`G)@2y{$gXjl$4rKk{v+D5* z``dfG8mXW2gufoIM(XF>-)_7DMSHvg&~|djyaYho`yqKPTVOS+ryu(Mhtxl~zdh?m zi$RHVFtP1=kvIp?+b6>o$j{9m0NP%hp?)lO@2H-mg};v@`3Lv6n?KNkdy2Ata2HZf zKXhHRG+b<_G@dqu(ZmO&w+~1z_5AJX8=?B)3vwm<_Sk@2fVAc=rZ_oO{ zy=qxMxWB!pAIQ(Mek^v-b=I#&>gR2jp708!?(ORX5GUOZ_w@m2dk2MA02aFql~(|I z?+*fq^V>)EUvUmM%s<+$Ntq{-yZZjQ@rpY$CC<@;``fdAaQCOhIa+YPXx0zzZ?`yS zANu~etQv3^Yu1m^`Z;&XX8qv)_N*V=-`>-Yk^Q^e@!MO!8mXUiS8>)4?nBP=67F2K z@rt}+Ej9D1?vC#8YNUS79oRNrp^rF|9GfGBS7ai0c?+)q^!9}VXgkS;R{&b>EaMdb z?r%3<0pR|2C%j<8bYD z80S=jJWm|e4=RrFit#a0Kc}kMJdwQ0>(l_KoH_q!?~-L+AwRb`$4`yc&lhjFrTl|) zhMa%2Sw`M}g?)WF|KLm}>j!HSSwA>i%KG8&4@#W#jl-UPa1UhW)mK_SZ!^oxtC9No z;_b|ie&qe4WcycIKW}fD%6NsJ^7m5fcm<%n$tvR&xvTG=*Yy(KmA-#2^^ei|Irq0` z{b=v-ia$Wn_SUb&Ik~In!`Vzm&xe!u?%KP^66cJlk@wHJzundu*y0tve=cg!vQ+U0 z?a;hdTC_}9{DBrNcOJ$$_qXT#!{0xb`Z+WDUjBhKfUF;UD?aN7*BNsD(N-pg@xlB& z=O0}C$ok>yC88hP-%ehMesE3Zq5a_1GrL>4pQ|_z?T0@EV1EA4ew@Q10Qa{)v>$dn zvs?BjVr}W6{qV;#yW{*X}o{V7Ko@{-;eza z-!hH&&jBz$zp@59iaEJR)B^Yf?IelQL_to{JN)!Hj-cz>|^gLYtj_ohQ1 zix$-%0JtLj_Cq-Ts`>)}*N}U>LjAnQE9hX4S9mk5$1Bv&d%VKxd5>4f&wISW44}s= zsMJnxFaR&M~lJX zyo9@6db~n@-s2TVhCDC9T3?S>c>lb|E7Z?>yh487;}z=X#w#)rqp0zU+{L=6@d^NU zIvKA3aBMeT0pR|2;}rnzzcOC21r*14#XgXqTbzS%+!bd206-lye*oalGmCQoJd4%* z!4^Q_0bM%!I{i(p2+;1c9hFvI>_CISwFbH zJ?jT|G-mza9>}a8+%?(L57cq8esE`I)(^gGne~IaKeK*tzi8GE?lA4?2lk{M-G?|| zU*;d&-+pu-=IsJ--hOl+=IsJ-r|i*v@W=Wx|KQ%*qx&##Uti`Q+>Lv5ALi|ETjn3k z&wqFy=I#I0?v{UW$M2K-FmL|{yCwfXj&ouLJBm4rq=CDNPt0J40NjUsVg@?|;Lha} zGuTn=T>KRC^Aj`JQOsHL3iF3)FrtiC09p)6oWoHq zY9-DA^x_;q8yO_d>2r=H66XLo`j|feaJ({qum$pS^9TDteokIV)N&LhuLR)!cJfL9 zj@0Cp#5u=y@=D^IYCtL7U~0F{%xl5v+> z%kfIaU8*yAW#caNoV=28m;2kvD;ak=XP`git~kr+#X0H*InFtg$#Ks5d5&|w4QRXq z+d027UIE~Y%y+uTp^B%8I;qCDX`ML25gkzR!yaK?wv+)W5_qQ9b05EqpUa^94{{gJ%KQOhw@5VaDRK&5AJWL9p$o^Cvr6^>j&4bvVL&ItEV4` z!K@!#Rm=Lpb-1h_+~1z{gKK?RKe#&B(+?f5L_fF!ne~HfmN)IiyyQc>TK$9j+i%*7 zd5HjA7rkjO=IsJ-CH1Dgn3sHLm()K{Kfh@&<|QB675*UGv773qy_mQEvt6zJ!TR~z z_hR1u8@pQm!Ts%z?!~-)vdllYhJ5=$*ip<`@(R1cZa)Y+1YmxC`$5 z>JI?S&);(p`A78!?+QB_fAS#mkLnKqtg-ZXh5C7qSIEzMyh7!t$E#W^>hKD)*B-A> zkLvLX6|5ewFsC+NfxE1d8Lt3v-fp}i+fj)#UIAe3&UgiY``e9I09YS1UaNtB#5wEdv_s;Y6?fVpan2e&c_pJLtNP@X09*7U$$H z$C9CbaDThSIa+XkyTv&G?hvv#2f+R9SwFZ7siz+pce8$Qe|y#sj_p}LxSJ~L2lroP z{oszRo_;{1dc4A_bB|Z3pZ9nLGVs#w&6cwTY}B+-+#QLJQ8Xj8_1-qtSQ;fM@)gKgdL!4`==$cTvYNe*oal zO!EhTHW!t7A^`4hH-7-&eo^xWTcCnv{$L-ddC`t?SxoM@zdh>*=k2sZT5zXq)(`Hj z&HBOp?LGZKeK6|>cl>7kV2+dZgS(2eesCXh)(`Gn?&$~e^Q<4-9i8=q`C!%$?r+cf z!9Cl?D>4yTs_}~4McvtW1%NxrjaLAeJLfp({&wRPT5x~6@d^N27_YPsi*pc;cV%%7 zfa?qv=K#3B-QpYopOwWqTVVCW;+%b8y~N@i06l1N4uJdHvwk#|4D%22D(eTC*wYW} z{mA-3wrBm|`c>8sMpV|17K6jQwngnQuk|>$nGEXZJzjAvk@1Qw!RXWD74q{QuTVel z@e1{G;}!bgC~CX{z;#jM6#(vUH(mkY*lxUH3*_g#j!7AJ^es_ zp7nz>g!u5Mz0Jy)M-xIFi!2<9a+uzRbpUcWN`2&D||6JZm;VB;El>pq|PCEqP{&w0?{lPvkw$qO458f44YiWm_1Bv=M z?U47QxWAot)b}RvQ|*it@drmZJs+<6gJU9ekh~J0o!kOm*|@9c!^yb2`1)ymKT5`3 zKTk%+U7p`gUdg!2HU08f2XW3SK#p_P4UAW`9F+#+6~}hu=f*3xz-~k16`vL6SH>#< z+~01z0?^L*g&oEc{l1z2tm9am!?o?py%Oi-73QM({aRL+EY8t_``h#TQLJEDoRe3W zQ)m5Xr$A->z`ob4AFRY>{ovl(tRJlQon0B~2kV1dD?|NYMRCUsSwE2D^mv8(d5>49 zbM|KLBul zyZM7H;6d{T`=HOo9{{+&J?jVex6=+80Jy(B>j(FK(2jCh)H%6Zq^BQPzsmZ-9VuBq z{Cqf3gLbMTcqM?J11Uf|IWp@9#uAQKq95G7Ltcq~_;@Ay!5u7U$&ES9(4icb@gmXW&^c zGEYPw+}&nz4q){AYTQ9*agG+OpJ)AOr_c`dgZuijesCvXPd`wb$ojE3O*iRB`##Q4 zKe(eY>j(Eh_IQQ*d5>49KLBulJMAc!Mg4=PnD_KU^IB=a{q0#lc(yw2 zD3^tPaDRK&5AJWz`oR`G{Xl+BUWppCQ{{*H!F7h5f6#LBO7agrtE?Y`FP|?{&}Q^GhCFDIN>}c9ff6F!9JrSz!qPb`*atfCpC!69a%9#Vre7on0v_ zECIj{ITvSZrL3^L?io9$2w||}hO)v^9(y{!q&&~?M?SxVKtBJ9Kt8{OKt8{uwZQ%> ziI3I?@(;<+cU;H2f__MTen$oJ`6X;o9_wJ@>n0L>rR*C*#E zHh*AWpPb*`^9PvN^9O*QKLGUn!4}yc>?8XFK+hildj4RGGQzd-N}_gaT2>$f5PzV> z*=bR=0K^{v9-I~(3_$$B7G>vI0OAh-Thp>vEdcQcfF0MB-EFUXhP;yVOYXR?>=1h0 zGvt+=zuV}O;{&0V&o5~$hU0DnhU0DnhU0DnhU2dDO3p8FUdj0-#RJ>=QC22|iQD=y z-`0=Cxi~G<5a&Iaknzd~eHpJDknzd^8Lu3W@d|*)`76pU^*$GJLB%=%IO5*n5DIMj}91vbPAiM&woqx>n{znkO4X+xf?K{;>$0?fj#zm(+Cz#s_np;kfJblI{Eha<{#HRpv&e z^xM|qC`I}F-KJ67`Nwwtv7LXU^{egtBYpp)B@M%MxCTgG>q*0Q{!x}TnN)ArKSHBq ze`0UFopYb<`bTVIuXQz_4*vhRe%12_xVv5d*sg!Hb3eB8kGZT`9QAXahy2X){M`m1K6(K1j~;;f zhnz#-ehTv|IfuRhScj8y=s#BPDxb6c>lLtboe|D#VdreagAeV8e%LwN0N%PE`+e-3 zZ2&jy$9^9>XPYgszr@bjW*^8u?3`@?AKQ<*nw_%^;MezqS9Z=efbu-UANl-t0{Q%Q z0{Q%Q0{Q&*)&l#lsz0>ze17{MEba>XujKn5Ut6~)<@4LwBJ=8*-SCH<7Y+}8a5wy6 z=Y<3KzTNPLofpm)@Q0lj4&a&H@Q0lj4&V=V!ylPfUps|AGOyZWhkg`u%3#Sp;3(sj z1Bj1a3&cmUGyN3eqn;P;KZW?H-ZJ{>Q)GH=-X67~0W*p&bnv+R=ca9Ss=T@n`j}a-4t5n0^!{#kkwkj~wS9 zTx%h5?tsL(0}|&BNSp(ps`c~(ceSk_%fRW4R~Y(w;}w7p?#|;CfNlNI?~>t>knQCX z`>D$SvPc?ktbt>C4(@(pF)~P;1NgoLAaM@hPxo>xkvR7j66e~7#W?`pmFYPEx1;=P zVaJcxd1BU&pR4o4tRKHv=ZRTAer~_=O1}T`=<{8Ig5E>0}vlQ0P)cS5Ff?P^iznBdR{o~02z!|w1X_cJh6Un0zmTz@{jtx32kBi zpwAH><_`cpe*ozDgDtW@*azan`~jfn4*)%Xum$oD+EJe_`#}DY&u{R=ePgJZsZ^N{Psp4@T&YPkzqLQwid&2w*kX( zw*kX(w*kX(x5YVlCFi$0ujKn5|6`pq7_UIM1`O?Jz|f8c4DD#Z(2fQS?f75oUFA5R zjOj-)AjaLEeh`p22jN-^iE{@e&K;0AcK~+CcV(z*J^jF4ZR^J}aC+kvnAjVy0Q~N* zJYE6V)(`zI86FAQZgCFo{_l$cK;j(0k1ha-a{zBx021c_KD7s=Ha+(i66e~7#W?`p zmFYPEx1;=Pk$>D%=ZRTAPSkl~){jT_>Aa-sIr=!UPkB}K9Kb#MR0k!_i=ssSk@e$f zohQC_^`o3e8RIU`vy@X$8vtJQ0Pv~@fLC%3efuelyK)YF1HdaehyLyLuIyY+gu$Z~ zuyZ*9u!WtI3ZLJ!7xODSCl$bzJ(%y>IjI1i-UD9QIjL*`UfDUR>;t^Ab5a4keJ^-r z=cEF7bT8(+c1|jQ@;t*I`TSi1`TTYQ`TSi1`TX710{Mr0|D*MR{k!u0k54Y{>SK|A z$oD_KP=S2@E?Zy#n~qF8IUFdj;^x zUGPWd)faZbADLI}kzk%!%vsD68-V!e0f>(tfcPkOrk_H56m!bHKH32?7_VptS%P_D z{oVwC<`3i_^?MWA!u&zPWq$zZ`2#@DA8e8R!9KD-0QCF;pyv;^K>k5H>eFQ($UpM= z?eDJn2YDqezMHu`{9>IQ7>>IQ7>>IQ7>>Iw&cQ4B{)h8QzW?z=oiiA(K)41B?P$Qzjs^_vXu!~p z1`O?ZuHIFS^Oq`+Y|EMgH+u>pU^*$D8+Jo|yIH-nvem_2bQZRR^n{qw~MI zS9MV0yeLZKA6Y+cs`JFxu70>biZCs7umQp=P_zNUD+h-9(OL+vyoK-zz;^zTzCYOJ z40!*%ItrvFu$_M_ziYXjf9Q8D5mCK-ko4KkKjM9ynh)ZU{AJ`HpWIcxkk{tBGVXq8 zS2=+{fZ@3N?p@^s{s4yK?lrW~$9CtsotDL|eEr#RnCwG<8LSywK z=O5*K(bqkbzyA@h34Yxd{;a%tJUG9-oETRD*dYf*9$qB?I|L}ThuQ*m$T^U$57;5+ z2)909hnyqKKI~jhgaP}ob2$O9k8)X_*9S7N4?C|90Q<1>`T(#GJFgEw>jQSkxr40_ z*dgZ*E}m807cS=xvXB1xKH-lQoL^F(uG<0q5P*H;^LN=tK7W^XyX>P}`r|IgD;Zwd2gfS`=yUQakGnj_E04Q8$19J!JjYAU z-*sNe_dnXR8nMH8W!zO>0bn1-D*&wz;S~V)N4CRwrF|H$v=912SkjAgkiq>SE!-b+ z?g#C#^Inx#^vAY-@RYRSc;!C-4|iUocG$cGK#x}ddb|SA+1)A-n<<^Z{7}|H|_!uJ#XACKwm!!!1=D|2iaaK5ut;}mg`sVxCPn76MGQn z0x%4o*aJHRczT^JV8^jttv=ZLfE~w{>kO?A*l}`Kdyk%dJhun)tDoKVM?3OkieZY=q_k zoNFHz=ibLioYM{oXZnM72te-AjvVJDz++)6$N3X`#2+0ya-7qS9OoRbWE6E?3E;dE zfOe2qdEDiAmB-z~tKyFiukyG{UgdGuc!i&$9mXr|!*~V2{gLgUKe8S4$F_cydHc40 z{QEyI(fF`=iN=S`OEf;V^#k+7&bXWBCI6A?N15r9S0WRk9{_sub^xO1AY6^(0N6)<|D1i;ITPS6`^e{)(2jh5N$Uf4$a|@+57;5^rLvFw z{yF=|@1L`ee11vm1NlewhuV?PFJT|~{1W!@^cwOHIlrX!f&8QTL;JYt5b_T>zoglL z{G*<0fEMiIHOVWD591Yp9T=BVx6IJSNzejBgc7u|D1i~ z_s`i!xx{*j46iK)N5)-_SJHxgkXLeURO>_h(cx7dcgZU`zl42|S8{$ydsZWM7_XGO z#w+En@yfY7Vn;rwsK+bq!+52A&>zAQ_lE%NV_QFVw{^JothV)Iu`b$TP{u0|u01Qn zdF2&=9WFnpw0OTJ6#Mll1`Nu#%0QAOP0HWuOy9Ma$ zM**^aa8=FTOT|xp<~HOXcCJC|qoe?QpL2bgn4H;Q3)ms&m$W`$hn!#1`hXpBehGPH z@1>%1_F?a(0$?BI5_&H0rLvEFehK@?=a;aLe11vm19r%JsjUy#A@8NKkNo~Q`^fK~ zvyXg!N$UgoM}6!FHTIFuFJT|~{1WzY#|-(0oL|!VfPPeeXdfS$A^(u`OPU?XKkB&# zXu&>Slf2USFkWeV7_XGO#w+Kp@XGrTUTGi3EA7L0rG1z`;3)ds;#_@haSnh!xASD& z=Mv}Mhs3$|VR5c~Se$zwBXOSJKWBX8_s`u9egC{}B+ciSv}Yy$=-5%ulj+z|&y!I* zN|?lWC8KDILCi}8@bO9j+Cg6BahK;U<#Cti;^c9cypr=vTAZU^E9aNImg5|ba({@h zxIYANe`H?e^RHSTBfPRWS6*41E3dZowtvayB><`peu;W6YEZdb0Eu&q)a=9JT*mg& z0(K<*D1gip(W3P+)DQMCrXOD#(~mEW=||~&n18U3vHaspWBJFI#`2HS$1wkBcJ%X) zVn;v!cy01Z!@k;wJUTGf|=gM7+bBzy+bB&L&{G<3> z;@ta?IM+Tb&b1GVa{!EU+L7Zt=O6S3?T{86OY-@Ntq<`>#|~L%U>~$2greQ%<17cF3ie22GcZD|2J>U$Gv2lpY%`%&z}-n-L2ibzL3Sj<@z!`R1> zIs732?Km=r9RjeABXihMzZ2KETkJSezb9N?16l0GZGFIwiz{t?mwh}q$Nt@`D{cSo z;#t-2LC$Vy`*+`Q%YV1$>#lq8!Swxe0!QZ14|!dMK>q$YfnU2B`*-C%mevC6SMnZY z>jU~B@39bga1Q;D_oE1$okKt5`-2NW-{*v*8UTL?ecoFwxA!^G;{6L?$g2-7fFZA< z#T&1CG4m?=C_v^_+*JWGuXwx0E49OT<$&?0}|&Bh(8<fmrko98$BwjUO7#~kn%d>u*UjQ;*!ROB}fMI+@ zi{g*0AJInvGOyyU3XpmA^g8@uyh7&<5MDVTymCNz<$&<20YhFXV7yWw>&KBf?BKX7 z(~ky-KfuHW$av*|_`?D5hXWGl3Yb3>Fn=gOJ46lI?TSCp;;x0|;tv4#F97iefQJ`= z_`_Sk4$%+xK|4f0cvrMT^yAjrN7j!63qbq<*M9kCGEw{iptTTx0BC)PKLEsCEqNvL z_OPP>SwG@g6(H-!m9@+(^wEH!9Ss=T(SV^H4H(+d0O6H(ZoE>!c%^{FIegAOY@P_< zrD}Q3KlUG@qlUbC!vYxcss?u#19+j{Rn9-2UEJ<4|M++XvVO2d)(`eE zoe%Y+J*%O9G&_d*2QAO~!DnUT6&$r_u`=Wp`^fsySTf`l?XdFz&<8Eg`oU+FdBql) zSM0-h<%E;*3P6unYPsdL0D8Rg7Q!p-!*~T?@vL-S0-!xBEW0<^mt#(8^I!#Ho2i$8Ez&MVOm z=auM(^Gfm$-WA6y0i0I?(DIyrIIm>gCt7s%Bl_s-N8D9cKTga>^rHd7D=@JEL;Yxg z@XA{Z^`o^I>IVVimHw3RN&$;=xRyVa_2ZFhdDf4|7r>BLPcDEVuUd;Cuh@shx!zUQ zk9%q#SwD_0Iv?uCZ57D+!4_FR*hkin_N<2b(d-!N2QAO~!Dp5AW6>h%2m7$`N?n`R zMQMkPS7<@YvwrYdWnQsG<`w%eUg=#KuK@IT1)#A+cm<%xD{mpZ(msq=02UFI^rJni zp?!Wb0JIho=KxwC66ePDq#t}%<`1;svoe1Ga9)WTIIje7UJ0;xR&~9- z*tuBpb~|MKs#z}nz+E}7L_eHYq94vHQG*tP;t%yX$17>!ypr`R=auM3wCL(bJgcsL z#9ejuBkTwutuut>0ETwNvjQ;GkJdtX1!mGx1{M1lCXov9%Eoiy%3P3!o@>3S) zjfntYhtP+8Wc}c?vUwtYigx7s2Q9aC>c*?mxy3nv#k0~l2f+2jtRHPLbEqE#vVIWA z`q5ep^N-fYP(KLd^(z8dKNdjZRRe~+@^xy74_~L2`0#aViI2tVppI8yiLX-+)_04e zGOu{M#w+|(1B6!&AU-PL92n|H`ze{1v=-p5j8_U6uN1I(Ndva)A66N4UWtAZ~V~fs_ zf7Hj079XfUKEH%5^7$p~BcET=o)z@tx0mk;H#@L?CFhsW^0yxvo?pUem3hS$`CJ3` zk|1zK8#n&#C(29k5_2XSR%Xv(BqZ25MBXj z{=mMzPw$uWMX?-hyh4litR&7spZ2UI&Xrf@4{srH4xsfR{s7QC2s<*b%pYjMXJ!5X z;JlJ>c3uhKyb_?@mH4A$hs1e%R^kt|a9&k^0B~OY{{FVl(s`9f(ft0o^GaGcuVmbH zUP*jJi!v{{vL^m$yedF`|2&>m0rLCjEZ5{XZ-DR$eKcUG9}N&*c?;o{14BFbQ^qT8 zVZ2hn;v632Pg$G;U_C192kTLG&IDSp9(84Hs2{DxkXP&@>j&@3;v9Xju9o$KmAETw zL;YawF6#$dWc^?tSwGsd8tO;0W2hgrJnILaRpu32Wc^?tHeRV~vwqNytRJ*I>j$4z z<`r9HUa=43mEM){3P6un02)hVyaLeUmA4RHX&=Tb06ks-XwPb>AMII5oP&vcR^|@? zt%bz7a@YI;fOlp70KjKu{s6#dW&Qx*yb?8VUJ2m55}@6c_yc{kXEluTX1VwSEu2@P zAI>Y$59gJrL5o50hx$C9f91Rq{cv81eng9|e#Eot>POsFS3kmz0@!;+xZSWE0PI*_ z<{$B_06;$kXf5Cmd5^yJ0e`IHz0}5b{1p5l@6po^d#?yBXt}*t1R$PO`KkQ=AY0gb zUhp~l$nOvGS=oDDjaTRccF6f9w4CPw$a_V6R`#A3eu^#ZJud)E!}I%t1oHcX1oHcX z1oHcXtp(Pvsz0>z{Qe+Y&WL`C3$SbZh*!ya5E%#a4`)UBV&(hvkYc0CG z;{L?UtGKJuBJ+y3YrH}q4S*f>wKNA1A3Xr^QQu4T7Q(9rfN+S9$}0toR|*)f8cICnteTmkck0_G0|Xh(gz4v0ShIIpCI^Q!s-Eu2>p=iJ45 zbf5U6^-&y^-yd{d$@_ziKG1U+MH?`Tj|PZ8)V2BilGZ}};Vr};3gq_(`BUVTygx|5 zc%?sOyaM3<5X;>kq95)L(GT~B=*M47oGY)gezd16aqcZ7&K;0AcVNgX_F-|ZEiBFz z_zLuc{>b`4e`NijKl1zM^heeY`lGKOC2;;C^rJCxBtEi!v=+ViD0js;7hXk+Wt_|T zJ_KyM0^7OBk@ceiGG2KL;gtizD+h#E4hXLlFkUHOyi&m89E77C7UuxCUSfIm}w70L|wZcSQ{vApYxoDcP*#W~hRb9`j|AbrXw%A@`v-{EZl{Lus8j~)Pjd}#Uo zsP*m2X|_WpTm0XyEjx4nPPKKMQ12llr2&v{q;p75FF_oKKj z`kwUtbFQS`eh}+d^|6DyTzk#mKWB?yUB&z7)gNfVKEAk${G<8647Xh(5G)IWq*wEU*MLtgP&WnQsG z<`w%eUg?0$?^IX%0O;`wKw}B)sJsHu;k=S@*LfxJ5iN>r=kK3K9|g$YKaXcsfc*V)F1MA(I*8f^ z2(Q4z1`Ol80m3V9A-r;680Y*c4SiY>B! zun!xr)U`M59rB8HWc{G!HeTVU_^dLo*dp_aeHgFwu8dazdb|SASR&&UfF7^Bh44!I zFkS(m2aQ(%+Ov{42NT<~k~jyzXJ!8I7UB;8tq+NF0K6;n2LL`R^9KMvEAs~c=ar~| z^GX2cl>qIo#2@Ix?GXKF&r1A(7S1ct59gKWhx1C*pv9p0Lw(NiN?JItL_eHYq93ir zaNOmy%KG8Fl6iZ3R>CXXZhKZkJ9@nG7Q!p%uJ8&#^Wcab#w)bw@d`kDRc+*yefc=S7^c4xw3vVKzQXXFu$sVb3l0I0OF&LR}Cn4 zWxP_rc%^{x3c&3LG5sjFvz#Y_a1D?+cR=Qe4oI9kAaU-1#JK|I4+YF03eXM-XSYLu zZ(mq0{=iRt&jJvC0Qi9gApYuySn2#oI`KlamUl;zVQ0?8GOzb zb`B(fJFDgS{o1=10PK)+2j8~e#eHgC*^mqlJJu8WGII2A>@dp4ttHS@~$lxs`&H=PO#2)~7SLP1@ zd{*WU0DM;F4*g}v}Xl71aN-{z`L^ZGL%<#UIqX@D?2Ykd1Y~~ExrQ%Xm>T#589FS zgO+Fg;Iqp5(Vo>S)(_rQ){oaRuat>jfqpa(%6NsJYGaAaO91qE1z-__HO}QdVYV<{ zX&;5=12y1XnVy59i?Jl>2U)T;l{iO>HnvNg186NI&b1GVa{#=ntRK!RiCQ181Xw() zYKH)=h4=%Vw?4!ldRNrJJWr$@q93%Jc8GrXc-2#b;kfJLm9%hPiGDb*L_eHYGH+iz ztE3;!t5>2QJzjx{JzfE5ED>G-=<&*12(Ppc;}w7&uK=`XHPnyxtcLo*XO;7h)?$QL zSwETwVMpdw)(<`_i*rzP@vKt*!4_v%Ua@}guCjjcS!MlbmJju#@oJcVG+qt!j~0V6 zUa1Fdyi%Xrd>6pKcf7(~wPz)~0?^}?w-8*e=|_9I zGG2j+i$0Qm@UF6cG@lRkgU>4GAAD98=jfa*z5@MdcP0Ml*dhASEEj)R4A#6>@{iVH zB+j#b@UF6cG@lRS!+9k#;k*){wHS`Otq<`><5gLW;(S;1BYa)}j#sko6P6bs=O4i< z{FIEl=z}f#^DDGKUR(X)cP0J+5O$!&Fh0U^0OAj{h-Xy*8+QS)g^jz~hn-UdGK2@q zPuV$ljaL9*ha7l7%k%kHd{+7VtJVT`$hkQ5hn+K_cV*{H0EiQ4-Zu9Aj{@ZL+u5Rj z{#9wwKmV!#`TRuwR6f6?-4*mh=2ryz=Uyd?)nP{odXej~5Xkz`TJ+Ap!b3#;;;a%N+MS5Iw>=*Rhu5&d|6V}w`dH#E*gKc3nc(T_758s~C8Tx?t%@(P4& zfbhxz;gtizD+h#E4H)uD0ppbd#w!5vdPF!%&m#lj8ZexYo0^C1WUWtA@JXc z+&b6sO5)?dT*oT`zI?NeR{|WE>v$#O?yYkjuVmakGS~4+;^VHlj#siSdjDL@>n2ZUD+2(KIvUMXO_QowknK-P~dYv5JRKVDh`uk!i3`wxLvIsbUW zA@C~aANvo1S6M$^S_7{v&JhMz*1#)^a{w=_fmc~So?RoauzpqPgBBlO1Fy1vJi7*7 zW&L09ilYaOhJ2bi9J2_8c{>=XDcw&k2f4TSAn5^>_2p|0fS1-D3BddTVE>`V12BI8 zc*CJ5>rY8O$QGZdfcS%be5L~84**xzezOAN4*)N${ZR$P9{`?R+jTu2qWA;A$JhQ! z0JKB&W=Ra5h@dtqYhyG3l#2*0OaOkHhApT&B zk6pi1T$YQWHr1`O?Jz|f8c4DD!u@CpVJ zFkUHOyi&m89Iibv8_|zPW+VFX_-sT!o}7*7$K$gR{diaAY(zhf&PMd( zw%Le&9G#8m$33$V{Wvij(T_)FBl_|9Y(zhvoQ>$m z&um0Lj?PB(aA?6)8+IxoS1e026F6&UKrZL>WqAw?@g{Wv=NnhFf{3Q0GlTQ;F@L5KiXpEF#jNs^A7?!|7b0S>sPIhVg5lN=N|-e z{;>cOueR$SbKw<2tvz<(RRch3;gtf$D+P>K4H(W7m2lhjkLCBHysjqeS6)|>^{egr z$5$=RLF%D?Y}Y^Lc>hC|saR0hu76CAF5WBJu7Aw2Prt60KqjIpGn|(IXtfDhXMlb* zK<0@K$UM;jnI}3R^F#+E&K0nEq5?KgRDkPnQq9<|f7E%&Uvm9}Wx#ySb_2kxXO`#h zHUPZp0pOLKBivemS8|SU1Hh~LU4Rw_MOf@yX9etBX9et>Z4mD1b*x|6Iokl9TgUoU zKEM5Q>sY_a=eK`;9qU*5{PxeSWBrQf>D%`|uzqFdZ1b+LewEK}|L8i_uk!irkF8_< zDxcr}fpx53<@4JgTgUoUKEM5=>sY_a=eIw-j`gc6Ysf#UKlrS$es$9!?7y<_e_;J8 zpWpuZb*x`yUVUyI>sR^w_UG2Ier4x{)3sQ?%ICL#bRFwgc3wDIJhqPYD?2Y7zz5c` zewBIk*gDp)GOs?mj`b_!74Hh`SH>#<&#hzq%6J9fbL&{YGF}1r{5saJj8|-d^(*5Q z`@s5@@e07x>sY@sUIF;%I@YhuA8_qs>sY@se*pNvI@YhuA8dj3EAt0{kFI0=%KQP~ z>2<7MnLhwLw~qBI^9O*>tz-Sl`~l$e>sY^{9p$n(=k9asSihnj0z9{l^()#Tz|-qk zzoH!lU;NZZ*Rg&@JIbZJ){M>{TgUnp?T|SCz&h5iXosBN{@6O!uV{yy-~Q2ctY48= zHi}~Xio6owxpk~x<@4J=w~qBI@=C_t&#z_jBu5zv_Me1M64hRgH7<3hP(> z?|+n$n*Na6W&PZEg`aA`P(K*p5d09Ze_IA@Cy{b2pv;v6klKesps!1{UC57y7Kez1O?^@H{EtRJkOXZ>LPJnIMR z=UG2kKhOHX`g!IR>*twQte@L>Mc0n#2kYlHUZDl+=QdseVEsJviuLo%E7s4ASG=nc z{b2pvc!d_MpBt|Luzqg5Vv7;|VEx>9#k(5O57y7kA0Qm-=jIOpA72~Mk7w6L^n>+t z^9R{Jq93fEn?KNk^>gzF0M^fGN4YEp{r!hV^y8(q5&dBOJnIMR=UG2kKhOHX`gzt5 z*3YwkuzsHPgZ1;QA1|$q=m+cPSwC1m&-$_d(1?DpexCJ%_4BMBFRY>dVdpx7iO;T~ z{$b}j1NitF>K}HlGh3kkVdpvncwr6o4?EWxz?C)BKk_--FRh{eVc!Kni~WaC|FCnN z0hBYHqS9dJY?Fzof8_Jqi+K}H_ zHty=#HPk=soNWMHPUxTCUWC5?{f`3V^V|7T`TTYQ`TTYQ`TX{FSI9r=xz2jK{qx() zU1eTzogwpz`z$lBxX&{4iu)`xuVSBNAw%X>?6WLD=2h&oEI{T}dn8!*sVo8E8i4$~ z2MDjc1?E@vdlL>|epPwZ0FWB=VG=De%JJzuP;Q;KY-)V3F zb_~8NgU(?`Jx^a-m_HOSe<(m+)yGZ%>kPC*0Oyqe&a3JV{FL)b;@o*9-}!M~J-)A< zv+cYZoZns^3CFAYbk$KDuX=#^!&`_y91wpvApTH*yc(R}UT!y^-_9_gKkRnZ9|E{P z1aN=Y_dl@i^OqjyO1P{aZ2*us_ZAZ84oI9kAaSmM#km3&=L+=n1OCYRL4RcZpg*#H zFg~(=Fg~(=Fg|+U|G>EW73l{y{MmS=+|ByYC^`}!nOD)G46j)~2-tYlI`8TSmso9H zf)))J>PG{FSKdN+<$&_diDTgK-W!WO!wq z_rCvu_4eNPKQQk0zW)J#aK0|9R0D?67{0XruZu4R1KIokjjANl-t#z#KCo$-;+-(~&7 z&Pin-sDIcwsk|%HKkS@T0IYxH^LJVQ$mh4S{*lk$W&IkJ}HT{$bz$ zK>g!A2a$i+_diho$mh4S{*igb`bXv!>mPPrI9-eNt9*Vt>mT|2cGf@g`Ma!tWL~lU zk$J`Xhw+Mch4m}r6#&*hj8_0y|1e$wVEw~*#THnUqfMob?adA?Iwf{*lk$W&It&)O(tgY}QBAFO|5{b2ng>j&!}SwC3+$oj$hN9GmlA2wdW zgRFnpctzKa=m+Z`HeR6x>mN2=0budH zykd(H{b2ng>&KNf)IW?@_$k&u%pV{e>mTM10IYwQKiFbKKUn`Tf1m~5A2fdeVEx1V z0f6-n^9KOdKWIm}ECzknKeB$X{*m>A^^dF{tbb(vVErTO2kReMKUn|B`oa1~)(_S{ zvVO4sk@bW1kE|c8e`NiL`bSBAvVKJUqX1byqW)2UtRGSTD1h+_gp2wIfT4aw{R6;I zKb~FtnhFf{BkCV$k@e#{DlpWKsDG3e#w$kD4+UVn0uc3&0$7}ri65>2>=435{R1sv zhX7Ik0028=Uh=})XDa|ZWL^^W543&N~>?+GC5#~Tj)t=eMv{lWc*eysK}%s+~L1d#RP%GxgkkoDt*wNC}m z;}u%icm)rBy!K(^6`6>;viUB6qJy=M%&Ygr+ch>`p+!8a0`z$GtpQ|S@l96a6@ID# z!Yc=aR}KiT91vbLpxl+sOB66(DPX(;5Fer#`U8Y(fW)~266X#`oI4=?a6sZ*0rQ6f z<_`tPD~a0M4`SUX>&Ler#6HWcAK!Bj>podOe&8V1eX@Rh&q2%+vwnR0LCh1ge%yW# z>kL^x)(*Zg4pqqdvGXAIS!VtC!&U6F%=)qO;Ct#%i9g`;wS(Aanf2rLgE+q=>&Ler z#Q7yzKfdSSFGPJX>qi_fSnMb-8Z6IsM&}IxujIJL27p&R0CvbZ!mR~(CFck?0KAfO zAPLyHoO)MwE~f(de1^lT;8lJ<>dsa0%HD$nMekk(uk!ir?^^}0$_qvDy;M7w6THgj zx8J!6UgiDmhgZR?yuW>Q6};L?=kLCJ0KCfQ@4k2dyvpadzkC3^>Ycv}Ugh(553geX z?%5UShkgG8>sMPVSiiFGe_;J8pWptz)lUVfGq3Jm#s1yi`McP^o6p}pyo&t8&U=N= zS68urw>TpFA-sC|z^!qLhn@F|7B3#a{@u*0mk(h7ZsyhMD)#RhuShuLAI2*Hcdlap zuJH=M-K*HYYrF#RzE$ksHD0j=@(<$``@s5@@e07Q zyZQXxmk(h7ZtwhE?B6A?Y!t=%6?rAVovYY?MP3PT_bT@9l2kGofq zfAr4ZMgBov$@#m7SCM~^S9X3o@(=n$?&{?O$Ulr%VB(7hkbfAj0K9wv`G@g}+(rIj zyaI4|75Rtp3c#JK$Ulr%0PbEz{$acVP|n7UnZCt2nTY(u;v7KHkJOQyu!2U z@d{5j^NQ1R;}rAg?59-*O}7iCI66--vl4c_l42Z^S$?>&K}Z zF;C3;v3VoriCI66--vl)){nQ`hFNQTzc9PHx0JG3&>n8!=DJ z`tgl7VxE}w<1IIGo+$o6AIERxJaPE_sLdNWPaJ-?k7@W5dysz|+ZB3_pK1W?=mFqW z4*;)@?K+x%3cNbDE8fTPK48bmUG4jW?EJYsu;XWUweJrScxeyp_?2C6+UGxYcolXO za~AUwo_}?C6?O=~^REuC!VUp={?*}C*zv@kumjxX`B#TmVaF4D+IvMj|LX86?09xh z*nt*2|LUEqu;a>}wy%%pUp=-8JNEAlJJ5pXU%hh`@1NJF3xMZe9bU!xB?9pLt3N(~ z?+*&V^RIs80KPw1{lPxKt3N(~^{eU+06hQd@GA0;>JI=s|LUEq$Umw-0Py^)$5!$E zkLnKqJpU^5isxTFv1iCDo_|%IcVxoGE94(f>>2Wk=U){^ME)VX;`vuk>>2Wk=U-)B z@%*dIE1rL4ydrmze;BU-@cb*|6#$-pWxN8w^RJ9oY=Qj4c*Q=De;BU-@cb*|6#$-p zW&VJpc>b070|3vzGJmiI@(=R|0G@wk{s6%9ugo6+c>b070|3vzGJgQz`B${Vq89lF z?GS+HU(pT$c>WdbkT~c0SF}UooabNB4vBM~e?>ba&UyY7?XWmU{y|oPPyis2_3uRRJu{(MOzrRRD`~07X9n$og?&h8c3! zk4I*hQ)m5ne1=(g){iG=n73#BczlMLepf&0T@Cf)#0=R)){lE;$Ul0#LJJ$O;KAEw z$Ukhn0&uk6m5o;b$~lm6S3O?A@*c17ta`k{)6Kl%h7IEtI&Xl?OB|4Si37qb2ZUD+ z2(J_{UMXO_0>B-O<_{3A0TSm9NSr$$aqfWVxdWo-3Yb3>Fn=gOJIZA-x#K>|tRLKG zne~JFEVF)apJmn$?z7DL@!UG{kE|cD54lh@>j(E)X8qtk%d8*VXPNbb`z*75aGzz? z5AL(f`tjU4@{g<^pIb-%k@bW7sqMW~5Uv5>)icZUOBw)P^#Jfn-lK0Vz$ZHviDLIu=j*PIKHoz-#_PmYI{!@E%@Gqy(bKS?@icy!fb)O*3Ov#5c{bM zee6A90Q@dMe*c``1<3E8^Sc1~{d0a7Aisak?*ioa&-q<|{Qh}-7ogm3e*Zka3jjNy zA98+4d>5bq`Tg^U=bs87^XmRN>L2#LGdy_L9QBX<{`rwP@((*N11)ZykDOm}V2=7n ze*gT-H>3WMd39ip`bXx~t@DxdOODKue;BW5$H@65_s>VpFL`)Aa(>DEbL1b!EB1l> z!*~VY$Q<<#;}xIP$oVA)=BR&|KS0qh-#qgE`GGm|5Az3F+&V}7Bfo!sWIpo#`Capo z^GoiZkDOof@Om2z<){g^o&IPl%&D_}+&{-GJnP59bIjYbe%wDt{?XNsnCa*9 zOODKuO=SJJb&mX_$1Akx@e27zk5{;>9L7WqA40(P!} z{*=8Z3|{ejclrHue(#R&bIQ2}{N7zYzl7(#=JQM9dv|4c&F7cI_wEW{?+Np+kk{IK z!T=uK0I%}<=MQY4u9n|Fzjp(5wfz410~_xNistvvAKgIxBfo!sW&`<0e*gTb4dfsB z{qyr1$UpM?=g)5-|H!;Lzk&S2-gkxvpV~nFVedQBwa7p0eP;lVZXo}#_niSeuz~y| zzkh!32J(;0s|Plae`H=gx`F(|c*VOy{$acV@YDwK591Yp^Bc%Nj8_1j-$4FhykZOF zAI2;8f&9aG1>npE@(<$`fJZlwf0#c&xCb_nf0#c2+`EDN!~DS($Un><lI=0C0W-`G@%f!1Ei(KWK+VE%FcAA;41`$UkU@0B1Ikf6$JCFP6d{ z-9Y|9JIZBI&U|14`3LQg^GojCK>m^6KYw5Y`A2^L{Lu~MANl-}GaJZ1^7$oCZ6N>1 z@1LLFK>m^6KYxA$`A2^L{QL&;kNp1mQya)X$SXO&r);}!cre3(B#xHC1P%pU+4gXRwa=NC~c^F#p8 zZ{#><3yusjPh=k$OUxet&eYMz`~kqnD^Y_7>Ij$hbdC0nK}b7UV(|6GZ?P`oL|f`gjWEb-^lY4w%|-gc*Q<2_c2}pI8$d!#w!4vUs;@k za1YcOnZ-GPdp9tLvp8o9&iW+I$=y6p1aM|Cvy?aoz`3Z!Ie_yUn7vw@19-m9ce8$+ zuQTMHeqc_W_2W#Pg=hWXyglp30~?s>XZ^Uh<_1|m9;n$wS3e^8vGIx?M8?wN6>^*& zuaFgG{U|zE`xxf6ML8>wd4*i9$17xaJzgOnG+x1u2g(=Ii-E=~0A3uPPw^K#Rr_ z@dp6sRrLp3kcm0Y!QHGMWquW%XZe{=w0iT{)U z@2!^Ksg{E!@2|k!3n1(O@WBd{SAXJ>)%Uzwi#J~PVzek1;Z^ie{t|tZi|{J$s{AD$ zd$|a&{B|p^(8sF>njOQR3V*admUkuJKlj^}??*XMdDVXEwZ|)o^Wj5m1Ay~NS~#!D zdlicz!+BL+pD6wSsiQ^l2YDrN?!1yX@A>2ZV|=)Q5;#L(gjW(D-a_KTfkAvM=`$K1 z*SM?j2bk#okQVL_(GT~B*x@b24)5cY*zs+3ELp8%iLm7BEAa9G3^D?|z5*{MVA!JZ zYS>5PRXp}#oP#B!ymBUrenf$%IO;oJKh%#`4>UW5Kh^ATK=i`_(GLeiKO7L2i~>(p z%m0R24#J(Uzz;8g79TOEF3;+JSO9I@jTXfp(V|?0SJ6lLOY~7L!mGHe@|Sq*ak;M5 z2cv{t`nYsGE|osGt5**+JBB~i>3emEfd5oPjX$JrHJsy{%{R}VB^ z4S%Zf$^qe(1Hvl@gjWtoIFAB%Rm;y+%caHr6}Y$nnjK{?4t%}_@%Pjp;MIW&Je+_p-;BGuKLNuQjaS1y8n5E63Za4w&iz@5es~K3ybl5Vu4G=)vjg09%Y|3|tb|wILU`qURF-t&Ljbo! z0Jpr(ckxsHtm=FhfVU9+@IFL8{8@>9xE-P&Zn*&dtOOXg_$u=18d2Nw4}VtD!k?Ao zAO5T)|8UDC&iz?QoO_EuC(b*(k}z;P1aOu}d^oRU-X1NA48#amE)pNnNBK*PiRB{k z5zngpC1j#pBtGJW?lLAG-8a;aR}VD(82(h#4+q2!2gD8sM(q%mcnbj@sg~bUEf?VN z3Y=H~Ek4RSit(%-SpaR^^%lY_??ZUycO|^Kr}lB1`UCoLv;wmP+*W}d2^h9$b`1My zcKBV1ez+Z?A8xtmhd(P}ySEU)`w+nIO5&qu2V&4I7hd_Z5?*-=;g$DMS%NY$k7!Y3AV#=yk@$!{%3oqkEEkE7cvj^vArs{?h>!A@ z7_Z7j?kX%Ve~IzRfp}KsFEL&@Fl^EEBm7bR*Vc#WM*(bK4{#4Ts2Sh&{5dCmK z^uqz+)hOU=meRskIVC=>xh`6s?som-hPDpZd+gAUR}Xv@c_mTnb_n3S62N&Saqhg5 zIQLa&iSyBQ>bR@IMAp^*oH&PmxE&G(uNY|K?yyB0cO8&%*8$NF2V~rJ;Fa92`$Jl| zKVE5^mkB*9qhg2m@k;C%)+XBYV_3~-!0Wf`AIv$Met2y{^uqzs4+lg)91xa_0^9YE zMUAD!xmPnJ|8PL^4+kXw@P+v559r6M2bz8if2#4y0pXPc!Yc=aR}P38i~?SpkQQFe z5dGM$f4uVgM-Z+I;j9lnvwThP)dNijhd*WUvFu#VFYy-g9;5^E9;5^Emgy+)T($ga zjaq5(xeDwk53$@_eNPyn_W26T7C?&+Z&BY9Mhoxb)BD@`iGEkVzdz=QXz|h7$HjVA z)gJ&JtH8Mge4qkn6EJMi;(XXgv%~MI`lDlqoL}OWSAU>|KP%ytw-CUel>mNMaxPBK z4$R@)a^aOfE8&&55MFs7l_luI?@9o-Ljbp2-aq$eCGVem3wi(C`>4F?+*Rcj0Jr0{ z!mDdUZJQ_hvyv7=501n~^+zWLCC;5!66emVKPS#R9+WU}I|Oi+NPIZ2UxRXQT!;46ASFavu`Z4^erXLQ79S(>c4vg9%Eb$fscs)u0 zuSbb~cs)vZC)-iPqY?@D;(wL6J(ufz%9wL1ZZEn0jG`)K;%cP0Aac8Gqs<)R<{ ztb|wILICeW0KY4VkDeWfLAP9Z<C)-bZB#`tZ9F!0iygEf@XpXC?aKEkr-O z577_5E71?P;~Fe!it zcqL)rb_n1sk@#?4iGKXQ?7e@CT*q}LN=GEs(><-39$xlrtDTlYlgOB{7I{RVjDqnm zAfO$I1W5ZCwgo|<-W%G1w4irsI!khY2oMZef;ibfXb6r#!D3*54~Ah-iMd&VYd{-N zjs)b1d3iw~A=zCAV_;V=lI;g99tDF+Ue#CMRDa*C+Y%Edv90aD4lv(T*FANRXy zd#hrL5Di#A7ypQT1c3E(@sBuG0bu>S<{trw@B0KX80Q~R^YEAWzK;WOtioU7`#uiT zTjcl=?GgSp_hIoP0DP(sZ^`GkyB_i#y=yMt(Z{h0KNa7N3PL^~@)iPk9|HJTh3}^} z-V1j<doRy*e(o`xrTVC{3`K-Q!FQ#pP(knGXWd9p_XknGU_ z2(P>k0eqi90N-aAY{|UxeFixf$KN&-Uitfp!mIdHfj4}kQF!H#n^*!rzi}YzQU9sz zAO2MG{ka2)SNW;NdL&+DAY{{gfA^ijEAiU;Am;etypk5qEAeyZmH4^yO8ne;C4TOP zdK*8VXsEv(Fo9R%A3j#%9}d*~V@#ik{vlrLKP73G=S- z%703DRB^WBgP#{}=-`{}=<}AHKJ>`=cDVX-izU zLqP}RUgU{@`-ik}|B(C7-9P01b8jK{pL-vNXLA1GPqj9_Kg##1)3C!ieJ51_-+L9H z-Xi-)y^oxK_){f&pmWzF*#m%Up6t;umk52lg#g}%0RB`@jPE6D(gS30&4pJ!R>CW9 zA-wWF5=$l$4uI<+EnM@&E3|0h6##FMc-7EH;uQcNtHdh+uE$%jB+t7(R&UYb$H=R< z@Y<|LlRZFDA1m>5A1m>5XNmZ^kCphjw|E;rZ?K@af$JfFvqb#Ec_s61Y!RZtrd0nB z|A>7Afb|dYk2qEVU_2N9@J&E@sv8G#{HXs_USByNdN?3@I51HU;gz=#!1ogc@cl&L zmG37Cue^ou%KH#r`BMq6eD77fHtu+ZjD_D13gCOM0@Pb%Kd<+ZG&PdEWK0dW#l6Mqa&z*JeF@tfYmHmH4@jmH4@{MEu;xO8neg zyp5kX@Jigk^$@^WBL3mLlK9~*#6P?b@ed!XiT)u^<(doNVU#8W z%|#C%D`APZ5WvSu0Dr2V0A69W{P3re_|Z>&yi4r?Uj0l0UT=w^0DM0k&GbqE)LUd;)%(c2 zicb~T9?1}d_(#+u{3VW65aJ(E^YE8ARzZk=@Swia>8U<@Q}**42eKaZpUQeT@b7NQ z^>YV)|E9R_gF$rQzuuJhuN;u~+b04%sPA0TTw3sRluH{x<`oa>yRrdfUU>_7zuo(g z_uKucrw9`>)}t8?19c*k7N%3u6eQt03R#i zmA4SU`w+mN>a#axUNz|fmbm7^D<3Q2mA4RHc^~o}i$9eBu7?1wxqKhxVAD! z3V!ZmmFNS&?ID1VmBbGpD~TWOQ4&9VtR#MTi|dIW{!~IA*Fyl;{Kv{G@mkkh03Ry> z>MbU$uOxo>SV{cwv6A@VVd!R+#f)n(R_~9&( z_~BzE`G>c-p7>FJs-Hq$HRL#nJ^*~I1n{xCp7`NoCGo>;F7d;CP~u132eTV^i=Q%H ziP!pA3E*QTfNL)6L?0`OAKv16;)g$##E*&duCqkKg!4*(dW$^o*89lwZhWe+4HY+? zf{^$T$140K?#Top@gr&;{u1|Of{^$T$140K?#TopZ1)xdcpn1zQ`P(<{CM1xK_6L< zsCoEH+>;4H{3DK4_)FZA2}1nCTZn(eu?qjnKID`Fe<}f74*^_rIoH6)O3uac7IH3* z_aWzB`BT--#rbeLFeeCMdpv07#(*3@>OU3jfr#UP_=f|szH&hP!vSH*Jv(q;(XJkZ z0KdKi_Z8jMgAm~JJ8+BA{vL$z>PtIt@6rQ32wAs(e#iG(AByMVKnSmXea9OSxT^;t zyt-${>`VZ5^&o^-u18`C!o-aO+0W}gmHi_Ecq#}vzr+DKzr+DKzr_1^r|?R=)_Em> z^GX2cmH4^yO8ne;C4TO_5ac?a&h>-8XnSNH7rVw!y*j8|WYz+F8E;nnANd?Nz;dl15_ zcsx|pEL=npg!uW51Cdt<6ZN0U@xuX$9}YU>QEO#fcYUNs$*RUSzqlO zVxoGi3nB4id5DSXOcz4t-F-t$ROh=8GVdN5Vxqd-g^+nSzCaUr6<<{eLZ0fzf$Sgk zpUVE>fau|X=;6RbJ%lCeeGuwsr_QUZeGux=PMuesMG)$#PMue`ErL+r?9_SHSp=b; z>eP94wGTo)+Ntwuy$?bi?bLa7r4K^g-w|GcqL=z0)ZHE76@YVn5bDm3@QN)!sJlDD zD*#veAk_UG;T3@OKJMo{+JO*1zuL$BoR4-O#Lqj6--xmHsSbqr`E85OMc|tq2;o&{ z@lR6=2m!A4|9b=;?LY{y-oF-sqa6tGk1PH8b^z}0K!|@_>hFxe-5m(w)ww?I`n|IQ zA-uZO-c20wfROlcrT?J_+~0vPw*RjYINE^_;A;OjBJgMj zLV(WVzmC9D9SHG{+ZJ(;`!_of;vb#Gug!)9$x|H&@sF$h{}6#kI}qX@>-|59z|jtb z_(y!c)YUjaN47Kb7N$1EPlmqK5+$^$?b|-oJ${_35I9!b_4skTLf|;iuE&qn3V~y#4I%z+$1wg}`y3U5_89Dg=(zc0GPPQz391Z`b3; zsS1H3e57BCAIB>Mjsxv_{8+6JI9A&A_;H{@;8<$cA#faT*W<^j3V~y_ zU5_8fD+G=M?RxxJtq?d?+V%Kxp!#kMEK6+&i61M~|A;`YIOs!sSP2(O7-pt zRBZ_J^Iwd>QX4|z;A-`&5m;$MNc=cneJTP6+7J>yPF4Rb0;_EZi67zqj|d!ZLrDBM zRed?NfDjgUy%B*;Z3tPH ztW>kNphXh6rvXbf-+@e{`}YK_Z0ujv`-t(pp$}OndJ9=6Iw0#r2ZSZQPc4A&Qw!kx z)WR#@rxsrMKDF@5_o;~|$v+&B z{KNHlr|?R=)_Em>^GX2cmH4^yO8ne;C4TO_5W;)eqgKOB(w z;ehaJTL0K6rDQ*!)<42Q8etv~UU`{Ac;$dZgK7Qa$5Q`r?p_?9zkB1r1pm0WC9ki% zkG~wBzw3aUBkX{jBkX{j(){cIaq#6?2mziSVB_kwSqK4M8DQgTQ>v@Uxz4W-uyIwT zx|*Ep{K^0uSDR8@P0n?Gevme zijH(0ACnjZnA%6b&02^25XCchb zv2k^I7Q)z$jjNYuAq04SfQ_ryW+4Q4Wq^&VO^+D#?{NS5aJ(Rkd~*qabRU5Kd=8(jvo$)9u9~e4ouWTSmO0_0la=L zfY;B3S6)9CUU~goc;)qT;g#3Vg;!obPxinR?)7s4ynZggr9J`&>*wOi;g#3V1@QX00A4>A|M2>`_=nfeg;!ob7hZY&TzKX6^TZOw53ipK z;PrC>ynZh6!|Ue~KfHb}@x$xq59g}}l3x$z2tgY|Rc6#@tA=jOEt9AS`BQGC*oS^{agUApNoHZ{apOR>*wMhUOyNA@cOy=2MQnecOVAi z{Nqv|g^#;C5CWX*qwsNO2SR{LeH1?K?m!I2_cUDTqwsNm2VyYJKi2ywd>rjSe0ZFH zTPu%}5K#b>&Kb7~dypLp$h8A+pwzsIAvt8E%^RDkR2z~rLO9A{nOW~Ej zXDPgj?^&W?03p1J?^y;QzGoSP@G8D%8U7OAvkXFbcbxG(b!@Fuf#w6r^G)TF#k}tPxKFaT)wabA%NRM0Jq0afS)%^eL^1} ztG56q_=oVye@b}efbmN0@ix4o9;H11G}!|{lRW^qe{xJr`KYU*_u>@Xw<3QG<{!=-AI3V%E0f`?DNc?ahjV>(c z`?~`8{;mMNznlCVKjr(o!mGHy8%8(o?*<|3lDNMc{_4#+u>4#+8+?jHiUe+c0IA?NS9f5^GcUjLADIlcZN=W=@eL(b*&`iGp8 z>h%u+y#678*FWU^U9W%0`MX~KkaJSK{vqe2di_JrN%i`N@XG5S0(kvH0Iz?@`P5$j z5MFuxLwM!&58;*9KZI9a|41yM<7oXu0Iz=t;PnqVzuoH}a(=tlKji#fuYbt-yI%j0 z^LM@eVJyM^73&`Yc>O~FuYVY?uz$t+hw)15AI2-Ke;BW{{$ae*`iFTfHm+Fz5Wwpn z0(kvH{M_pw;^$uf5I^_&hxobIKg`dy{$Xs_`iB5s{}90IAL1Wg{}BK1`iJ<3*FVHR zy#68n;ROYGsv8G#{HXs_USByNdN?3@I51HUVTsp21n~NY0ABwPUU~gPc;)pE;g#1v zgjZhw5MFuxBiRFsJFkBT;Pnpyy#676?)4AxbFY7hpL_j7{M_pw;^$uf5MFuxLjbRT z2;lV(@ei+mh<|wfLwM!&58;*9KZI9a|41xB{P6mR0ABwP!0R6pKfL}S@x$vM5mLGm{X_iR>mTChUjGn3_xgwUxz|6;&$a$xY}fjS0ABwP!0R94A71|u|A_UEu(=$+ z9}GhLBi28{U*h+JL5P3E`bYRn{C+S9^N;Vwzr^}S5b{*9{t^BX>mLrp`bYRntbaHV z>mO(VA$r96M*w2|11%(e{A&D5tbYVydVDJWCDuQJ5Iti3Bm5=SKY|cFV*MlhCDuQJ z5SGOH2UmLB39-Q017ylCLA3+$e-iUvR z^^YKoSF^XE1%&bH_6UUU&!b58Kp*S-kn+qaITLZ zzq11&z@>u@? z%KqVi@X7(@>n2gIFsFJRwi`wT@^@B<5Y!ScN`5Ypmf3wY1+{WA~}KR&a7_bd<2 zKuG-f*aF_OeE$qZKELDx3wXiuy)zKv=erm1g5~xZ2=Vj&!tL>;!gtL;2-|;V0WVm- z-hvR|`#rp1`AQ2yfS*~oFaFf)EeP?C{sLaGeAf(w_{Z)A99*(}215Mf0}Ee?`-ShF zfe`=r*upm=@ctPH@eh6x5|3gG&mgBd-Z+r`qyAIbKOB&AARUl%ARUl%ARUnJqxb>K z=@xh;01xUr*8;Bu;6Z(tTHuv@x6On4uC&0b^gRydc7DKesRdricN09o>s$-GlK0zr zfY<33cqQ+*^8l}hTi}%(hsp!I9!k6tfCqSeGVw|P9^my*;+34U%>%q1Zh=>F{_d_G zUfez1f{^of;{jfQ9_LyRa{g{Sz$*ZkS`c#n?gPE&B5MKXtkVVdqoh#odQn5OzK_Ufg}C1tGk8bsjJ7ezFB2yn1sUFYZ3nf)HNy zdU$d7;TD8^$Fi%3{j1Y02m$Wu;luU$R7xO=(VoRdYj^VNe{Omj3vlF9%?}d@ajBX-2G$= zLV!2t@#5}7EePWk@{fmG5XLLyAE#Ro#w+9>=UNcPE94)SS`fx7m*pB?;bPGa&yLxzW_go7?fc-tZxO=GuA^!0|4=?UsX+em8?C&A* zxYU9W|G2A%#N%8GLi}S_4~fU=7KHf+5|4*l5b{)S&Li=7s0AUwtMf=aKG}j0;LUj? z9uKu3M2}t%iO0h&2+?C#4~fU=7KG`6#N%8GLiE_*L*j9%1tEHbvvK1RKb#{Rg|K9Q z4~fU67K8vHe#Czb@uSvaR}YED=@x|X3W>+VEePWk5|4*k5XLJc9-nMM7_X3cJk)|P zULo-a@guIO)94~b?dl=%INgE};I1ALk8>>u0rvNhcwA~hh@U^u!-D-v3qt&Se-BIi zODzcVb0h^Je#EB|Ki}0uHgUQIA%5QLA@O*)1z{|~zdn>&2=MAWe*BZE4*}ks$KyPd zp2~Oy1wtH5Vlct115wmfScH^#DcdKNa<80EAcGLU`qX@X7&k=MQef0`aRU{}ABg+ps|V zy_A0l@Y!uxApT*>Kg7@f)ix{;|2*X%;^&{;h6Umuru;+v{NvlOK>WRwe~6!da2png zUrqUk`1vnvONmFyKZNaz+mLwt%NYm(ZrX;#J0wDh3x%01W-n8*n*o_0( zKk7e~{lkHOu{qyA=)iAp&i4;G@Sis4`v)C3-HLjEyMF$a0Dk_J0Dk_}=~ng+KmY1< zEBl9^e-)NM8;;}WU#+*I9(*eBisxT-W}+Sd{QRqA4*-7tl>mPJl>mPJ)vs;NdieQQ zzqUE+;pbl^d!Tba|0>x7fS-Sr>;b^fzY<>g`Bwt?`Bwt?`BzB~klN3`N_qhB^RI+g ze*Ts4%Fn+NUitY~i6!X6&%YAD&%YAD&%a8%!cY16SBX~u{QRrLD*%4}RpJ$&3W*2L zze>CU;OAc%OOSZ*{3`+c{3`+c{43)X5)Yn#WxPV-!Sk<-S4cc~{+00xi3iWWGF~C^ zIG)bGGOtDA5nfV@xq|?H{*?fJ{+0N-pMNEO?&n{LpZob&;^%(;mH9am51xNzY)9h3 z^REQ(^REQ(^RL7|{QN8Nk9hu77^HaqRS@DI@%*dsmw5hF5aJ*4{HySnc>Yxo<{wBr z;`vtrkf)00UxmNK^RFC;=U;`t#PhEli05CS1%&8vphDsi&%Z(oi62Nj;`vtrFg=iX z#PhELAbP~}ufkvA`By=R9`XFE@RxZ0RS?3Gc>WbyKnM`ezY0J&Ke5&#o_`g9c>WcB zO6FZ89`XDu0D0bx=U;`t#PhF$FkT_?i05Agz<7nkqiRDKuaJ0z^AqEmI@tpg@@j<@ z@=6;*fa4X`)CbxS0-UO_3SVtQh@U@GVZna94IzGhs=`WtwGCl@j@%%epBSG?{Cu@S zHnGx%5I;XqA@NvhLm02{uT^Rxz*2=D-<0|gU?n|Om7dCY1qH%6kZG)hS5SE+X>PpI zu`*s^SXa~N3a|WyjYJ$&CZqKk7e~{lfv_ zl>@>n2ZUD+h&%gxmIC;DmIC;DmJ&bwJxhrn{+^}yxxZ&Ae(vvCil6&?mcn*_&r$$? z&r$$?&r41C(>41D?>faR!;O|ol;O|q*cd7n9wS1TA_X5awseUhje3u&Erw%hQ?*)+WQvF_l zWDmp-zZXCNzZXCNzZc*lzURbG`Mm%a@jWMicrO5+3L@D9K)e?q0P$XcAmlsYcrQTs zOS~5#2>DL<*bs$}my7dD4h>QGxLllHvTumO$N4UVG4Wdb@iSeBqz9((okJ8p9_vCR zJpjxPQTRC4g%Dm{yA_3xLtO~r)%*~Jk7Hd3;nmI|3LlSk>*tp&4^j9y(}hU%LFfC1 zD14mn*3U0FG(_R!a&dmiu^|c{FBj*R92%nVak)6ZWZw{lkMms!`Tl%)i2UPB7ec;2 z-#J9#|?Yu`YxF*KS4O<4_kufcYT`AIG{7#w+9>k98r8SI9rkbRmpa$Un|^ zA&ghZKQ0&NmmC`+|9H7LzvR#miAOlpu=f4=z99-9=erQ%=gUJBKF)L@#LssQQTTYQ z3n6|!KinJ_)5p3H;^)_H{U;GP)P)c~pC7(2wSX``NB;3x7sA+%{Nqd)LV$fk6h6*( z>*tpo8lv!Vxj4V%*bs$}my7dD4h@lbTn^`#gr^q&*f&JtalQ*7{;@nn;&G-6Vg7-{ z{LnIz&x)7!Z5|8s; z2+`xv5Q)d-E`;cDY>33;ybNIb&5GI33vMwfFtR>)_%5CZHQVoiO%3n9Rv zAy(m+yAa~%$A(z2zubioKR+}?;&HhPVSbL>AjFUORO08$Lu38XrYP$1lolg3KcB~bZH(%g8ZV`aR;uwG81E4=dc zZej_-#Ek=aeO3RdtcL@MR}Gy@{O}eMKYV>9EV*$Y>OqR4#==kXg;(B(@X7(>mHw3Q z%3GM%qW;1AqXhWrStoMsgTe>zj}kX%S|_r_eQ|xo`=i`HBEb8jY~C%`iTJ6ebs~VK zbs_*iCv`B+KW-ez{!#y_>>mz%c$|MYAm0f)Am0f)AYYmKd9MQaeRl%*eRp!Mf!}v0 z-=D|(?t-D>eRn~~_vi7xyYQEI-(3*${dv6aF8n3lcNc_we|}9E5x){Nf1t$0KtP!mA4-nL6`Nxqt2>Jf}>k%q{rTDm ziO1eK2>Jf}#0ZJUo;e6(2@;Rd9E1P|M@T%{a}WX?9wG4<%|RHika+BwgD_qp@z^^D zVZ1`(aqk?2@e29JkvRzC74nZq<{->#k$4=LgAm~C2#LqNa}WZojgWZkor4fRKQTh$ zv1blK{QU3;iN|OTLj3&T2#H5~4nq9=@Cb>=Xb!^s9Er!CIS6Ar5|6!e5CWVXA@R6( z4nlwnBjg`P<{-pBUK}C+cw`Pj{Nut1iN}#S2=R}zBP1U8&OwNOtc{R(?45%!|3Kog zXAVN1>hK7O$7l{hfP*6>9_={@0S=Flc#P&CM2{0ABp!R_AViO~5fYERa}cHn5|4Z5 zAViM~BP1S2<{(6m7e`1u9+`s>mRuMi@i;OEAwY;9G4TlZrA8sZ+6alq-Z=>46%vm< za}dTWBp#zV2;&tJkMCAM4qfMe`Q{vj+`OCGg1d6WQWN1sZ1KnQRldF_$pwZPqGaN}X zgYgQJ$h~PUF<;LSS$WKBC9k2+O3sX|-f} zU}44em9Yd%u~Ax)32-p2;o4~pC&1yf>Kmn1pYaL{#67xByfm2HC|z%`$$^vrqQJjVrhINt&jyco7U9#rZu$yYiSj}H?6|O&zsim=;Lr&>5t~H z(icBJm~w-5$_>QN52tKml(Gr)b0i)ie#G2CSP~*m$|fKL@Ek{g5Y=Lf5Y^I8t!%s^ z7~)|3ONfIB$nhgY&eQ_Jc!eY_#M+1(uaMS-=o|s#)$Q>w%m))oIJeJ6AglnQ5a3|S zIm4PE=9~f?PFZSLWh5Z$5#mSuOIVjAAnOrUGVw2AB@>0j56_(?euULh{MWEribCQ? zSYO4zg!NSv!V+&GfcGJQKb7%H^^kRmYi_*4AaSgWS30`JtI{6yLA9W`b68(R3(@8R zI7`IOomUb+oQdY=nxBiGJKM$2!}==ms_q}|QQ`*fg95nM%Dmg;=isi-4CWt5%sH2c ze_ROpdH8-X&OSkif4ms-^YE8&5zEaVe%DEPzx~4KSOP{hAm-<2u~GxpLVgb5-kU!Z ze@gu0d;*RqAp1w0snO!e2*~^Gac&R5OWkipK;CaZ9P;x3Y`W!7A|UU#$NW41%R}fP z_sYbG0|0sm5MvYo=pn#`ke>s99&#^PjDrB6huljRBPRgpA@`ESSQ~%`htNarC0iSv ziols6^r+oS_H(ht57Ss}+SpueuV0I`Rfc6liFa6s(gfY?I;@=E62jnPf>mCU;vz>m$(LF&35 zw7Kz06*OJ}_~kU0n4g1i@7Vyv&jHMB0OIG~Li`*+?nC?>09j&w4uHHeKL@bE#3Fvg zI!kTANBY_po|{`%J{JX6uioS%vZ4B*`#?HKQ3$lb^nNK zYS5?dAF)Ma{D^%t#*g?^jqxKEq(dLZD?DxngjWs-uN)9wIUu~sKx6!fod>{pr7g<% z5$nzvD_JLki5U=kI3V+t17Z&c#2yZapDSQ~u7LTu0%iPgJtR!*+t6I>fuB0G0f;>S zgm-a-R}w#R3+N&7gMCmBi649_>LKxCXX>MjAM+c4*aN1$b}N}E_5hGuh&=%0KExgX z;!}-zCF}O6$Cy_VKceOVAg?5T#23cvyh0xtsOyn|x*i#*>yd%F9vKi`Y3If(1&mh; zn4g0T>|=Q-eh%Pj(tLSX=T&EshN|-_oUxVW67h5VRBln{75gwh*QYW+2XG}lRhfTW z+IZZ0{&6k=W&B`^GJdd+GXKb9RgWK8k9z(=&CB?~v7(>LGHRoRu6JQU_EE-<%#u2< zs7G+{l&j^umYSFO2gj=96hU9wmH0WB$gwhe0LU%G9?D&_2Y`*I(tHpAZEp4ez_BuW0B~MOm~dVR;Jgwb zKb6=6edMvK`+3$}?12`}D~TV@D~TV@EAbCL73V7fs0Vo^fb&ZBeVkWvE>3LG7(Zek zjqxKsRb%{!-)%R>j|>Q}z{CvH<3|RBSKgxT=eb2aeh@HT=}#H26fi%BY57xS{5YO8 zH$O*hG4M?HQ}^D=&LtjhSY(V~bS?8D|OHSL*hombSu<}0+I=4JfgSe3kDi;`FD!+51n zWxN8=#47-qCBiEJO}z3J!Yl2=cm-g?ql);E$EqGb@>q$VgQ6TOvj>3OLhJz`_aT06 zY%k&m$I9%1791@R}w#*R}w#* zR}u}f4~jk1=4JeFUP=6LUP=6jEgIuT9IM9o5ud6tendSYQ0y~A%>mT)h+_qy9zSvm z;T1H`eF(2I+woKN_(464S7<@ajaLBTScRW5KhI1A06m00?4yhy94qc8)_8@Vs;^6^ zdAWbZu`)l$Q*Dfu`Z)mZCzkPpK)HWKpo|{`%J`96)c3D)ANBY_pxnPAP{xl9px{*o z>b&xOYVj!FrxyS4eQNQKjom?=ufP)Dr&!^Y+aIp`Z`ewH?4n+ z`bSYe|Hr6*unZW!*NktyTpWKtm;v~C6M#6_1RxGx9N&MQe+vF_aeV)I1`t2~a{T=u zfvwV}UpP#bDdLMcS@O-L_uJ@sb0IzHS&_ljIe|-ah z9`gNpZUH^y`*ZfOeGz)d_vd`7k1RqD`TqRr)W`NkynmHO7wg2wH#*1ul>lEzz_We4 zeJbbP>mR}^YJRj+=M~4Q;ZsdW%dBz zyehqxdI;dW5+Fa7*aLmI9^&WDEAeyZmH4^ys`PX6s+>hRUkTv662N&S^RDws{3Esq zdF^_??jNy_0FYO;c{c!@uVmh3xu*2<4AlKR19d;ofba_3&4BRAfw~@SVZ727#w!KP z&p`(MR2e^5k1FE_>rw0dIb#;JW&EJ#HecbV zI94UE*rMbW`!HVVQyH&R591Yp%o5=hfF@pf3*nXaVY~uB3mUHg($bE>PE8EQ;0BCcw2LO(h*#m&{N}_@DN&x4T030jwD(JaU|8PAdeq_zX z9(XF}mBbI{mBbI{l|+N=gJKW0Ie8^5oL3S*oL3S*VvEN35yz@Ae#ED0j2}^t0EEwK z;;L^j&Of5&0HB8eajXC!eh83TfLDWY{*n6tuLk4%gHN^6hCSpv7V2@Njdi@`{gZyn;JtKzQYV@X7(@@741mK3K*|45Yi>H zhnjY!E%tCg?BRge!vV2}1LEfjm^~CQdniCX#A}^b0ywV(a9)X@JFmpgomb-L&MWbA z=T+(FozJXIM#Vv7LSe3cz105)F%;3h{IKQd5{9~lr{ z`A-S291vbPAiPq*c%^{xN&)k85RQ*)ehz^9B^J*CaKFUt0f74@7SD5wx;=6qb$bAa zPgS=EfNb;nIxz!c4{ss%a6s(gfY`$Uv4;ZWRV{vmuuWdwQ2d~ubG=*6yJh^KpL4xi z&bwUi%DO%Kd41hZ$0_3n=@YI6j;lVoRW}2$M-u>hGy$-O+{2rH3ignDcryTd$UVFS zJ~9J6|TH#a?5cBpoakTkKGH_$BaPRev9`X+X*y1}q zu__^~+{M>m}`Z;-3&Z6X%0M07`oL4gMI1yaIPKAiQ#*t_NEfue62nN&)k8kbysCehz^9 zqGkNxeqtFvxSv?YkK981T>CIT*QYAu2lqwG_z`!!!b+x$AKZH_;|E)m@q>Mo@gt8_ zJ$`Vk?pvtG4{Bb<4~|vIE4Hxt3KnD^Heab}x6jmhMLo*+LCtNx!cTFmN?x%=$t(6@ zywayKUa20&D*%}#GG75`;+3}$UTGi3D*&{h@d`j5t9ty%VYG064EC8aS^6a9#<(u_CX6p0)Z1c_r~9YcBS{Q#r3BemJir zemJir8e|_7d#KIHD{0}plKA1glK7EZ)aTvo=k;~F^Gf1Jwz=>MkDJG;u16EEyoK<} zxhuQ^kS#bt591YDH1P^R9;B{FM}De${y{y;_(9Ff_`$I%<3}E=>y96pSM~hk$Ih#;TC({HEQwwxge0pV2!!c!Tq6fj;XV7vklFGdf`kHG(Nb_c>`K>XYR zStmLme(r$yxdY3cP_cwhs79_f2E0J#P9kaGvw zhkeJQPi5b+0O-*kUF=__(FO44Jm_<#i})eHs|k3pi~TDB*kZYh{i|dT?c>2N_OFsX z^r_Bt5kHbW0AxLoed|@``;Jui%4CyaLd~D*%}#!Ycqxyz&;pEA7L01%OXwyaJHNO8gv#%3~$= z0Kl;d{2$K@-a`BwK<-2A0f0|s_5i@KGJ62vSeZQlIIqN=omT=luLQ_XCH6ocu7~(} z9xJg2S~#!LcXt4sS8~3n^QxRh`8}rq&MN_&S2FK9uVmiME$aTUF;?l^+H&3{uVmiM zV+yqQRpuWYEAw-7&K7@*_>rGV?9rfy#E-1G*u#7<<+YN3?5w*10d%bpR|vz^0pF58>54J7#ABu;=C&2bEWO*x?_ES0Hr; zV2>sM_GkiNkHjngDcB?NDg)31_DH-^fc8kdQh@eIyvjfrU9*Rp*6g8x*+T)dhXQ5~ z1i$7Jyk6+$_i|)$!anFt~#@Q#;4V+hBiNH(Udi?nO zj&DSuj34oMsHjH~KQbWpfJbFOc;$fD!vV2}17Z&aj8_U6uN1KP3iRQ9^fq4s;C**C zUjg8K^fq4s;C=KqU$Mo6_z~};kHcQXk9Z$_0BpX(PsNu!$M%qDkb!#q$Ur@QWS|~D zGEk2n5s35&b)4WI5dCAaUL?=S%LpDTjv#9JUm%7V>>bxTGUuNs;5(4k<*4HHj&J^p!AO`TT`$b991@XCR@9=VUY9vKi`=}(pMgDuMVL7wU(-dj4^>4_=k=qq7KJ zmGR@YMewSOADu<;s*E33``}d>Ki2z6g5MJ74OTyaI5pU-F7AN?rlD(l2=h zV7-qkVvSb-uJ&<1r|}9vXYm`jhO@{&Zd-f~fxgH;I*WgbKwsn^SNs1x0%iPI?_Y~R z89%P{=i6ve&p$5pcSgY0yWrKiKJMo%1HecV%R@f^TaBDsPxYEbH;b*pfs+NCT>TjM6 zEneDM&p*!f|49Tk-CWNWoyC8h zfcm_9+am69FXKmN@oTf=b&14}tNs5F0rPV{)gMKmj34ne#Gs(9OVA<%_4tv2x*i#* z>yd$a{K!B(eh?_*2Z1tvSugA$}}X6XM58H6eZ+s3ydZ)oMcgI9^SNAE&Ab@#C3lLi{*YO^6@Is|oRA zwVDt=4pbB3$4WIJek@fJ;>SugA$}aFCd7}`YC`-tUQLJ}r>Y6@QfOYU*h=di`UmdLsg5 z{;^Wc-U2=9`A6R6l>J1wS_Z@(4v0M*5PLWv_HaP#p@6MR6tH!P0_FZy-lv|{KQ`)v z*|e~Stl%&)>}e*1Jsr*1Jsr;^4*c`Mde2u)exD zK7Tg@h=b|A2~08s;`G=g+{OkbxS4TTo?+Wnz0Q*-T z>fn7J0bUtk|LT?w-uIDnonIed|LQ{>tgqx;=T`>UzdG8%`bzEvcz%HWtFLuXKbLcz zpB-TT>d!jJKjd8Jrw7=-3d1_K2bOA&53qlAaf{dkz!wJCzY61Bw7~w=#VukFw0L@e z{i|Sz)FRghpB-TT>f#o$2UhhyzO)7Q5MDh#!2Z?6EwG31>I(zx zU;Wh<*h6^r_yGG?Kf4w75MDh!!2T8Okyt{CV*iTv5a9U%_OECU0bUtk|BCiVyuwet zKEVDJ?U8r|;FSUPuV|0ND*(?Auzy8+Bwq2Uuzy8+Bwhh{dVu{avj+_I_yGG?W)A>g z7-0X(?71z2H3wcdjNQSfc>k#+JgFr zc`f#@E^a~nLxAT8*uVPH7Sulkcy@sOt50k}{X_ix=>hhyzO)7P5ApNI2iU*5xCQkO z@$)YXuz&SeTTuTHKYx6H{i~nd`n$1|Vt$VOEAqjUgxnV+)-_OHy(*$4Ko%+CQlJHY;x`8fdAo&V3a%vTP`eC2@5R}RR0<$%mr4#<3^ zz~6-P+dY2pasQVheh>)sh%JOAte>a*CKO=(Jn*L0g7x!wF93dOW#e2N*3Sc>5|HOz z*3aYp2BC%UiuH5GLE#na=Zu5GE7s4AS8yEG&y80ASU)#jvBiY=!TP!J3N2VaH(mi? z{oHs3fc10Z6#&-HZN7qOSwFY=3IOZpHeZnp6XFN!=QdxV1?%TFUjbnK+~zB`m=Hf$ zKQH44>*rg!cvVB7QVEx?u94%NsFXIR6=jP{ZF(H1ier|rwr|hkL2ku3QHh=Wal*uz_hJsc2wC~!~8KWg`Xgvb3iTf`pT z0`?GI@jc5y%0C3)dzSxgGwdM%-?O~18TJrf@jc7`wi)&qjPu&~o@FrYAmtyztN5N} z0Pacohwv)CXBmKhvjz4LUgcqjJrYa6-3(xT)davEO#tkXc;!C@dn8_E0D8b4iB}5H z9*I{9&>o3b83=~DC*^}pEP;QNfZ0P$YxYpU?4f|!Ljkjg0%i{d?n(KFcx~nt@(%$r zuaJKTka>mtL;O7R3i*fldFB=J5ApNNE94*I=b2Z?Kir@gx6~@XA{V zuN)9wDNx1__EE+U0`w0F6WrhZ&+6weE%$f-t@=3t?(g2Fehz^9yU(@iyo&p~Vc2g| zKW87{)xT9g2N3snLyP|$?r8|_oZ>lv?9TOdB7p<`(sJV(z29 zP9(5WtP?YEJ?li6HpfBmDj5nulb-`<@^b)9e$EzLUx}ZykJ8Tpqq}L(YLD5YMv=^pSHQ3B>a(10d%><`&?U9KT8b z*gk{xl>qdQ^%mB<0?>Piz)@ zpatt6-`*_t0Kodk4>pTE0I>e??ai=6Cv6!1~9}r~E^B#rnsm zQ~n{mV*TTZ&9H~?iuI3gZ-zaDSFC^hU^DC?ykh+$JWd>|#1c9V>L0X+0IYw|9s;ob zL3<=#;ip*tpgj_=0I>c+dn8@~VEu#kNW9`xq5eU8Bwhhv{ln}5L$Us0_5i^8huMQI zuzzLt0Koc(*#iLUA7&2#tbdq20I>dH_5i^8$G115{$XB=`o|NSk$(ul`p2g?BmWS9 z^^c$5jQm6Vob``SZ$|zhe$M*G6PuBLh@Z3m@$JpXKg7>j|MOXe#DuC`>pQXtS{?B^gH>mTX92~Ge2SpNvTskLDJBi;*uDI@Ri zvi=bWm4H0&vi=dz3lA-XSFC?94hpYW|6m*xUa|gRyu#zM{$acV!1{;riY+F@57s}7 zS7^cdhw%yk>mSA|0IYu)uK=+AVe=IX#rlWMR{&W5u=$Fny%xV8wD}4E>mN2>0bu>Z z<|_cKf7pD*7N~#Ne8oOcKQH44>mOzOVEvSGMmiReaAbz}Yv-mjx)<1sxX7O_Xtbe?5v-mjx);~U} zehz^3kAJUz4uJKK?KAatBI_UTE!K&wf4qODzD{KQ<6zpSmiWQ?$NOjM>qOQ+-dn5_ zS^wBxtP@%P*j}s?S^qFU2jSNH6XFN!ALi$1!TLuTKUn`TKWB@t#V-!c&jG~xM*z&v z`BZ-t0rPVJvHlSN;}w8d{{SGo0ubvT0EAa;@$Li&uh_>gCO~)vV7>pV2@qZZi1m-q zN11=b`bPlD{3F&s(4yqkms210{3F&sLW?s0i1m*El=(-jf1pLltM8>g>iI{kf8eR= z`A4jO1fa}6Li~t;@rpk9O9>Er&<8)70I>&s@V*3yJ?Mk~JON@4w)kQK#2)P9D-keW z0a)*UBLc=N09X3Yr55%4<5Fr-#*cG-^ijr-OMN_5Q~ZD)W&Bw0-xt5GDdWf0{zoHF z#*fb8FGZk?AN(%0j2{_*J<_rk+|7WjOB|4Oi374O$pG|_b%_FH{D_@H53H{w|A-e+ zz=C2A7>a<|Ljkjg0%i{d%pMAuJrpo|C}8$bpv*sZFJOPS%s)P`fcGrR{NrN_c+axT zKR&a7_bki&<6{eW&$7%vKCpoIEX(|3_X6ItEc1{40^YOy_U1p1FJ`594%7b30^YNH zV)OTF!1sH2&+^loXWR7_SSSAc=G_U1>s|a*e*y1VetPpq6A;(C0Cq3n{E{a&eA|UyBJjgHbDt`a*b3gbSIn|NC@^AvMt2Y6N zS4HEevL5w5vL4=toC8UKeaJbG1lWh1BkX;oWBEY1T|MxMdI)e=54>U@0_^XBSL`F5 z*N2~apa))YtioqG!5#qi_rNRmkVX9&7<#nLPmXdf-*=BkQrNhxd2chu!~y_jk=6_^JIpyuWMq0PsNXIh;mP z@M?b#@9&x(AoX26yuWLD0NB;L7Jth00MP5D@6R{-$h>-U9`Em(9%%9EJl@|mJpjBp zkNm^*U?0dovdv?43Bax%_OIASq7Q((dU$`AeF(6>hxd2chnxfXKo9Tl=036>`+InQ zmwm`N6L_jk=60QUFr{;t^rzym#ge~wAUycYXcW)Jp(_jk=60Cx5MBA#Dj z_5jf9;r(5+2Y@%{@&2ya1Hh~Ecz@UI!4`Ob*X+SQuz!`uN_d6*!|Xv`VgJhP0bqX* z@9&yD06fsc`@6Xh;T7@^vj_V?{$ch2u&ams!|VZ|*F*l1`^b8{Igk9K)Z^88ktcvA>7>qtqjujT=|`?8A74{DXZ65aLH{!9E1o z)kFTlJ|uqhddNR=A5jmo1o;R1kofWHJn|3rA@SqQdE_7L!{bL>x972n_CO!IddNS_ z9sus@A^$LY0NCF{{$ch2@IVji_S{F-V}B1TeX|GqKyG060I;iv{KM=4pw~nGk^2x{ zp@r!ICcZkaeaK4Y&3QbP=|S$6dXVkV-1Gq8V+QT;zzxY>>u8T#18_-tA;)#etd9SxlV+k2$($-D0#&eW)E$VZJv4MeaO0mK*=lb zL+&LbVD@0=6V{0z-!@^L_}Og})`|aW+k|!EXSYpQCw_d}gmvNvw@p|l{=&A~o7Y#1 z+wwZ`c3aTjv@Nd_9a!9!*NFh~y8Rcn<#i%}%&QM>%j?7d$h!UG+wwXw0OB8?-Imvh z0g!b&vu#@N*EVPWAaKtP1o{#neM;cj0b67}>V0HAypMme`CA(a76JD0+ne+Kg9O;e zf7<-MkWRsIypK@M*_d~E{uT8QfahPa4*_`o75g~dih6*eJpYPgb-I=PgXdqdk4vqn z2U_s_EB3M8ih2Ox`B&_tGZXay!1J%n9sqd$mDvLT&%ZKzumyN!_Fy02Rqi9}!Sk=! zN3sW6@cb*Y2LPUbW%dBT^RIFrnO8jj%JhH*dH$8@LDOD~u4Z}w;Q3d%kIXBce`R{0 z1<$`SJpl0hE7OC0Apc+=i6sDd{uTQWfahPa4*_`o75hlM!cX!1tK3J{gXdqdkHjmq z;Q3eVBk_t)h5UnkBwhjF`B!%sG{%pU9m`G?s90MEZNdjR11SGkX@2hYDUd!Pl+zcPCO z;Q3c(5B7olBac<~51xNz_Mpv?f0#W0@cb*Y2LPUbmHQB0A^$LYun*)PW)A@I{Hw7K z%DfxTzZ$Q?B^t!@uL4l&5zoI0K&eMO|7zUFuWiopBc6YyePlgWE94(ddLaKO^@!(R zjr$Ni;`vwOJ}ep_|6m^i#PhF03-%#EJpXFkhs2L~{+0G2yh8rLJ|up`^RLEzNc@QB zUyb|l_z{6@^Xwn174i?W2bg%gLjGa)0C1|pD%^Ml;F${R_S{F-<5Y!}zS)C)AU803 z09dV%O|Xw-4*&-$>n$sh3Nr`E>+rxF%eH?dZ0x(2hzq$cm>T(5Bi6Wm8?rn zrO`D#(1P!mP!EX)1lUIj7_aoF*oVZAdLLO2??d7T0rnyBg8=)G_`&xR%^olm0kek! zC9l}R?4d1kADLI)hpbBol)UmjgjWR29_$?XhuH%F-?KD(0N8lXGOc&T&-tEZ?j!5L z_bl0m_&MLRG<)!=kbjsx0PsD_JXYC1_?~6#Bdqkr4fvjA?8AY_2YH@04BU;3y$u^@%MxE zKC&L(hnzD(fPKh069m|YoHOBl$oJ=bpPG6I!1t-yhX8z^ntjN*27I5IVW_#UI6wX-=D|#sY94xAM*VfN(q)$Ls-s_X3zb*aEyVdjR0Q0J)FsAMsv* z@Ko$Wz7vl30tCS9fuD-^0*v)Y_5g5f_*?{XAK5<+4N?CvJutoQ8>0SUdH`4+UXwnA zS38HOf8;(gujYrSf0!QlscW~Q{$Y9mm>;74QSxf%5cvoDNG#z~q5i=>1lTu3{eyi7 zaA=77hw%zOb!>?GNA4r*acGG82m6roOZE*>|6m_-e#!C>`G@feee4{f{*lKj+Jh`X z{ln}5;M%RIf0#Yk0`-q3UZMVx`w(6s|1f*tr}hm||1f(1I5b54!|Vaz*bw!PJXTqc zLqpU*%pU9m`G?s9!156F53>h=okP?=avxcb`622bW)HNub}Q;1W)A@KL)1Ua9_$18 zM;@!}AIn43Kg=Gm;JzX953>h=LqpU*%pT+w>L0le;T7@^vj_V?{$ch2uslTmVfFy9 zbBO#S_mTCOA0q!K^|*E`@{dxF`62R;QjeWOY`A3r;$UjOw4h@lilzJQ+ zBL83?77dVpunz%3{D`>$`w(Dxi2Q?nNc`A2ME;Tc5MCkwU>_1cuHB0KgMCQ+m>(kl zU>_bo;+i^-RrZhNA@UEi2bj2Th&8p@1Hhr7uENv23*gug>-OA7*5l9+D}A#E`#^4B z_5iRvME+s+0I+k2{3G`vyh01p15CVjtM(!5lKJ#h?8D+msR!8(%}oygK31|WIh01% z^gs))ceBm29t7Bjgb4!1EBz_Cx(^2xPhz_9pL>@ zajXF3{6p4>05Y#+ofrUFx63*)0OlWgofrUFxAUA-W?qAF{z2e{CFCC^Kwg!=+K4T( z9`!!59^QwXGeLlT$T<@P*oT}m;eE*0E$hii*?~Y?7=NeoX zp{~Y0WuZ@s@u#aR9v^YD`{6o$!xiA8+%pPd*;^?_T5BSH05%Ley1Hjpl<{!o@ zjgWt^k3=7|I6Kn( zLx2k-A#5kn>B(%9E94($543o3g#06q zRo3Ie2>FNEgMDcJA%4C#LjGa)pdQFSavxcb!z1J$W)HMDIMVz>{QU3;`G?tqeIWnH zVFNE11(OBkbmSp zvL1&=$UjOw4vsYc5Iqi$kbjhVoERbh$bDoz)<(!bn)J~8L-e>XLjF^d6_#yd+0Ba-UAM8Wo$B7a0kKBjw3i${7koa+Mr1^*O3i${7@c0q)k33e{ zKh{RbKg=HV56wTs&o7Ly3O9S8#fu}X+jAdTj|(H@A7&5sp}B$h`PvBCgxQ1sf&3%) zA-qBh(*sOAIMP0hS9mJZgWN6k0I-%cH$4EH9qCvZuQ0l%2U@&1x?Sv%SOVa}2o_`? z0)#a~!7Dx$Si(Leel+n4EeL0?Awd7G|587OPP+D9_ zUTgLsuS!1$a3ReMW)JqEbBXZ^vya&WEquNbuRWY*II{;@931JKXe}^{nmyQu&bzWs zTuU>x*@JrM+%A58VT5R4_CSjlM-$@5g%ojOA7Q00Zg6%Sqa0XEQ7!fXAg|j`jB*?V zkmudQDRQz8S+^e?9Mu8pt)NMoe{*9@s7q`-v{RvBrm z0+981QP(8_$a-8zD;f47dYnybANC=7tfkcw`!GFZef1+_307q6Lx6*64aYtNIGk2} z?8D-RtP?{YdEQ-1D@*nv@q=qo_F=rj>Xm&+{CIIB>s^j62zMc^kj)jzbVC#0;1Hj=CR{CZS00&cUVD?}OWD{l&_VL^}|DYb6MUkvK^R2%1N z;irUGl1EVwwzx00Fg@4@ay8QfK#0!LhwXZ9iSBdnIiSt9xQk0h`4v6A?~(QV>YX%F;43z|KshuU2HJgl$8=HlmJePuS!eoiKu zJ=h2GbF&AZO7im`iJza){9OIx@Hjue{Raw&4>~}vjbjBM`-k{BfIRPVW{7$Oz~VwO}_~Cf~poaikhw(sz06-4`{`A&(@KpeyN4n<+WY{{4AD#yQddU5Q%ftBL zc>tit6ZS~z&>rk# z09>OaC*>Z#47;V9`KLEEB;hz4;l)*GJ6mx?LnZl2Z7Qa1fYl613;5K z05sWyKc#wz*E+8ZP(2J#J;cwQSK{Z+EAeyJL;T$J5I^UyQOdSKo)UZD@p zyQQCV-n~9QC*e5nik}ludz8E~KWB^5&k2-%4xq`;0W|qJTObZL#Sg~8C^SsG|4zig zGJbUKL>w&R$NTR@9BhgojDt~l{9qi6!s7?yU=-8%v1!w$ImW?h{3zBX=I81k=I81k z=H~zy&pBV2*J>Oz;3qMD__{=*fv-yp(D}-M3Gst*u#6vhorrab#1EgZB!2jOCGmrC zkoJ(Ln&uzV{3GX4nO9}}nC2gG;g#kquJqN<4NyN%>s_`e*ChnXbqRoMk7@ofPO5*Z z;>R@qDE6x&fYoAKWdrqbpJ|uHH{yE9vl1ZmS+4^#gBJN{t;iK z4)zG|uSc)FIDUV3y$^e&dtV*E`YPS~>HyYP@!r?4myzBJ2iw>CSYO3^UjgL$$9f;@ zt9b8g0RD1IKEGu9BKF(ky{`d~_mDrbi1lu~_cZ|W9`g1@%)3dQlkzpkdLR2fNgV~A z?PK3(y&u(4;Nd>@eb)QXL%#obwvTjvw6?E55jPRjQm+ZVC#lhipW-+z2$ z5&J$#os;tYM|^)h*dwXK(M81h&Kh=CVK#AvIl=kc_n`CyfQ%bFhKPXKX+b=UwtD`8jySd6)B5Iq!15D(79!SLM8GykZ}icS}F# zyj#W(&bwv&;Jn)uKQQkauh2Q?-O|rF?_QsulW?4O#m@<-JxX4gpR+~j=LAYW2hil_ z0Gj-qEf5Er;s@g(>K}yy8b|&V;VoO%;LHPW7Whf08PB&=<2*H^znJufYKfy zLz6uSOyfsf)?QEi@O6o?UFRzUbiOiRLi}JHEaOLBCt_V9@xyr~@xyr~@q=-&DSk}w z^R%!E8B0`$>s?SZO}GJw>TtbFpyU;Sa$N#ontvdj;^@*j$*X$&;JUqxAN2Dwevns9 z@q=-&j33kdqewH<&(rb)+@(FH`A3o0+B%Wz67b5_i2!`vK5<=Qt3I8t4A3}ez%>7e zGquDI&b!n6qm$p?ZQ5txgskzrL{?M!oJUH z1wEw2rV9H$s}=N+?~3+R*!M~5grCZFgVhTAK1rRE^8MhG73STf&Pn-x@P!KdK1rRE z@?BBV>5Vyn1120mh#b zdWfHM-X*Wfd6)B*0m>@_lvm}vOJ0@pF6XOq-sOB%&byqi%6Zp#1@3a*HC_SWyj#W( z&bwv&;JjP#^+{9qi!{#Bw6T0FUgI9SGy)g{EiGJZU{ggDp~KNttGe`WE5aS-(niyw@GsDDi3 zNBI7Oac~+xigk(kx%!9sx%!9sIRKA?=C$y1&Q}KfB*qV4mq;}5b%_BwUl}kVelQM} z@guJju`ZGL;q#Tm51+3jelQL;#SgWIJZ^G~kg-H{xZY*N(Rg0Q!KV08u1gp{ruj$8 z)$%@E=2bm@aK0+z2iNUo{Fvq+MPBRcMDY*z^E54iqS+p@E+L?PZY|W$(|VUJ%5@2W za$N$z{d}5#AZ+V=^%EaIrujz^Kc@Kyw&QgFs>#oFeO2yX{b*^Lf7I3`)BP*u)ii#@ z{avniElm7W#SdSX$nxCRB?joa!~k8F$h!R>BmdaFfc?bnGf|v;Vtjw??giLm`wZ4s z20)MPGsr&-fF9dtq6j~g&%fHefc4e(nJ5Af_uJtHyBDy&+CGCk>i5R?*WR~){Np1t z*x&v0@%^>GwSfKh6EoP~y=hCnzxKWb%)3dQlR=%0?^1U!VBaUHqre+I?ECCqfF9D~ zJ3Z|C>|TH#LC=k!dZUMZpQKLssa!YMy?}k6q|V9r&*bm6?_0pWPg3XP;hB8?)o(3e z-zTYaa&3Hn?I(7^9!VYA0}YPmWK<8iD340`UP9}Q*xVRJcNa~zS_Tb}A;1%^KdG(2%6L|HBofCLPJxX3tkCIo^ zqvRF!D0xMD7_Y$HY!CQHq7MP+VZ0&$J&acbU=QOJ0onupk$43l+XMcQcm*Ka1OAbC z#h)tep}aDC5Gd_IptJ{p($5J%53>h=CVK#AvIl=k^$@RhUKyZz7@&HHpF6L_&z)D| z=dOqNx$7Z*&Uu%-vRRaVK4IP^udIdgs+@Ppt8(7ud{xf7oUh7xm-AIQ?}lM5;s@qk z;}w|5dAE!ooOjFk!FjjzbI!ZQEA+v6xAb$)yVvLEB%IpA=u`SRTc|xsUYVb>Md{}N zn*1CwB$RiY1Cyk{rkU>QID-}fL6mhs~~I}rz);s@g(>K_(A7za`R zu=v3^i2BDgeuRw_#=&X)DApzB=jtEk=jtEk=KvVbIbWGO!_PTi8Ss-BKYU#x(ZJUw z2IzccfX-JEKNttg_>tF%SeHor@cByOhtF3MKNtsT4|%F-{xQuzavqg=RmP8L{t*{m zX};o0U;W$w_4BkYVT)=05qBnRT>@UcbK}P}|0wparuhf9<8=Qj&%3hEpzEu0|LRLS zr};-M6Q1s0DX$Q5ns^1k*NM~pt8qTa{Sr$|5YM?^QtmTwzvLfg|LUERf9&dEKQSD< z8@=|kH^sUEn7FHleDI~M*oQL!>#K0ie9_|G@RzT@v^AO~{8X-g?CKp)KwM@8AlE;3 z^`1-s@~D4zQ$GJ{SMOU9*mN`I-QT|{)(!Yne-eQ`H)DVIzuuJVAG>-#jDVdZjG1~@ z@0Jz-=#dT>25?uXe*l0U0_+d<4*<|Z?#*~0)IR_~54ksEfA7%*K#z1DGW+;i1j_TN zclG`t0_FMCz20|Iix@^|5B$`d^M9TC!2VUT2Y^@S-yLlB^#{9g8_Qe#N(7d>aT|;F zK#N}QQ>jJV#-cp{?CSky1kQA^f0gV3;I7`6Bk)oe`A4z`fc?GSjliZ`uz!{80pNk& zAEQOVtNp$2C!o%&yLz+j*hd4eb|;|Dt6uM;38?ex&H2x?!|f_BZLRa_)%h<*pybt? z^S={;l2@rv>%0OJ(~F`35MB|0f0Xwd#24E!y22~AfPa+t8^jl0(L#8|7Vr<_6#@8% z@rnTa!~7hET4}>S%+CQFXv06u9spL`@DKBI0LR<#kKh<%KW7X0huMRDz(33$09M-Y z53>h=rFQa<#4GXIsty039s+D?!#}8p0970QK|RFJm)h_T>LGr<(uRLf5ApK@ZTJWE zNavnYbNC1KNavmdINpYTaK4i1b+rxuAg=^C(1w4IR|2fG;UBy=L+0J3HvEIUl6kjk z!#{YQzRbIu+VBtZO6J|F4gVmoY~G#5kKm(vp~nB*@gv@9)fhkGgxnZE;)Gnp51vnL z^A#pC-V0#!6@dxsl6Vmd0!ux9#ET68$b7}0ny@Ze4)>CU7B*k8#e{Xqa=4caQ#jUF zY4%}@a$N#oU$~bHEo5B+;LxyKmjE~x?j=JDS(mT{>QQ!Y2K)G02>re;0kE9zB`f1c zlRYqzHQ9qeX%7OWJqVQcAW+(aKxq#Gr9B8}9F%AfFIEeKSjG?9ql_Q4M;Sk8k1~E7 zO83N-@nc`QC$5Yiv_}~~{N4> zxkqt&|2eL6*Il)IuO;dqlDPA})bjpX-n&%ZU(0(fo9?fT8|~p9cho<`Kk_|Taofc2 zuf=;Z)BDf6xo|kWzgGPm5y$ThFL`Bk9JKIz!=-NUPPxC9^KKIKbM8Q5-c8$B4q*R^ z_afU&9Vgu2wG-yu^8Q-RyVLv6Z_f2|zt5|@myGr>K<#0G+9TaFN;ilBFc=DPFoeUf z`=S0pdtC13_g?e7i}wIt>*ntV{XXI9mZ%4QYI^@*u`Ze3KRCUAF#E?lC4TsPCD9Gy(Ns*gIKVKxgB**i3YK*hDjgx5Fjns zLkr|l0`MMQ#&d}uX~EtQKhlCd0C-PE;>Uxju9niNlM+ADg8l2|{k7y(QfHcfYzlj? z)A*sh0^6I`B>^N+3h{awzx#w(DT^RDp<0Owuf6#&k=<@+Tb&m|goJU3vPf5hD= zsSkecPQ35aw10*7eUe#x{|euqPxr5;`&Tj0Pvb`{jL16C_u*up!RIT>CMN7({bc4J z)B6Xf_n+r|xO}fA>K}ip_ebHqzUlqvxR(qonWlTm2w+_@y?=0ge!JfrUh-;s|KRxE zrF8N6*QfWN`@VRMUNToT;(iJ=#G% z>LTVV0@yFH`%(e$erKDn05si~%Adl%Pg-W3Ot(}Lz&eqBZoo8t#8r5*2ezrE@uP$9 zoyE`JDfbUf^N(r%G0i{brtzbL`rt)`ZBF`#gGW12glLdYv~G&$Y*EJZY5a(<-27P) zKREAlU6Mu@>yl~y(ZPFU7pM8hra7*6c^~~W{|MD^?)x0o`HBnra=s#fdhOAISKqm# zoUhnont#L;Agy;fB*>#eo>W8wjI!X z-aS)qL3{j40;W44|3084m()Y@ow0y3|%9v@9W=2h0?GZ7G8Wj($a0pS(zA6y&# zP6UKk*&feDAn}S%g?aadrT?G3_Yb!8x~c;O{oa%GT9)5!UEkK#Wv)f*HPh2^QyD`w zuc-#fQvnR!ok(_i%1H?`1pzVLYIeZ`k zJzhQ00JP(o2B00Q*WgwyuZ|R;<<<9QpvSAt4D@*Q-(;Z2s}E(sbS_@~FAYFD{;&aP z$5#u`+Hw6W@Ql`u;~6lWi&uX$1EzEF>ZdbcI@i4W?hN$i-T$cpXve>70NU|?6+pbw zynEHPc!qeTdG}i~VEWL!``0pH`p~@l!x=DrXx{yM!4ACdvo(D~2FwoW{HHR|gM@&9HZ=$!r-4iXS_PJg^M13jJJngP>?bpBs80R3@q1JED8 zRRHly@#E0~h*ydq|7QkFAJX~K0oj3QAe|q|K#y0qWS|#6ey9Ox$KPoH+VMXWpta+l z7ofG{UuU2fKmIHOz4&q9x&-v%$6w7r;1%QGUvB`~@e>U|JASnQ;+6dIPYPhXLO%HD zkw-ERc!l>eHYR_Zfxs)SOTKZKfF7@I%s@})-`xPTvpr_Yvmu?L8LV|%>!`N#Hn@AHrC z@!sbj+vDf=Jpb4p?|uHUJ>L8LV|%>!`N#Hn@AHrC@!sbj+vB~@KeoqvpMPwR_dfsF z9`Akru|3}V{9}8(_xZ>6c<=L%?eX5{AKT--&p)=ud!K)7kM}ydwiE09O!@3wJ; z@~F~4@wf6H0Lr7-|2(%-On~)KWdHMVJ;`_QBdn$8D!)SYH|SKS%w;`pU2m@7ZZ^Cv;+A^0B8s5AHDvZsDD_0XZGhjyN>#Y z@rr$yZoVG*hw+Mimo_G-e;BXi57a*jOR(c5J5c`!0PR5j5dhkO`bV$-AnG55S9}Kc zea^0<{?Yb7zxjIPA8r5hjS1=>ZU3mF2T=bAEW!TO*>%)E0zf;Ee*}Pbp#IV073v>7 zUZMWc;}!Cc90G=*{lj!FULpT5 zor_nff0)iS@1p);Y-iusv+JmT1b}uR{|Er>K>fq?p?Mee57UR{UDQ8JADVZOf0#Zr z@1p);`p~?K`bYRx*l$0(j{1iInsyIO^mv8(M~_#ifAn~T{G-P!)IW?@ z>_2$)0O}uQbQwSB57a*lVE^-*uSfo20Q;YBOi=$YowNV>(F3S|1fA0#sDA{V(;vt` zg3jp=)IWMUNBzTCBAuiD5diuF`9}cg57a-5SJFA^AI2-`9Q6<5m2{5$!+0f~qyEw3 z73v>_B{6=W{t*D$f&3!?v;*~zUi?7)qZdC=|LDaJ zK>iT`+JX9q@k;(c{Uh*-aS-*7z$>mxkbeYTq5iQkLH(o0E7U)NYN>P7KLS8IkbeY# zcA);z(>dxNJ)NWe(bGBdkDks^|1h0vzC!&Yu$^|G{t*D$f&3!?v;*~zo<30j=;;IX zkDfk|fAsW$`iJR5aS-(n`&EjAbMlY;eQjDghbw5uf3W;xXSDbE$IfW)^N*d;-sc}X zqrJ~Rc1C-jf9#C*KL6Mm?S1~SGur$7V`sGY`Nz&^@AHqH(cb4DJEOhNKXyiYpMUI( z_CEjE8SQ=ku`}BH{9|Xd_xZ=pXz%lnozdRsA3LMH&p&oXd!K*ojP^eN*ct78{;@OK z`}|{PwDxRkDbxp=N~(xz0W^(Mth%s?2Pt4|JWJ5==_8I&v`DVbwy_9cIL{IQ z`$vWI^Z~GcZ8%RK0Q;YZ^YnQI`a6g7^!W_*cjo!K))$%m&w2i?^*3PubDqCjMi;kY z|MPO3u(vs9|8w^L2v?xLGy8v(zLvNZ`=7J_NAU*$_CM$OiEaOLo}bwEKj-<0ZU6I@ zSL}b@@{0Y>d%T*{-N3g{#FgU^6IfUkk60KM1t`AmDM(;s^cF#*bbqxY7mk2Zc3LhbgOu=v6L=WYC8|MNxsz(M=);3EH+ z%;|r=$UkQNYZv*4$8!j0k$=1b>k{juxX3@6b>brbSmYn-qj+NQo^H4U7Dv z^r^Oe(Oy2t#r7iq$hm>-Q!^iAUiW&H=45AurQ4{SiJPyWcBk+w~C53O38{CNS!*+2uHzu}wm zocPqrYb#)F@|_vDv~sEd*$&8Hee$LZJhQS{fNTf#@pI*hzncN;KX^JF7=>rtmjUZ5 zb4U7MA^}gVd?EwZSLW2AzjxI)U-|TBS02qk@dutk;K7wI^??UgzS;*KT)FOQ+)8%n z8LugT>>%*O%J*cz)>j0cTKS<2SRIGJrIoufVEQ2N%*wmVGjuMFuJ~95yt!EZ)XHZn z;AaZ(#L6EOK)m{u-Ld+!0*F`niW{$d`oWb~tjZ3sBtIho53GDk28>tvSK*4AGhn>p z&;9Jmn=(-7gIf`JV&(09;Hi~g?E{xqezO3wgSUESTa9sE_7R(?MNg;#ob`ngZ7 zd^rP!R}MU}a^?O66kZ7cuWrmhk5?!9zym99=mQU~{CELcUcIvbEw4V1fgZ1ZI|Dsl zJ(+eOm!!hj?|m0J1~8I+p>{xp?)988DsGgAcC! z!wmFv{y+wbKipAItUT5So?5xo2QIDry8^Vl8omP02)s&UxU_OC12*q+5T9E4-V9Xw zNLM_u^5zWm^l`pCqt^L@EB~MZ=#K|heyalLj|W#CE`aQiKmM=)vO^yHw;3=yl^XivFofCLs<(vD!Q!C%u2QIC=u>i7zw|Zvf zCkh}t2wYnEr3{$P2|Ts(k1}97*RT3#88Dp_`0UE(%QM6)0uQd>3KP!##qtNrGYq&; zfCtO3vi{DG?T(e%0S)FW()9X7vD|o-pMiN7V`citzbXNj%IF%ew#V?tGb`|i#Sa3P zR^Y)FcxnYcZ-FOP{>8PoWQs~3yjA8E0O=zG=~q3Nn3w>I9|Rsq+)aSR4_)!MGGOsz zV?titk^$otfhUrpy1-K_*Y<%+EC2QtxI(<*t)5x=!vcs`1TLkSAzfj-BJfn2OA=tb z(y#gl88BWENCz$D)spFaE^xY7-d=G>1;~y&D?oO{D@^C{8K(32t4!zX;TgvEwOs(~ zT?3xK0Uo?*8S7mGc*U`0tanWxe8zVyW4&AYj6xszS0!E@TgH0V^pWjIz)j0o@0vcc zm%?J0dXG4&#-ssPm6!i{)+pvHu{3 zq|HBGU4S_=!Rhxf2RDd@B_-Fyz{6kmN`G=lS=O6l2b^akc z>ik2N*ZGIWs?I-jMV)`>8Fl`lUsdNHvZKyFWO};&OdZToqy;Vb^f7WRp%eF2Z1*K&=qz5alTmI<{$SKpv^x%v^!RH{=q9A zOe@Rub8TJ1XFO2WqP8yKuX?boUTs}+>+Un^{Nv>Aud4HpBL!&l4_?vcAAClee`u`g z{6k|^=N}rYI{(mE)%l0Us?I+&R(OW3uQXQpRkps;Sk?K5#tNC2@hXm$@k(Qb(Y1An z#;VRgG*)%~p|Ps-4~~`G>~pd`?Vkov5*Ta|LLuzPAE2R>umU{6k|kEP(P4jn%)q zCOzDAuCcn50n@q0>ah&8I)7&6fef^4zm#^oBA~JQ#R||^ohyLW?Ha4o1<<-ZUy-80 zgDc;b0nvhK&Ofr{T_BHD7pSk09oZj#h3w$3V)E14 zLBQ;w2Z1*K&=qz5p=Z?j2Z2TX^^e($i{e$Ce~4Fg{vlq~`3HfgR_gqN zz@?Qs{~++pN}Ye`iaP($UU;2Hz=wiq?toR;f+r?n_ew;12^nS1O|q zmwJ=|Uo61mr5=^~Kkwe^PYdvcQjfB}mbwD|u)dah2K-_DYxS$(kJ4up+tD8=&-lbL z-oGmT0PwB?d}JB#Um5VT1$h55-oG+HS0Mi={e^$!E|4Aegi(62)M!}=m?tWf_bc7P?a9Q6<5mBtG7595`t zK>fpbrDvf2QCJe0i28>CvIF%G17tbsAB9)AmBtG7kHRYex&rl&!YcrJ2I?P$SMgV& z{$c%VWe4gXJ(i&UVSvU8^$!CyR>(hkyh8rb;}!Cc90D!l{KIrE%aMPW&NWu3f0)j71?nF?oumHIV>{{}2FMQ7KMW8{Q2#J} zh*zk8m_G6qX;0?yWxRi7`pC~nz{AUU|H|}{$0`9Am+}6U>4W8(Pb{PUVZVyNN0#ya zRSUd-8Sh`Uz(l@()}t;iC_9$v;fMF#MS$CptBFrD)mUsy)nz;v#$LZ!iU zuCYRG!gQ{&LN&v5uCYS>!&suR!W9N+tnds2G*!*s5(Lj9xF`4^T^|7h9%_%iAr28dUv ze;6QMq5fg|5U)`Guz5FMk)pvzmQnvOedK2(;Qh;}f0#b74WA*k7SYJyfykA&;QwEAZ5I+b!zI<~ZczF3+`oP8IR}?^Y z@K&E#{T@%_YyQ?4dE5DoIL0^7fo0ppeI zcr*irSGpp-5&x0&Mq?Lve|iV93w$KKH3>j==oy;@kRAF}r!rvuYYF7sIRVzcmcZl7 z;|y5;T0Y|oX`dkhJzn987T_~l;9|dsgZjV>y52xtG zl%JF9fdxy_#*l#z0I)D2<)ISWM&VOMa>L2aA`%C*!|7hpkU)YEJcGCxS{`P&?Z*TSS z)_vG-w|STJNo_o`KdhU_M_FGPT>*bsUl~0E{;<9>`c?3U^_Ah?>pPZE|0w=|YPpko*AnU< z25|57CznwFFhEzJ{$YK7^bFKLtj~{r73v?>=SOy+{$YK7WI5^|*5^lKh5ASNRhV~m z1?nFL=ozSg7@%K;`iJ$$ksYXiSbrQ@j{1l3N@Iokhw(~Rp#EXJ(lb#1C@k^#anBO! z9|p({)ISW6<*0vH|0sL1oWN>`x%Vf~}@4AeiYf0V`w^$+VGB|A|6=&=O#4+At- zsDBusu|oZ$$1Bu7db~pYqsJ@MKYF}E{iDY#)IWNvMg7A7jTP!22578M|1h0vtWf_j zoy&66KTPKuE7U(s=eh#*kDks^|LCzD^$!DN2kIXNh$X0hm_EcS)IUrg`HB>sKe&YY zhv_3fBLVMSLjA+^k;f_l_bj3QVfx@E;5(L3|FB<0;I1XqKU&}?mr(y`fxDJa|1dlB z4Aei&4g&Wqq5ffZ5P0tr>K|qYfe$XB{?Xd;u_e?$j3v4P^$!EMpZMM-)ISX1e&Rh# zsDBu*xS#lrCDcERSG?k`CDcERSA51#E@3a+c*S3J*AjNmjaS@zea8~^+so)8e&ijm zl(F2igdKVVxcBLj9vv?OkbxOLiEbu|oaB0F4#uAEtAS z73v?RbMXrG57W8E3iS`uxyB0hk5=a&TSEP#W%~!0Q2#JMyh8oM0Pza-57UQuh5Coh zyW$nK_JZtWf_jKx2jahuI+wqW)oaNQ0<< zw069A3H1-NLt};dhuNXAx-pkIj3pW?)ISW+SfT!5fW`{-j~=g3|LE}w^^YE}Q2*%h z3iXd3uTcLeql@^Vu|oaB0Pza-4+At-sDGHwHCCv9n9em;sDHFN|H&oPKTPKuE7U(s z=h7hRA1zDnSwj880Pza-4+F$2)IWN>Lj9xS71md%e^k7}`U-pD6|bAN~+Gj|Bv4jAxXaPQ>1^BCsSG?6bQhttSXy1nbEjL~f;8+>2bOlD& zc%^5UKcEi+_oVzhJ>2B{-No|vru;ktZzupa&rIh(QUS6fUST@tGk7c0Ie!%a)A=Yo z!`S|f^MEf8;6dK%-*bg{RX;=fKKWNAw)0k|k8H;bFnwgpXMpMBaQRi-hf9BU-`~A_ zKz7u(k{uDSbLb;reM}+{`j}vJX-9dV8obgK_C7U$6UB1wC)yQf3c&ru^1U;zcuN7e zpIFY50iY}Fed_oOd!HJBewDpX4Pa27@q_!Ye^vYe;Dt39_{M$MzcS$I0(}2I>|Yt6 zE3kiM=cMWx*uM(>qd17zzY6_70K9!4_OC+!C;-2(5Bpc4e-waU+K2tC@~bfKYOJt- zWq|yF{VM}BR@lF?{syuG`&ZWgLzZLz%6O%*!v2-^b2%6O$`VE@YA)7P)U{*?i; z1N&D7$a3so*?08f751;}oFZL;{VV&vR?ooxm3?2UUxocE`@U9o+?dZV>9GX+R|aUT zuzzKM#tQpaJzin|s>dtrU-fu}{i`0Yuz%I#751-ss>S}50U9gpUm2jW!v2-%Tw{g( zE7Q3w$Np9E2gIqd!v0n9M^r8LuZlmS&ar>hV>|Y*43HhzzcN59!Ty!$L%hQNmFYuQ zVE@YWp=V(K%JiYJ!v0lHAK1ULU!}3a{*?h5E9_qxpewL{Wp>CP*uOG6^sBIc)w2Wp zS7wJS$NrVsp|QgLm9a!uVE@VhjTQE<4A8H_{#B1x*uU!W3j0?*USThz$1Ci9^mv8+ zk}|p=oPHH{SPYOI*uOGBmSY#kbgr?&zK`i#S74{8r*rHrna=gAup4DMm(Q_()nf^+ zFhIP*GYk-~@T+>fGCL5X^s8Wbk5?G09G!viPB~ z!v2-T4_S`=D~lf*E9_rc{LmHHzv{&g>|Ys6^sBIcWq|C!{*?h@3HGmgyu$ufk5|~g z>hTKuS3O=~|Ek9;>|dE`WjXe*4A59%|H=Sef&HtV&ar=GI@efX|H^c(vBLh9>0D!l z{VUVCG>H8xW4o@v{*?iG2KKKE(67S&RZkz-zp{B(mSg|Q^r5lB{*~!NS786j^datI z|El+^uzzKM?7;q&0kRzXS7wLC3j0@Phpxc>mD!r(n>|gbGh5f4@udsjB;}!O=dc4B^RgYKLzbd1P_@S}F{*?h5E9_qxps~XK zmFZkQ$NrV+T$W@1%5<)=!v2-%TvuTKs;6`8Ul~jEtFV7%fb78jl>xFG`&T_)VgIVf zE9_tOc!j-;9dtX(c={?@9_#_)#DXLx5q2tET zu=B6b==J}2Pd4zdW@~S=mio9yizap>N^RLLO`uyEfhsdk? z{9TUjXVdwKX+N>}1M3n3{r(>qtNQ$1UeWLWfw8L3-_;ep{vUWoum48^+Vgj1M}7Y8 zsYCEb@BAzHqdkB3XAZ$1z5XBcM}7Wo);WGveg5v74?S1rlKTAJ?2mLsd;acs9J(rfD2Rd3ct$M?T<~#E9_s{y8XfwI`8%WfX;bVv8_wC zrqFr6|3@;g-~S^4{r(^D2llVZf(0`}eg_g`RiD30px^%^(Wl@41H7uw-_5te74`YM z`O}5;@Xx0HI59sb(C_~NJL>ayc}2hfN4lba{#63{{Xg)G`utt}s`~t00_pRIyxz6t z;Kl^JYJt-e@Tvvwn1ELT@_H9iyK@3w2~hrV#{}~g0b3`Yo?yNrVC%$<3Fa#Trt|d) z<|_im_O%J-t8w}QGzOOEZ@_%j>;HlIs@MMm^Hs0^2j;6@{}0Spz5XAVuloHzk_R`3 zS6ubY`=2m7@|!i62PprjZ>9Vr0+xS7z;qq~%RdODevsJ?^}%z z2@tPdeGt47AYSc12wo8|UOhVkuLu~gc1GY80prz^Bk(Ht1EhXv1YWhkM@QgQ3p_Le zuLQ`BCr99w0NJrKdS?!h1T20$J9?i1mY?rG_;CX)e!TkNr!znUjaT~*{$3w=cJwEG zU}yAv0J0sJke?i_t@<;>tA|GG1{kkCI@&P6c=gcev;oGeCr5W=fJ0)@U}tn^A9!~3 z_xr&9gTD~~+41Uw4+KDV>_7Nf0}8KT`Lm;^3@E$;urqqbfWj+|)fM}FtghIdEFT&T z`@ly>NBY1+qmuzJUfmi1HMM5YYYfFza;~$K6XZbvkyEw`q4hH|KQ&Vfbr_x z0We;D_?ooJ;KNNH&yN1N0j7_g(Z4jn^pRiqn+=KS{PF?WQQt~-M8NEbfY}j&)(#Gd z#gEP5ocM8mI46GGJDd|gJ~W&YKkgmQi67^ObK=M5a8CTVbvP$}oE*-HA4i6B;>XG1 z?Rnl+n2>dxwf2H|8ad^zosg;s*ffAtV7yZN0ARdQ{78V! zyXS|B9|;KauHr`mY~HQ|Oy>mX57P(tMO%RNj~3v*r~vWm zR}LwjgS+At>mR(rc$N1>XTxs1;yyL6FkVFiE&sTDKz7u(k{ubCNzLL%1T20;z~Tp= zp+Qvsarr>aD?Ov;Rq%(Cnn24d0xhqo57Rk$)dJ*I3y@a=WCwXAKz5K<1Wf1T6#>&Z zc}1YrxyDNKZqFZ>kY6-V^GeSMykhc`gk$=M&j|WJni2frzlsL7I&XpK54$4z!}h78 zKWv|x^$$M7>|p(aK(GVrb^@kz)<0;V@rw127GV9O1z7(OAYQTlAwax3bqM>31Ol(# zm&+Ukj909G5HMb`ZV-kYQoDRWcGS0$9TBkj5dqVA1We}yXrQrVYpQ%OE?8LqXs=-X zLxA+b`iB7NgY^#r#;g0MbK(cmMz^`iB77!TN^)*}?h;0n<6_ z9|TP2tbY(NowNQC{DBFX^^X={{i6leCb#eA=TMY*wLZZcb z&AWF@Q2*eqT7B${uE=$Ri?j20N$Sf0WJi4~*%5&+&CcJAfSn^80Xs*Sz~KSxuv411 z$C$5L;B#Z_U$wv&$JoCTAYT3H80%dD;?)<&*uNrR=Q@9GjQuMDcCPdG7;%t*o$LI8 z@r{`bVc4OMca5=s)dD{|#{N|cylV_z36LEh7=u>=WXJXx`&R^tKk)F+jj?}4z|PtJ z;u!l^1nivcKOKKM+d%`3S6>`s|EdK(H^%-|3v7>{54Xy8Kx!WtWB*E5h*$3#WB-bP z@#<&C*uNrRyn5Fd`&R^vS05N-|B40{Ug6=}W9(nGz~{!;ziNRmjEu~Pha*BJX(E%38r>|eFOyT;hR z5@5W-{*?gZ751+P1YTkPia_8M_OA#8USa=AV|OU1RKD5ip(q>=^r31Wf1e8e{*8K+rk%uQXQK4)O~7S1s_lG4`)o;EQAIUkNZ? zVgE{i@e2D_1WX^F8)N^9fazmC*%5)(4jO3j zgZ1+kVEw!WSU(paUa@{IK)ho8oPhC)^>YHoE7s2m7_V4A5B|Uu&iZ)^uzubGte*>z z9ju=VkR7a_6EK~#eonx2&iXk4(>d$sG|+g(`gseme%=DCp9>JLSU(paUa@{oz<9;_ zIRWDp>*oZFSFE4YK#L!&pSJ+(=Pkhcc?QxtG4%gX`~V<3SU=|#7C%@&Ct&e|^>YFi zKb{;ZexzpDSjj%@JjI`0fASF9| zvwp78RbXNLyaiZ47hrZMe&89hgY|P>VLE61oPg<^^>YG2=ZYWc8Kw``&o#OVEUcfm z0PE)hq>oo0RQ$j*j8}>u2{3)Ie$HE&K3G2|5cGli2Mrw0@{fl`sDHG;M@OiCw7^3n z)IS8sjweT`e+ZBrJ0sLT2)u5Ve>^)v{eys=BfS40>K_E`9N~1PQ?4}7Ks(oY|3TD0 zS|G)b{uMhT)IW5E@e1`10mduTKM2^l&L172{z1Ubb$)1s`UipPTxaGV$+g8F&^Z%# z4Wjh%q5;`aUm-gpQ2fz(xSg{duc*%1X8s{Nu)g9x11&egai5_DxX&O!yy8BC0P!mC zGt9_fyvq9wGr)M2_Zen@@hTc<=eJ)zAUoZz70_0T-kXHg^2YDqxc92&DOy}ek0n<5oMWEHW z#!6wL=O_vXC5W$mMa?T+5qLEdr|Ba;Jm|w8K35+m=jaauqCX6X{s`yLW8a5^7|x-G z&IyK|}lmrUKLm(ZtbY(NUa|f`z<9;_2La<1>mM|*@QVA?sDHEo>mMz^ z`iB77!TN^)*}?h;0Xu(}^$!Ae{x0hu1nm4>);}~>#w+Y!wE*iMEx`JR0OJ+*uLKyc zuzy7$@Cy4^1Ol(He?=hh3j0?YE7`&NM+>n2(E_Z02rxUaeL{7bk6z*0n<6_ z9|VHVv45qp()wz9jQU3lu>R2ktbYhFUSa=Afbk0ZR|HHStbY(NeX#yP!1Td_0u3~s zUp^o^>RZW<2$&raFgqg9+Cc*?ez5-00<3?u0P7zD#4FZ61c+Cxe-JQUvHn58c*Xh$ z0pk_xAHg434zm8y0<3?u0P7zDWC!aX0%Qm49|TP2tbY(NowNQyz;w>~2Msh{vHsBl ztbeos>mLHdE7m^*h*zwC5HMb`{z1Ta#rg*U;}z>4G|=J)>mMz^`bP_}{*i&SJP-Xp zQ2!7hJ6Qi9VDW?X4+0iHSpOhk@q_h`Y+!nL@dtUO_z?ltKYCZ7elEaxrTBqc8Lt#S z5)gQ$_>q9XE5(ll1YRkAB*1je`iBNlfra&t7GV8DfZ3t=foI4L)<1ZK>74Zs0;Y4; zKL`Y!D}JPBm_Atl(C8|#u>R2ktbYiQK3M+{V7&Tp{;-gM=_A)aW`OA<*FR=J(8nKT zfCic!x&F}wa{Z$VM&Yv88Is@XaWbjbAq6I!W!ZTXnp%H$S0OJ+x z5MaEzJ%3$8An@w-1_WMxy8*^4ey~b^Sp2wrKz7u(k{uaHcJSdAKO$i9BLWsba?6L> zAK=yH1L9SEEAc7<#;XVzuOeW)A`tvRJFvgo0^fZA?^(9M8xG(-O98UuM-E`WU4ZO( z!vVZ!Nx*de-3RcVB>~g<(F1tTl7Q)abl}GPQlYpjeSG5qyl2@0UmoB+%NF>?1Gk4; ziC3coc+XN-h*w7s;Qd_!rjPGF@V>llNWk>*h65ir!1R$1@=JE)7nIY!kDUW~`GD-G zZzVe-VCO(az|Mh;fSm(LAitoT>?q$GfLDhH`raS{CkFogxm|H);NPEzb2;_B8g3awf0xg&b5h?j_;e;U4K!Yz8Q}fh7C14$`@1c0 zc<_9@6U}_FRc0Z=foo&wC|(N4D|hZy29SmKQYkv=LxX)^bZg8{dofHJ^jJpihL%A z#wyQOiI`O|Cq{yYJu^B30i z{dod{&XIp;tg;>07d<@C_vh&f6V8c&zCVw^nSs7P2VlI?_vZklk23@0AHg4Z_=y4X zkKhmfD&!x*9{>ge`?)WyA^&KBr`M2ww7?5%$Ug+gj==!=hXC1ec!2zafa(0i z0Qm<2)A^YJ@(%*0^K>?DUg^_7W67BT@{bls@uPpm;Q{gwU17XJ{vp74h5Um+;1%)@ z0)bb^KL`X~A^*@=Ngsy?$Uj=(!~pq63!E7s{}3QM-ZH?tU4ZO3Gr&rpK+rjI0|KV= z!vo|W1Wf0H0rC$T7<^5U^l|fbSSPl?8?VDUu?60G9oC5gWXCUDhjpR=+40uvuuddkI)CGJSSJ!N zo!@*N)`Eo@}VVy|8^ugRYzXyz}k(rZ@Z6;E9QJAVBt@rvhPwE)k*Y5|^qB|yC5`BwtOE1rKv;GwIs9a!A) z{3`-GS7kc@@cb(R`>)P+0O0vo!5;v4{#6U`{Hqq=`BwsD2hYC}AUk;e6@lUp+5uh> zDE{D=U)*hyu!nI{uP13EB-3v9|Q`o0Py@P zjg|Dl^RHTf=U=q|&%Y92yh8pVz<7oHgFxUF@(%)mSI9pI1YRNk&{)Y1o`2N>JpZZ% zc>a|Dvjh2u0NKIwuLzjVdHxjv(>c$-A`o9ze=}ifqec|7dSaY{-G;mM?U|`uaF)2 z{Hqxd>_Gm(TbUjC{Hxg&W=B5%DgiXm;>W#1rO;}z@>V7!9m1Ol%xRs;gCFuDYc zSA1cE23q{MEFdj8fBh=iVYdPxJ0f85BLWsbB4F`@?EU^=&R zCK6ye=X;jH9|-h(&$0#ho@ER0Jxc-N72mTIAYSo3O9G}3zGq3m^uhNm379_YmFARx zT%3JBc=>?rsBa}ZBJicz_k$6z?;s;!-$4?f&h5KY`(7;>Xn=jM76JQS4S@7v->U%- zuXrvFA8y~J=I^tFnF3t?F?}YE1d^!U((0Fyn1oe*=I6XoAqXjl5&xc!y zSL+khKXiq7wKkb^e#!GU%sIbgZG!p-Z)Ln%pUgSGWMeYt{F2j?Ip>$$F`09I$(<9_ zKRE0^yB*e)OExB`f9MLc1Nnyl z*|9c3{eyt%{P`PD{~%yGUz?!*K_KWH`G>|T+kyD8F+u*(0;eZ)&M&!RGUxn~J16@7 z9CL~B3i$`0Ve{_k3GxpDVctdlK_KV@`3DU&ov%%hf3(2!Hz5CLfwc+p4*{lg%tdLva^aN|_7Pw=ARk#4zapwfYrNtW*&i4y(}#awYjTd?*BTJNuPy$-ki_q6OFRd_bs`^b zI_El(fa#p;L;_LgyNe?E10Li$k(QebxK3;Vt`h~gKEgA^E3OlHMbL-#snf3t`oKDo zSC~F{PU>%#_m;-9{DZ)?--Ly53*0>q_~TtbcJNkz<3Hi8WCwu<3-G#G{z2gI6j#`H z!UW!30Q*juz+20EWcK+f&wFhFp7+`UJnvP2^uhC91xO$Hyw|k2BVga9=JQ@>fPI&m z&wHH#_Fd}T(;G7o`~mKso1(53`~l#$De7v$AH3rBe5>FO0C!JOR}20Cur)>g5&Qw* z{%PTr`vbs*DR>q90pPLer_Dg4&xI-S4~~@q_fL_3w7}N%`EV=o>h3A>4_zT%otq;6 zAYi<@ZHoMZfbr_w6!`}Mt-DHAVgr`~l$ZDe{lt4*=(;$UlNV0Ngf3{t^7aE0BK#fAAT|KQva^ zAMp9s6!}LB+&@MB(E=By$Ug)auaJKTkUlOu{A~hK_KV@`A3+q zV8^*B@{cfI0l007{3Fa)yaM@0n6CibJw^Tz<|_bOQ{*3Ez5;OnwA4R*z5;Mziu@zY zR{$QHBLAR)7C$abk$<#6iXS=uXo0OM@(%%~59A*Lq>pn`mz$=L*}iKN1k)hxCzv;tzTdiprKpfEsLp3q`d8q>smn&IL#x7p9mQ2!!~7 zxrBhlkF7HM5U}`h_f+#$`njg_b7h9h{)oVBWlqfgh`_lriw1v?yO?)_KR8yHse?ZN zaBdI&0C1s1gWwMUk4+UnH0gV$DE*7;vGiGUyLG|LU=_E}=i{{HqIPB}0Fh z9ru^D5B*_wY?aj#{SoZY`l@CLR%Eo?2zOgq!?nPBr62qO;I@(* z1b^@fWD~(3e1`H5*}+*984E2p`Y^|7feR%o5+Hq~IJldb;2F|Kikt=nULjZGtpcx* z-4O`9x-s7>#}DxecBB5LfV5=?c>a><}P*r1h0O+~NnvidR_t z;OG(v@uT$zf0gocISN2pUu8!Hf6#KUB=`dWc@_M@E0mw7X9S%qKTm+^Jgu+t!-GF? ztF*q#fW|8O1CBa3%g;6J0I0#<70-uTWq;rb%g=Eu>4P%^Zx!^R{5)M@`nZtt^Yme1 z@CTle-srvZb>Hyw`NiIEzVdhM{q~K?Gy|s&wZO@Fz~8>|7Q2<~*qEFTfb7_q+?N49 z!`^TIvB@V5u=m@4WAeLpD|^5F$0lDfAbmi&tB;MzwO8>OH(vQW2CPh8(+7UFC2{IcE3`pW#+F3&aEas0IkUwfb z{s165%pU;E4*4Si_I|thBLVh)yZIvl_I`WxN8pwG5eaAhh=BP6fOuv80ARe5KN1jl zC4VF!@Jjwjfbr^W$6X(VJ`m2^j!PfO@&LF$0^t6z{i*-> zN4moLNByhg(nkWUf0Wsg0OM7(BLHqk0Nf7yzBbxXzJr8tq8-MoXovAC+F`t+9a}jQ z-W_&61O7O>-U6_r2fz;9O8TH30;CVx!CRTmX$Jw*Iql%BOy|)K(+B;LZzV^OaI&KY zWCsB0!|VW1^NPl}y4hs{}jsUnF0dPAkendNd;}zmnw8MB6?J!G zB?6?6w;hK+c&iXU5C;i__<=acTUq>|&IuTOsE>Rr4LivIfAj$OBi{;FXx^nh1l0QA ztt@^}9|SCZP#?Ti8$WVEI`PU>%Xv4mqy}I|4}cxImFys|1jr7?58ldj&Uu%B>72ac ztxV_C2Z101#KC+k`GbU$9W@|3s<-wIcV zSDbeRh*!)%cq`)-=UoEEE6%&TmGLUME@yWHTAlM&0RXRhfczm|ab^Io^bE5DyyC4| zofBwv&Rdzzqd!a^=8s4?^G5{C9{|KF^9KOq73SUia}yAFg?X1i;1%ZGY)87nctxEP zurR^-ibE0r_eTKSA8r1@K`iSMP5|zY5I>NA@ZlkTAphX4EPj|BAcOMrXovCYZO7e? z0Jt4({=tX0`3HZl@rv^<0pk_tUEa!gMLY6~K*>=iXWEg!^sWKe(F0(IZY7;Feh830 z7(aL`(>d)RU^=HAyp`!Z+F|-IJLD*jANd(IAUmXUk01Hr0HhDIBLSulvm*hf53?fy zrVq|ndPbdpv_F^eqX*2fgYiSpkR6O4yp`F(_(8zzVEo{%=GtMrVjSeH0w7+cAqjxn z(dHj~c$tKw~{_M?+TDU7zcSP(>dcH z0n<6-AaB*zc^ci9uRfT6(7?hHiWB(Rf>F3UQD?;1%K^fxs)oK>{tW2$*U) z@Ad%bya%`)rgO#*T_HO-?-DSbbKWIjI_JDgz;w>|L7>$~4{$re6>dkHpX*k}tJWVN zb+p6g-Dro+yD9YN^=@7Pq_BN)_Wth9Nc+1r06SQ;2v@)k-Aek{87Ys#tz^f}2zKyR zUz)wY`>KQ5-%VH8d&u8+5WM0u>^rDGk@kJk74}~Es}5?vJpuM!`1c)z&Uq_)FFgIvu0PThWptrh^G6NH zA2lF<0FWK#4*+I|{E>j-4?NubkpO#7-~5pPdrv?5Bd|pNh`cg?M8Nz3K)f=405D$3 z9|;J&l0Om0=_6fX-+vtaR_P-F_Wg(1kpSaWv?Bm+M*!T8i_79ww4;2F1NuZej91YP<5jf7 zcx86TAAE-VQ3JB024n|-o*e+B53?fyrgO6+0j6`aBLSxKXou;e=MNH2cF2PSWCsB0 zqvsD^;k+_^m>ua>rVq0t0j7`Me1&K9<|_ho?ARI2v7Mg z(4s-KBLHqk0Nf7yt|;1J-xXOLOb<6+MLUdF(GKHPZ{B76z`UDFLWzkcXU@AV(3^L0 ztKPiJXJFpttxV^Pg9J?H76;R>nyd3xANf{ktO5Xk^Z@rqnXlmUucZ&(s?`U97o(5o zj;N2Q7X?eFR>491OhjIN0Y^)OnnDP0o)Tb2|dyc9_m1uT19_Kcdd# zylXm-^RDUK;zun5#laeIJA!cBj+dYhmH}IR5O^{A=<$jZvg{Dy2*{2akR1S|51V%Z zNFT~nb)xwL6xI65{E>jm(s}fUvEBRuGHAne zk$>zaywXQ+zJdmO^A&*Jd-Na2XovAC+F|h{+R=|6 zi9V)tv!e!lz6w{!4qVZ*1Az2lb|k>oiDpLv`tc(H{rHgp(}&rCL6kmv{-A4Rhx|dH zjpqdBtlQ0wbSs;8d;Y+!EPlkF+gq3L3i+eHVvZfX`HHuiV~53$Xotm*1X%otb{I>d z9RYAV0^oMox+L0R>ykL{8n2=q#;a(D@v1lPa&foFKT5`;bxB+&+W95a`6B<=uk)!* zADXYoE3FeHX9BJdiyysx242z5R|Hy}1Bm*t_!0Hdk00shnrh>`8vwT>0CRL6d1X4c z_>mrNI*;?N={(N6rt@Ar=iJ`XdOJ9I1Qd~*n13DEj#a|mAXRy= zV&2WSNwL`sU8G$Q?*k{PM0-$xtYlqlp5TJF*<`DY~e1?5jbl(vB3^raM zkH9PWBl61p5drfD0P)KF0l;`Ae&j>_~v|D%ueMw<7>< z$9Pu%h<23kgh8KZhw&=fVZ4fV7_ZC@`Ge1pKWadB)PU>&Abpq}0HhDIBLSv!vm*hf zbF(7>rt@fr>7(Zl5>9r=g9Ky;0O`Z*05FGFrVq0tU19n#I}%{}=*?I7RlWI&z#Ka^ zhjZ-c%~!mY>%;8m%~xftX7S_SXNQG}Xh#6tjsUnFMdzHtTb&bdJB(M+4&zmvca2xQ zd6&=d{dSWx=Uu*pU_fu)#jSetE}!B1?WS|aK|aHDZgDW_W3J9yeQ=940Psf-aDSBf zs*oYfyJfy20DtgS#w+TBz>CqxSC6#o5-P*GB+ae~><|4~rkRPE4u|yz)30c;#`h&#QDR zQ*E4g1K@T9!0j-d$9cD$M+O5eex!$+&f~mmI*;?N>D=N+x>b;Y;$RK99pMVM<0a^W z%i2~S1YV3jdc1-@j8`HY0ohRlvIBthVetci^kKY8fZ1X5ZUW2>n|BjnaWKxi#*#(; zQSvBTCz?M%QLV4c9|^cDokxEd+sz*!gZ6h9`NwX;D}D6lD`>DcUjgXNR{(nR73aiu zz9JxBHC@9W3D@eJ zKs#R%Xy+>e?R*6w>ciqk)JH#lB;oYqM-8|g;R?57k$+rq#VcQW{77GT=Kc2kp=aXN zJ+uC`M-O0Kl0Rjy0r;Z_z#qDm@{gkjz$*dL`OyR56>s%F&-&NiegJtbfj^k_ul=WK;TtZ>$|(72e8kOZK(T-PLEnYDW^5+__q8-MoXovAC#z9jp;~?`7 z132%tKyTiK9ld#%273L&bj~=)XPC|{4kmrf)p@HA<^}mCO~H1IfRz)kB~mtgkM2Hn58N$U~-()ms6T9*K@_~CId#1D^yA%1uq zwD@53-Fam?k8#j+9^;_tJjOxOdE}MpJlYXt z;OmkAxE%p-J6?i5I3%q;2)r15^myg*Bi~AdBOp6!Kz0C-K5X6vAbl+IkCNjUugo6` z+nn25UJ;N#>MP_A09s#}KLFHmFz^b`SmYliKesT^o39wv+#lfz_s1gtNc$ym-nDf} zoOkE0OG21{K9~Xo0RHF!@<*DM`tbvR^uhSSTeb0nKtFz@Tba%+eqc`2Dr1p_0=N(z`iJ@ruJ&J2FpZe{1gofv%B06QP<@Zd9cD?1-mbqKEx|tA$47hdG^B07xH4R;3RBvg72c^pOD5`K_zcM*>Xe zo2$}C0!-)USLf(sb5;6~qrj3|SEY{#oLrSY07xH4R;3RB;?;0f`bdE3>UkHxD$T?; z?;1;DKb8Q1SNY+QaJjEc9S1?5eBP@A(nmhmIRU(t@oFRWC7S`JkB!uqY<4T-)kf+| zMj(w<;T0dQc{c*5Q(v-l1s6qj#T^sPySPF+zcckE^JmD8J7#^!(iIjzPS5(1CBWjx z#;h+{0xW*?{6R-~95ll5{M~Ga{J|?)e-LQ>L7>$+0gr>mtDZl2h5V6zmFYa6Pt9SE zfcYZ=<_`eUhxr45^kM!;fW;5%}l#b;Q4&b~_oEI()8CEm*N^Vs**`pU5H5|@JpSl`zaybQ3uuK<+STHjXy%4-+> z&kJ`gKac(hyuy30Ew9WUkyqxA$Sd;)0OOUvha7l?_g;Iv!h5eRuh{2@Kw$}1ee72h zctybTk8p+bAztwcyoa2hAzl%{`z3rv=pW@e5B;O?{ReMl{iDo|^o-&UEa;;h0dPA4 z;C5Jk-s>mKXSf}<@56pYG|={a*sq9o7_Vpt-*ht7(hlyu1^{;S0N9~hN$0dffOO9O zQM^^#KZ-!xKZ>_%`$rKleV83`l=CV-qXuLL0PTyK9RQ>cvm*hf5Bh`8uz8pM$Uiq- zVe@YCP|lqRm>qGSIsozqew8XFi~i@uwZ<#PLHfgZ#W=`eH(vGTU2gog^DcniybGW= z@A3+KZ;-{Qd6&;<=Uo7c{^yJS=UL}u3Fa%cIoG_)b)o>xyId!d?G`_%4+0iHsE?h2SY7_F?j>8}6gyj-g>i8j`jvu^L8$SrN@q@Q&<45}Jpv)!uxr`tAinttP z{?P+qhi)Z1$SVP|gS_Ic%ntI3fZ0J_@m6LB=UoD}{9yi(Z6U`q0 z%nqy*d8=0E1X`W*=h`|k`oq|6{)oIXe?-9i0YJPme*iFE$sY*_yplf>5O^hjB*1vZ z^={x5mxJz)!Yf}Va;yU2{#f)sPv3uVz1#AN>)n=D^ha*cn;d0w=DIzd)6xLYM-PBM zbSvqc>vjRshxH9l4>z6D4nD(lPCIxj(|NSR^kH^DoSKjsKk_qbKz2yyWFqp~{BY@< z0Cdh_~v=gY%W1p`Xk6(f-_CUu66$<{!G1bk6xofa!dW9gH7*xY@z@ z!CTF>!*~_#2!ME%h9m&)59^B@?O60bPZPkR|9O#fp$}xR=zm@sZkP7}xb}h0*@(Mc zmk?;zB?N3;!g-f~sh0C@4}i{l0CX;(jvoT*_(7nJ9|YR?L7)pHpNFnv&tpA7gU8(`t!9C<~1?muF--vd_>llpZJc)lFh}Q)9Gj!_IPWg{52gvA z7tcAjD}KZ{X!C9j$R9Nze*jP%wDghs|57pM*=K<#PzPxXOVyG%313x z^GDL)-sn8~!`4^k50F7=#v=b%zyw>7i0xYkM>nqD^d-E=Y!+Dpl2-6?qmFg60 z$Fur}^_8gs_#?k_R|DXcZl(BP{qAsu;)nISOTg=9^$+VSlYmA4!IVLoKaxI*qaf$a zq5M$;@&^FrALb7LT3?wz5>WhsXP7?{@M8M>^!i{S;a&6}EEV_CuLwiZ>z7IZbpyWM z8v56w{y`x0uf_f@Up%q?wTu3PWxne7k4lqFzkd_}>0{Ae?R-T5^+A$)yxfZ)ny+w0Z@vQ1>r2LGC=RAu8B3xa0dPA4;C2|V;=F5pk>k9( z=s%dkoy}KCgSIYVe@@nf0|0;Y0QbjB=_vd>uXs9@JIf}y$0Y9eyZE=AB8Klf3@g8SiW($ z{!!+S6oiXE5I@WxH6VY~fcybK>#JTrFFphDoW~c0{-K0zs}DZovic9&dN)k~_H%8$TLZGA z24n|-9M8#J=^Vf!|9IuE9gxl=ugo6_DE@dk>U@!Zl+$#}eAUtUqW^gccQKE$ymrz5 zyu7wx6EfoZjo0a{<3UB~x0d7YT9DC=FY{a4oUUIu}% zf2DOvx?+)kB!4XOkFx1x^KRT1wRyJ}&q27k@gto?nD-gT#6|zXM7Tx&^YUF$=_`Ze zXVL$B_pE!p-@fR7UJ|TD|MN6uEc&1Cl0o{g{R%(R7{`?7Qps1lTOf^TONd)%y0;SIn7r^LgPjQro<1 zef!d_Dt)}P{)3DBqv)fYhYYqa@{dLS@tW0JO6@Ma@42hN6wmF{>_z^uyX4e+OXpXA zba(B88BTTQ&DCGd0Ov%@Yj0isumL5WLxU$*eSNj)e~ujIn$_7LTEscIy6AsitrHjh z&&z(vBL7(AA7$Q6!OQwIoSyhO^wy_gV}kkzx1+63gROUCUfWymGHGb{83?rNT>|ZT zm%t+b_)D69h*#sQUU%hh+4@SndTj%cSEm{Pb?1%AW&uVwLo?#qDb zgZ-mU9r{EDOdsqYwJ~`#1Evr4ef?4ftgnpvpMUkLTLPfIuh(520rh=-O#!sNQorr* zDS*~j>VN)28L+-G>ic?E2CT0P`z~!v-kkyKD|l*`j!l|eSS_Ix;X=FpP!A%n=)Yex%|=cir+!jyplg^UdbOdujG%G zSM2}M@{03r%PY>iHLuvOXk%i$LPe4NicTFeUJ;-_j8_Ec591X9yq~x+F$PQ_W23C;;ooR)x44&HLuw3?$n{0SL~O%F{ydQenlITnpf;s)bdJp)Vz`% zHLvK8ttohAI;S0H*TJg*z#nJV!K(nkA6rxK%KF5qZ#Z~med5$N9K14}(~h(2;Fal| zc5F?-E7Q5`0IzJ`l^x(!0B8qz6#&`+UbX$Bwx-}!>F4FVI=c>D*}N+|z$=?~We4Uf zn|C>1ZB30=cn0UIv+Kqy0<^<;MSyk~uLwXNTT|l|0qEoGy3JPr7(dRg8?OK`er!#R zSG*PUu{C{rp2&Vem-fi{4dDrxD|M9O}nRbM?$2MPqaBn*fe{4-{z5>Ah zgIiOZuK?^nj`y#&$2MQ_3ixAtZ1WYL0e@^wZN36<|8bs+WAhb&e{~#jaC>a?6#(jd zdu;O+ea`nRZN4G^e{4<79|WL}?XmfT0Cc`Rw)u(xbiOq;e*mC9wx%{;0iZs%$2MO9 z5URR)ztrI~X&AW^IWA`heUL1s?dT|iIBL7(A zA39ZD>-IVEW08NXXx~S9l*Mx>YLR~|@((GhH}4{N_2yjw+K01!9{`Gj|FPyD8x!Q` zC9mZ=k@+Cbs6JA4Gm$*1LQL^Fie2C9kEd-G{pxwVZ zb?6^vpqHONkO9lj)prT`$HiIy5B6Q!m>`}90CsFlkbeXKcI=FhpO?NeutR-ike`>n zG6ay~ZM{iCqYurosbVf~}z56o9(bRkap1M^h? z=nw2C27vy+eqyiB59X`3&(FpL^HuQ&9xi`izAF9zpgvgGPqaRx@<+=n%)2eGd+nexm7Ic7Rtli^>kn zR{@|M;8g%<2Y6-kuIvD>Y~Ga};FZn0vIFy#&AYM#ys~+h^VRm)cm=|7zS^1^uL#f% zo39Aa4&xO8?u#0)2ykE2<|_c4uePSfD*&9Yw#UXR-irH)n6Hc_>OY7$7y#ID>Ja9u z0Kg9J!x^s_2e~h5yi)%`%vZ)M^&hNxMLYN&fboj+F6*@+8gRX9yuuW&zOTkB0GxMO zH?a7@d6#vA5I?xyHC{1(aJ_51V*Fs;*?0wj`rx`fu$}AOnpd2!xNZ+u&>uCgs1Md_ zTYa!z+vsuK@h;G1NZ-uXu&mqYAJ1 z4En=(1>m`3^oQ{Zz|n7|KM)5CuK-ZzJzn{KdmtS3QS)kNM1R+op36PV=tm9DwFstamMb zXx_zoH^h%t=eQO7zVbaXTbF#_!B*$E;xCKNqdzPfM1L3%{b4}#hpn%oKP-OijNBg< zKcYV@enfv*{OHYBoZH*^iU8MF#w!9`Um33maD8RGBEa=k8$WoyPa8jQ{%%W=1 zv3_o0qPN}!6B*B0KMz;Xjyity<}2QccGT;V-g*~T^wzt)744{XE<0+S%MSAgZ`JyP zK+B4)X^9*xz<;8e$M=Z?*W)TXb0yj z(+BO~de`*Po3B7Pt=nzAOQ2ns5TG5#D+2IGSnm>O*Chb7ZnyO=0Il0?y~|s{AAGMb zWE0Ri-|Gti>|k9j0I-Aa^;!Jj`f6)xItLk;f23n&@;+RffAGD&5I>-EzSr00A6rw) zYxxYUch9byKLBvvJ-c476FKj0P0b&?0_$DA*VpDBe6KI86S3YsyKer#tvK&)P0b$w zIPcOQf$i>(0JuK_;Qp}qL4D947C)#D`lHnc{n6@!{xE$o|DZn#OF%g0AM}R-%s=Q4 z1DJo%AB9)A)q9S)Kid3*{wTb{70f^AkHRYe%s=RlUjBi3w~Q_vC7s)RMWCIp2(f}L_Wjk zE7Q5=UFgGfu6Y;wFrCYedf!L!Z`v;QFT^YN_y!TwQLBMrh{6WBVUi<-| z=MMnvCmj62EBw9Jz!Jnk?lTxbe{i4L0Q!Ub)W$3NgZtEhSMUe-sROSN2f0rjc!hkB z>vrQ6`ww#6URZ*eTK!TH&jSE|aNQmN_=E4gw*3eB-fQ6%p85$&jTE<2D%na*X0`2)9N-=*LWKEv}UW4r7ye~=8VKM1t`Akg|F z^2+=HKz5iv0LTu+bJGXsE9%4SQ2#;bBLMD?0JuL)=ZqiJN6@+JBj}v@Is6fH&iowl zytRXI(0CQ+-2lK2);|IOJGf76@gv5;5I=n0HD1MeH^dK*gBCwx94w;?!o@gfK#YS1 z#5ia=k8#j+zB%+bXgZH^&~zT-VDX3Rqg|I!AMLt?ed6d3;}!c<(;vnw_8D#0CF(yo zM<4CFg!%|^kcwjc1MA(w5^B)vQ3l+11n2MaI~D^rkF@I&-0Hp~?Ye}|aDRmO;q|Bx zKfE4g@k2UC{$VVU&TYOT;oA9%Ks#R%Xy+>e)VcWsfclKudkk60KLGrN<43FWIPaRAH-|p&2EgZC(|MeC+xW4_KhS5CQuBS$R_9^frs{u5?~yx_Zhe@3Hcze;JmxYKaxM7+MYisXU_*iG;n_e!2J;b#zE}+wDDt+ zf2>L$ao=Z=f23os7Wqdxza;EmE%J|CQM7f*B7T%}4Q$?39JKvJ%%+@_cie{y-JrPNj&+Fv?6=K*Y6Bh_@SF+6LF*s&+yMIwjDw+n6!+V)FKT@)Z$H5O zcI?AhUrY8~;=ZW$we0x=i=v)C2(1peUn9|oxZdCe>OgWnYyujr4R5%dvw1%L3nqQEQogWnYeUSZ#7XN3J-;}!di z?u@X1W!<3EFV%Pj4XR%e*1O>f_+w{eyy6w`2fzR5^*=XW;R^NnF+pC5i#Wc{OL zhxr4Jk{zaV0^k+D^D2%aKszvB6@L(*9p(=L;8pMk0NG(W2OvAlAG{UTyTKm>u-@hO zI40+wKX^s!4+5<}2+$7m2LRb&{s165u-|Sv@A-pdpdIE90^~URCAfn92U$OF z`w#NmsJ8!LSeM{d(g)u6F_uUlc;6=g)Ca!v3IP3q?{SP*(g(it3cR8|@SRuS74?Dd zyaKPN4}9m<;}yQ6H`R()_>MjRv;*JK2Y`0qJ9^W(c!lriP3PhjzN0sti&yxL-gGWr z;X8WMIpfFn7~jzc8F1eR-_ZwvcHleu0MHJ6N8i&2zN5E!S9ah#`kp@U9eqz9_>R7v zcUk|iUqxPVzdZo3gLTdTzz)_q%?|R4`|V~2^AFZP%nq)1S?4r6xZY)*)9m29%lEI0 zC7gHp{*?iocUk8&fb%ZDyE9&K-sOA9#;c-I(dk&gEOMY%2s`5=L>TmG>*G=I=h;MMlH)(6*D+v8dvv}1Ev>w~=79M<|^ z{Ma7X`d}Q~9@qLHuQrFZJ~&@(4xx{rbJ{T{|JWYS$v-xS$j{sOu{nf3+W4_OMn2fa zkL@w^(Z-L>A@pJKgLZJg#G(P`UGA3z06J%W9sta{+%K{Cp*#xy2=Rk?6#Nn52lJ>o z`3Lt)EPgN!hI|mDW*lUGUK|BL`#$Cm0NVF4fA9+I!-ag1&)~j~`2zssV8{mnaNZ61 zAOOv~bM&#uKdx5&{Og#1yzJK{qO4G?K+Y3Zo5w8yvy(CP3N3< z+jS!6-FBTQJ1igc_;Kox<%0ycPPBZG0N06@4-()y(egn8TqjyS2tamNJ_tZ|SUw0q z{Rfc`T30LeuSGr>0NR0kFaYc;gM6^pzZUtR^-(-LU|$*JgVsk;{iBc%TA$IwgWoOf z;E)empV7mEuM`0Jpq=)1cyMiLafW=*fD?n)6ae|40qQ@9e9+E^drPW+pdTdiK|3Gr z%z*vXkPq7Va3=;IF3&(dXy?PJe=YJsJ0DK{YccP({iB!<8u0YmH>DL-YVL-0i2*OH zeP;%k4_cqh=bNAPW{gj2dz)snZZ9Tx5B!_`oz6u@c9B@ zU1BUbGx&0O2G%78oEWgb8seYO7GuhUU47J&bed%LB_!VF#mu*0swt*U)1_Eq>6cY2K-^? z(4Sm|KDZBO=iHrK{bas^`*3#7-N{wxgZprH&fUpX=!5k^W68-?=woYY{s0+nU4=fl z4`)|wuKqwKHS;Lr75fizAI^Bi{)5b;j92VG$bC5D75fizAI^AnWR?A$p$}8-aP`lN z)Ua+h;EGk(hXKP?*N5r+$g1nZbbfNx^pduIdKe~{lhn?8=LLLdCT*7Px4g+5X^%x@={KCW1WKKOmD>0`JGeenBQ z(+A@Rzpu4l#rVPRYXbm%@cY^TKp*_R*6d*abAIn^b})YM`&zSu@q^#jnjMTE{Ju8W z!T3@0O7WxSmEwo#9KuojKt5=^Qv5KT^9sff_``Um_)+sp@dNX&@k;T-=3PAFg*ED< z*16(`&AYsU`mlKyfZ_-8LDPATAN9P8D{}k*VDm1396#!L7l8UrSRBNHh5cAK?;3sB z7l7Xn2EgZC;}!cmv;JYc%6-W&z1I5JnE1SFyxN%fylcE-|8vf}77cP=GCTwRFyQpW z=UoHtnE1SF@#D@3)+L;GEq>fF@p_cS5B47neKR<^{0!5Vw)mlb^w5Vz1NwvchXM3Q=qm#| z=#S7>1_1rRb&16f`Xls}!4>KU34K`nP(MiX2L}=B5}pqi*iL^S{|JER9|530kbkuL z;Q4TsJ|Ji0A2#o1e*i%KQRxFZNB&{@_)C|6EaFF6-h-mbKVDM&NONLXx8qjIKYH;a zT@lvpxI+1d%~zP=lz$+en+%kHSRTa`nfZs!R{)fM*n9;*`9~c;lz&(r#VeS9SRMsn zk$;r_QN>Yk?IQmu{iAH1xX3@sc}pheMgHM^csVCxoyfYgtxL3jwa7oxE)d3Qk$-sK zK7^u0{!!-LMgEb#c2Yiw`iITCu}^gX{{NVN$PVi(1GdW!>nlTmc358-0<7a$Ul{_d z<5*uA0<7a$Ul{=Ee{Owc0I2`D^_AfjJZGZrE2IABbzd3vyQ}-ksP9tUS4Mr8>b^4S zyHxj;QQxJyuZ;Q+)_rBU}u%;kA7R`W)|Hg?$D9@(1?eY~M%zKptiLKJo|l;cVYW{y_c1_I>0J+h-uJ{M--Q z_fel8_`|wEsm~Am5di7~c~k)WeK_kMrG7=oqeA~E_eba-#eD|&BlM5*^8l=WluU5zTUXOM(Rf9j z!yl}x+0WHJwegBqz#puuna=aRXquMV{awyitg8i``}>JjAMEu-=EJ$4So12*yT+?H z?*?EFuV@GJ593vwcdaj3oOi7+S)6yRFIjKi1*sJWYn}JzU0l(dcLDU~T|UF#L$>_9 zH}B#K#lbl`XMNDRd|&^2_lEf+Yn$-3!no9Tq-RDBd}TC9ThA<`5U&8{>)6dxQPABqo}UFG_m<_Bp$8Xt-en*UJ!bHxV>Z$Lk& z_@Du*KdAVi0jfW!_@Ma@)gM%Ru<#%355)%y|G|C8iVqh4gMLu)LGvGfQ+P@CADpjj z)Rum*0Nf82fW|rf;En2^(+}D>&-DlC2W^~}e$d8w=?86`>pdp^<2KYw{crC-^1M6l zKg4&9Cu$Z|o;%}-1oFJg^>(wXJntG$EazSFcC)KI?;1}m=Uwu4v#UJL&7bQY;#Z^o zBl|(~=W5~OBh9-4*5HZR503K0><34AV)lch{v-DX^|^V2><1YiG$8vy?oTuz_Xp#H z=09?OaDSrtkE}l!A2k1w`-Abpf|p2tFg|GhBhOdV=jILad_{d;0Gh9;&kI2PL49ui zBkK?9^TL0)J}>--^au5M;XkB5sL#!R=smQ*J$+pogWf~?+Y5m2q5bUz!1vJpcC#zJ zhw=fW{?I-69${YC?_<0~^RDs%Sln6juJ*U5{?Je99LpO1L;KqcH=+07_$UCq#~S`a z``gF;hw6;lIM46F@iFc{w7=cP`HlL6v*?ZbL%?`{2pI1V0UYN=e+U@w4+8l;*5Fq~ zf3QW?ADnk>)QY!{>!_?hY~PZ8itjP5qq6?syjzTOzQ?$Z(tI_pqq6?6^^)>EM(rxU z2j^WgpZp%9cBOS6=_vCb`8`H?NvWgEuJU_~+EsoJ&bww;ns*CcqDkM+-Pv%~yj$p6 zw$Qv=^alaWyM?aRJ+$9vbewD6E&78kq(2H>OCZm?qjr_&U0a7^4@#eFAF^4Z^ttvS zoBzo3?x zB(4Vw|3Uqs^9F_g@O3!zADNd}f2hxW-N)=I^Aa29YQb?{^oIan_c8yGd5QH0TV!5h z{Xrn}5*z0PvR#di^K4h6<2>7y^@n~+?_vER!1>j<|Iq#t8|T@sM#uU8mHyCqoWk12 z`$NVcUSjK&R`cdK~->%XHtFFv=o$AXC_k%?X zzQ?5g`CVsXPb)1^_3nx~)dW;`Nxa1B%H;kaZ%<#BEpmU5w-In-FFClOLOV(o{FsXl@@?TjsDAoCy)IYE6N_CemH%9GB^}Hta&+q!oaDPdz zYe_$7vuLiPNIzHr9OpWZQve+2;=5^_b8c6EC~olWs_xRH{`sW-IhGAMx97UAO`?bE{LG=Uwvl@%n?(=i~JUrO%Bg zPW;DD8{d_bQGXcU6+phb+B9AwKu^Q=T8$5zuh`;7^|lGz zsNS~jp>y=ccWFL1;=2OI`K|!H$0#q!?=i|t^1M50SNT0g{YQR}zv2Ev>sN)E@b#+# z&^V_bEC7vj`oVGiq5VGN`a|bA(>Qm#(s|DD`p#P4wffGJ`sbe<rFc}KCN%GoUloA!s{(L-HLgFzug3L< z;veJsL-D9_{h{-<POwEKgjxS)E@$zmoyo{65za~S*=sws2-$RNPm#;K3I+OvJUU0{vgX;X}yH=m6fT@ zbw;_KSOCu3C-u+cBi^W95iRjX_3j8v>PP**tA13ji%eacW*_$-I{#4su4@ax$48p4 z^ix{DqONULahwBn>2vDZG+(hru8T}vJE?yjLv2$3Jmmv0yPDKLpXK0=DK~HuZ$;VKWP0?`a!P48Bff9ko)0m7CqnbI8FiRJ-A*{ z0D2FuUyY9s#c^!j&F^76k@IeT5AMIRdG|*CTywjhyR)ad5#LpdalR{n@1cEt_8vFl zyJ|7ccM05x?-D5MzNWgCYN7Mz^n+$4<-BXay>*@cFrLUhzp2jpgNhrNT}|}Itl3o> zgLPkLusWlY`sXQs$wYr}ZudBj>Dn93SLM9R{i0@9dEOlz=Xu^WJ(x_iGw~ldcJu$J z|4@F>asM&VA9tco11+)igT_mwqZE%aUQ+tOV*SeJUE?L1ceTEo`a?JO^9BZ#e$ar@ z4<>)kpDO)evMb%g*WqlO|4(&ahbz{vd>w9loGU(PcBOHye6Izt1}{;4=W$*#(H|52 zk@uHO{73V@h5pEOEvci%>qqJQU;(&}Dgf6}PKN0jO=P3oWDGHzG&A2#o1yW+k+n|DB^dG7RY44Hi_Ho{|_fY*>8MXnaePebpKr+dOR|n)+<9Do zOzNLk=A$~*%Hx&%U^(xuq0cA!V*}QG_^rPEN%=wU_er{zfYuo%_0Koth{>e>`K;+d zTFIpTd3#d-d>HP}^#?icTAk5czn1H8BlT-pe~|n7tj)@bC;-_N>l@fO|J3Fq7|uJ|tZ!iBT>fLv zEb9;2IRDheRU>l+kx+^;8KG(YvHHgCLxKV>|T!(iv;&m_Qj zB7wiZ`R4(Q=ciuY{2K`%Z@2u^f0OFh@>}tI)Q$AVC{N7#gX<-!Kll+R_0N+Zoahgn zeZBK229EY8nqBR^^A8g+YF88eG0`75H$ieI|DpP7Z^_W57k$j_>UNz z?{E64_|V~N8Lu^-nAfiuuf5UwmFYoF`k&f-I2O|5r*7mw2#k(%_>TaT-^ct%Sy#qb zMy|(Vd{+{{*L{i>6a7)~U4AR^-G7?aeXI`Fj<&}?td2=p|HG`Ltj}2h;)&!}1u)Sc zRex~c{Qp;f>}c!UHuc9#>whB>)YC*)n_s@;!FMkmsN*u2yCcF*jsNAQn6FCU{aaR~tZL14N0y8zy);}QV+gTT(_H&P2+2ETa= z`a>y{Xt;9^+^ErIxd54x1c}NVh4a-bzBDjbv^n+_xL=319e;m|Lc16 z2Z7b*egIF`afy4NKR9q!n!g3$OdS^omYc@_yi>=;ft}6YPc3i>0R6$AT5g^O@N^xQ z0MH-0$G-+}ppHub=nswBe-B_+9hU&m9|SHoKLAj#*Sh!$ti;V7^rc zP_N^n{!oh!fL(Q5)E~OXE&vDWxTrq}tTy)oc)E^DJQezbz)JG~fHQSm)E@+vn@0e= zQ^!U9L11U|>C^%j^@m!V1n_hnmuT^G(NVg`82|_BxWqlaG6Z6?9e`bRT;i$D4uOl! zcL3DuxWs#WJOtLYt^@d>hD*Hpj!)6Tm9A$Sl8MDU{@WNXmNT7Tx@;@ zKx{Sw@SC@MFa%bcivXUk;}TD0cJ)gD&eU;nV7d7l0N$zN;=s=4KTa)h3Bc^?p8P<^<@B0*Krw~pjYDru+n@6z=1k04y-o64q#Uu7Y8mjUjb0B;}QVg zL$l9!0DMrxB>=n!f%(?=Qwv-IzQgKV+doB{B3 z9hc}q^ap{J=6wLp)NyfOx%qPd-l^l_z|Q6uQVU!H$nUY-{1Slh9|_3s5&i?ffjTbH zBEQFK^LGL4s^b!Xy~j5I)a$s&uJq<-0en!yMe`Mb`IfgZ|FN#+_bB{_Kb859@PoKV zevj-wR*E+_|51J`^B+5luWSBe`3C)=`3hY(>_M9VR-4}E23%~qmDo66*K)hEaX#O2 zOSEymuI08{jB~g4K;Ce{#)`y0;^4*cV8w?B(PG<)c>43kw7`OpCC^pu(Js_F!Tl|$P?89jwAX5 z7mZrk61*@6d$H->xv(qu z=ShF)J)8j~{XwAQ3`u_w*jccQq(9Wcc}dbAx(6ssd@EcANq-PnZG!Z~x56cuzP$%% zQhY01LK|fNQSz&#KloGgEoWp&e-K#Lat@dD2Z4)CXMIV3=&771CjCKRrC^pxe-J3S zXwn}9b{6b4=?}FS=ex39P~-SkxX646tTsWW<6Gh4z{Mu$cYG^cWLIo4%G`AU;Y_)MKj*v9#CliK;6I<>R;kDX1b?K5?%Z5!vyX`Lbb2c9bF zLD{bB^VA;%f?wfrgI}c<(1QemUjYby1t9eYf$)O>f?okJ{So{swcwkl{!k15R>`01 z9{jq&ud4na5c~?CJ@^%XLVvIagI@tK{Sll2ErMSGF#Qqy3c#5qb0*C`gq(!R{wU{m(;w332a-NF{ju7F zFpqDAOE7(WUCq1nIA@Z_F_-L;Czc(&wf>R+B#8 zmGpU`Kj@w7b?S4|AM0B5=O5H?k@@gF=38U{^`y^}!O0txoFTut^!b6L&y!uT#cGrK z{OLONd9o{gD>9igLnZ^eBCwpe&pSiz1G^%yGwJg)Lw#=RSI)1B^(!*614*Bo|5#1> zd{@%v=07eb)>p4npPT&cwpcBzSN2zg4e@LGnNc!CLNBF^{aj4G={Xyk* zrjEOs>V(C3R_GPn#(e*|X$5S#%(p+CL=pkBvi zVESWS>q`JWsNoVV%&xwYTHq4*uyxVj1ps{>fa#Bm&2IoW5c-^et&5%o5c~>&S;OkV6ra$(#onM*$IM$v4{Wy^LRWcvW z)Ti6`B>)$9EXUg9R|kgtD*KQ9ZSt$IlGFlr#h=>IE_plbiomA!8u5=SEf6c%Zu+V0 zKhCzuufo~^B)j5IT}k|ETz_n8zlEO~kALiFUjZ;4|JdLD7{GY^<5-*gYCQgNy6wu# z{Kv7jYc=y9``fPW%zx}?JHN8wys5oL{NqY%jrhme)*A7TD~Vr?$3Hf;$*;!aA3NHv zoo$@&Z+`&FIUfHw)+WCikAIwQyP`M!ajflY0LhT`@sAyC zU+XLEiu`Ik{&B2Lel;HdINb)Zimyw4WisPf+t(}&*xyFrBW4C%LS|sTl3o3s#7l5- z{jsU-E2*YGuCx&Ni1`W^kAIwP`D(4{k1L5^jj!)+YWs??$&BzHxW{<>V}ILMolSon zYm;A%$3ITD$*<~(Uzz?m*7mh<(;wkKZ1vpq$Bypy72JS!7Q;o(ycCze<#(i-m%8ug~42W^}OXGnI1i!;mpg_W58DDBGh$LYcn zjVB%}Y_}NaZtW#c#8cg9eE5hm{ZWoV(;sJBK59*WTxt0@H~q1xm>Eoe>?r0E(;xeb z*~j!pIbWInINkOc&h*EzVoo&uvA>u_O@G8TLri^|cTIn6YWqxW`lFoNO@Ewixi_%* z$CcKa{=+>^VOQ>>4A@_IwZg944;FUip0lv4(%0totNn#{F6@fyCGzJM|Jc-a24MQ5 zr5cuEU8Zn*P|-c4le%qvWEdKhCzCy_){G(i-Qx(gx0ullc(XQE+Ml_7^PN)~`l+ zd-flv3#Om!iuX8HXoJFkxSBBkv7>z!qTxpR!xfA9k5c29|2W%nRb=Z|;Rn+_aEY1P z{RaRkFY_Nel2#j!f9y}{Zan@WJvbi!IGq&ac>E(c0~Y(o;~(Ka034_@3Si@0x^_JN zu_-Cx@%YD;7Pa$u{NrqEjrd3KE8KiM{t^5tJryp#|4RCNJpK{<3N6OtAK?c9jK@EM zU!@j&^VA<4=jFGu_(!SFO@9QxN_~!t$3KE!0T_>e1ZMy+9{&h_m0I8u+BxsPD(%YR zAJXUJ@sHqF_^I*uNAN2EzkF%{c;veOFWoknDd_4ZKqfLE29{<>%X3_EZ$FcSr@sHDKrXG)f zlykeqKcvsc;~%l{9b$Am{;?@}obmX_l@@)}c>LpRYmN9v=?5+TA$>j`|Jc!{J|B;N z>`&f#JpOU4O@BTf|2Ul(zJT}R~G-+)FvYvkALiFQ=gB=KlUfqHy-~umU!ZL{Nr?DmgDh{l8c)D z*q_+zc>H5W(&yvxk4=dokHhtmVN6FibCrY1>$3J$oALp7dyV+V>7>uc;~%BQG5sNZJ|6!FKbSNQE`vgUP@j*-Kd!W>)yCr=XIs?g zYs5dazhpfAQSPTU{V_TJF**NnW+-JQ=Rf>Bj^=icVA(pu zI$3G_LKPKlt z^7xpX|CpTruyxTj;veN%8H;~hOs8um=RcU&fJ}dK{$niv)#UugSpKWY`H!*uSCjJ} z>3r?v{70loEca7e{9|(dV=Vqr?$fvZSCjJ}WBIQp=ReY!Y72}^&VQtnVUzP8G4Gc9 z6U|CC6(MF>C_KOes{-u&hcZnGu=5`!VCO$d!1iC2fSvy+0n;BPVCO$dz|Mat|JCKj==l%D zKQ1?zU)0W@EC1Ey#v1vrE;pE8)XtwP|JCIN&)3@d59PnQ+`usq)&Jnv)%n`Xjk{x` zkm`RB(0S*}jTrz*rE>q3@?TwU;Fzrg%71maq5Psbey;pimm54^Yv<3E|LU%3o!hqa zgFNZon`VAdJAbaT;g3!KZn`W8c{JGB2f3blySn)}+{!sp_6Afk_w)5x8fAw+$$80_SuJT`9 zZs3@$=igQQ1<+?_N9lZRZ+f);hjI=)Ha%MZL;0_I)1&o2l>h3kX&keSd*nE$ z@?TwUAPrUk*iWs&$@3pCH*g8S&JQyG)rkf!p65>a8NS%SFg5KG*{VNeyF6cexq*xNLr=v#6L(GH5>JKx(6?e{ir(~S{SW1`cx-yK{)h5k^`@C$ z)XtwP|J7a7IA&X(KWF}{%MDyS|E}_1z1%=*j(DoP|4RJoL<7fc{rtJ|UwyH`^R;&V zT=}m~G>`_%^Y1GE)yoZ}7>RFndRXQ|`L8ZFuqik0k?Vgb|J7a7NMjODg;A?-#q+hj zXy=f%W z2_V0Ra_>Ag&GWT({#^O5deh7=I-dXPuIbVIqRM|2{sZ?I&wurD18J}v(D~XE4Wz*e z0OxTy!zupp#RkvUj>kVvG;qu|THrm@g6C^rZr~C?o_Cf1D*Q)eaPs;e%71m&G}11~ zeE3t!f7P2Ft^a|XPC*SGn;xzIf&5ni^rlDaf9PCl_>XiC_aA6+xpAZXR{^}-7_I-I z^R*`$NP{K2(wp;q?H3zJgQfY30PJkwrk^DfUMo}6bTp~A5YCOe~Ctv<-dArjyX~^2JQU$Q*+E;qET!4ub!G?{t}II zJOA<29P^iGX0Z6jQ*%6DtGUGTU%fa7Vii`x*~ju~zSM#5oU}m|t|rWUBmEPtA#6k^7kbcyUhrifqaB$AvlOFA={o z{qe&&{A+w&GBP`Veqjzj9^Z-_&h*ENbK+NIeU|^~sX6g0@e|6g&vx|wnTK=nrIp!}Z_%7$&N9V+^(2?#~o|;2v$9tgb9e8mL#46q##u7mG zgBRvtP+?avuK=?D_~9H(Fj~MM1IT{x!W@h??m=yk<|{tci*q1WVeK$__a9HqVPM4g zz%X(D@#viR6%~v5kA*qrFOkME|8d(Kv(`x!ng3XrW1hRLqq6^abWZ#V6NK0Qcxq1k z3R8;fj~C~}uP_O@{N``|JA}A^K#0rOn-#`NKZw5Zu_sEnqz*^;6C^%(;qL+fmp@2qP3g;xG)D| z72k^E!>r_o!xkJ-23#24Lu2r!p<;P)_*5FTyM{pcLEPi6X&dM4s7Hryu9@M?utok> znoF|(xNZ1#Ir|iLrT(BkH#PC-um`En4G7MV?27u_fER~W!dcYv?`FH=yleFbe>k*6 z&eWzqF3ibxIk%hscyVa$^aiFsf?wgO?wY1PH<|J1a75AL7?8&xeUt&W4M#2Yx#^FE z;W&pI2{n=D-ACs%Gf;CAI{W29L{iXBbu)W zT!=Z5+5m3Effr*I4d8Op=jK12ig}m3-FS(7)ISE$n--IST@k?C9>8PM;yzhN-4@<} zE#OAne=LOm;62E%Z2jud@HhcnZorNB`qfk6qXKxjK@MmBw7H}i(KQ4r?Nz~!XRO@Ey4NPobMxSJ5rn?^_80C;Q~ZsZ0) zZyLrDKpyA$9@OXNKLlKEP@kLs5b$yXZX|vxk8=Sh8r0`D&INq2F{;nccc?#3Bz^sgDaGp#Uhlwh zge6j+oBr4`562PX9Me{e!CV)4aUPB%dT=!V)s}fUjBi}P?C@lidTwm>aNBV>O>XRE#Z<{}LzC-ijL)ahx$YDk8f4=&(C*Ue_X2S zpP%f&aYPSdm<-n6ivHkF9qCYiNaI|p;vaiE)E`ns->S}^AL&qkNJkB-_{Yf(^@o(# zuUGN&^Bw6A@~fY{CD%W{-l6_b{G)bDuJf~Hp87+2FnwM2;Nm}XIMRXRh;dGSmHLCg-VXJL)OP9*wIIKe zK2QCjdr*HU{*n5FK=_Yz5Av(j9|W#<;5ecO$*)p>5ZE$L{h@V+)E@*E=czxmGLia2 zEyzW+mXZ2H_n`jJ>PPAi0_Qv8yX03tdrQ_ICp*+1T4DM1s{Z+r4)urDWCm6IV{eE0 zL#sI7s`Bq1>5#W;-RDwO|2+IhQWNA?>u=5V&(C+HKgh2xRq>DO9qJFQMt!S_e{7kj z{?PiBS;^u&TWH10fGzX9ht|jpxSpO$@sGl;q(8{73cKQ)Yvr!6D}5{StHQ4Mbrt_8 z?27%N^~CAQ8=UO02ekreKyZd+SL9a)T<_3Iw8}YM`HyT@C)3QJmF;^roEf$_-@!+UaZY}9&xXv~uXi~6X!ZQF^n<)dp0CKS zl9doxoaYRu6?y}<%yUju{KJ6jX%Z-*>HR$}A)Na7{3t74p! z$;c9IoS*NI`$!wuIKSQ*vLzU`jdSs<@$s=ZPe!H@^(UMFWQpW(8iT)F0oOZZeHyht zuIitkPdriakKe26pPw8uOE}QMkE{CU;-ZRw{Bji^+?&{|W}iQ)>YpF!knd`~x`#ZG zk9%^+kTFaK^W=#H&Ubd)S!vGCk|z?l-uWZ|&7w=>iF)%*02KfD>;|)|k2eIMnR?h+gl4D_*8QmV46Y zo}vD}kB=gOVwkL8~9IoJA3f2{PR&$&8i`eU^xea`j7 zWF<7}i#-UB=s^fc*B`4r>2q>wYq8RkK8IM17TFIj_oUAuZv)8wV`oqL9D+E2><2gZ zq|dqbYX0N>d!^6cLJRXBn|nij4#51!&K~|1`W%4ykLBJ_pQjdi|J6zlj{|)U!2HK* z4=(_H4#4!s#U5Tcz7;;e!2HK*4<9MUAU@f^{KrZUpB4HXfccN*p5`lbOWY&7iJd*_ zbHzUl*xaK&SNy|(_wSWHXB5E3`Q{!BD&B)}0~_Z%dzz`Kgl(KJ_cXUNHeus@r6+H| z_=k=2)gJY^;vc3zF7}2V2O$X4AFI8gj{;!&W2HCrY5+`sEcd9-75^~(v9mYyoM>VC zV{?!CT;Iy{$NTrXJ~#cbxkr5-{yg}-Z^$wLnEqJp zxjr}jaj`dKGH7A?W3@NrJ^)OAtn`L#DcvLQzgq55pDX@hRj*`?8^1Isfmj{ zF=Wng2CVkPsW~Sau+pPGKhfYUYWicjC*F=>GBEwIvqyb?BJEE!{js^{`rP!#`}b0x zUry0*(;u6A9{(^kv9l+|f??t^Bm4)n!DG`HCJwChq>3<10ziLo3SaHHJ~#bwu_xt4 z&uRK&wI{8{_=o9_@E=JDGyY-vW4R|iNPnKJgg4*WlY+!B(L_dIbFb9rnBfS#f3H-k zSVn%2%{}Q_43qdN_aAsF#Xrn{Ecc|HF-)Qb?27kT=}B#4n7IE~?YTZT|8X(3Q2fLE z$7;HV;vc3zR(e`BVEn`UNBBWJuHql&KX&%CGJ#>@{v&@Y3={Vs@87G{53*PDADdHu z+%=71Qu+_}pyD4koP#qYyTUN>7Awh06#p>)k?o55-2BJIWQmG@nEzN!wyXF@;Xkw* z#rTJf^Wax_D#bsF@u3wjG#r@z$YT)0WMKN^{d+lT75^~(u{n+N@PlY!`eSFB85I98 z{jr?p62(7Ef2^e0NAVBSAFDmhSJdaGKQ5*jPVo=ZAFDAZlD9MdVftewW>NBXMgdHJ zEXTY{-p;sz>5rW~#s?MuF#UnKo%djD!t}@c_cA`H_=o9_&EY?yh2kG(K0CwX1fckb z0n6c|0#N+JfR*rSY{B?PVONX~D*jQ}72|`7e-w75_#nSkVONX~D*j=1b*@YOvAf3D z#OJF0yCv|8YXSdYE#Nn+`nBaxeVMwJ?ydxmSNnHM;Q8CLA1r}$x99jr30%EB$3M<> zsXunt$jBO1{;P9c>W|$ua=5Fv=lI9DF7?Om8d=}D+jIQmT$lP|ca1#p`P=jUl5<_^ zkKHvg%fq)r*XjkSKX%u+7w6&Ip=$|T?NWd2uHiD6yB)fgKw|;K>eB8S`PFyVXK(Pp z0;q8SWXJ|I7N|dV*T|_2xZ3?fYJtn(yX&+6IM)SXzjUyMO90t_Jm01MI9?;u|L%J5 z61s`QUFwgQYPdv;>^~mvQhyw;QBC}0J^Di}&Zc|dGMKI6AJ2E+0yNA5kI~}V6fT3GtVe(F9*qU+kJqQjufDrJuQNQbK>cxTiu~%2*5`GG z#sc-nbdCJ#ch={1hO1rbkKHx$t6yE8*BQ=rsXq?Zs0Y*6Wk)^VrT#cxBfm1>aF_by zr5gE_0S|YnKaSVPuTp>Trw(^PtfD{2uTp>L9@HPZYq-Q`M}H7F*QNfLuHmBoAaJ!y z{c&vym-tqBd^8rQKVF~0Mg74R4=hlBT$>`lO8r5gu|WMXT_e9r{h@nMf9$T2U#0#a zaIQ=Jaj=Fut*cJKJPuA!9gN+61kJqQj zuV$et{bYUCALqK%9|vo=#8Y9M z^97&pQhyw;amCAk!(Hl+mulQ!V!*>)>W||!@~gtGs6P(Y82>2jiuz-Bjr^*xD-f$o z(>3y|G|u@{SGyoq@vX?O3cI2|e|-vsXq?Z$geKkhWSe0icIEs4VSn_9v_Fh)E_U^a0wv$kB7TtOUG;E zS3kThkB`G$@~eaCd~Mnl#!2S+E*V+)5B!t?;XmRp@vX?O47l2*{eA~|4{S9)wRv()vjBKjq}p3Y@9!^;Ff6P zys_Z6Ta0tJb{prxuVA}38XrEQ5`mOmVennj82l^F3NT8hCzeb)&;Nij>e2qL&E#Nq!KX57i z2V70q6)p~(>%uw3x5CANt6jLWnA>sDIH!r0ez34B_nd`YmA*F5yH^YET-cTSbJHKs z7YxAkN68sXe>_~U4AUQn3tnRS4|TJ%fR%<)vmK8(;p?jGX3$u zf-^GHAB_d)aHc=5cAfPV`onpm>5u0NW@-AP5s!V;JdP2P~-SkxX646 zoa=&2$G5`8fva86@31ReWLIo4%G=sajv_j z{&2-&{-e}5=06@TRFV0Q@PoK{)=}<1(o=l_Pi6k&To)oOz7;M5^B-5c_W`&zh0DPF zhxFj0=aiFn4%^92li~ZIj{73vHz7ytJEUv^Wazbso+-t zOn-zQ1Q7fRfa#CmSE&Wx-1JBJtxSKE`rPzK@GE@w;8*zUh5ld<2EPJe`Xe|4LchVU zQb%R|5&Q~3@GAhOKVVk@$gWI(K%WN?Oa?7Xe+0h*5c~>2p+6W64}O*Ik@ZJof${U; zR{+dP9#~++{o2%9BS2$;F?#89YXN=Er_!k1HKga~y4(kFuqJ(Oyq!A-j@L9ZoEf$_ z9CJzh)Jwy;B>RttL!Sq5Jk364SKJqVXM8K_b5j!nb|-y44ooL~J`P-)qCPkMp`Utv ziu&C2hk$ESn6O=c2#`KE{gI!F`rPz~fP*#ab5j%C-+pJjIrVt~#K5>Sz7>5`0mQ&S z3wkxvAKc%LZ$*7>`h)x1?+m-5K2PUZIPYSA`{JEpSJdaGKd`?&fNN9K=cYffzdeB0 zr>M`9mGGyqznv|p&kex-cHRTjL6*oC*xw$&?i$EO0I(}PRg4e32laWHuLxj&djQ8{ z{huvhR|L5C1K&#AC+jHeZ)Xd#rDRuX5#s~*AiuKpE9`F%V0Y5z=0C8%J%H(?&&_{e ze|rGerl`-&e_(%m0IyF;pJ)Gp{q1Z4g6aMP``dXBP*V3F*xw$&?ixs}`wyPm#(RMF z`ZyPGyheR)!&$&fiBpdQ$CExc|B>%OeQy3k!0x2a&3_1(PWs&Zhk$ES)aN$N1-w25 z8R9aW=g;wVsn1P+@ccRYgZkX`2hX45TT!2z{^0p@d@Jg6(;qy44!fd0H~oS0=keKJ zO8VUN2hN`da6IXA(;qm09)R?@=?|PgXAkm(g6R*OKMx={1I}ic{=oV30Ip3j{$ct9 z=g$LpeTwl9(;qm0&K8U`nEt@|bKZmT57Qqwe;&Z@8c%bW{=oV301no8{==*U=g$L( z6=6KC0Z+|we8d{^2!QkFY{928yTbW%-h=O9c7^lj0ql;I3Em_751c;_K;O#j3g^!Q z2!4eYW>@=Wf{@&KDW#7Zw5#>25|~>H_`7QXyQ=l8@~0lE)~`z7k!t;_1b(erzbb*R zR_j+K@XgzD{A1rt@GHC^;~(F<4Z4=ZYu^n0M@oY=_~vbyU+tTr|43=D24B4`$3OPX z1hK+Tkzf7VZFzln-%JoI0OVKq-wGT)vw|oZ_nJ9fVjLnFyPco5G%AGzcS$HOb{yoaY2jhKlaT8Z@=@>{x;})09k+Z zXM$MWdFfc2{HkB+kBu`ytnR#Yx=nu7ui_u?Z4F|D7UWm^s`$sonIKlU2l>^}_4%Av zf95R!JKAx1cW`Pw`hy;4-^>*No7!=CcW`ulj(;4T`53^JR$Sg4?5pA*r)Gj!-FfM3 zi~Ooz#XsJj31Wp7?Aq{h<~? ztZ)zVtJEI^j?M(J!c&o7rT!qWZzhNp-h=!q^#_6eOb{!)Ir&xU4+0x!f>_~Okzb|$ zAn@MSAXaEWewF$|ErM9#9^_Z=-JIhe{h1(Ecq;O%jaB?(-%JoIya)MJzlwhxoe5%v zHz&W^SH(Y0%>=Q+w<5nfx_-wno7LMhL9Eb%{OZ*D9RD~q6T}MlAip}gKIgwWIupbS zPep#UuZn-{n+alt_aMJAE9uYt5`bgvxD3a+-aG;y_*RU6Sc~_z2C+g5@~gtGf>_}m z!hZnR z)F!_&;OI;cE4(@R)#KIr?x~p|R`^!rSC3cgyKm0~u|f;-tH-PL-BUBM7LI$w<=w&K z)%tGukBtF@|3G-)8TvuZq7nGOdoccytVHh-EA)7C#y+zZvi|7L_?w&mD8H5YkN39v*G=;k-(%y< zcz@8S`$Z4ha1PF3eQveC$h!H znLM`#@HlxQf%mp%Z$KdPl8u%B2*9kQUwNDo*jM?e5;$6UH3E6wH9tro&%19|o|8bH zcTZKmmO!3&k5=9}fWofm&sUpV7u{N^iT(^3fPjSx5N8nZ*$Q}XD_Mqs&sX|mW8x(O z9;@_6KQS2r|IqaZxsQM+EB$dau_Xa7RQf~wO28YH{&+hvvehQ_$A^{vI5k5Kx7y_T z?i-co1^+8{0D&L=BWPIm$ceSGd|h71M?q8le$}Fk@{OzP5*R@9VM{ovc&iU4;{s{j8U|nlee@NF}Y>w*>D&f`UsQ%cPwDU@HRDZm; zm1=vrIjTQ`U*YCEo1^+8_!WTV=BWOVK3{2$>kmEEsQw5)h@ZOH9MvDeuhN^3>W}=b zM)imE`MTDq{@9oF`NihA{)mBrVKAycf-?YEX<}2sZ;GykzK>2saCEA&SQ^E-zp z^-O5-0T8#P+@vGS1KIT8j$dtdt^vAx$;gr9m&>v)d%3oso<5c2_ z%3o5fUy)fVe~IZ2aZ%-(Ec^%At4?Q{|JaxKuJV@@Jdyfb`AZDgm^iiaml*KgR_b%* zFR^jHG3j&VZ!g98rLp7oNB{t4aCHa>{-8cr{u0w48bK7i@^~c*Y5n@0K`VZ3| z(9XOE{fFrf=<@(@nmm5$aq>j5z6gBaTaEgUerkdICDFpnC-@aw;Ea0-1iu1+Q}G1y zyo+@P-ecT<@Xc}Zo-Ojco4?hl{|J7CpF;kUXkm86xZ1nnwy8gUhFJ>eyh`Bjtpz-^ z7Vyhe{*v;i4p#X~O5lH}@|TprKdJJUl)%5J@|TprzhhoCy$6Ixydd?*|52U)P=0FZ zkAHVZj(;dWwe-ioxFg3ul%HDqULo zDL=LJN0C4AzL<&QJ#bN4K3XE;g9LDKK>4YqKZ^W`5FWuxaETW1=lm(e2Lq7)u>8A- z4+bFpVflBJpIZ7O&96W;ry)2b`on;Ql%HDq!vN){ zmj19jcMB;$we*MOPgH(t=?}|ur~K5?AC~7%`KhHpEYF?tQ%iqX{$1s#mj1B(yUI^3 z{ZZuKh46?Tr2eq{yUI^3{bBV#l%INCi~7Uz?5u8BqPYwM+YtQ;a`Kgzi?*Op;yUI_!v&r~}<=<6)YUqz>VflBJpL(Uq z_(zd{H~1C46)tg)y#MN{l%E>DDOcD2d)hvnZ@e(H4H28UHZ%sfj|(Y3weC^)51#+fQ8n^Z8$4Gwffuu<)@aF*f=lk%KV4&Q_B)G{sW9Fm$)bddV zC_lBlT47fZP%*dD4;FR>$rWFhp0ltkoPqKEL=qoa@E=Df9#n>;3K{j*Qu@k$DUaPKEkfJvTgN0_RJ#i zF5uKR)E{E{=085#M*SgeVE*IHZCJR9r-G>P{OuoYTSI?rpT)n% zn{&m>{KuYI>JMog^B+&lQh!Jlng4ihmii;>s65VJo5d@~Q{e*)%zr#Li@-UXSAS|nErTf787pVgZkX`$7{0?9`RI+51RgXZWh8L-UGwL>wi2kOZ^dS z34rO3J+p8e@vSgS+)B33LU_ct;)pVX`)C{WM;?PW4He6q+o(TQQ+%+nE9nmmlekCb z?b~PJGULrPGn^Uz)Sg*5+4xo%Ceb4Mk0)j!Jfa0>pTe%FKQv#Nns{xN3}Cg%_@DvL z%|dv@w_<$IfG1`lJmSqci<5uKR)E~=frndSYA8mv1h*`lK^%NdHZv- z5FTMy^n<29UYmvR2)m-^H2v}1tn>%PqK2#@$y^ylUcw$D<3 zET{OO0UvFH@Q80EeGa>#n|N~@ghxD;SVn%2kG4sFV3^3R*dqJ~TC7X^6U~3@ndRB= z`4)zW?20X(n1%3&7Gz7wt_VCg3*iypiu}s-$7`v@Y7@i6>wi3#?y=IOJ}>+SpK7^D zeQy3E{2=bJGx0=QzuG>_H(yT7()>sMR^+1QKi=HNue+M`x#^FOwy{4hCVg(hd3)-? zb!mU10l^uPU16BSx60%EiDV_~TGZz@&a+)npW8TpEm`7fllt8J$8*VcSDF|m5n(Jan#28E^co6G+J!vkPZ{d@9{f{To?6a;#eQx^WxmnFu=$06qd0)nBX@-mSU7S!b{qbCy z6N57VF#YjFnnjnJJOyF;W6vz--JPM&*&_3j?X#SzmqVZP9yu>gp4)jg!}Q0S+vp8e zL!U>DywkMAh{AvW)lYCV0s}bOd>}`ZoBe>3CRBAwd6U2 zUnL;>kLQxF)we4A2fcIfE3_!=ivC>ZKaNzpskYCO0SNd?1&A{U_-7UH<~Fhn0l#1E zzxrq!d5M6pSNpHFCnh7{Ott@NPvSlT&Q|-co=9v-z?EwMmH3r_O;!G@*AgSs`HvmU zYoM=74yW@Uo2vX*PbAi-^B-5L{a1Tt$rE+{<7~D6YWpmirOto+ZnghPTvX>jzEthM zdUG4utImJ?T($q|qiy87I{$I7I{&dfG2|U>@+$-OBu=gK9|k;;Sh&uA9IW-@*T>ioxZNgL?=$HD6S#}i3S=={g=YX9z@SrDt(FN(|HrE34x_E}J| z*dt7S^%vFo4{02o|9Gi7|MBKFsv@2LI9{Fq2tSCQ()o{r)%lO`9{_azV|R7_V^7j* zI{z_U<-d9&sXLwjxK^G2kRH_ekMwnEKCdMOsq-HO1ZRNe)cFqs!hZnJ`H$2e{3+>L zo&QMvL124Q!aDzv`h&nn+o+v&{v-7Vfj75NZR`9;>JPQxI)l!Cr2f!7)B-YOkotpw z^tsM|r2Zff{0i@(^B<`{2!tO5pz|N8KL`ZBN^eenmHI<1^sUIRQh(?k5FYWZ$gfg= z5D0z+K<7WEtN2IoD*!tGvAa6|5u5>l&VL-N_Fo0R0-*CB$E*EU*{;a1UaHQ2NT2Kc z$6r+YuYzBtdyrqfRPDbCewChz{OWjh{v-Gm0GBN_S*PQXVe?duFN6 zb^arrqbKmh&^wb~rE~NIo|~mV*ZGfRB?Mke`dsHf3=n6~`40o6&vpL8{KuZ8&vkCX z{KxjhWOV++{KrSzFffMaKg@r;xsCc<=RcBN@o~kkbpE68A7o@|Vg6&!EcLn0n3(@~ zBC$R_mHCh75>L$Uk$K{4iCOB+&3}lC>RT25gZf-wH_cakk3EU+7X87AYBSZ z`J-*r=jw9<-rRA#xcC|YH5q=OYG|nI4IWM-@o|KnPhOJiTKR((< zt(NB!te3FGo7E>!0~pwDAXUo+f)s?L7|X8@o%@rTvNSRucRQfKD755c~>&&L|oX{0e})T47gw51oT7?22zL&so@&z7^Lp3cKRhm3KC~ zQr!&s^M6$N4*_BT|Kk{-Go!yZ2I!RO*DL>#@4VHTZnEz1y52*?BAFBT`u0K@&LyE=xhw6VwVHU| zoBmM!kML2cMc$vN`XAxd0GR$z{g3d2>CJQeL-jv`UjZ=vq52>CR;E8x|3msb^#|Ry z>VJek$7eVFq52=e005Z&Q2meK45_2?{wvl02$liB^oQzy$gWI(sQyPV8MH9{q52=e zeE^vLQ2mc!OX(hY|CQ>01iu1M*p>Jd^|=A6|B=VwO~Yj>)&JPh{y6>At|6fMAN$*k ze-w7b_{Xs};~#}xL3o58Wc;JBEAcDFKMK3zdZOklQxmHHu_NVYFhKP`Hl_Rw2B`kW zl@vub{h|6FXIqSanEp`xk1H*N2?wS>RC_>kyXgldIQ2menZN@)LO{o6I zu{QO&0jmFTI^}0DK=nV4rTh%0KUAkd`rPz~>VND=`58=qsQ$;Ml%K)$hw6V^N#~^*QW{fa-tjXfys{{zLUY_P43elU)t{2LcMm z+6WWIJU#SJ-AN$)FCgnIM zLq692FC&2Jf1FPF8EgVj{f}cQKZE&?Y*&nb*f>}Hj~yvLgN<|5|JaoBGZf>T@sBHM zHVF(- zBTN`A%&w@_PN)0~X`J(^RR7~x%Fkf>L-jwT2Tgyd{zt6u1{oii{y_bYc=O;408D?N z{zm{;Vt--)raw^sgWrnr57Qs0|G^fFG?@O#b30=braw^sBUUyO*PyU|M=@$jbFi? z#n&aj`rfVi{Ks;t6Q}scH*Yn5IZjDMv5 zPz#TLr2f!7AnW2=G5(SIgFx^r09r43y2^hQ{0e|pSkB!1m0|sl;8#dbD}MFP%~^j0 zzXG6DoLj2+2ka^Ut^0hYihn?#S1U!oRK-7nU!{9sijQxV&z}ds0-)8XTdMd+@GAh~ zS7s#_V?+g@{8t8G3?Hxi*H4K)vUaM0Ifid16s2j2eiuBto(<5O6#J{%6|yZN@}z69|9ErXjc9s zx8VMg2P^-P??HZ*&e3zSWd2=zEAp!XPz;j%ssK1XwCbGlcK zVVqEAt=7 zzsnZnS82ZDdz|kuKB)7xHfm3%3f<#?YI=_Yds7wg4CHl&BdKn92H?**S)NRl#xsEa zAaK6Jy&oAse-OCdfiRCfcU-2jajyF30?gVM=NTW={@q{Y1RzUfd{F0WKjJI$!(Q>Rjr{RQX*ymw&&ii>zAlI$!%yRo8NFsxGhnyT8hDF1sS% z)%n_QlP3~5*&##L`PzSTYtGAgzC%up{61j}e@ULG_aF<$F7E*Tck)C6Tjt5zv458> z%&y4vv6DQ2e?y+A7SsmVdmg~I$rE)CstN2?CvbOoH3H{5)E~(26F^~CR4mvLUjj!u zdH-$+?Coe*7~Dwwl-ZT`k-?1wV0K02h4Zxp%&w@_a29}o*%cf|*cIG}#yJgc%RC%M z9I&Us9j^Sx;yi@;5HOjsWuBT-EuN`bT<`L9lPsOXiyvTOKd$u0mU+epmA~XWmHt?qhcJ&8T;KhAr9ZaJGd`&NC10-eNB9q{6e)knuT=Wu ze242L%3osM;ADq8OqIXHfFm95-&Ot+1NL^fi&gnc%zqr|aNn!)mz4ekPo+xZra#Vi zxPMpqOH6-U?|dV*fE#iDv1R@&0Oc<+|FJmF7Rp~@{$tBL@1gu9h5z9CmGYOE{|G;b zdnkX2>5r4?&6U5z{D;1k@|T$Z*gO2Xa3cfrA4gJuD1S*Y&e?;?Ut&Py?1wQZe~AIt zJG2tzFR^hhyHfrV8|RDjvP7=$8c*CZPuo@g661;2leH^-tm%3oso<7Ao{l)uFE$N3KD66G&3{c*j+*+=>5nb*oD-41Bp%oF$Mp_pQRFY-d*u9A=R2Htk-sDW(;p|( zOpW{{0hs>K-2QT7RDbO4&>J9s3Gb2PA4ih^K>m^d%t}sn=y8z0qy)})=%bLoqy(;a z=+%(Fgh2Km@`HCxbA7k4D|$}kFJX)9Keo)%*S_4qCyS>t|3UAJ{3QVtc13^w#m1=q zIGGs0i3U1y(5&2qIK#^gbiD(66U(^VfEx)QzsHfpOYWM6c?Dp85GEM&72HSw`902e z$bBB0h8qdM-h*tZH$AF9#INp}9@QU<^JHYZKMSpN2Tb!qYR1ew&5S#&v zHR^K%!hZmem6-ovU72iGHqK9WsDx#S=0A>fsGViIHqQ5UxEdvEw{af)3O64gAHlBx zXhfO*2z~`XW3bR4d@7Av(;wjn(>*xOO@9Qx0-%|}^hf?yoJ&l9NS|x=G5rzz3LT~S zs?Z(*i`_ScY|#IC3&L8IoGe0Y0C6R@GG=X zZmL3maQ#ZztiDa2$e#*+mF|IK=JWV%*s{R71ytn z$E&a_u3sthS7BFNzf!KS!mhY}1@Bzy57y1VN2LDvT6LPd1irNv@Y-6yn^pd+@~19W z`L9agf2+=)m%#cB*?*M4-`;?Em&3UPesM#Nf3W@sUJ%1%@Q*e?*Qy1_hxErUZpiTu z*8e~Y>W{y@A@eKdNd>_DCF?g}zS2)=d{BQ}s`6j4{s&rMN*R2s%74ZDq5v=n4F*;I zE9QFzfMGHiRI3K8{{aBQ#DP=DFA4w`2aX~OEdX2s*fn%stpCAzHv!oXGQVinAA@S0 zA#%6jr=&jy)j9*~f1m~R$G2|DesJRq*RP~ME>-otLw%p;7sg!@Ydw`Bjp`X6Wk2RiumTXOt^^*?YAxZJ_d-hy}( ze~R@#@Kn?vQ9on)HLEsefi{e}1qW&Pj`X6{I>W|bPdaAu>A^nm1g8=i3;>{WVNc};8xxDeMc>W{x z2LaaqKnun{Qh%rg`L6WG&)!nj|G-m$C`X^?_y_BM;5|UC<6GtU2lI>K&8a`WbxYPC z%zKV+Mg4K9s(;S{3c@3viu$9lD+rHx59*J?t{^<( z&8a^MyMpi-`t#|^8?gQdT2Ox&!1^D!2RVZQtp9g zzbM|E`s1DrdERB627D{(k9#)cd6)G+(1QBoo(*S)H;??(Aw1$9)F1cI4{E;R>WA3t zXXytCM1JZZ?C~DdAIW_59$deY{xE1u*}CyqvrT_hlrz(ml9R z1g0M>!~6&9f8aeB|1kf-`X6|6#y`w|oI?E%d@II3%zv={M{Z&M;}q(D}Tj@Qr|6u)({O0CA%5P=x z0kA7h0J5um57-p}*8i{;uq(BI(N<4o{)4Nv`Y9Xd!LO`^S$iKDd+}4`SAW6@fX1LP zc%aVp-CwQ%*8f0@w7z?L9v{pj41oOV_n7BSc7-V+0P?FJ-=4<@>wf?szxrj4bGBgp z4*=v>f5LH2fb~BBkYC+Ho=AZ8KLFeZmN8GBs1}?P57Zg|_$+y%?!j5~K%McACGtc9 ztpD-urKjtRe|(lakwAZjGxeD|;~(=IGB45G{!X3ok9#&`Uc&kx?_N4nXZ+)y4VjlP zKQ->b_(x$^a5eEBjDHxw`XBFJ+Es@PaVEq1AMakO*BSpX{~!3J_;di?bEL`X9N) zzpeZS^PXqmKe+$k`jvj_KUe-^U*bLjKCb)+>wn~*`eda)#IMxi^OgQ!{SUO@`1q|# ze=tWp0FIB(SNenXKLBuieA4v?dE%#2edmv>{JX6GffgJeZ&mt3T=dgZ93S7P^atyI zpasXrS1SF%eEI-5KFk}i{s#b#4+HigXFq^({lWFCPfvXTBg*`Tc>5z$SpEsX{NTs& zmrK7r#r^H(KUjO<-AjKk^}DGB<|~cCw^EBy{lWSl?_PQ&)ps`k5&5an;?t?Vv-uBc zoSUac^#|*JynE@-H{;=4e}o^zPyO-cQT@UCA80YEKUn|c-Agx5jq4A5)_AH>{UJU0 z$keF*VEqq#t5N;Inilw0+<#>g0PBCC#i;&}u6-occP@A$SBgHJ>O0#wXZ;Vn$Ef~b z{SUnPsQzI64}7ap{lWSlXfdikSpNg}7}pG?sQzI654`!P{>XMUsz0R9|9tbP z{$TwN++$pS$l6EsNAN2=)gx1*`r~a>)WNqJ)gO5bGX7E6l}0V&ALc*yp*|<>!T3jE zSDG0{^#|*J;5|n52kU>}TaD@u%~yZC88T#GYC`(_&o__i57z&{J;wD1cd>qYYE*x) z{s-P;RDZDk2fo#){?Oe12UDZ^L;C!;Q+;RCAFTg@dyMN342+l=M)e2lf8aev^#|*J z;9HIA5Bb4A-aM*5q|g6+^Qit{{SVw@Tz_zPboh_p+ym1etp9=c7}X!F|ABAC6?*d@ z;0&=Y`Ug{te;5FL9>8x$eP<5mUBgKq^*;g#|B-;~Kl)M0EPzi>G5%rx1NA=wxOs~4 z57Qs0{}I5SZ)W_%^auD=0Drt0GDPbo;&3QzFl=G|1NA?udldSESx@y;raw^sBUXVmX-VMO?$3uPYPyFpE7-VRitUtE)L9D{A zV8R2_A3wUE{sZ2CK-M2y``Ld4U{>-_pZ??1Qy5qd9O{Eu#TaD#!&*Gsr~iOgV+)LP zn&_*2`j0=}jNvYxNZ<#3`VV+c0%xig0ri>W|Oe ze$%kULw%l55b%o?u(i+iT><}~0)BKq{fB_xq+RjmTl?hg0=``3Z-1!&uklm@j#JmN z#i2fk)ew08c6$#Ht08di_PoCPY9GXE2wbhspZ}mw|FOG9{n4oEf4tfUv5FSdA6KjM z=g;=3igwqiKh80)fu4%%ySr=D=g;4s`PD;xo_yI|qy9L2JM2nt&ip02Yt$bP-ws_% z;79k_2|c2eGPaK^+BxSsW40iKYt72QGBYceeO@3u3?x&3&f)c{OEqJ z8eE&gFo}EQ_{Y{h*LPo^qW<`9mH+CYK368LO;LaRQI-GdP@iiV(>3ak->LFnJ=+Jd zil?If_|^4!oWI(~L>2Er{gL{E9rc4grndN2)E@@C+6S?UZ$;vBD0f2984 zPaW!mSj9asOw=C)9_oWw#ZzIJ1dzx1);@^UrRf^=N9qr@_|g4bjk-2P{gL{Ez}7z3 zuU?;`{z(0ydvL|;+7yO~`h!6Dk8}^}kJKLop6!EJ#Zys#r2ZiAY9GWZ-h=uh^#_3; z^g*oRTTy?c{!j}LtN2#bAE`fd53cVXuQC3S`h&osK8RJ^gZkrls{B_E^|`*gyM|#h z_@gTS)z&`uCr;O>KfYV#zxvVr+@E-Diu&Ux)%lODeXj4mK1Ka8Tg5-Zf8gfVrl>!D z{+1m7IMnCb>vWC!W44NaJlhAcif={z@ssNO$E$s=?;fmSm_*m+dG`mY#qk=3i36{u zd%RSm{;(F$rl&eyqy8xDitll-M*We-IX~ht36p{wVB< z{qgz~{dqctt$VNsuT7Bw7!aHReLh{oFwsd?^*OC%ca8cZo!cgm?TY#%o!chxgJg-v zYZxZ+Q+a&6nr!!_8iq*#=09ld$7_s#q;uQ+so+<5s)IG^kCYXdv(H0)j;Qb-_$dSO z7$k=?;79j!)LxsS{z!R*HN$b7zdnUwqBQy36M z@gC+so=sj&Pi6k2@`LF;vi|r%@|=2e^B=D!U#o9b_z!w#eO>b(hx+vA2W!?JG`NTQ zLk5s;o+p5CGtc9uZA`V z0CRf)Un5V{J-C|+-he>nC5J+P@E+WMWmfV~pF6hTaiWC*&^XZoKB@$MbU$~6!K)F- z`Xl@xTEGtyz&Mxfa_1R5CxJZg9ty3-dr*HAc6A>Bc;{$gcBQ;g+J9yGqXbNUlz{1v z5-|Ny0;WGo!1PB6nEogM(;p>Z`a}7zwExQVhtAh(|CQ+v<-gMYE7Ko3C#?NfrazSb zO6NaJe<=Tz_FtL)Q2s0JzcT%y^Um6TW!^yfueATl0Oh~Z{wo8N|4RF>%zr5VmCk>d z{!soao&PZZp|j!Ie`Wqd`LDG9D%lkWj`Ck=|CQ+v<-gMYEAt=9f2I9b=0BAGO8c+O zf9M>&_FtL)Q2s0JzcT-!{8!q4W&T6?ueATl{D<;iY5$cCXXU@r{wo8Nb3o@m3{d_n z?Y}brq5N0ce`Wqd`LDG9%KV4&U+MgZjdSI{(*7$O=gQBZ{a2bWmuTp<-l2QIE z?Y}bpq5N0ce`WeZ`LDG9%JhfwUupl9=?~?y(EcmaAIg8F{a2bWmuS|a^|CRP% znf^fjtC;$<|H||S@?Qm@{a2)0U?Y}bpp`X(E57Qq4wExQVhXC!rGX0TT zjOvekk5T<`uFLgZ?Y}aaajwhtUG2Xr0Iu(9|CQ;Fb6u|QYX4QLW5UVuT$k&++JBYm zmvZFDFkHcLoTyg&uTVx&eaF^@5I?ZAJ<8T)XSKNOU zEzEyB-+c>!TA2Sh*X8=I&VQKyxZ1_SRaiS!k@=6t0@rtS{=@vo0}EW=)%g$eAB_bp zTyd{jJeB#6t6j2JeO>b(=ep#(+7D-=_W3RuvU<>f!(FcLs?QC0xJwo;E3t8YxXblj z*_Dm+=eu0rl_lCZKi8!;knNiPxZ0(fkhR-5Z!BQpD*AkMd_1s#g{!a~i!y3_1q`r~kydQkI~ z>5u2TRFInCOn;o~%72XNkE>n!51s!o{n1#U|Iqml(;p8k(0}Oshv|>T0{w^1f0+Kb z+NJ-{`H!!0omw+P_8*i0S-w8srT@_R4>O;`UHT8~5iVOi+@=4}`H#Y`=s$G+qp&Ob z51s!g>`MM)RDWFU(tqguhuPH&OY|Sg8`Z9|>6XCUTEO333)ofV-z|UYp(_7w2|QA* zUzNbGRqIzJ@YO2+ZV7y|I)DDc68(qrUwyASfBwP}{fF{jeX}}${=yRdhw@*2waUNy z!V>+5@?ZU0m4EkzCHfC#R=vNC-{8uk5(SInv=+P?w)f-Fn zAIb{6uZn+sxJ3Vh3zD*x3ROY|SgfAue`{8uk5 z(SInvX!^RO=T9!te<=Tz0ZU8tAIg7a!2A;Zhw@*g{@_n7Ezy4{uXE}T-Glx^`L9xc z5O`sU{zLh%QhyM5V~PGl*``x}5cqJ3{zLhxQ-2U>K0^PYjMk|?)Pnv)Ij~cI=pOVR z%72ymgTM<*^dHK9mHLCglS}j;%FLbmgTT@f{fBaSr~V)?zeN9`{8yD8%qd$4D(;@tKuIYE+OzS%zt&X%74{-1i~X) zP=B1N@?U+p1mO|)p#C^o<-dAkiL15Bf3>fQf4s27_@MG%nUy@b#1&!XzcOHHiSa?@ zzcOHciL1`aDqq+Y(o+8FS(z$H{^Gl3os9Hffx2+b8f2h8}`5T?=~Mn;3HZv{_%LVzWd=40w3|M82@;@THg)-5zFS$ zgN%PXQ`I+kVTti5Lc;Ro5+hj3e`UbZ5@TM-=@gG^!2A-UWU7K;{$pv0aW&+P zVvDRl!hfWvBGWhj@xl`0gUEly7FmD1v4p@!ygB0^$*%agA1)#A!TeXz!u&_`5pQAs zejv7H@9;qx@FpKjw?Co8~LN$I=b@L$-^q8}^_L=im(1 z=LWp7b zxGz`rKR#R{r^cKZy#1M~{>K|jWZ}pn7{J%b6WQl4ERnZk-en83D>8k|)B$|Cs{etx zJ(hPcw+HZeRsUl?yg>l)1_bUNo=aT{{}BNEM*wCePlm?{03N3VUI-r*0DM#lys<=8 z1g}N_>kKqe_`zs_Jdgx1&IvRhq4GlJNCIbuz=z>$c@OH3!mbebh^J!w!|dvnMd}ab zRohyri4s^?3;66>z~?LdQU26pmHsG!e^}{{5_q!GA0_ZYr9VpGjY@yKvPk`*{BR#u z`s0;F>JR0Id!y1HuPjo3C_mf_mHv2Tk@`dV;hwDY$197}AIcB6ROydb7O6j!A8x+V zAKzc3{!o6nbyfY3yC0}cO{ek>&rT=(lk@`aw)6IXp zvPk`*`p)J*zQ0KQp{nfWKkj~z`a^ZxO@G|?AoU0G!^O?bf86~b^@nQ3oB#O!BK3!= z%bWjrWs&+r^_^{;KeI^vp$hf}JibW%G2i0;D+4~aNd2)c)i*Hz@%SS3$Hi3N+5E>d zi_{;hslK!Mk5?9{KUCk@#`*UbsXvxeeP1uMe2{$CN2ZhAFnJ@f2h8*>5pd?sXx}Wa8ZBo=8rE@f6TXV8JPa~ z+#>bIx>VmF^@mzee_TxUolSo{vq=51n(8~7{&;1P`UCZyIa!_^Zgu?rBK5~|)OQZR z^vB%~Qh)4ha^5xlao>Z~AInY7)TTe~evtZOrOCP7^vCxXsXtbm^aiFsURk96Kz(OE z)lI`G{Fz1Sk994295bKC7pXtyTl7%|d~T8YV_noY;7?`$@%SS3$Hl1cOd$J@Xa3L9 zzCXs!>O2#BE)MJx!rU2b#<1&4mfYAbGfREV4YTFqVV7OwZb~W1PO>R+nbwk{T}uY3 zaw&~QXq7n34wfT<1ePL1lWKyh#K|O9NfokSZRB;;bP8P{8L+l#mjaep$E)oc+!A%4 z=Q+{i*Lj-Lnp0sf%%>iBukAJ5E4e>i@gMu}GHM`xrz96t}hz8UEc$Ik2eZ{wO{5*{IN7IaQ{5Te%o2 zevYLt=#SU1iUM#HAn1>0uRnTumJ!^GxpW{ z2ls(L05E>8B|Pwl^oJuRVfZ{V!#`L2yjkM48NLo??J$3k2UW)~e{8=sMt&OOO8SHG zbJbDS-Pgd*_$x-20EBVnyyw;*AFZd!o508357_yBz^Cf@^X6aet>@31!0*)a=S|>C z_567g_*Ttd(gc2dNy&fZ{O#5s|4`3=I6s{A$B!>5_SMOhwa)tETbEpu4{bQlo%P3; z>iP4NDKnn+$8Xg0=g7s%#BtUiAFJojogdEn<3siQx%2f|f9$IHubjW#`Xd0&-){X8 z0OxPF{@7LXUparf^~bL2XK?;@>yKSE|CMtaT7T@S`LCS6-TLFcn*YlA+pRy2)bk(C z-){YJyq^DX{&wq+BlY};^S4`n+*k8oIS-`u$F7?H%K6)^Kd!C$ubjW#`r~{(|Ka@Y z)*mm_^B>ONZvBy3SC)+Pw_AS%z&SImKLX(V?baWuKlE45-){Yp`oncZ{gL`Zfb+Lo zf295p;QZ~@AE`eCIKQa%N9qp&&fjkRk@~|WqW(zz;X0!JNc|ze`P;2OQhx|={&wq+ z)E@$zi`Dug^@jlGZ@2zP{oxW(f1I!9KT7^~%8RnMu&Y({$JxzAe*n0z z=D%{@TIvq~N9y^HnJqtHtR7c6bTk^M4hTyFVe>|Bo z_ELW|0q6Lo{%8Wu-%kA@pxEvAr#!;c9|DTq{$$ELO#LCC*d-@77yTii@W=(`P=6vXb42W`P&yJXgWl|`P-K#C_@00?>L$AI zMm_+P?{MzuEgLCA0F>`={`NKLezml3$>U1))#?Nvi;sxaWI#H9JNRP=aQ^m%37T@T zg!8x0P0;uQ!0*ssInVa=gyJ8MhzA8YfBW174NR2acesRX(S-><0Ps5mIDb3%0~Yk+ z_iKJ1=Wkz~pbYW2QVHj8UxORKnt*eXZ`r8$M;eJL;r#7uaKD;Ll<#oHF=g*zL-Sd1y&41z!1>#~ivGFmL;=p<0>*|V_X$qpZ=<> zyJz$5Dq`oxxQcwU4flN2AI{%yuk%w;e?&j^ zgH?Yxf4eQqPgniXv_)^I`osC#ZM|Mn^@sDfH|$-H-RPkYG;wls^yvq{`P*%U2mLW@ zw-^3!{&rjXfj^wT-P)kRA1`umt<^*tS9)FNZ+|cS5hEsnKbjgR@Q3raTNMTVaQ^n9 zqauH#x4N6bm%ty+-@cGyMS(w@zkP0EJ4*!qupXSA=nx3g+4%4-njx%0QLNjHs$dG7q}TQ(|pLf{YQZ(p-f*%rb)cm8(p2j42_ z59e>UJ`eiC`WzgUN)&w#`--9(tVST{59e=(eMKPX59eC-Z&>zm< z4nAkIH}nU)?p0U-1cLrx{&oPcuLuPF!Tjwip-~d_M{`^S{b79$`-*=R^at~|AmnD`b_&|x9GjMXV z;)AObd;s7)x9!8gAlix#0LLGp^<8|B|^jH?(Q4Em$ouf{MT zBtUuHlbaPET$oUNFaT%MEf{kX3=^V6`Hri$Xx*Jo@xh=!X0}M5&!zZa&>zjUJ?M{e zzZ%1YKDVtVIk}mELGU>rQB6FV7-!8!5>Eu~Pj`%L*+{C2z@3TJ)@&531^v z>?kea)aL+}5g$Yyfj>@emS1!=>GL$M%u({Bu1Wh71Ap9)yKC@QTQ=JEDLU#-=yL#T zHp*HG{K5Ni0NB0?`h)shZzX*m_~Rt*R|BvxA$=bB<1B9N0WdcqeIEGZsx9(UPiK8z z_+w^^Jlk_gp9lWnT|%mZCMi~*=X3sc>T|snjo8Q^K3BAw^m&-)2G*qgi5CJ}HcFpg z2&~yCeIDj{sYCia%yR?F6Vm5lo*P)0kUkId+`!xfMMd;87?{35pZk2&T+-)3fB1Zq zHBQhUCnod$64nv)htEfW2MGlI;qy^z(*DGtKTb@_zq@6l^m))9J|DGaqx5;uA3h&t zJs9+d&qu+(%iPgHfB1Y9Yz6{BfB1aV+=TK|2mRsmQPa@pDpAIj&qvKcpR0~X^YHQc zC~9W_L4Wvs)H3wBfXnj!E1!>oeMKOQ5}%KPM|cSE`6&31hX9|Cf_;^MvhMnP6!p34 zDE#5`QSfXhpzw#!N4c%SxbpcZ*jFqO_`~O;eE#G9x|_=9qYQkZ0zMyQ;L8>8`6vTl zsr#>dKFYvTb^n#mM;SO)_h0#Zl!52!{wtr4GVo^If93O02Ck_2uY5kr=Ra;zUIQ_X z&qw+E#}zgImCr}{{KuPh|CP^2`TWOob^n#mNBR86<8}X)&qw+E$Gvs`mCr}{{KxHe z|CP^2`TWP;dj7-bqkR4&06ZTBV)6Np0PuX2fV*jz?5*cNcs>e%&wuQ#=RbHp3V_dl z?5*cNcs>e%&wuQ#=RbHpN+o1p9j@m;PE1Ol`~1h#_526VN1=qzfBa=V|H1Q70DS)A z>3aTy=c54l{Kw&X{^P);_Lunl$KHDW#l%Xrs&wkm&?9N z{oxWM4vZ_=SE)Z-2Z;kEWM8HJ5O88r`%8TOV?&L9JTa;LB|iVLqn`gbFsc0|zW;o0 zJ^yjXr1qEi{`152{KtVw?aT1}=TF!3AK(wZmCt|tWj+6KVp4lQeE#F-mrMQ`%qR^B-Z9+%lySKK~Jb^;4?D=RX2)CcTxBFGzQgA~8skb|?)%Rh zHXn)VjX^yfLoSA3pzaMP-90Ce?#J{}BM#4D55C{|LaDNsSVp|G1*^M;TYL zudb;4am$oOqR)R^QTb#2l*X>le_RpyL!;g2KW?i00sAVAUBy4rsd(AcPox>;^B(~y zbC6XzkC#f^k!G#Wf24Et0uH2k?(-k%9KGw%%HZ=K=^VX)6O&p?eEuVyqZe>yQmc>8 zf22HK0?K;DZt;30;Fc+^a6bQ$hL20gruO-d0Gz=p3Y*&JKLUT8z`6^-=OzMwJdsYa z@}_ZBPP;^P`0T6Ce+2%xBb}D@`HwWNTtfPT_n+gh8vG%};u3*BPE1PUxQ@UdXC|eJ z{8oWKd``H0N3j!cnUeDImk0h>KP9c^wrcQ)_LsPI(|V=va9$jr#|g96fP3&lzTKIe5;y`R(heY`R(*Yh9F?^D*2qHCSs$E(kN&2zn#^ZR(cTB+wh zoZrVQ++Wx8AI|UNGjZqY`48v!@hbX{YW^$d_wl-WuI|5bejl&Ye_hXiIKPkA_LX}6 z!})#81}k;{mGk>t$REz{W5#I=AMI~9M+Lz7eavcMTsgmwc`%GC=l3ylhH>TmK80(G zUE=&cX6MGZ^7&f2O%{Kz@`v;L*aG;^6>xqZ+YCo4;QT(eGM=jZ;ru?fOP;R$;ru?f zWKLB6aDE@#J}*`NaDE?KOFyst;ru?fuRc=shx7Z`BKwu9Kb+skHrz+5{&0RDTYW#T z`osBsY$v`{^@sEO*s}a))gR99V_Wo#Rew0YkFD3wR{i1pJ`HNmh8^59ap)Q0#WvyZS5U_fd&=lVU0S!Tdh5890P(@2Ujz`v9<-2>ikP zJ^(J%AI$Fqz$exNe=xrf0BfAUAI$Hg67o6+{uu4~eC@sItqOlk_oP33@;>m#T+jML zzVN^w%5u0&c2XThf2{Uo0lc|U@sBW_mwVY}ptC;!3q4sGR;gj0 z&-JW76gLR-e7Yz7@zB+ZG=zCR+LQkH_SK3_1pYX^P5R@Zs}=tU^L(_I?JK@j&>z!1 zS!CAdL4P1WT(T2YN6;S&Jz0HVHU3r5AIm-I5Ab06@}fUhd$KISoCJdYSnk;tRjerJ z5Aa8_+ts?MKcqkAda`%H=j@N5Kc;)KkYNE32>N5RCz~2JL+Zh@|LXKMS>doU2n78x z+OyrRaTW9j_#?fQMqiIyf_3b z=Q=zGONoX2R-Uyt=V%6akagU1wc;O*apf=f%D{Q9)-7!%ecl*XZe7Jc8sp0SA$=ay z#B}aK>GJ@zcr>`I>p{jrcoyW$@~ zf55&<`a|)LU|&u5Jfjr<2tb*G(&qs$`aI~5(LB$k&x8J$&MSlTdC(toc`cDX z5Bg&vuRhY}L4TC>N_-yJU^TCBihl%PIj@PNk*HV}@+wLi0l;x(#__sK8UaxFW4h;+ znlu8S@JCtONh1J?op^ei*}(d|++RDI`GYhP^~ZE(92Q&*{4tj~O7V}t9}Ah)6#oeP zQFu`M6NCS1H8Uq^#Osy5d>Og)Fr#dXHuyu)rJJrMjVuQKnCmGXbwbMOC zupYXaG!lVgGmu6iaC#eu2bhyI0-((E(H;jB=DGBFW1e#ef}J@aQKHQAxgG~CzJoLZ zAlQi<=HNlaKf*jO_SLXHU+r-!p@bAm&>zb^PCnEjjg#iNZ3a$PycKC=v7tW{=e*}? z(#T@aAJaXZoP6kN(g;d~aiv)5w^M$GG|yGX>1{fl`B2Kw5cJ1r&-N~tEqD#ezP{-m zmo~hWJoG_-%=MIi_X^}^fa|`YKa_v>P3%tu5cCJ_cIflxHY#oq^v7yX`FGz;k%pi@ zmV3&-OYMxm3i@NAr~JD&U9D(F&>wR><==&U#S)EirTn|FuZ93>9K03mt0Ca@Hs#-i z*Es=UTq*x9^||UOe$nZk^6$dFNLd01 zx#NS5fBgL7vcJUfLB~H{y11PGaD345k8fUF>?_9yZCQTtV#d|{x{41v{_)w18Lt)K z_@Lt-H`e@Djt@HiacV8sE6pg!2Oa+ifa8OXe+0nsL0jRc))xM7e9-ZaQ)>%qp$4?6yFeT{!OKIr&I ztHwVZAGD4-wYKOF#|It%IJUOLIUOIgR(rVSzjA!g>TXYse>gs9J(yZoJ?QwL6=VP$ zAGGESfa8N!si{BoSB?)_*QWk(9bTzrU#0#K;P{}mbLtNPjt^RGr~VM&_@MQ9>JI^q z4_eWu{%{F%6d#MJKU{}dP4-pl4*`x3+Ru>sLxAIh_DrPy5a9TreHp1g1UNou??>tn zmoT5pzIwRkzbf%T*;mKbejy(-cYM%3nNw?v{&0NIUYu5qe>gs9zt8nG{^9tbJw>0a z@ejub?OVD&#y_mjWnZ;w{G-GNWnYC+;`pFFUIB1?&_1#NI6i2vT4P+rJMC1?abJQ#$UfZ)ir+t<3c)3K@=Lsn5F8CuUVLleqIeL{S@j=;FX_N?Xe9#{H z060EKpFT{3060F#D1d;%AC3>&E@|>ddMjBnfj>%oP_|Ft561@?%|IP#T=~nhJ|__P z!|_3v2>jvrpl!GYe`K#gs)Ky)w#t5n^nryxo=A~Ve|g}KW?KdRxFbbh*}8b$v|g!> z11VnX^R;2tIzD(I;P~K$fa8M&D0ZU9RRM~f==fj(ik;~AU;&E$IFq8`2Esgde9%A` z?T!z6|L$kB0Qk!(Zm@k%{;R*QCs`dI^!eJa)N`qh5BhxVpq@*8BJCXT&gHMvbE%FG z`h4x**K?_k5BhxVXEe_}uC!kHeC;30P88typwHL-*~R5vvh`D16M4QCUHezE6IFuo zL46>N@x}iuJ5d1Rg8;ZMTqVM|l0N57asYoOJ5eRPwqtJ;fj^d=D1h-n{1wmF3JCkJ zNF1m`_n$Y$l^BOR;!z?1j1Qs&_s};1#s>j#*S&x+uH>iY`C0*CT+t??#Nl1wZ;5vl|K%oyv{E1 zoyZ@uWDFdy{BdGZwvU15D}S7sls-4`PUR2VR|YyY|J5y1(&x@!a(mSu>!+m8oxh}0 z^Ix4wS+kwLVVUCa+7t-K-<%58Lg|UlRD^mZ^8C4V=Fu@W=Wo zI$e=F;qmlWfd{1~oWCUS$BC4`-T6xbe>{;gzB_+O;E$3cUjE&{A9tjz^3Gop^aps5 zZ{_?YL4SZh2snR9;ExlNY$W8eV3REd{x~zaoq+HE2>em<>C3+xrt>XRbj_o!e@W0E<$i+;^@rbTSbu;A`B%dol(pK{C2K`}u z?))XGKcqikU$NPpzoel*+=IjV12#kIb1sc&tD-+(UlH*AA3=YVaV7t5&>z<4%#@D5 z3i<=~RjPwaJglX1{v7rd0pI@-^au7|5%B#VVU*l5*5|(eqcN@+ z_&^;O>JP6B!}{YuT1$NYM`K*cJLv5Ajd3M^qVwrbR5q|acLx3doXGbZ$iEwaGkF!2 ze>dol^;24ReJ?=JAGb_NpZg|(pg+8}`|g3DKdjGvTS3qtCnhflq>+>P!Ry{Df0av0r33>z6B%j$B9Yj#i99*8AZE9bv%)FI{BuJz#j+F z-YDPA5%k9$>3##>=8?vgEi&!D`j6`=L!$nmK8G9EB?5n($oCuQjC@Q&NF{`;1AlOsN8MU;18YuaOhezE4_W#bx>gIo(T+8a^!X<)E%A?o9jhYkFZqQ_ zOZ;PZM{!Q;kC97D{G;DdWYqfO)q4K?@(v9ve24aze7BxIe|=Nt4~&V$#d`ky@{aW& zrwQ)7=P2>m6@OTN1YmcEh861IG>Ih+cBE3BKXI}88IE?OYpp*PtDoWJj+C(V$71y} z9PLOuTYoH8Kf}R})VB4yKA!{*r!2ir)HTr1}{y?`Z#(^C!Mq^IyHb zNnQi%kMGv}SC@C>XR!WQtog6{9U4|yfBaU>f3>?KUxxL^FSW|~!Gj(7cdb9hTE)+B zv?G6s^CzZe*YJ6{!@zmg9|1Vpp<$KtCkEhPhlW+wAE`e~Me^@jf295p(C=s`hxJG5 z4*{2VEEzJD+PM^hcX ze=qq{a&W&-U{xF|!sBF;h zs0VEU1OPTe8duUE0XUdOiLHzcl|Rb3l3lW)^2f_*B-)bMQ2FC%8oRcAHq`tj2Rj<= zww89({3Woj(%9Ahk}EF_>#k;$EwTWVIVc-00IzS-thLp5<)t_0CH(S^=DF>}D=#f} zdq1rVwk)r_l>K4rRcncD(JRG+0uH9t$JXmWJSd>7SL~L^1}~=-&K7bSJ}yygYS;_` zIEYmg#x|{R0p&Y(W8DQnyInx|4xRL-r7xg-2iJC#p#BiRaiz*%-&E8DfWRM@gFjG$ z`U60NKXm?s`U61VkKN#?T1ViIgJ3lPeyhMA9zcUf20W_l$36i7m1d1vtOxo^i!L2C@?c zIKSwgam7D=O?INc{9e}a@VMe11KEiJoWJDQxZ)pME-m{qoO|cgxZ)pIURvxD=NCOT zuK355mlnIk`9&WdSNtQ4E9V#8Gp_hY0Gwa+=5fV80^t0jJR4^-P+dt_(Ihm_ivW|*xJ_oNTbAKSNdaXTk|6T&M&&PJ*+>RUvz8xZqBHnKb%+X zt~RF$fWjZnFZw`R``d&5aDLIRwjWO=3V%4i=mTxa^vEC1FM3z|IRb${oL_Wn`$YnQ zKb(VZZJRP3C8!7W<<2kq%ILePjuJn2e$nrYQl{fO3V%4i=qsa?>5G9soL_WpTk(&; zAI>kjwN05G`NR48?rJOk5oWFPi$2g+{38I)FZ$KC_O}PX`9&XSEB+DYx$}$O)uv2$ ze~3Sx+$>9GYn#&qK;aMPHeB0Q{3FbB=NEltlrr7@p}#`@E817z8Kq2jf4B}=WUq`; zrn^5(UeX_H+ngru4*||Ey0ty5Kb!~ht~O=5`$Hw1U-W^t;vYeOApaHL>Z@(VKZ5>n ze$fZo+TR}Zhx3cx)gIO#&M&&P&1r%@FZ-{YGjna5G97PK;vdd0`pPI}I@;>pEM=Tu z^gE-9e+2yj{-FMNWt1`lJBE2EU@ z&2c6D@tsl1^u?eioL}^nQOfiPIKSxH_OSkNe$lOM#Xo}nDC25ae>lJB18q(d)KTV# z^NW79O_>g$*jHyaYqUSm*8cXOKVV;RB;M5?)*nx9mi_>L@UMd5Rp#KZ{?anXyol(kk0A<~Ee$iJ(DboS)J8a2Ff2?hDng9svuDl=M56X<7KgxPFtUqS9 z$iwnLn?lk9PIWM+ljb00dIX$b^no_TDuD7G&Myl7NI>`wd5X5SIZaTad`DT^6`M%w zmDhF-bId5pbdM_m%zp*oJEPALXz&N8QZ5nr!}&$mwpB;q59b&4TLt}5c`&_h;g6Xu z^4$5$1Aj1ASZ%8Ye~6vkx()pyKEJCS`ort4Jdp0eFr95PxX%ON{GuKuVV*m$n#WaR zo^y#tTWKT){y3R(x2<0dDeo>G1U?zl0=Q$Ykhx3bi);9Eq z{M2`~HP3_oaDGv*3_*X`7WG;Z^oR3{di4qV!}&$MUIqQ}WXka86)xxx<`;$LT-#Rs zqoF@&e!t396q+;W58CYjxb6Z7`h)pJ0dS=T5cCIg8v@|k4j|}{vzvAPgKQw6=nv)> zRUMS+p0yrVI{!h&X#&(ZC_#>D0?aQ8fUG8f^IU($Jdgm$g913uT?Zu>N>HYIo(o`p zQPrXNM`K*+{0G??CBnFx?^1ZYx?@cGJU<1cFdt8?A$3E*Qf04rSz50sGp2*6VJ?ga3G5`{nJyA&R;?j7R; zK;e(suHu7-$D}|0qMjd|>ORgAPml3|5`_oXbtybvJv=7;@ptw7$5fZX19kAR_~9D= znC-quV8<9Ai@#j&|CsMmc)Yq{O#0))RevmX-zD&~HXnW}bSXSu{cmmQkH4t- zug-NTJWxXV;}2{8tCgGQo~(jTdH)lsuu z#Rm_MNq+=js;l_m(__*f0a({neDLs?^hfFs{nb=g>+aq$KHML!gTe!CCH;~5L%@8O z!UJt3{gL`Zz*3jO179xvk@`cxN|(X|Z6*DY`a{6EE`bV{->J%YPKs2V8<9Ai+@=2U(I)AGi(^+1Fc*1$5K~T#?RV(;5&-`Sm{!D zpsl1oK3wA;=eiUgXe;TDU#|CmtaK?n&>zwtAFlC_rLL@{9bg)^%m|9UhbZXpAex2ltLif24Wt{-E%{xRU;8 zj4Roq8^-wXJXeXOuB_LewfVqzly!HdOW}dG;=@^>RN`D$7V^*9d_-WSE1P=57#|T> z>QZ>%t)xHFxo!Q`d{=RUy<@UV(z$JahxGa3G3k$VZd<@qm%;<uX3J=sF{gLtr3jlw>W=2~{f24a%1k87(2ltN2-c6%K zz-(6$tixl{9|4%^Dn9u1nDj>g)^!ynJ3J=+5%^=OD_slzpxxf&kMvg3AAvvSyNVBP z7?U;#{IS%f@W7W#e+2$m=~8&0t@w!iajsD!@W)D{js}0oYp`QXZx#3hJjgoAcNG0G z+xYUpAI-K3{IRalx`98Y-lIP}c4H5Q={(pl(s60h35$E|wm8ZyKH?9l^m8VF+ zd}A&78|5hyu+&(6Zj+rTpt)XsO?IMybBz`5Y2_($iLxfbbpBJ>iLRrpq5%G~=D(Wn zmUUMp!ni6cHGrpOC+e@l+794rvJ(ZYt85^k=#Qz&9{|EAnXQb|1m-J8HG!qdY655{ zYKR6N6u|XLz`4qt0*+;1rE;wR+KDQ$RM{CoV_b>Py>BV#kJ)4at(whV%K4 zpg)$nvif}fqoF@!Cwl)?&>yqOvh@C|pg(MjdjD0>AM29!>icVh{+LSkuFro2HkeHo zviDyFU_RN@-hUN0Qe)Ff*95x*0tV$75HN|DPixw3j8sZ zw6phL1^!r<)VB9ug?T=e{0!cI74!$}E54QYUj_YPeeV5N4gDc~?)_J(KfGQ^pL_pR z&>yg`(w7hGkJ47d`osF%`>%rjfPKZT_5Q1d{@6~y`>%rjfXzU_`>%rjfPIxl$*}$? z<7!xcSf6|URnQ->uTmWs>JN|hVf_L7iY2`NDvXkINuPWFRRGEy9M&J!=iYx6?2`GU z&%OVuF|H`g(N@FyV=ApBKL61eSJLO+f7KXQ(&yfPm9ju-Fj${^|5X6ylRo#J?Eoz0 zRdiT?tR#Kz^B+NfoJ%XU_g@A5;kDiSuY&%tKKK5spg-o5KKK6ipeANB;|%MMsjfK6 z=RZP;b&1uy|0?JY^Pu-%1^r=t?)_Imf6OO+?)_H{{ULqs{Z~PMtaQcaK5vkQk1UyU z$pY~Hs{qhufX~ur8UjFl4#1}%0)Nayp9A2&rD6S1b~*{5U1IyH?2Qr-{8x0-V)Yr; zAGEIk`20tMKgy0R{8iwOdFXSL@EMc9A4|~Z0DS%<@W%@5L;&SGik*0_OP6;3^1vS} zT{^*YTQ&ION2!F=UEq)Tf&9CT{?Pm|;2sR~+<^Q1LcpUW&hu1<$5oi;{wt5fz#pX! zY3DG{%Ufx*hk0(`!uc^j5Px_^1^qEUkbl>6FzAo@f%wC-Ht3J}f%!vzhM+&@2jUO! z%#8YDAg=XV67|R6vb46#`w{iWK>XqLD(a7c_`@q))E@)$hvFYme+{M4ETvx9j4BkGTV_`~NvqW&0&KYaejv`g8o0UU-<=xBZa1*A=HHzi$kI1(SIxgW zKahXdz-Mdz-KBy2y9T~k^Y5+@)jepDzSmoTf{TlyR8ql!H*>rc;_{aP}{$1z4I#}~x%?{|;#anTjEFP`- zucij_?>hh0!J7YS-9TOg=fB!r;~!H4`5B!5s$b(Dvjcf1od4?b8vmFd$d}>#SFhLn zS4#u=cb)(0ztsF!D+Bp=o&PGeuK45JK>l6lzY4(0fR0_XmEs=(SQ^NSW^f@4df|uCg{{30;UFZ>>~G$;vcC$1gsm#8|D00sXtso{wwFdO8wzFfWyHIRST`LFsl{;_T#|E}|2 z?XK~Use$~W&VO~V=Dz}e&|&KQS4V69tNDTayUu@gu%16(8qlypTSTv$604$}qa{jBvxYBnx4}D`?>C2tDzA>)cR-7j2!Q#JKms;2P zuNvb@{qfvJ#Xr(1Y<1M^Kt1UER{@wGsL!4MDgaAqlsNxYI=Agn;&COrB%Rw9a4wBR z=f6tlwp~JF*ZHr~xorVU1C4g)ze?w}1;D;aV^{Hyebrk#n`V^rUj?AdLD_HtSeIt4 z^Iz=?{@sD*x$|G`3;x}-GWcePeZjw*))L{)9T~fBOa~y8wh5*|P?(;Cu*Ci{%qa@7psbrUUTs7voEE$i) zFwf_c?c?0IVV*A)YYDT#{oxrU`^xu!gwcL3S!AA3AFLB+CE0MEgGXy(tfgf2dDi}_ z@)WtPWG6b0@Y%XAd^TH_@=W}y@)UWL$QJclaWmx);*84w}V1)zVDhl8!*@=29+U)?i?y5u>SK5EYl^Vc< z_5P2Qf%ac)7@0!sX27RpN{wEwCxuC)J(T&og=KbD~G^j6w`75HO+U;5m6 z)izZ%(FCU64;Z{3@bRiYnt%1{Rev;r|4{Wu6ZqY#KbpYrSN+ih{-o-U{e9_k=ZE{> zRe$X7OP@PG+@DnavA-{U?)-4SU-ifSzVx~C!~Jg6AN%{#=gtrJYgK>j?@OOMKirn8 zKOXB#pF2O?ntK1ovwi7v=Z6b4@kU?z-1*@G@N8fDocZCz23+AZ4Ib-Lcpw4Zu>RQJ zmp=Es;X!}w>`R~frt!cZoBGn{%nzqJIIdL3+c!#|Ge2BeuLNxBKgY^_|9Rk#oqg$Z z-|_NC969}Xn~ ze>~QgK4*S70D(XD_qAU6KKd}vclM>veS>`fHube8ZrM1jKi zT}}5p2maXEmp)%k_dADqzP~ShzA!PYKOXB#pU+M35&7fUzW8H0-R~Uq#~Xd=^SKE= ztOvFFJlmH(UzixyACL9LYRePD`eT1z`h0a_Sbyy7OP{aVsQ5?FADjBp=UX-^{t@)Y z+c%1x*KFj&{h_zo)R#VAP4_zo{jswz3t&0j?;P~U{=W1%?|0UkSoU8%)<1{?3WM^Oce9cBNPGek2pKsYHjtaorH%gzc*(g?Pj4SE$Row5aItqX6>`R|7<9=rW z$1>yW?@OOAOo(fPUGi98`h0GJZrr0;(pzz@S zzU=k_gzu0(FF^T@O?~O}0u&y6`$p+=13`am>PvqZ2=>*^z7&h&=K%nJP_a0E9)QRC z(&vt!2mSGEUpmV1^PoT8=u4kFejfD4vwdkb$IpZQ0Dr)2MdhRs7)9I-c$-KloOTp9lWf-a#_<7(D@F44O{5<%tcJ}q< zj-Lnqa9cTk9{A(!8{N9Y{IMzZhvVmA*6vI_==ga6U^8%BIes31$I>Y2rTmFuo_kz5 zejfPajXV-bBd|rw{CGBvUB}NG^IW6d@$;sg$hUI*JdF08ea$Gx&x8K(9CZ9V=#RH= z^sE(s1pToo&2z`kgZ|i=RtD!U3HoDyT1%Y2B*dC(uv_O)I)ejfD48)=1e z{5c698q%p^w$`=jnb<0OXHD zAFAii)BT(PC_@hIs`; z-Ou?0=4TMV^-4gxpYsRI&mf@ak90pLN>G0sy07NHO80Z34$6>2N9y^HbU!EF>O}&_ z>-mql9`(l$n4du<%K7tjKj#mapFwr7KlE1Ve$F2-KLdbW_569dpA#i0Lk?YA^IxU= zIZ+4o$D#A}{71T<6K_TSIP^k2|1qcgitrtjA#Pn6!s&iad^u%E1k(MSXe;WE2&DTt zQG)sdK(VjV{hX+SG6X=euhRXTcq{S;fWjZ?eolM`^#_15&(r;!_;TtG_lI6L-5-Uv zq6~3=2uSyHq6GDa`@3&X>pbSA3&X>pbP;J#ufDk>fi%F z7+2IEcq={tgmFdvf$!i0Kp0olANX=Un&XOmj<(|CP}+s0j#||Xc_={{5`lC-C+eUK zi9oua6K_Qs0-$_Hx}Ou@K^X!dd(x}Osz zC_@17J6wXcDC*z?Kv{Rw{hW9!%8*0fulaq_{has?$`JG*$CbZ>7Baq^j|ikYC(%}v zArVOTbD{)|aRB8z(*2yMgXTMc@*U}ZPP`QjegNe=(*2zH4$2S!;X9~5@a24XT=~nX zKhRcuc)fC4#S(!((*2ydjM7p0dzg1eV1f=^p>vt6Y74BDK<^J-(AI-K3{Be3) zY~8>gqq;A(*&iBk>3+`KgJA(k_jBex4?wz~Gmnxm&(r;!d0d5gp6=(&BQebLbU!ER z(AaIv^ElcQpX+sDUvVV9*ZkmA$}?(vojA=o_>wm8tJ%~2oG77Ld!XjOO80Z34$boo z^?X#ipA&7RmEl0mf0gd%#CK>dc}er!ea>YEU#`_>dz~NYe$KpJJt8|%CDQ$zD4`YZ z>#`FCr29Ehht|aJ%T9D1aTWb@*@*(?bT2Z#L+kGMWhV+q_jBUQwNigwcA|jh+Wv^_ zL;>l3PLvQEJR&>MB}g2o<3j!*)u65Tz*H;5lJ4ikcZj0`knZQimy6XJ7y?k@C5%XllXb7NdZKJQjFk?!Zjm&*dUu>wt-;kF8-`#DiU zR>mi){z&(8qOD|?{ASf3(>;o>p0E02p+|EMZzZemsi;3_AmTe@Cq7v9N4lRAUoOk? z(^Y>oZP6R5{z&(8qJ*s1ORD}z_j96-VDEbD(lo|f$wE%UM*#2V1Ynyw01G{u-}rJq zFcJ%Y4BPF6KX^Zi(z4QmA3cg&5CGba6;{^WT z{hTTxRTTIGJeca>1FswTgMgJ+;E%c9?F4R4S}pL$LT@{P@ucnoe^?K`rF;N!IOMRIXEhn2>L^B1^bFX&>!GI)&cv9K+qqs zuL!`tB9Qt+bu`;5^@o6_K2QDOx9YIjVPCP?8~Q^%2>Xga&>yfFSOWGHfuKKNUlD+n zK_KXl=C}&_!}=Wd6-xyD0sD%71^bFXLw{(r!@f#&6#aqw&shTY6@f5HR(qOJ<4KGQ_8qA*8W@qs!x&sAbNtqjNV%5W?LqiHQUmGpUITxs<=mR6s} zxZ*;EI<#H|{b7B+C#`S+nCnrPqphUR1F)c5jxkEKiU$3$+|#=Imhv-1{jr)>>hZKv z2mR4p+k^hFKHrnrAn1>|9)&r+gAZ8xY+bLrVw_`%aRM-^JESqL#8Ck_y-lokEU{Y9 zA59(%`osEsPh!rXKjwNA=4dPF^M?K)Eu*c(&Ov`H>%MJ_E9vvV2CK;e7*7^J0Meb_ zsKYix`Eu*?J=w~*IXB5%Pj<<}*RskL6@1zNP#OUa$1J ztGf9eC1_xJy%Ny0MH~D5-Zvh)Lg%=IYD(N@yuVV*Daq$b9bJ`eMJIq8qLl%FBakJX+O z%Xre~L4PzgPS79L=kV|19S#Nk0UqRAfd>gR^aq7GzFhh|=#SB!^!cfz&x8Iry^X>g zCHOcL^hZ+<2K_PJW9nq|ARmW<{(#Lu05$`GhW=n;YK#&-4h8+O++$*D%t1a-qSz&? zJtn3`2|iE<^AvepF)=mj-~+Aud?;DWuLy)uGM!4mz8V5x zU$F#y$U^|^D+2I3CxG)@G(kUu>d<#I#+AMt_EjoT_@miYjd7*cg?+^mjd9gs9k8#` zUt#t6;Qos)y68i&uL!`t8UkQ5d=LPAGWp9>3F>nIu&)w;???d06#$Mal>iSW0QMCD z*jEX_cO(Gz6#>{+3BY=l0N7Wl1Rwg2QsO}Z)aL-WUX?ob5!izn1%T_70nR}HoP#Ry z(+n*1ZX+;`S*tp(`NuA?+`ECmTgbx#fcBMv)!sF!1Rq+jN{LGdP@e@ zK8QN_KwI7PK?Bt10QO*|20;6&ycOev_zpf~Ul~~FG0cay;sbTOl}jx57#Kub@qxG6 ze7Q@k_O4GQ_`rAU$pC%Gs)G--)sq>ZJ_oP|tOnqPky9BU4+0<$s>D?vD&N8QAikUr z)bYs-jKUWV;1swPz^>MpGjMtvUfyI9#{YXj<;^h z0Br^Uv>8<5!3-?)_7E6{m7zMG$-r{&P6BU@@qxE`Cj+a{=PJPmzGM5SD_@4~qdNFN zTiu%h>T>{lU@ZZ7VdNVbpnU~^_LWM!nStrvTiC3MFXsbwT-SCTqrIOHI0bzUU{~v& z44mHf!&HI~wAEKKz&MWT-~(;-lMGOw1K0!062S4+_?YXUEee3Ps7lquN>_e*8~5*`1RrRteHmbU zP<8Nuwt6Z9;12>|90JE%uVsK52LLsWN_5t{4)}KofYk`x*Sb3c;6Vc5K?1v4k7r=D z$Njq~!3VzM#SAc(syg_T>{$R0BBPI+g*(wgFH( ztHjw1Ec6~F0BeZ=cyPj%!@eQ_`-;G>*5@(+`zn;Uw)F=YfPF>4b(|mhX$D|l5%62R zFmlaC*Fir60QwnF$N7;@XJ8uo9Du(Z`aA=}ZS{NxU|)sSy{~oI)vg2f6#@6hk=E@Q zfPF>4J$StJg$&Rq1AsmmwbgSOVEi0_M+x+K28PGgP1m?`*jK59M&h-t-_8JH6R8f3 z-SZ>g&H(Hy0v_!zjJ%Zr`mX@cf2FqC{7+m5pW}7G9~l^4uP)hC+A6Ma*S2oU zz;a_vJU{Ya2B^NS6j&f0Dt_m(pF8IVI~8ox1CBQ_&{5I zJ_Dl-yW~jgdl{H+STe_3f13fuCQvzJ6ZoqmtsAa$<&2-Jtz<2MKQb_EUp-!VbwsQ0g^{~6z&H*{*iJk@@^}WOq0a%>vIKu*VAvMDqU$DKB%^m{y0DKxeQE0pR28;Sim0{7}hvH z&A{nxltXA;KG0UzY;hgn!BmHI)RESwGXVY|VC8kZ^=JkdO9f!9cBJ)u1{gmFV08!n z*y=jqZzoXn;O!Z}{;O0%3KIU^48Ud}V10gmt~YYWc(a}y$0Zq3^e@=yINOGx&-X2R6_b3`Xd9wZT000ApXG;_I|uD@}mrJ zKZCwQ`uzOJB~z}0@pAyMukgUyep?3UlL0`V41is&2QzSbn??z}IBKhBGB7-@-pPRV zIXy)valEztpSyDI{XiYCeFz+B-J1c}R|M#dQXSvO0R2}0=)VH6tMz6ER@024#|r@T z#|^Gycn;o^0qb*m)llMvk*{Qc`(#iD7A;*Q=8mus){;5+#neTH8uneUD2YtBXUfXZzxpM1sdTUYQg^_(3;QlMrLH{m*^CM4X zU^+1lJ;JKvwG7aQthN$I?P_(lyN=V_q|fPfMhWPT3=H$&;~ALli8<-nMv3FC7c;=U zYHBM!&{kL6SiVE-Oz*ks_;?2BZ&zDMpYLitl!4V`0nkH_63`zR7`7QM>X$mC&lv?k zi5Et0&cIyK=Zv2NI6rcK1{gm_<%~3_j$;{MY(j0t2YT>q22O7qPbK(3TV4M#S3YcC zeJ%sm=Zu)31pHSS;2ub|6(4A;pJssZa{!FWsE%uHavhB00AL&k0Q^@OSWT8CBR>G3 zKQb_Ei$0$L>vKkzPy+s|Y1hI1qNs!MC;*5LW?(v5$c$jAjxS_@F)x&0%nJbgR~b0H z?K%RCk^zAJ$iT4Oe$&5j<<{qn#GwTIR~g{mTKrYI`wVUM?F=j=HNj|~>Ub*yj1Qs& z^CMT?T)sofi;+>) z@yQG@mWmRLr2>HeDg)SmMS#&)0MH*97}kTA3`!kRklcZd67XMTfP1#_SFjnVQjfGA z%mDnm1je&Ie>`6 z{2ajfkvB5{`-%V~?yBRu9j*iR6#@8=3BZ4q0oYfmgx(7JBLl;JhM#1>`kXrl@K^9( z&A5)?wz@L|jGv=&*jK4P9?k&lD*~{u2*7`p0q$>C3H2cK$E~HU)aTac@Ly%X`kcEg z@K^9(Wng$*eJ=xypQ8@gS1bYk$N=ms0*FlzfdA@Wx^nJsR|!5auJ&c%0{>M8tk2W^ zgJ`SQGB7*`JO9dcaDThLoDbA-cLumm20*%h5O4K(2DranCA2a?e`J9B+W{0`-xar& zI=Jk>zS8Og|5XNt*QW3NNo+1F;->$a0=wFw%l0N4yBz?!t85s79 z-k$;MbMACP3HYxvFub;(%>d))sGPg0RLAxI#+7sb6#(vU*IRuq1Ki)P5@Hd^ zkbjy1>vQf3!(YLFwbOMB^Wdj5!1y`p;LbCY0DokFd({ANf4ko5`3!J>yGn?ip+9bS z9mD?i+cRK&&K-34EBLQ6Fl;kCmjTAl_2qn!KQh4m?Etv1Pj7Y8E?3U|?J6Nl2Kpld z!?B5PXTbWLyAAPI@Ly$M*uL8Q%dUg_+x6wL$iN>N;2uZ-+~2OZdNKpt->wp}`k+5D zFxt@P@L&DgQpX>#1oyY&ui(GRz+8$I64O{p|os%(`hR~Z<#+uzB+bkgVCDXTiR|B5T;-dg+> z_qXeUHRkn)26>Q`MyGnNYfRR*ljxxXEM1^-nBhV|gd3^;xc3n1yi z)}5uTq|ae95ZKk4$pH7atF5F`;lIj2GwuxkRR*ljxxZa)^|u)q*3LKlnk#qw9M%#` zfIl(-`-+vrz9O)z^;8C6UlDK#_^&bm`-*_|Is8|5l{%!){Z{Z_WnkFP@OTEezrB7r z_#*?buLzX3y5bYAgZtZ6Laht`RR&;R5rDrP4_HeNWx)E}JqZ6*28Mky7k#pPM;s;a zUu9r;T-~1m*jH&>X(Ymbl>yjS1gy{Dzsi91xko$vSO3nHU*NyWz;x2*o>AbB3=Ge~ zpJssj+tpTlpwF+FbsgL%qqdShhyN-A*5};cjuP-+WnkFX_k0E%Klkbb{`i#Z7+$Y# z&j9zgqjIls@Ly$sdy3Rn(&zABWx)E}t0??e85s79-t=i#?)W+Px8pm&9~l^4+rOOw z?r&FHi4EYt$^iFYsjZ~X;lKL#r4CXJ)^;%t{8t$m_8~u+0msj|zg_)tDg(njc-3cI z$7+K);lIiN_o}I_q|eWfd^rQ$->$b3JHvmKfnk69CBI(2BU%99j|>dk3=d|2``b~6 zt&ICx&t!o6`T$s;A8Eal0qb*HGVovB?aGH^6ZdAo@pJBPS6h7}1H<;!n;GE#c2sVQ z4F0Rnx(@Dv1i=06_^b0H_hi8O+*Tj_R~ZY wKJ-ThmK*x~{KyUWxN_@rs|on8GB6w;d@2KupJV?O)dct>1H&4pGv_+~9}KfIm;e9( literal 0 HcmV?d00001 diff --git a/source/sid/boing.sid b/source/sid/boing.sid new file mode 100644 index 0000000000000000000000000000000000000000..14392677f92161227a5d2a9772936470055d6060 GIT binary patch literal 289000 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 hrlsGJM7seeO=5(=Ab@YTcNnzo33wGrD>O? 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 literal 0 HcmV?d00001 diff --git a/source/triborg/triborg-bel.spin b/source/triborg/triborg-bel.spin new file mode 100644 index 0000000..c67e49c --- /dev/null +++ b/source/triborg/triborg-bel.spin @@ -0,0 +1,3098 @@ +{{ GR0KEY +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle │ +│ Copyright (c) 2010 Ingo Kripahle │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : Hive +Name : GRAPHICS-KEYBOARD +Chip : Bellatrix +Typ : Treiber +Version : 00 +Subversion : 01 +Funktion : Graphics + Keyboard + +Dieser Bellatrix-Code stellt Regnatix alle Graphics- und Keyboard-Funktionen +zur Verfügung. Der Speicher für den PASM-Code im hRAM wird nach dem Start der +Cogs als Heap verwendet. + +Logbuch : + +11-11-2011-dr235 - erste funktionsfähige version erstellt +23-11-2011-dr235 - keyboard-objekt in root-objekt eingefügt + - keyboard-pasm-code zu heap hinzugefügt + + + + + +}} +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 +_STACK = ($3000 + $3000 + 100) >> 2 + + +' +---------- +' | +------- system +' | | +---- version (änderungen) +' | | | +- subversion (hinzufügungen) +CHIP_VER = $00_01_01_01 + +CHIP_SPEC = gcon#b_tv|gcon#b_key|gcon#b_vec + +' hbeat --------+ +' clk -------+| +' /wr ------+|| +' /hs -----+||| +------------------------- /cs +' |||| | -------- d0..d7 +DB_IN = %00001001_00000000_00000000_00000000 'maske: dbus-eingabe +DB_OUT = %00001001_00000000_00000000_11111111 'maske: dbus-ausgabe + +M1 = %00000010_00000000_00000000_00000000 +M2 = %00000010_10000000_00000000_00000000 'busclk=1? & /cs=0? + +M3 = %00000000_00000000_00000000_00000000 +M4 = %00000010_00000000_00000000_00000000 'busclk=0? + +x_tiles = 16 +y_tiles = 12 + +paramcount = 14 +bit_base = $2000 +disp_base = $5000 + +max_x = x_tiles * 16 +max_y = y_tiles * 16 + +OBJ + + tv : "bel-tv" + gcon : "glob-con" 'globale konstanten + +VAR + + long bel_key 'letzter tastencode + long datadr 'adresse des heap + + long tv_status '0/1/2 = off/visible/invisible read-only + long tv_enable '0/? = off/on write-only + long tv_pins '%ppmmm = pins write-only + long tv_mode '%ccinp = chroma,interlace,ntsc/pal,swap write-only + long tv_screen 'pointer to screen (words) write-only + long tv_colors 'pointer to colors (longs) write-only + long tv_hc 'horizontal cells write-only + long tv_vc 'vertical cells write-only + long tv_hx 'horizontal cell expansion write-only + long tv_vx 'vertical cell expansion write-only + long tv_ho 'horizontal offset write-only + long tv_vo 'vertical offset write-only + long tv_broadcast 'broadcast frequency (Hz) write-only + long tv_auralcog 'aural fm cog write-only + + word screen[x_tiles * y_tiles] 'tilemap + long grcolors[64] 'farbregister + +CON ''------------------------------------------------- BELLATRIX + +PUB main | cmd,i,index 'chip: kommandointerpreter +''funktionsgruppe : chip +''funktion : kommandointerpreter +''eingabe : - +''ausgabe : - + + init_subsysteme 'bus/vga/keyboard/maus initialisieren + repeat + i := 0 + cmd := gc 'kommando empfangen + case cmd +' ---------------------------------------------- KEYBOARD + 1: key_stat '1: Tastaturstatus senden + 2: key_code '2: Tastaturzeichen senden + 4: key_spec '4: Statustasten ($100..$1FF) abfragen +' ---------------------------------------------- GRAPHICS + 'clear + 10: clear + 'copy(dest_ptr) + 11: copy(disp_base) + 'color(c) + 12: color(gc) + 'width(w) + 13: width(gc) + 'colorwidth(c, w) + 14: colorwidth(gc,gc) + 'plot(x, y) + 15: plot(gc,gc) + 'line(x, y) + 16: line(gc,gc) + 'arc(x, y, xr, yr, angle, anglestep, steps, arcmode) + 17: arc(gc,gc,gc,gc,gw,gw,gc,gc) + 'vec(x, y, vecscale, vecangle, vecdef_ptr) + 18: vec(gc,gc,gc,gw,datadr+gw) + 'vecarc(x, y, xr, yr, angle, vecscale, vecangle, vecdef_ptr) + 19: vecarc(gc,gc,gc,gc,gw,gc,gw,datadr+gw) + 'pix(x, y, pixrot, pixdef_ptr) + 20: pix(gc,gc,gc,datadr+gw) + 'pixarc(x, y, xr, yr, angle, pixrot, pixdef_ptr) + 21: pixarc(gc,gc,gc,gc,gw,gc,datadr+gw) + 'text(x, y, string_ptr) + 22: text(gc,gc,datadr+gw) + 'textarc(x, y, xr, yr, angle, string_ptr) + 23: textarc(gc,gc,gc,gc,gw,datadr+gw) + 'textmode(x_scale, y_scale, spacing, justification) + 24: textmode(gc,gc,gc,gc) + 'box(x, y, box_width, box_height) + 25: box(gc,gc,gc,gc) + 'quad(x1, y1, x2, y2, x3, y3, x4, y4) + 26: quad(gc,gc,gc,gc,gc,gc,gc,gc) + 'tri(x1, y1, x2, y2, x3, y3) + 27: tri(gc,gc,gc,gc,gc,gc) +' ---------------------------------------------- CHIP-MANAGMENT + 93: repeat 64 'colortab empfangen + grcolors[i++] := gl + 94: repeat x_tiles * y_tiles 'screen empfangen + screen[i++] := gw + 95: index := gw + repeat gw 'heap empfangen + byte[datadr+index+i++] := gc + 96: pw(@grdatend-datadr) 'heapgrösse senden + 97: setup(tv_hc, tv_vc, 0, 0, bit_base) 'dynamischer modus + 98: setup(tv_hc, tv_vc, 0, 0, disp_base) 'statischer modus + 99: reboot 'bellatrix neu starten + +PUB init_subsysteme | i,tx,ty 'chip: initialisierung des bellatrix-chips +''funktionsgruppe : chip +''funktion : - initialisierung des businterface +'' : - vga & keyboard-treiber starten +''eingabe : - +''ausgabe : - + + dira := db_in 'datenbus auf eingabe schalten + outa[gcon#bus_hs] := 1 'handshake inaktiv + keystart(gcon#b_keybd, gcon#b_keybc) 'tastaturport starten + + 'tv-treiber starten + longmove(@tv_status, @tvparams, paramcount) + tv_screen := @screen + tv_colors := @grcolors + tv.start(@tv_status) + + 'start and setup graphics + grstart +' setup(tv_hc, tv_vc, 0, 0, disp_base) 'bitmap_base = puffer in welchen gezeichnet wird +' clear + + datadr := (@grdat+4) & $FFFD 'ausgerichtete datenadresse berechnen + + +PUB pc(zeichen) 'chip: ein byte an regnatix senden +''funktionsgruppe : chip +''funktion : ein byte an regnatix senden +''eingabe : byte +''ausgabe : - + + waitpeq(M1,M2,0) 'busclk=1? & prop2=0? + dira := db_out 'datenbus auf ausgabe stellen + outa[7..0] := zeichen 'daten ausgeben + outa[gcon#bus_hs] := 0 'daten gültig + waitpeq(M3,M4,0) 'busclk=0? + dira := db_in 'bus freigeben + outa[gcon#bus_hs] := 1 'daten ungültig + +PUB gc : zeichen 'chip: ein byte von regnatix empfangen +''funktionsgruppe : chip +''funktion : ein byte von regnatix empfangen +''eingabe : - +''ausgabe : byte + + waitpeq(M1,M2,0) 'busclk=1? & prop2=0? + zeichen := ina[7..0] 'daten einlesen + outa[gcon#bus_hs] := 0 'daten quittieren + waitpeq(M3,M4,0) 'busclk=0? + outa[gcon#bus_hs] := 1 + + + +CON ''------------------------------------------------- SUBPROTOKOLL-FUNKTIONEN + +PUB pw(wert) 'sub: word senden +''funktionsgruppe : sub +''funktion : subprotokoll um einen long-wert an regnatix zu senden +''eingabe : 16bit wert der gesendet werden soll +''ausgabe : - +''busprotokoll : [put.byte1][put.byte2] +'' : [ hsb ][ ] + + pc(wert >> 8) + pc(wert) + +PUB gw:wert 'sub: word empfangen +''funktionsgruppe : sub +''funktion : subprotokoll um einen 16bit-wert von regnatix zu empfangen +''eingabe : - +''ausgabe : 16bit-wert der empfangen wurde +''busprotokoll : [get.byte1][get.byte2] +'' : [ hsb ][ lsb ] + + wert := gc << 8 + wert := wert + gc + +PUB gl:wert 'sub: long empfangen +''funktionsgruppe : sub +''funktion : subprotokoll um einen long-wert von regnatix zu empfangen +''eingabe : - +''ausgabe : 32bit-wert der empfangen wurde +''busprotokoll : [get.byte1][get.byte2][get.byte3][get.byte4] +'' : [ hsb ][ ][ ][ lsb ] + + wert := gc << 24 '32 bit empfangen hsb/lsb + wert := wert + gc << 16 + wert := wert + gc << 8 + wert := wert + gc + +CON ''------------------------------------------------- KEYBOARD-FUNKTIONEN + +PUB key_stat 'key: tastaturstatus abfragen + + pc(gotkey) + +PUB key_code 'key: tastencode abfragen + + bel_key := key + case bel_key + $c8: bel_key := $08 'backspace wandeln + pc(bel_key) + +PUB key_spec 'key: statustaten vom letzten tastencode abfragen + + pc(bel_key >> 8) + +DAT ''tv-parameter + +tvparams long 0 'status + long 1 'enable + long %010_0101 'pins New Board + long %0000 'mode +pscreen long 0 'screen +pcolors long 0 'colors + long x_tiles 'hc - horizontale tiles + long y_tiles 'vc - vertikale tiles + long 10 'hx + long 1 'vx + long 0 'ho + long 0 'vo + long 60_000_000 '_xinfreq<<4 'broadcast + long 0 'auralcog + +CON ' Keyboard-Objekt +''*************************************** +''* PS/2 Keyboard Driver v1.0.1 * +''* Author: Chip Gracey * +''* Copyright (c) 2004 Parallax, Inc. * +''* See end of file for terms of use. * +''*************************************** + +{-----------------REVISION HISTORY--------------------------------- + v1.0.1 - Updated 6/15/2006 to work with Propeller Tool 0.96 + ------------------------------------------------------------------} + +{-----------------KEYBOARD LAYOUT HISTORY-------------------------- + 2009-08-31 (Y-M-D) + Patch for german keyboard layout + Author: oog + Added german keyboard layout. + Original layout is commented as keyboard-us-en (US-English). + German layout is commented as keyboard-de (de = deutsch = "german"). + + Now there are two tables for keys with and without SHIFT-Key. + It should be easier to implement different international layouts. + However, it's bigger now and uses more memory. + + 2009-09-05 (Y-M-D) + Fixed bugs + - code bug on home-key fixed + new replace-codes for de_ae, de_ou and de_ue + + New + - Documentation of control-key bits + This should be helpful for translatíons of this driver into + different languages. + - table_alt_r + This table contains characters for the german "AltGr" key. + - AltGr+F1..F12 + Returns line-characters, selected from $90..$9f + + 2009-09-06 (Y-M-D) + Fixed bugs + - patch table_shift for "?" + + Differences to Parallax driver: + - Different codes for NumLock, CapsLock and ScrLock to avoid + conflict with german "ß" + Codes are defined as constants and easy to change + - Easy Cursor codes implemented to avoit conflict with the "Ä"-key + Easy Cursor codes are easy to understand, + for example "Cursor Left" is the character "←" + + ------------------------------------------------------------------} + +CON + de_ae = $A6 'replace code - not used on keyboard + de_oe = $A7 'replace code - not used on keyboard + de_ue = $A8 'replace code - not used on keyboard + lock = $BC 'Parallax used codes $DD, $DE and $DF + 'There was a conflict between $DF=NumLock="ß" + ScrLk = lock + CpsLk = lock+1 + NumLk = lock+2 + +' +'Uncomment one of the next constant blocks +' +' + +{{Parallax cursor codes start}} +{{ + CrsLt = $C0E4 + CrsRt = $C1E6 + CrsUp = $C2E8 + CrsDn = $C3E2 + CrsHm = $C4E7 'Conflict with key "Ä" ($C4) + CrsEn = $C5E1 + PgUp = $C6E9 + PgDn = $C7E3 + Bksp = $00C8 + Del = $C9EA + Ins = $CAE0 + Esc = $00CB + Apps = $CC00 + Power = $CD00 + Sleep = $CE00 + WkUp = $CF00 +}} +{{Parallax cursor codes end}} + + +{{Easy cursor codes start}} + + CrsLt = $02E4 '← + CrsRt = $03E6 '→ + CrsUp = $04E8 '↑ + CrsDn = $05E2 '↓ + CrsHm = $06E7 '◀ + CrsEn = $07E1 '▶ + PgUp = $A0E9 ' + PgDn = $A2E3 ' + Bksp = $00C8 'È + Del = $BAEA ' + Ins = $BBE0 ' + Esc = $001B ' + Apps = $CC00 'Ì + Power = $CD00 'Í + Sleep = $CE00 'Î + WkUp = $CF00 'Ï + +{{Easy cursor codes end}} + +VAR + + long cog + + long par_tail 'key buffer tail read/write (19 contiguous longs) + long par_head 'key buffer head read-only + long par_present 'keyboard present read-only + long par_states[8] 'key states (256 bits) read-only + long par_keys[8] 'key buffer (16 words) read-only (also used to pass initial parameters) + + +PUB keystart(dpin, cpin) : okay + +'' Start keyboard driver - starts a cog +'' returns false if no cog available +'' +'' dpin = data signal on PS/2 jack +'' cpin = clock signal on PS/2 jack +'' +'' use 100-ohm resistors between pins and jack +'' use 10K-ohm resistors to pull jack-side signals to VDD +'' connect jack-power to 5V, jack-gnd to VSS +'' +'' all lock-keys will be enabled, NumLock will be initially 'on', +'' and auto-repeat will be set to 15cps with a delay of .5s + + okay := startx(dpin, cpin, %0_000_000, %01_01000) + + +PUB startx(dpin, cpin, locks, auto) : okay + +'' Like start, but allows you to specify lock settings and auto-repeat +'' +'' locks = lock setup +'' bit 6 disallows shift-alphas (case set soley by CapsLock) +'' bits 5..3 disallow toggle of NumLock/CapsLock/ScrollLock state +'' bits 2..0 specify initial state of NumLock/CapsLock/ScrollLock +'' (eg. %0_001_100 = disallow ScrollLock, NumLock initially 'on') +'' +'' auto = auto-repeat setup +'' bits 6..5 specify delay (0=.25s, 1=.5s, 2=.75s, 3=1s) +'' bits 4..0 specify repeat rate (0=30cps..31=2cps) +'' (eg %01_00000 = .5s delay, 30cps repeat) + + longmove(@par_keys, @dpin, 4) + okay := cog := cognew(@entry, @par_tail) + 1 + + + +PUB present : truefalse + +'' Check if keyboard present - valid ~2s after start +'' returns t|f + + truefalse := -par_present + + +PUB key : keycode + +'' Get key (never waits) +'' returns key (0 if buffer empty) + + if par_tail <> par_head + keycode := par_keys.word[par_tail] + par_tail := ++par_tail & $F + + +PUB getkey : keycode + +'' Get next key (may wait for keypress) +'' returns key + + repeat until (keycode := key) + + +PUB newkey : keycode + +'' Clear buffer and get new key (always waits for keypress) +'' returns key + + par_tail := par_head + keycode := getkey + + +PUB gotkey : truefalse + +'' Check if any key in buffer +'' returns t|f + + truefalse := par_tail <> par_head + + +PUB clearkeys + +'' Clear key buffer + + par_tail := par_head + + +PUB keystate(k) : state + +'' Get the state of a particular key +'' returns t|f + + state := -(par_states[k >> 5] >> k & 1) + +CON ' Graphics-Objekt + +''*************************************** +''* Graphics Driver v1.0 * +''* Author: Chip Gracey * +''* Copyright (c) 2005 Parallax, Inc. * +''* See end of file for terms of use. * +''*************************************** + +'' +'' Theory of Operation: +'' +'' A cog is launched which processes commands via the PUB routines. +'' +'' Points, lines, arcs, sprites, text, and polygons are rasterized into +'' a specified stretch of memory which serves as a generic bitmap buffer. +'' +'' The bitmap can be displayed by the TV.SRC or VGA.SRC driver. +'' +'' See GRAPHICS_DEMO.SRC for usage example. +'' + +CON + + #1, _setup, _color, _width, _plot, _line, _arc, _vec, _vecarc, _pix, _pixarc, _text, _textarc, _textmode, _fill, _loop + +VAR + + long command + + long bitmap_base 'bitmap data + long bitmap_longs + word bases[32] + + long pixel_width 'pixel data + long slices[8] + + long text_xs, text_ys, text_sp, text_just 'text data (these 4 must be contiguous) + + +PUB grstart : okay + +'' Start graphics driver - starts a cog +'' returns false if no cog available + + fontptr := @font 'set font pointer (same for all instances) + cognew(@loop, @command) + 1 + +PUB setup(xtiles, ytiles, x_origin, y_origin, base_ptr) | bases_ptr, slices_ptr + +'' Set bitmap parameters +'' +'' xtiles - number of x tiles (tiles are 16x16 pixels each) +'' ytiles - number of y tiles +'' x_origin - relative-x center pixel +'' y_origin - relative-y center pixel +'' base_ptr - base address of bitmap + + setcommand(_loop, 0) 'make sure last command finished + + repeat bases_ptr from 0 to xtiles - 1 <# 31 'write bases + bases[bases_ptr] := base_ptr + bases_ptr * ytiles << 6 + + ytiles <<= 4 'adjust arguments and do setup command + y_origin := ytiles - y_origin - 1 + bases_ptr := @bases + slices_ptr := @slices + setcommand(_setup, @xtiles) + + bitmap_base := base_ptr 'retain high-level bitmap data + bitmap_longs := xtiles * ytiles + + +PUB clear + +'' Clear bitmap + + setcommand(_loop, 0) 'make sure last command finished + + longfill(bitmap_base, 0, bitmap_longs) 'clear bitmap + + +PUB copy(dest_ptr) + +'' Copy bitmap +'' use for double-buffered display (flicker-free) +'' +'' dest_ptr - base address of destination bitmap + + setcommand(_loop, 0) 'make sure last command finished + + longmove(dest_ptr, bitmap_base, bitmap_longs) 'copy bitmap + + +PUB color(c) + +'' Set pixel color to two-bit pattern +'' +'' c - color code in bits[1..0] + + setcommand(_color, @colors[c & 3]) 'set color + + +PUB width(w) | pixel_passes, r, i, p + +'' Set pixel width +'' actual width is w[3..0] + 1 +'' +'' w - 0..15 for round pixels, 16..31 for square pixels + + r := not w & $10 'determine pixel shape/width + w &= $F + pixel_width := w + pixel_passes := w >> 1 + 1 + + setcommand(_width, @w) 'do width command now to avoid updating slices when busy + + p := w ^ $F 'update slices to new shape/width + repeat i from 0 to w >> 1 + slices[i] := true >> (p << 1) << (p & $E) + if r and pixels[w] & |< i + p += 2 + if r and i == pixel_passes - 2 + p += 2 + + +PUB colorwidth(c, w) + +'' Set pixel color and width + + color(c) + width(w) + + +PUB plot(gx, gy) + +'' Plot point +'' +'' x,y - point + + setcommand(_plot, @gx) + + +PUB line(gx, gy) + +'' Draw a line to point +'' +'' x,y - endpoint + + setcommand(_line, @gx) + + +PUB arc(gx, gy, xr, yr, angle, anglestep, steps, arcmode) + +'' Draw an arc +'' +'' x,y - center of arc +'' xr,yr - radii of arc +'' angle - initial angle in bits[12..0] (0..$1FFF = 0°..359.956°) +'' anglestep - angle step in bits[12..0] +'' steps - number of steps (0 just leaves (x,y) at initial arc position) +'' arcmode - 0: plot point(s) +'' 1: line to point(s) +'' 2: line between points +'' 3: line from point(s) to center + + setcommand(_arc, @gx) + + +PUB vec(gx, gy, vecscale, vecangle, vecdef_ptr) + +'' Draw a vector sprite +'' +'' x,y - center of vector sprite +'' vecscale - scale of vector sprite ($100 = 1x) +'' vecangle - rotation angle of vector sprite in bits[12..0] +'' vecdef_ptr - address of vector sprite definition +'' +'' +'' Vector sprite definition: +'' +'' word $8000|$4000+angle 'vector mode + 13-bit angle (mode: $4000=plot, $8000=line) +'' word length 'vector length +'' ... 'more vectors +'' ... +'' word 0 'end of definition + + setcommand(_vec, @gx) + + +PUB vecarc(gx, gy, xr, yr, angle, vecscale, vecangle, vecdef_ptr) + +'' Draw a vector sprite at an arc position +'' +'' x,y - center of arc +'' xr,yr - radii of arc +'' angle - angle in bits[12..0] (0..$1FFF = 0°..359.956°) +'' vecscale - scale of vector sprite ($100 = 1x) +'' vecangle - rotation angle of vector sprite in bits[12..0] +'' vecdef_ptr - address of vector sprite definition + + setcommand(_vecarc, @gx) + + +PUB pix(gx, gy, pixrot, pixdef_ptr) + +'' Draw a pixel sprite +'' +'' x,y - center of vector sprite +'' pixrot - 0: 0°, 1: 90°, 2: 180°, 3: 270°, +4: mirror +'' pixdef_ptr - address of pixel sprite definition +'' +'' +'' Pixel sprite definition: +'' +'' word 'word align, express dimensions and center, define pixels +'' byte xwords, ywords, xorigin, yorigin +'' word %%xxxxxxxx,%%xxxxxxxx +'' word %%xxxxxxxx,%%xxxxxxxx +'' word %%xxxxxxxx,%%xxxxxxxx +'' ... + + setcommand(_pix, @gx) + + +PUB pixarc(gx, gy, xr, yr, angle, pixrot, pixdef_ptr) + +'' Draw a pixel sprite at an arc position +'' +'' x,y - center of arc +'' xr,yr - radii of arc +'' angle - angle in bits[12..0] (0..$1FFF = 0°..359.956°) +'' pixrot - 0: 0°, 1: 90°, 2: 180°, 3: 270°, +4: mirror +'' pixdef_ptr - address of pixel sprite definition + + setcommand(_pixarc, @gx) + + +PUB text(gx, gy, string_ptr) | justx, justy + +'' Draw text +'' +'' x,y - text position (see textmode for sizing and justification) +'' string_ptr - address of zero-terminated string (it may be necessary to call .finish +'' immediately afterwards to prevent subsequent code from clobbering the +'' string as it is being drawn + + justify(string_ptr, @justx) 'justify string and draw text + setcommand(_text, @gx) + + +PUB textarc(gx, gy, xr, yr, angle, string_ptr) | justx, justy + +'' Draw text at an arc position +'' +'' x,y - center of arc +'' xr,yr - radii of arc +'' angle - angle in bits[12..0] (0..$1FFF = 0°..359.956°) +'' string_ptr - address of zero-terminated string (it may be necessary to call .finish +'' immediately afterwards to prevent subsequent code from clobbering the +'' string as it is being drawn + + justify(string_ptr, @justx) 'justify string and draw text + setcommand(_textarc, @gx) + + +PUB textmode(x_scale, y_scale, spacing, justification) + +'' Set text size and justification +'' +'' x_scale - x character scale, should be 1+ +'' y_scale - y character scale, should be 1+ +'' spacing - character spacing, 6 is normal +'' justification - bits[1..0]: 0..3 = left, center, right, left +'' bits[3..2]: 0..3 = bottom, center, top, bottom + + longmove(@text_xs, @x_scale, 4) 'retain high-level text data + + setcommand(_textmode, @x_scale) 'set text mode + + +PUB box(gx, gy, box_width, box_height) | x2, y2, pmin, pmax + +'' Draw a box with round/square corners, according to pixel width +'' +'' x,y - box left, box bottom + + if box_width > pixel_width and box_height > pixel_width + + pmax := pixel_width - (pmin := pixel_width >> 1) 'get pixel-half-min and pixel-half-max + + gx += pmin 'adjust coordinates to accomodate width + gy += pmin + x2 := gx + box_width - 1 - pixel_width + y2 := gy + box_height - 1 - pixel_width + + plot(gx, gy) 'plot round/square corners + plot(gx, y2) + plot(x2, gy) + plot(x2, y2) + + fill(gx, y2 + pmax, 0, (x2 - gx) << 16, 0, 0, pmax) 'fill gaps + fill(gx, y, 0, (x2 - gx) << 16, 0, 0, pmin) + fill(gx - pmin, y2, 0, (x2 - gx + pixel_width) << 16, 0, 0, y2 - gy) + + +PUB quad(x1, y1, x2, y2, x3, y3, x4, y4) + +'' Draw a solid quadrilateral +'' vertices must be ordered clockwise or counter-clockwise + + tri(x1, y1, x2, y2, x3, y3) 'draw two triangle to make 4-sides polygon + tri(x3, y3, x4, y4, x1, y1) + + +PUB tri(x1, y1, x2, y2, x3, y3) | xy[2] + +'' Draw a solid triangle + +' reorder vertices by descending y + + case (y1 => y2) & %100 | (y2 => y3) & %010 | (y1 => y3) & %001 + %000: + longmove(@xy, @x1, 2) + longmove(@x1, @x3, 2) + longmove(@x3, @xy, 2) + %010: + longmove(@xy, @x1, 2) + longmove(@x1, @x2, 4) + longmove(@x3, @xy, 2) + %011: + longmove(@xy, @x1, 2) + longmove(@x1, @x2, 2) + longmove(@x2, @xy, 2) + %100: + longmove(@xy, @x3, 2) + longmove(@x2, @x1, 4) + longmove(@x1, @xy, 2) + %101: + longmove(@xy, @x2, 2) + longmove(@x2, @x3, 2) + longmove(@x3, @xy, 2) + +' draw triangle + + fill(x1, y1, (x3 - x1) << 16 / (y1 - y3 + 1), (x2 - x1) << 16 / (y1 - y2 + 1), (x3 - x2) << 16 / (y2 - y3 + 1), y1 - y2, y1 - y3) + + +PUB finish + +'' Wait for any current graphics command to finish +'' use this to insure that it is safe to manually manipulate the bitmap + + setcommand(_loop, 0) 'make sure last command finished + + +PRI fill(gx, gy, da, db, db2, linechange, lines_minus_1) + + setcommand(_fill, @gx) + + +PRI justify(string_ptr, justptr) | gx + + gx := (strsize(string_ptr) - 1) * text_xs * text_sp + text_xs * 5 - 1 + long[justptr] := -lookupz(text_just >> 2 & 3: 0, gx >> 1, gx, 0) + long[justptr][1] := -lookupz(text_just & 3: 0, text_ys << 3, text_ys << 4, 0) + + +PRI setcommand(cmd, argptr) + + command := cmd << 16 + argptr 'write command and pointer + repeat while command 'wait for command to be cleared, signifying receipt + + +CON + + ' Vector font primitives + + xa0 = %000 << 0 'x line start / arc center + xa1 = %001 << 0 + xa2 = %010 << 0 + xa3 = %011 << 0 + xa4 = %100 << 0 + xa5 = %101 << 0 + xa6 = %110 << 0 + xa7 = %111 << 0 + + ya0 = %0000 << 3 'y line start / arc center + ya1 = %0001 << 3 + ya2 = %0010 << 3 + ya3 = %0011 << 3 + ya4 = %0100 << 3 + ya5 = %0101 << 3 + ya6 = %0110 << 3 + ya7 = %0111 << 3 + ya8 = %1000 << 3 + ya9 = %1001 << 3 + yaA = %1010 << 3 + yaB = %1011 << 3 + yaC = %1100 << 3 + yaD = %1101 << 3 + yaE = %1110 << 3 + yaF = %1111 << 3 + + xb0 = %000 << 7 'x line end + xb1 = %001 << 7 + xb2 = %010 << 7 + xb3 = %011 << 7 + xb4 = %100 << 7 + xb5 = %101 << 7 + xb6 = %110 << 7 + xb7 = %111 << 7 + + yb0 = %0000 << 10 'y line end + yb1 = %0001 << 10 + yb2 = %0010 << 10 + yb3 = %0011 << 10 + yb4 = %0100 << 10 + yb5 = %0101 << 10 + yb6 = %0110 << 10 + yb7 = %0111 << 10 + yb8 = %1000 << 10 + yb9 = %1001 << 10 + ybA = %1010 << 10 + ybB = %1011 << 10 + ybC = %1100 << 10 + ybD = %1101 << 10 + ybE = %1110 << 10 + ybF = %1111 << 10 + + ax1 = %0 << 7 'x arc radius + ax2 = %1 << 7 + + ay1 = %00 << 8 'y arc radius + ay2 = %01 << 8 + ay3 = %10 << 8 + ay4 = %11 << 8 + + a0 = %0000 << 10 'arc start/length + a1 = %0001 << 10 'bits[1..0] = start (0..3 = 0°, 90°, 180°, 270°) + a2 = %0010 << 10 'bits[3..2] = length (0..3 = 360°, 270°, 180°, 90°) + a3 = %0011 << 10 + a4 = %0100 << 10 + a5 = %0101 << 10 + a6 = %0110 << 10 + a7 = %0111 << 10 + a8 = %1000 << 10 + a9 = %1001 << 10 + aA = %1010 << 10 + aB = %1011 << 10 + aC = %1100 << 10 + aD = %1101 << 10 + aE = %1110 << 10 + aF = %1111 << 10 + + fline = %0 << 14 'line command + farc = %1 << 14 'arc command + + more = %1 << 15 'another arc/line + + +DAT + +' Color codes + +colors long %%0000000000000000 + long %%1111111111111111 + long %%2222222222222222 + long %%3333333333333333 + +' Round pixel recipes + +pixels byte %00000000,%00000000,%00000000,%00000000 '0,1,2,3 + byte %00000000,%00000000,%00000010,%00000101 '4,5,6,7 + byte %00001010,%00001010,%00011010,%00011010 '8,9,A,B + byte %00110100,%00111010,%01110100,%01110100 'C,D,E,F + +' Vector font - standard ascii characters ($21-$7E) + +font word fline + xa2 + yaC + xb2 + yb7 + more '! + word fline + xa2 + ya5 + xb2 + yb4 + + word fline + xa1 + yaD + xb1 + ybC + more '" + word fline + xa3 + yaD + xb3 + ybC + + word fline + xa1 + yaA + xb1 + yb6 + more '# + word fline + xa3 + yaA + xb3 + yb6 + more + word fline + xa0 + ya9 + xb4 + yb9 + more + word fline + xa0 + ya7 + xb4 + yb7 + + word farc + xa2 + ya9 + a9 + ax2 + ay1 + more '$ + word farc + xa2 + ya7 + aB + ax2 + ay1 + more + word fline + xa0 + ya6 + xb2 + yb6 + more + word fline + xa2 + yaA + xb4 + ybA + more + word fline + xa2 + yaA + xb2 + ybB + more + word fline + xa2 + ya6 + xb2 + yb5 + + word farc + xa1 + yaA + a0 + ax1 + ay1 + more '% + word farc + xa3 + ya6 + a0 + ax1 + ay1 + more + word fline + xa0 + ya6 + xb4 + ybA + + word farc + xa2 + yaA + a7 + ax1 + ay1 + more '& + word farc + xa2 + ya7 + a5 + ax2 + ay2 + more + word fline + xa1 + yaA + xb4 + yb5 + + word fline + xa2 + yaD + xb2 + ybC ' ' + + word farc + xa3 + ya9 + aD + ax1 + ay4 + more '( + word farc + xa3 + ya7 + aE + ax1 + ay4 + more + word fline + xa2 + ya7 + xb2 + yb9 + + word farc + xa1 + ya9 + aC + ax1 + ay4 + more ') + word farc + xa1 + ya7 + aF + ax1 + ay4 + more + word fline + xa2 + ya7 + xb2 + yb9 + + word fline + xa4 + ya6 + xb0 + ybA + more '* + word fline + xa0 + ya6 + xb4 + ybA + more + word fline + xa2 + yaB + xb2 + yb5 + + word fline + xa0 + ya8 + xb4 + yb8 + more '+ + word fline + xa2 + yaA + xb2 + yb6 + + word fline + xa2 + ya4 + xb1 + yb3 ', + + word fline + xa0 + ya8 + xb4 + yb8 '- + + word fline + xa2 + ya5 + xb2 + yb4 '. + + word fline + xa0 + ya4 + xb4 + ybC '/ + + word farc + xa2 + ya8 + a0 + ax2 + ay4 '0 + + word fline + xa0 + ya4 + xb4 + yb4 + more '1 + word fline + xa2 + ya4 + xb2 + ybC + more + word fline + xa0 + yaA + xb2 + ybC + + word farc + xa2 + yaA + a8 + ax2 + ay2 + more '2 + word farc + xa2 + yaA + aF + ax2 + ay3 + more + word farc + xa2 + ya4 + aD + ax2 + ay3 + more + word fline + xa0 + ya4 + xb4 + yb4 + + word farc + xa2 + yaA + a7 + ax2 + ay2 + more '3 + word farc + xa2 + ya6 + a6 + ax2 + ay2 + + word fline + xa2 + yaC + xb0 + yb7 + more '4 + word fline + xa0 + ya7 + xb4 + yb7 + more + word fline + xa3 + ya4 + xb3 + yb8 + + word farc + xa2 + ya6 + aB + ax2 + ay2 + more '5 + word fline + xa4 + yaC + xb0 + ybC + more + word fline + xa0 + yaC + xb0 + yb8 + more + word fline + xa0 + ya8 + xb2 + yb8 + more + word fline + xa0 + ya4 + xb2 + yb4 + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more '6 + word farc + xa2 + ya8 + aD + ax2 + ay4 + more + word fline + xa0 + ya6 + xb0 + yb8 + more + word fline + xa2 + yaC + xb3 + ybC + + word fline + xa0 + yaC + xb4 + ybC + more '7 + word fline + xa1 + ya4 + xb4 + ybC + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more '8 + word farc + xa2 + yaA + a0 + ax2 + ay2 + + word farc + xa2 + yaA + a0 + ax2 + ay2 + more '9 + word farc + xa2 + ya8 + aF + ax2 + ay4 + more + word fline + xa4 + ya8 + xb4 + ybA + more + word fline + xa1 + ya4 + xb2 + yb4 + + word fline + xa2 + ya6 + xb2 + yb7 + more ': + word fline + xa2 + yaA + xb2 + yb9 + + word fline + xa2 + ya4 + xb1 + yb3 + more '; + word fline + xa2 + ya8 + xb2 + yb7 + + word fline + xa0 + ya8 + xb4 + ybA + more '< + word fline + xa0 + ya8 + xb4 + yb6 + + word fline + xa0 + yaA + xb4 + ybA + more '= + word fline + xa0 + ya6 + xb4 + yb6 + + word fline + xa4 + ya8 + xb0 + ybA + more '> + word fline + xa4 + ya8 + xb0 + yb6 + + word farc + xa2 + yaB + a8 + ax2 + ay1 + more '? + word farc + xa3 + yaB + aF + ax1 + ay2 + more + word farc + xa3 + ya7 + aD + ax1 + ay2 + more + word fline + xa2 + ya5 + xb2 + yb4 + + word farc + xa2 + ya8 + a0 + ax1 + ay1 + more '@ + word farc + xa2 + ya8 + a4 + ax2 + ay3 + more + word farc + xa3 + ya8 + aF + ax1 + ay1 + more + word farc + xa2 + ya6 + aF + ax2 + ay1 + more + word fline + xa3 + ya7 + xb3 + yb9 + + word farc + xa2 + yaA + a8 + ax2 + ay2 + more 'A + word fline + xa0 + ya4 + xb0 + ybA + more + word fline + xa4 + ya4 + xb4 + ybA + more + word fline + xa0 + ya8 + xb4 + yb8 + + word farc + xa2 + yaA + aB + ax2 + ay2 + more 'B + word farc + xa2 + ya6 + aB + ax2 + ay2 + more + word fline + xa0 + ya4 + xb0 + ybC + more + word fline + xa0 + ya4 + xb2 + yb4 + more + word fline + xa0 + ya8 + xb2 + yb8 + more + word fline + xa0 + yaC + xb2 + ybC + + word farc + xa2 + yaA + a8 + ax2 + ay2 + more 'C + word farc + xa2 + ya6 + aA + ax2 + ay2 + more + word fline + xa0 + ya6 + xb0 + ybA + + word farc + xa2 + yaA + aC + ax2 + ay2 + more 'D + word farc + xa2 + ya6 + aF + ax2 + ay2 + more + word fline + xa0 + ya4 + xb0 + ybC + more + word fline + xa4 + ya6 + xb4 + ybA + more + word fline + xa0 + ya4 + xb2 + yb4 + more + word fline + xa0 + yaC + xb2 + ybC + + word fline + xa0 + ya4 + xb0 + ybC + more 'E + word fline + xa0 + ya4 + xb4 + yb4 + more + word fline + xa0 + ya8 + xb3 + yb8 + more + word fline + xa0 + yaC + xb4 + ybC + + word fline + xa0 + ya4 + xb0 + ybC + more 'F + word fline + xa0 + ya8 + xb3 + yb8 + more + word fline + xa0 + yaC + xb4 + ybC + + word farc + xa2 + yaA + a8 + ax2 + ay2 + more 'G + word farc + xa2 + ya6 + aA + ax2 + ay2 + more + word fline + xa0 + ya6 + xb0 + ybA + more + word fline + xa4 + ya4 + xb4 + yb7 + more + word fline + xa3 + ya7 + xb4 + yb7 + + word fline + xa0 + ya4 + xb0 + ybC + more 'H + word fline + xa4 + ya4 + xb4 + ybC + more + word fline + xa0 + ya8 + xb4 + yb8 + + word fline + xa2 + ya4 + xb2 + ybC + more 'I + word fline + xa0 + ya4 + xb4 + yb4 + more + word fline + xa0 + yaC + xb4 + ybC + + word farc + xa2 + ya6 + aA + ax2 + ay2 + more 'J + word fline + xa4 + ya6 + xb4 + ybC + + word fline + xa0 + ya4 + xb0 + ybC + more 'K + word fline + xa4 + yaC + xb0 + yb8 + more + word fline + xa4 + ya4 + xb0 + yb8 + + word fline + xa0 + ya4 + xb0 + ybC + more 'L + word fline + xa0 + ya4 + xb4 + yb4 + + word fline + xa0 + ya4 + xb0 + ybC + more 'M + word fline + xa4 + ya4 + xb4 + ybC + more + word fline + xa2 + ya8 + xb0 + ybC + more + word fline + xa2 + ya8 + xb4 + ybC + + word fline + xa0 + ya4 + xb0 + ybC + more 'N + word fline + xa4 + ya4 + xb4 + ybC + more + word fline + xa4 + ya4 + xb0 + ybC + + word farc + xa2 + yaA + a8 + ax2 + ay2 + more '0 + word farc + xa2 + ya6 + aA + ax2 + ay2 + more + word fline + xa0 + ya6 + xb0 + ybA + more + word fline + xa4 + ya6 + xb4 + ybA + + word farc + xa2 + yaA + aB + ax2 + ay2 + more 'P + word fline + xa0 + ya4 + xb0 + ybC + more + word fline + xa0 + ya8 + xb2 + yb8 + more + word fline + xa0 + yaC + xb2 + ybC + + word farc + xa2 + yaA + a8 + ax2 + ay2 + more 'Q + word farc + xa2 + ya6 + aA + ax2 + ay2 + more + word fline + xa0 + ya6 + xb0 + ybA + more + word fline + xa4 + ya6 + xb4 + ybA + more + word fline + xa2 + ya6 + xb4 + yb3 + + word farc + xa2 + yaA + aB + ax2 + ay2 + more 'R + word fline + xa0 + ya4 + xb0 + ybC + more + word fline + xa0 + ya8 + xb2 + yb8 + more + word fline + xa0 + yaC + xb2 + ybC + more + word fline + xa4 + ya4 + xb2 + yb8 + + word farc + xa2 + yaA + a4 + ax2 + ay2 + more 'S + word farc + xa2 + ya6 + a6 + ax2 + ay2 + + word fline + xa2 + ya4 + xb2 + ybC + more 'T + word fline + xa0 + yaC + xb4 + ybC + + word farc + xa2 + ya6 + aA + ax2 + ay2 + more 'U + word fline + xa0 + ya6 + xb0 + ybC + more + word fline + xa4 + ya6 + xb4 + ybC + + word fline + xa2 + ya4 + xb0 + ybC + more 'V + word fline + xa2 + ya4 + xb4 + ybC + + word fline + xa0 + yaC + xb0 + yb4 + more 'W + word fline + xa4 + yaC + xb4 + yb4 + more + word fline + xa2 + ya8 + xb0 + yb4 + more + word fline + xa2 + ya8 + xb4 + yb4 + + word fline + xa4 + ya4 + xb0 + ybC + more 'X + word fline + xa0 + ya4 + xb4 + ybC + + word fline + xa0 + yaC + xb2 + yb8 + more 'Y + word fline + xa4 + yaC + xb2 + yb8 + more + word fline + xa2 + ya4 + xb2 + yb8 + + word fline + xa0 + yaC + xb4 + ybC + more 'Z + word fline + xa0 + ya4 + xb4 + ybC + more + word fline + xa0 + ya4 + xb4 + yb4 + + word fline + xa2 + yaD + xb2 + yb3 + more '[ + word fline + xa2 + yaD + xb4 + ybD + more + word fline + xa2 + ya3 + xb4 + yb3 + + word fline + xa4 + ya4 + xb0 + ybC '\ + + word fline + xa2 + yaD + xb2 + yb3 + more '[ + word fline + xa2 + yaD + xb0 + ybD + more + word fline + xa2 + ya3 + xb0 + yb3 + + word fline + xa2 + yaA + xb0 + yb6 + more '^ + word fline + xa2 + yaA + xb4 + yb6 + + word fline + xa0 + ya1 + xa4 + yb1 '_ + + word fline + xa1 + ya9 + xb3 + yb7 '` + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more 'a + word fline + xa4 + ya4 + xb4 + yb8 + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more 'b + word fline + xa0 + ya4 + xb0 + ybC + + word farc + xa2 + ya6 + a9 + ax2 + ay2 + more 'c + word fline + xa2 + ya4 + xb4 + yb4 + more + word fline + xa2 + ya8 + xb4 + yb8 + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more 'd + word fline + xa4 + ya4 + xb4 + ybC + + word farc + xa2 + ya6 + a4 + ax2 + ay2 + more 'e + word fline + xa0 + ya6 + xb4 + yb6 + more + word fline + xa2 + ya4 + xb4 + yb4 + + word farc + xa4 + yaA + aD + ax2 + ay2 + more 'f + word fline + xa0 + ya8 + xb4 + yb8 + more + word fline + xa2 + ya4 + xb2 + ybA + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more 'g + word farc + xa2 + ya3 + aF + ax2 + ay2 + more + word fline + xa4 + ya3 + xb4 + yb8 + more + word fline + xa1 + ya1 + xb2 + yb1 + + word farc + xa2 + ya6 + a8 + ax2 + ay2 + more 'h + word fline + xa0 + ya4 + xb0 + ybC + more + word fline + xa4 + ya4 + xb4 + yb6 + + word fline + xa1 + ya4 + xb3 + yb4 + more 'i + word fline + xa2 + ya4 + xb2 + yb8 + more + word fline + xa1 + ya8 + xb2 + yb8 + more + word fline + xa2 + yaB + xb2 + ybA + + word farc + xa0 + ya3 + aF + ax2 + ay2 + more 'j + word fline + xa2 + ya3 + xb2 + yb8 + more + word fline + xa1 + ya8 + xb2 + yb8 + more + word fline + xa2 + yaB + xb2 + ybA + + word fline + xa0 + ya4 + xb0 + ybC + more 'k + word fline + xa0 + ya6 + xb2 + yb6 + more + word fline + xa2 + ya6 + xb4 + yb8 + more + word fline + xa2 + ya6 + xb4 + yb4 + + word fline + xa1 + ya4 + xb3 + yb4 + more 'l + word fline + xa2 + ya4 + xb2 + ybC + more + word fline + xa1 + yaC + xb2 + ybC + + word farc + xa1 + ya7 + a8 + ax1 + ay1 + more 'm + word farc + xa3 + ya7 + a8 + ax1 + ay1 + more + word fline + xa0 + ya4 + xb0 + yb8 + more + word fline + xa2 + ya4 + xb2 + yb7 + more + word fline + xa4 + ya4 + xb4 + yb7 + + word farc + xa2 + ya6 + a8 + ax2 + ay2 + more 'n + word fline + xa0 + ya4 + xb0 + yb8 + more + word fline + xa4 + ya4 + xb4 + yb6 + + word farc + xa2 + ya6 + a0 + ax2 + ay2 'o + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more 'p + word fline + xa0 + ya1 + xb0 + yb8 + + word farc + xa2 + ya6 + a0 + ax2 + ay2 + more 'q + word fline + xa4 + ya1 + xb4 + yb8 + + word farc + xa2 + ya7 + a8 + ax2 + ay1 + more 'r + word fline + xa0 + ya4 + xb0 + yb8 + + word farc + xa2 + ya7 + a9 + ax2 + ay1 + more 's + word farc + xa2 + ya5 + aB + ax2 + ay1 + more + word fline + xa0 + ya4 + xb2 + yb4 + more + word fline + xa2 + ya8 + xb4 + yb8 + + word farc + xa4 + ya6 + aE + ax2 + ay2 + more 't + word fline + xa0 + ya8 + xb4 + yb8 + more + word fline + xa2 + ya6 + xb2 + ybA + + word farc + xa2 + ya6 + aA + ax2 + ay2 + more 'u + word fline + xa0 + ya6 + xb0 + yb8 + more + word fline + xa4 + ya4 + xb4 + yb8 + + word fline + xa0 + ya8 + xb2 + yb4 + more 'v + word fline + xa4 + ya8 + xb2 + yb4 + + word farc + xa1 + ya5 + aA + ax1 + ay1 + more 'w + word farc + xa3 + ya5 + aA + ax1 + ay1 + more + word fline + xa0 + ya5 + xb0 + yb8 + more + word fline + xa2 + ya5 + xb2 + yb6 + more + word fline + xa4 + ya5 + xb4 + yb8 + + word fline + xa0 + ya8 + xb4 + yb4 + more 'x + word fline + xa0 + ya4 + xb4 + yb8 + + word farc + xa2 + ya6 + aA + ax2 + ay2 + more 'y + word farc + xa2 + ya3 + aF + ax2 + ay2 + more + word fline + xa4 + ya3 + xb4 + yb8 + more + word fline + xa0 + ya6 + xb0 + yb8 + more + word fline + xa1 + ya1 + xb2 + yb1 + + word fline + xa0 + ya8 + xb4 + yb8 + more 'z + word fline + xa4 + ya8 + xb0 + yb4 + more + word fline + xa0 + ya4 + xb4 + yb4 + + word farc + xa3 + yaA + aD + ax1 + ay3 + more '{ + word farc + xa1 + ya6 + aC + ax1 + ay2 + more + word farc + xa1 + yaA + aF + ax1 + ay2 + more + word farc + xa3 + ya6 + aE + ax1 + ay3 + + word fline + xa2 + ya3 + xb2 + ybD '| + + word farc + xa1 + yaA + aC + ax1 + ay3 + more '} + word farc + xa3 + ya6 + aD + ax1 + ay2 + more + word farc + xa3 + yaA + aE + ax1 + ay2 + more + word farc + xa1 + ya6 + aF + ax1 + ay3 + + word farc + xa1 + ya8 + a8 + ax1 + ay1 + more '~ + word farc + xa3 + ya8 + aA + ax1 + ay1 + +' Vector font - custom characters ($7F+) + + word fline + xa2 + ya9 + xb0 + yb4 + more 'delta + word fline + xa2 + ya9 + xb4 + yb4 + more + word fline + xa0 + ya4 + xb4 + yb4 + + word farc + xa2 + ya7 + a8 + ax2 + ay2 + more 'omega + word farc + xa1 + ya7 + aE + ax1 + ay2 + more + word farc + xa3 + ya7 + aF + ax1 + ay2 + more + word fline + xa1 + ya5 + xb1 + yb4 + more + word fline + xa3 + ya5 + xb3 + yb4 + more + word fline + xa0 + ya4 + xb1 + yb4 + more + word fline + xa4 + ya4 + xb3 + yb4 + + word farc + xa2 + ya8 + a0 + ax1 + ay1 'bullet + +CON fx = 3 'number of custom characters + +CON ''------------------------------------------------- GRAPHICS-HEAP +' +' diese daten werden zur laufzeit nur wärend des systemstarts gebraucht +' nach dem start steht dieser bereich bis zum screenpuffer als +' dynamischer speicher für strings, vektorobjekte und sprites zur verfügung +' +DAT ' graphics-heap-start (temp. pasm-puffer) + +grdat + +DAT ' keyboard-pasm-code (wird nach start als heap genutzt) + +'****************************************** +'* Assembly language PS/2 keyboard driver * +'****************************************** + + org +' +' +' Entry +' +entry movd :par,#_dpin 'load input parameters _dpin/_cpin/_locks/_auto + mov x,par + add x,#11*4 + mov y,#4 +:par rdlong 0,x + add :par,dlsb + add x,#4 + djnz y,#:par + + mov dmask,#1 'set pin masks + shl dmask,_dpin + mov cmask,#1 + shl cmask,_cpin + + test _dpin,#$20 wc 'modify port registers within code + muxc _d1,dlsb + muxc _d2,dlsb + muxc _d3,#1 + muxc _d4,#1 + test _cpin,#$20 wc + muxc _c1,dlsb + muxc _c2,dlsb + muxc _c3,#1 + + mov _head,#0 'reset output parameter _head +' +' +' Reset keyboard +' +reset mov dira,#0 'reset directions + mov dirb,#0 + + movd :par,#_present 'reset output parameters _present/_states[8] + mov x,#1+8 +:par mov 0,#0 + add :par,dlsb + djnz x,#:par + + mov stat,#8 'set reset flag +' +' +' Update parameters +' +update movd :par,#_head 'update output parameters _head/_present/_states[8] + mov x,par + add x,#1*4 + mov y,#1+1+8 +:par wrlong 0,x + add :par,dlsb + add x,#4 + djnz y,#:par + + test stat,#8 wc 'if reset flag, transmit reset command + if_c mov data,#$FF + if_c call #transmit +' +' +' Get scancode +' +newcode mov stat,#0 'reset state + +:same call #receive 'receive byte from keyboard + + cmp data,#$83+1 wc 'scancode? + + if_nc cmp data,#$AA wz 'powerup/reset? + if_nc_and_z jmp #configure + + if_nc cmp data,#$E0 wz 'extended? + if_nc_and_z or stat,#1 + if_nc_and_z jmp #:same + + if_nc cmp data,#$F0 wz 'released? + if_nc_and_z or stat,#2 + if_nc_and_z jmp #:same + + if_nc jmp #newcode 'unknown, ignore +' +' +' Translate scancode and enter into buffer +' + test stat,#1 wc 'lookup code with extended flag + rcl data,#1 + mov data_s,data 'keyboard-de: store scancode for next table lookup with shift + call #look + + cmp data,#0 wz 'if unknown, ignore + if_z jmp #newcode + + mov t,_states+6 'remember lock keys in _states + + mov x,data 'set/clear key bit in _states + shr x,#5 + add x,#_states + movd :reg,x + mov y,#1 + shl y,data + test stat,#2 wc +:reg muxnc 0,y + + if_nc cmpsub data,#$F0 wc 'if released or shift/ctrl/alt/win, done + if_c jmp #update + + mov y,_states+7 'get shift/ctrl/alt/win bit pairs + shr y,#16 + + cmpsub data,#$E0 wc 'translate keypad, considering numlock + if_c test _locks,#%100 wz + if_c_and_z add data,#@keypad1-@table + if_c_and_nz add data,#@keypad2-@table + if_c call #look + if_c jmp #:flags + + 'for keyboard-de changed #$DD to #lock + 'in next code segment + + cmpsub data,#lock wc 'handle scrlock/capslock/numlock + if_c mov x,#%001_000 + if_c shl x,data + if_c andn x,_locks + if_c shr x,#3 + if_c shr t,#29 'ignore auto-repeat + if_c andn x,t wz + if_c xor _locks,x + if_c add data,#lock + if_c_and_nz or stat,#4 'if change, set configure flag to update leds + +{{ for keyboard-us-en start }} +{{ + + test y,#%11 wz 'get shift into nz +' +'Translate scan-codes $5B..$60 with characters from table "shift1" +' + if_nz cmp data,#$60+1 wc 'check shift1 + if_nz_and_c cmpsub data,#$5B wc + if_nz_and_c add data,#@shift1-@table + if_nz_and_c call #look + if_nz_and_c andn y,#%11 + +' +'Translate scan-codes $27..$3D with characters from table "shift2" +' + if_nz cmp data,#$3D+1 wc 'check shift2 + if_nz_and_c cmpsub data,#$27 wc + if_nz_and_c add data,#@shift2-@table + if_nz_and_c call #look + if_nz_and_c andn y,#%11 + + test _locks,#%010 wc 'check shift-alpha, considering capslock + muxnc :shift,#$20 + test _locks,#$40 wc + if_nz_and_nc xor :shift,#$20 + cmp data,#"z"+1 wc + if_c cmpsub data,#"a" wc +:shift if_c add data,#"A" + if_c andn y,#%11 +}} +{{ for keyboard-us-en end }} + +{{ for keyboard-de start }} + + cmp data,#de_ae wz 'replace ae + if_z mov data,#"ä" + cmp data,#de_oe wz 'replace oe + if_z mov data,#"ö" + cmp data,#de_ue wz 'replace ue + if_z mov data,#"ü" + +' +'Documentation of control-key bits +' +' test y,#%00000011 wz 'get SHIFT into nz +' test y,#%00000100 wz 'get CTRL-L into nz +' test y,#%00001000 wz 'get CTRL-R into nz +' test y,#%00010000 wz 'get ALT-L into nz +' test y,#%00100000 wz 'get ALT-R into nz +' test y,#%01000000 wz 'get WIN-L into nz +' test y,#%10000000 wz 'get WIN-R into nz + + +' +'Translate scan-codes with characters from "table_shift" +' + + test y,#%00000011 wz 'get shift into nz + test _locks,#$40 wc + if_nz_and_nc mov data,data_s 'reload scancode + if_nz_and_nc call #look_shift 'translate by table_shift + + +' +'Translate scan-codes with characters from "table_alt_r" +' + + test y,#%00100000 wz 'get ALT-R (AltGr) into nz + if_nz mov data,data_s 'reload scancode + if_nz call #look_alt_r 'translate by table_alt_r + + +{{ for keyboard-de end }} + + +:flags ror data,#8 'add shift/ctrl/alt/win flags + mov x,#4 '+$100 if shift +:loop test y,#%11 wz '+$200 if ctrl + shr y,#2 '+$400 if alt + if_nz or data,#1 '+$800 if win + ror data,#1 + djnz x,#:loop + rol data,#12 + + rdlong x,par 'if room in buffer and key valid, enter + sub x,#1 + and x,#$F + cmp x,_head wz + if_nz test data,#$FF wz + if_nz mov x,par + if_nz add x,#11*4 + if_nz add x,_head + if_nz add x,_head + if_nz wrword data,x + if_nz add _head,#1 + if_nz and _head,#$F + + test stat,#4 wc 'if not configure flag, done + if_nc jmp #update 'else configure to update leds +' +' +' Configure keyboard +' +configure mov data,#$F3 'set keyboard auto-repeat + call #transmit + mov data,_auto + and data,#%11_11111 + call #transmit + + mov data,#$ED 'set keyboard lock-leds + call #transmit + mov data,_locks + rev data,#-3 & $1F + test data,#%100 wc + rcl data,#1 + and data,#%111 + call #transmit + + mov x,_locks 'insert locks into _states + and x,#%111 + shl _states+7,#3 + or _states+7,x + ror _states+7,#3 + + mov _present,#1 'set _present + + jmp #update 'done + +{{ for keyboard-de start }} +' +' Lookup byte in table_shift +' +look_shift ror data,#2 'perform lookup + movs :reg,data + add :reg,#table_shift + shr data,#27 + mov x,data +:reg mov data,0 + shr data,x + and data,#$FF 'isolate byte +look_shift_ret ret + +' +' Lookup byte in table_alt_r +' +look_alt_r ror data,#2 'perform lookup + movs :reg,data + add :reg,#table_alt_r + shr data,#27 + mov x,data +:reg mov data,0 + shr data,x + and data,#$FF 'isolate byte +look_alt_r_ret ret + +{{ for keyboard-de end }} + +' +' +' Lookup byte in table +' +look ror data,#2 'perform lookup + movs :reg,data + add :reg,#table + shr data,#27 + mov x,data +:reg mov data,0 + shr data,x + + jmp #rand 'isolate byte +' +' +' Transmit byte to keyboard +' +transmit +_c1 or dira,cmask 'pull clock low + movs napshr,#13 'hold clock for ~128us (must be >100us) + call #nap +_d1 or dira,dmask 'pull data low + movs napshr,#18 'hold data for ~4us + call #nap +_c2 xor dira,cmask 'release clock + + test data,#$0FF wc 'append parity and stop bits to byte + muxnc data,#$100 + or data,dlsb + + mov x,#10 'ready 10 bits +transmit_bit call #wait_c0 'wait until clock low + shr data,#1 wc 'output data bit +_d2 muxnc dira,dmask + mov wcond,c1 'wait until clock high + call #wait + djnz x,#transmit_bit 'another bit? + + mov wcond,c0d0 'wait until clock and data low + call #wait + mov wcond,c1d1 'wait until clock and data high + call #wait + + call #receive_ack 'receive ack byte with timed wait + cmp data,#$FA wz 'if ack error, reset keyboard + if_nz jmp #reset + +transmit_ret ret +' +' +' Receive byte from keyboard +' +receive test _cpin,#$20 wc 'wait indefinitely for initial clock low + waitpne cmask,cmask +receive_ack + mov x,#11 'ready 11 bits +receive_bit call #wait_c0 'wait until clock low + movs napshr,#16 'pause ~16us + call #nap +_d3 test dmask,ina wc 'input data bit + rcr data,#1 + mov wcond,c1 'wait until clock high + call #wait + djnz x,#receive_bit 'another bit? + + shr data,#22 'align byte + test data,#$1FF wc 'if parity error, reset keyboard + if_nc jmp #reset +rand and data,#$FF 'isolate byte + +look_ret +receive_ack_ret +receive_ret ret +' +' +' Wait for clock/data to be in required state(s) +' +wait_c0 mov wcond,c0 '(wait until clock low) + +wait mov y,tenms 'set timeout to 10ms + +wloop movs napshr,#18 'nap ~4us + call #nap +_c3 test cmask,ina wc 'check required state(s) +_d4 test dmask,ina wz 'loop until got state(s) or timeout +wcond if_never djnz y,#wloop '(replaced with c0/c1/c0d0/c1d1) + + tjz y,#reset 'if timeout, reset keyboard +wait_ret +wait_c0_ret ret + + +c0 if_c djnz y,#wloop '(if_never replacements) +c1 if_nc djnz y,#wloop +c0d0 if_c_or_nz djnz y,#wloop +c1d1 if_nc_or_z djnz y,#wloop +' +' +' Nap +' +nap rdlong t,#0 'get clkfreq +napshr shr t,#18/16/13 'shr scales time + min t,#3 'ensure waitcnt won't snag + add t,cnt 'add cnt to time + waitcnt t,#0 'wait until time elapses (nap) + +nap_ret ret +' +' +' Initialized data +' +' +dlsb long 1 << 9 +tenms long 10_000 / 4 +' +' +' Lookup table +' ascii scan extkey regkey ()=keypad +' + +{{keyboard-us-en start}} +{{ +table + ' + '$00 --- F9 --- F5 F3 F1 F2 F12 + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$00D8,$0000,$00D4,$00D2,$00D0,$00D1,$00DB + + ' + '$08 --- F10 F8 F6 F4 TAB ` --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$00D9,$00D7,$00D5,$00D3,$0009,$0060,$0000 + + ' ALT-R Left CTRL-R + '$10 --- ALT-L SHIFT --- CTRL_L q 1 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$F5F4,$00F0,$0000,$F3F2,$0071,$0031,$0000 + + ' WIN-L + '$18 --- --- z s a w 2 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$007A,$0073,$0061,$0077,$0032,$F600 + + ' WIN-R + '$20 --- c x d e 4 3 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0063,$0078,$0064,$0065,$0034,$0033,$F700 + + ' Apps + '$28 --- Spc v f t r 5 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0020,$0076,$0066,$0074,$0072,$0035,$CC00 + + ' Power + '$30 --- n b h g y 6 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$006E,$0062,$0068,$0067,$0079,$0036,$CD00 + + ' Sleep + '$38 --- --- m j u 7 8 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$006D,$006A,$0075,$0037,$0038,$CE00 + + ' + '$40 --- , k i o 0 9 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$002C,$006B,$0069,$006F,$0030,$0039,$0000 + + ' (/) + '$48 --- . / l } p + --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$002E,$EF2F,$006C,$003B,$0070,$002D,$0000 + + ' + '$50 --- --- { --- [ = --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0027,$0000,$005B,$003D,$0000,$0000 + + ' CAPS Right (ENTER) Wk.up + '$58 LOCK SHIFT ENTER ] --- \ --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $00DE,$00F1,$EB0D,$005D,$0000,$005C,$CF00,$0000 + + ' + '$60 --- --- --- --- --- --- BkSpc --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000,$0000,$0000,$00C8,$0000 + + ' End Left Home + '$68 --- (1) --- (4) (7) --- --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$C5E1,$0000,$C0E4,$C4E7,$0000,$0000,$0000 + + ' Ins Del Down --- Right Up + '$70 (0) (.) (2) (5) (6) (8) Esc NumLock + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $CAE0,$C9EA,$C3E2,$00E5,$C1E6,$C2E8,$00CB,$00DF + + ' PgDn PrScr PgUp + '$78 F11 (+) (3) (-) (*) (9) ScrLock --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $00DA,$00EC,$C7E3,$00ED,$DCEE,$C6E9,$00DD,$0000 + + ' + '$80 --- --- --- F7 + ' ===== ===== ===== ===== + word $0000,$0000,$0000,$00D6 + + +keypad1 byte $CA, $C5, $C3, $C7, $C0, 0, $C1, $C4, $C2, $C6, $C9, $0D, "+-*/" + +keypad2 byte "0123456789.", $0D, "+-*/" + +' +'Table "shift1" for scan-codes $5B..$60 +' +shift1 byte "{|}", 0, 0, "~" + +' +'Table "shift1" for scan-codes $27..$3D +' +shift2 byte $22, 0, 0, 0, 0, "<_>?)!@#$%^&*(", 0, ":", 0, "+" +}} +{{keyboard-us-en end}} + + +{{keyboard-de start}} + +table + ' + '$00 --- F9 --- F5 F3 F1 F2 F12 + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$00D8,$0000,$00D4,$00D2,$00D0,$00D1,$00DB + + ' + '$08 --- F10 F8 F6 F4 TAB _^_ --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$00D9,$00D7,$00D5,$00D3,$0009,$005E,$0000 + + ' ALT-R Left CTRL-R + '$10 --- ALT-L SHIFT --- CTRL_L q 1 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$F5F4,$00F0,$0000,$F3F2,$0071,$0031,$0000 + + ' WIN-L + '$18 --- --- _y_ s a w 2 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0079,$0073,$0061,$0077,$0032,$F600 + + ' WIN-R + '$20 --- c x d e 4 3 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0063,$0078,$0064,$0065,$0034,$0033,$F700 + + ' Apps + '$28 --- Spc v f t r 5 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0020,$0076,$0066,$0074,$0072,$0035,Apps + + ' Power + '$30 --- n b h g _z_ 6 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$006E,$0062,$0068,$0067,$007A,$0036,Power + + ' Sleep + '$38 --- --- m j u 7 8 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$006D,$006A,$0075,$0037,$0038,Sleep + + ' + '$40 --- , k i o 0 9 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$002C,$006B,$0069,$006F,$0030,$0039,$0000 + + ' (/) + '$48 --- . _-_ l _oe_ p _sz_ --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$002E,$EF2D,$006C,de_oe,$0070,$00DF,$0000 + + ' + '$50 --- --- _ae_ --- _ue_ _'_ --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,de_ae,$0000,de_ue,$0060,$0000,$0000 + + ' CAPS Right (ENTER) Wk.up + '$58 LOCK SHIFT ENTER _+_ --- # --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word CpsLk,$00F1,$EB0D,$002B,$0000,$0023,WkUp ,$0000 + + ' + '$60 --- _<_ --- --- --- --- BkSpc --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$003C,$0000,$0000,$0000,$0000,BkSp ,$0000 + + ' End Left Home + '$68 --- (1) --- (4) (7) --- --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,CrsEn,$0000,CrsLt,CrsHm,$0000,$0000,$0000 + + ' Ins Del Down --- Right Up + '$70 (0) (.) (2) (5) (6) (8) Esc NumLock + ' ===== ===== ===== ===== ===== ===== ===== ===== + word Ins , Del ,CrsDn,$00E5,CrsRt,CrsUp, Esc ,NumLk + + ' PgDn PrScr PgUp + '$78 F11 (+) (3) (-) (*) (9) ScrLock --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $00DA,$00EC,PgDn ,$00ED,$DCEE,PgUp ,ScrLk,$0000 + + ' + '$80 --- --- --- F7 + ' ===== ===== ===== ===== + word $0000,$0000,$0000,$00D6 + + +keypad1 byte $CA, $C5, $C3, $C7, $C0, 0, $C1, $C4, $C2, $C6, $C9, $0D, "+-*/" + +keypad2 byte "0123456789.", $0D, "+-*/" + + +table_shift + ' + '$00 --- F9 --- F5 F3 F1 F2 F12 + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$00D8,$0000,$00D4,$00D2,$00D0,$00D1,$00DB + + ' + '$08 --- F10 F8 F6 F4 TAB _°_ --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$00D9,$00D7,$00D5,$00D3,$0009,$00B0,$0000 + + ' ALT-R Left CTRL-R + '$10 --- ALT-L SHIFT --- CTRL_L Q ! --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$F5F4,$00F0,$0000,$F3F2,$0051,$0021,$0000 + + ' WIN-L + '$18 --- --- _Y_ S A W " --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0059,$0053,$0041,$0057,$0022,$F600 + + ' WIN-R + '$20 --- C X D E $ SHF+3 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0043,$0058,$0044,$0045,$0024,$0014,$F700 + + ' Apps + '$28 --- Spc V F T R % --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0020,$0056,$0046,$0054,$0052,$0025,Apps + + ' Power + '$30 --- N B H G _Z_ & --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$004E,$0042,$0048,$0047,$005A,$0026,Power + + ' Sleep + '$38 --- --- M J U / ( --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$004D,$004A,$0055,$002F,$0028,Sleep + + ' + '$40 --- ; K I O _=_ ) --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$003B,$004B,$0049,$004F,$003D,$0029,$0000 + + ' (/) + '$48 --- : ___ L _OE_ P _sz_ --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$003A,$EF5F,$004C,$00D6,$0050,$003F,$0000 + + ' + '$50 --- --- _AE_ --- _UE_ _'_ --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$00C4,$0000,$00DC,$0060,$0000,$0000 + + ' CAPS Right (ENTER) Wk.up + '$58 LOCK SHIFT ENTER _*_ --- _'_ --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word CpsLk,$00F1,$EB0D,$002A,$0000,$0027,WkUp ,$0000 + + ' + '$60 --- _>_ --- --- --- --- BkSpc --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$003E,$0000,$0000,$0000,$0000,BkSp ,$0000 + + ' End Left Home + '$68 --- (1) --- (4) (7) --- --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,CrsEn,$0000,CrsLt,CrsHm,$0000,$0000,$0000 + + ' Ins Del Down --- Right Up + '$70 (0) (.) (2) (5) (6) (8) Esc NumLock + ' ===== ===== ===== ===== ===== ===== ===== ===== + word Ins , Del ,CrsDn,$00E5,CrsRt,CrsUp, Esc ,NumLk + + ' PgDn PrScr PgUp + '$78 F11 (+) (3) (-) (*) (9) ScrLock --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $00DA,$00EC,PgDn ,$00ED,$DCEE,PgUp ,ScrLk,$0000 + + ' + '$80 --- --- --- F7 + ' ===== ===== ===== ===== + word $0000,$0000,$0000,$00D6 + + + +table_alt_r + ' + '$00 --- F9 --- F5 F3 F1 F2 F12 + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0097,$0000,$0090,$009D,$009F,$009E,$0094 + + ' + '$08 --- F10 F8 F6 F4 TAB _^_ --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0096,$0093,$0091,$009C,$0009,$0000,$0000 + + ' ALT-R Left CTRL-R + '$10 --- ALT-L SHIFT --- CTRL_L q 1 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$F5F4,$00F0,$0000,$F3F2, "@", "¹", $0000 + + ' WIN-L + '$18 --- --- _y_ s a w 2 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000,$0000,$0000, "²", $F600 + + ' WIN-R + '$20 --- c x d e 4 3 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000, "€", $0000, "³", $F700 + + ' Apps + '$28 --- Spc v f t r 5 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000,$0000,$0000,$0000,Apps + + ' Power + '$30 --- n b h g _z_ 6 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000,$0000,$0000,$0000,Power + + ' Sleep + '$38 --- --- m j u 7 8 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000, "µ", $0000,$0000, "{" , "[" ,Sleep + + ' + '$40 --- , k i o 0 9 --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000,$0000, "}", "]", $0000 + + ' (/) + '$48 --- . _-_ l _oe_ p _sz_ --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000,$0000,$0000, "\", $0000 + + ' + '$50 --- --- _ae_ --- _ue_ _'_ --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000 + + ' CAPS Right (ENTER) Wk.up + '$58 LOCK SHIFT ENTER _+_ --- # --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word CpsLk,$00F1,$EB0D, "~", $0000,$0000,WkUp ,$0000 + + ' + '$60 --- _<_ --- --- --- --- BkSpc --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000, "|", $0000,$0000,$0000,$0000,BkSp ,$0000 + + ' End Left Home + '$68 --- (1) --- (4) (7) --- --- --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0000,CrsEn,$0000,CrsLt,CrsHm,$0000,$0000,$0000 + + ' Ins Del Down --- Right Up + '$70 (0) (.) (2) (5) (6) (8) Esc NumLock + ' ===== ===== ===== ===== ===== ===== ===== ===== + word Ins , Del ,CrsDn,$00E5,CrsRt,CrsUp, Esc ,NumLk + + ' PgDn PrScr PgUp + '$78 F11 (+) (3) (-) (*) (9) ScrLock --- + ' ===== ===== ===== ===== ===== ===== ===== ===== + word $0095,$00EC,PgDn ,$00ED,$DCEE,PgUp ,ScrLk,$0000 + + ' + '$80 --- --- --- F7 + ' ===== ===== ===== ===== + word $0000,$0000,$0000,$0092 + + +{{keyboard-de end}} + +' +' +' Uninitialized data +' +dmask res 1 +cmask res 1 +stat res 1 +data res 1 +data_s res 1 'Scancode-Storage (for keyboard-de) +x res 1 +y res 1 +t res 1 + +_head res 1 'write-only +_present res 1 'write-only +_states res 8 'write-only +_dpin res 1 'read-only at start +_cpin res 1 'read-only at start +_locks res 1 'read-only at start +_auto res 1 'read-only at start + +'' +'' +'' _________ +'' Key Codes +'' +'' 00..DF = keypress and keystate +'' E0..FF = keystate only +'' +'' +'' 09 Tab +'' 0D Enter +'' 20 Space +'' 21 ! +'' 22 " +'' 23 # +'' 24 $ +'' 25 % +'' 26 & +'' 27 ' +'' 28 ( +'' 29 ) +'' 2A * +'' 2B + +'' 2C , +'' 2D - +'' 2E . +'' 2F / +'' 30 0..9 +'' 3A : +'' 3B ; +'' 3C < +'' 3D = +'' 3E > +'' 3F ? +'' 40 @ +'' 41..5A A..Z +'' 5B [ +'' 5C \ +'' 5D ] +'' 5E ^ +'' 5F _ +'' 60 ` +'' 61..7A a..z +'' 7B { +'' 7C | +'' 7D } +'' 7E ~ +'' +'' 80-BF (future international character support) +'' +'' C0 Left Arrow +'' C1 Right Arrow +'' C2 Up Arrow +'' C3 Down Arrow +'' C4 Home +'' C5 End +'' C6 Page Up +'' C7 Page Down +'' C8 Backspace +'' C9 Delete +'' CA Insert +'' CB Esc +'' CC Apps +'' CD Power +'' CE Sleep +'' CF Wakeup +'' +'' D0..DB F1..F12 +'' DC Print Screen +'' DD Scroll Lock +'' DE Caps Lock +'' DF Num Lock +'' +'' E0..E9 Keypad 0..9 +'' EA Keypad . +'' EB Keypad Enter +'' EC Keypad + +'' ED Keypad - +'' EE Keypad * +'' EF Keypad / +'' +'' F0 Left Shift +'' F1 Right Shift +'' F2 Left Ctrl +'' F3 Right Ctrl +'' F4 Left Alt +'' F5 Right Alt +'' F6 Left Win +'' F7 Right Win +'' +'' FD Scroll Lock State +'' FE Caps Lock State +'' FF Num Lock State +'' +'' +100 if Shift +'' +200 if Ctrl +'' +400 if Alt +'' +800 if Win +'' +'' eg. Ctrl-Alt-Delete = $6C9 +'' +'' +'' Note: Driver will buffer up to 15 keystrokes, then ignore overflow. + +DAT ' graphics-pasm-code (wird nach start als heap genutzt) + +'************************************* +'* Assembly language graphics driver * +'************************************* + + org +' +' +' Graphics driver - main loop +' +loop rdlong t1,par wz 'wait for command + if_z jmp #loop + + movd :arg,#arg0 'get 8 arguments + mov t2,t1 + mov t3,#8 +:arg rdlong arg0,t2 + add :arg,d0 + add t2,#4 + djnz t3,#:arg + + wrlong zero,par 'zero command to signify received + + call #setd 'set dx,dy from arg0,arg1 + + ror t1,#16+2 'lookup command address + add t1,#jumps + movs :table,t1 + rol t1,#2 + shl t1,#3 +:table mov t2,0 + shr t2,t1 + and t2,#$FF + jmp t2 'jump to command + + +jumps byte 0 '0 + byte setup_ '1 + byte color_ '2 + byte width_ '3 + byte plot_ '4 + byte line_ '5 + byte arc_ '6 + byte vec_ '7 + byte vecarc_ '8 + byte pix_ '9 + byte pixarc_ 'A + byte text_ 'B + byte textarc_ 'C + byte textmode_ 'D + byte fill_ 'E + byte loop 'F +' +' +' setup(x_tiles, y_tiles*16, x_origin, y_origin, base_ptr) bases_ptr, slices_ptr +' +setup_ mov xlongs,arg0 'set xlongs, ylongs + mov ylongs,arg1 + mov xorigin,arg2 'set xorigin, yorigin + mov yorigin,arg3 + mov basesptr,arg5 'set pointers + mov slicesptr,arg6 + + jmp #loop +' +' +' color(c) +' +color_ mov pcolor,arg0 'set pixel color + + jmp #loop +' +' +' width(w) pixel_passes +' +width_ mov pwidth,arg0 'set pixel width + mov passes,arg1 'set pixel passes + + jmp #loop +' +' +' plot(x, y) +' +plot_ call #plotd + + jmp #loop +' +' +' line(x, y) +' +line_ call #linepd + + jmp #loop +' +' +' arc(x, y, xr, yr, angle, anglestep, iterations, mode) +' +arc_ and arg7,#3 'limit mode + +:loop call #arca 'get arc dx,dy + + cmp arg7,#1 wz 'if not mode 1, set px,py + if_nz mov px,dx + if_nz mov py,dy + + tjz arg6,#loop 'if no points exit with new px,py + + cmp arg7,#3 wz 'if mode 3, set center + if_z call #setd + + test arg7,#1 wz 'if mode 0 or 2, plot point + if_z call #plotp + + test arg7,#1 wz 'if mode 1 or 3, plot line + if_nz call #linepd + + cmp arg7,#2 wz 'if mode 2, set mode 1 + if_z mov arg7,#1 + + add arg4,arg5 'step angle + djnz arg6,#:loop 'loop if more iterations + + jmp #loop +' +' +' vec(x, y, vecscale, vecangle, vecdef_ptr) +' vecarc(x, y, xr, yr, angle, vecscale, vecangle, vecdef_ptr) +' +' vecdef: word $8000/$4000+angle 'vector mode + 13-bit angle (mode: $4000=plot, $8000=line) +' word length 'vector length +' ... 'more vectors +' ... +' word 0 'end of definition +' +vecarc_ call #arcmod + +vec_ tjz arg2,#loop 'if scale 0, exit + +:loop rdword t7,arg4 wz 'get vector mode+angle + add arg4,#2 + + if_z jmp #loop 'if mode+angle 0, exit + + rdword t1,arg4 'get vector length + add arg4,#2 + + abs t2,arg2 wc 'add/sub vector angle to/from angle + mov t6,arg3 + sumc t6,t7 + + call #multiply 'multiply length by scale + add t1,#$80 'round up 1/2 lsb + shr t1,#8 + + mov t4,t1 'get arc dx,dy + mov t5,t1 + call #arcd + + test t7,h8000 wc 'plot pixel or draw line? + if_nc call #plotd + test t7,h8000 wc + if_c call #linepd + + jmp #:loop 'get next vector +' +' +' pix(x, y, pixrot, pixdef_ptr) +' pixarc(x, y, xr, yr, angle, pixrot, pixdef_ptr) +' +' pixdef: word +' byte xwords, ywords, xorigin, yorigin +' word %%xxxxxxxx,%%xxxxxxxx +' word %%xxxxxxxx,%%xxxxxxxx +' word %%xxxxxxxx,%%xxxxxxxx +' ... +' +pixarc_ call #arcmod + +pix_ mov t6,pcolor 'save color + + mov px,dx 'get center into px,py + mov py,dy + + mov sy,pwidth 'get actual pixel width + add sy,#1 + + rdbyte dx,arg3 'get dimensions into dx,dy + add arg3,#1 + rdbyte dy,arg3 + add arg3,#1 + + rdbyte t1,arg3 'get origin and adjust px,py + add arg3,#1 + rdbyte t2,arg3 + add arg3,#1 + neg t2,t2 + sub t2,#1 + add t2,dy + mov t3,sy +:adjust test arg2,#%001 wz + test arg2,#%110 wc + if_z sumnc px,t1 + if_nz sumc py,t1 + test arg2,#%010 wc + if_nz sumnc px,t2 + if_z sumnc py,t2 + djnz t3,#:adjust + +:yline mov sx,#0 'plot entire pix + mov t3,dx +:xword rdword t4,arg3 'read next pix word + add arg3,#2 + shl t4,#16 + mov t5,#8 +:xpixel rol t4,#2 'plot pixel within word + test t4,#1 wc 'set color + muxc pcolor,color1 + test t4,#2 wc + muxc pcolor,color2 wz '(z=1 if color=0) + if_nz call #plotp + test arg2,#%001 wz 'update px,py for next x + test arg2,#%110 wc + if_z sumc px,sy + if_nz sumnc py,sy + add sx,sy + djnz t5,#:xpixel 'another x pixel? + djnz t3,#:xword 'another x word? + if_z sumnc px,sx 'update px,py for next y + if_nz sumc py,sx + test arg2,#%010 wc + if_nz sumc px,sy + if_z sumc py,sy + djnz dy,#:yline 'another y line? + + mov pcolor,t6 'restore color + + jmp #loop +' +' +' text(x, y, @string) justx, justy +' textarc(x, y, xr, yr, angle, @string) justx, justy +' +textarc_ call #arcmod + +text_ add arg3,arg0 'add x into justx + add arg4,arg1 'add y into justy + +:chr rdbyte t1,arg2 wz 'get chr + add arg2,#1 + + if_z jmp #loop 'if 0, done + + sub t1,#$21 'if chr out of range, skip + cmp t1,#$7F-$21+fx wc + if_nc jmp #:skip + + mov arg5,fontptr 'scan font for chr definition +:scan tjz t1,#:def + rdword t2,arg5 + add arg5,#2 + test t2,h8000 wc + if_nc sub t1,#1 + jmp #:scan + +:def rdword t7,arg5 'get font definition word + add arg5,#2 + + call #fontxy 'extract initial x,y + + test t7,#$80 wc 'arc or line? + if_nc jmp #:line + + + mov t2,textsx 'arc, extract x radius + mov t3,#%0001_0001_1 + call #fontb + mov t4,t1 + + mov t2,textsy 'extract y radius + mov t3,#%0010_0011_1 + call #fontb + mov t5,t1 + + mov t2,#1 'extract starting angle + mov t3,#%0010_0011_0 + call #fontb + shl t1,#11 + + mov t6,t1 'extract angle sweep + mov t3,#%0010_0011_0 + call #fontb + neg arg6,t1 + shl arg6,#4 + add arg6,#65 + + call #arcd 'plot initial arc point + call #plotd + +:arc call #arcd 'connect subsequent arc points with lines + call #linepd + add t6,#$80 + djnz arg6,#:arc + + jmp #:more + + +:line call #plotd 'line, plot initial x,y + + call #fontxy 'extract terminal x,y + + call #linepd 'draw line + + +:more test t7,#$02 wc 'more font definition? + if_c jmp #:def + +:skip mov t1,textsp 'advance x to next chr position + mov t2,textsx + call #multiply + add arg3,t1 + + jmp #:chr 'get next chr + + +fontxy mov t2,textsx 'extract x + mov t3,#%0011_0111_0 + call #fontb + mov arg0,t1 + add arg0,arg3 + + mov t2,textsy 'extract y + mov t3,#%0100_1111_0 + call #fontb + mov arg1,t1 + add arg1,arg4 + +setd mov dx,xorigin 'set dx,dy from arg0,arg1 + add dx,arg0 + mov dy,yorigin + sub dy,arg1 +setd_ret +fontxy_ret ret + + +fontb mov t1,t7 'extract bitrange from font word + shr t3,#1 wc + and t1,t3 + if_c add t1,#1 + shr t3,#4 + shr t7,t3 + + shl t1,#32-4 'multiply t1[3..0] by t2 + mov t3,#4 +:loop shl t1,#1 wc + if_c add t1,t2 + djnz t3,#:loop + +fontb_ret ret +' +' +' textmode(x_scale, y_scale, spacing, justification) +' +textmode_ mov textsx,arg0 'set text x scale + mov textsy,arg1 'set text y scale + mov textsp,arg2 'set text spacing + + jmp #loop +' +' +' fill(x, y, da, db, db2, linechange, lines_minus_1) +' +fill_ shl dx,#16 'get left and right fractions + or dx,h8000 + mov t1,dx + + mov t2,xlongs 'get x pixels + shl t2,#4 + + add arg6,#1 'pre-increment line counter + +:yloop add dx,arg2 'adjust left and right fractions + add t1,arg3 + + cmps dx,t1 wc 'get left and right integers + if_c mov base0,dx + if_c mov base1,t1 + if_nc mov base0,t1 + if_nc mov base1,dx + sar base0,#16 + sar base1,#16 + + cmps base0,t2 wc 'left out of range? + if_c cmps hFFFFFFFF,base1 wc 'right out of range? + if_c cmp dy,ylongs wc 'y out of range? + if_nc jmp #:skip 'if any, skip + + mins base0,#0 'limit left and right + maxs base1,t2 wc + if_nc sub base1,#1 + + shl base0,#1 'make left mask + neg mask0,#1 + shl mask0,base0 + shr base0,#5 + + shl base1,#1 'make right mask + xor base1,#$1E + neg mask1,#1 + shr mask1,base1 + shr base1,#5 + + sub base1,base0 wz 'ready long count + add base1,#1 + + if_z and mask0,mask1 'if single long, merge masks + + shl base0,#1 'get long base + add base0,basesptr + rdword base0,base0 + shl dy,#2 + add base0,dy + shr dy,#2 + + mov bits0,mask0 'ready left mask +:xloop mov bits1,pcolor 'make color mask + and bits1,bits0 + rdlong pass,base0 'read-modify-write long + andn pass,bits0 + or pass,bits1 + wrlong pass,base0 + shl ylongs,#2 'advance to next long + add base0,ylongs + shr ylongs,#2 + cmp base1,#2 wz 'one more? + if_nz neg bits0,#1 'if not, ready full mask + if_z mov bits0,mask1 'if one more, ready right mask + djnz base1,#:xloop 'loop if more longs + +:skip sub arg5,#1 wc 'delta change? + if_c mov arg3,arg4 'if so, set new deltas +:same + add dy,#1 'adjust y + djnz arg6,#:yloop 'another y? + + jmp #loop +' +' +' Plot line from px,py to dx,dy +' +linepd cmps dx,px wc, wr 'get x difference + negc sx,#1 'set x direction + + cmps dy,py wc, wr 'get y difference + negc sy,#1 'set y direction + + abs dx,dx 'make differences absolute + abs dy,dy + + cmp dx,dy wc 'determine dominant axis + if_nc tjz dx,#:last 'if both differences 0, plot single pixel + if_nc mov count,dx 'set pixel count + if_c mov count,dy + mov ratio,count 'set initial ratio + shr ratio,#1 + if_c jmp #:yloop 'x or y dominant? + + +:xloop call #plotp 'dominant x line + add px,sx + sub ratio,dy wc + if_c add ratio,dx + if_c add py,sy + djnz count,#:xloop + + jmp #:last 'plot last pixel + + +:yloop call #plotp 'dominant y line + add py,sy + sub ratio,dx wc + if_c add ratio,dy + if_c add px,sx + djnz count,#:yloop + +:last call #plotp 'plot last pixel + +linepd_ret ret +' +' +' Plot pixel at px,py +' +plotd mov px,dx 'set px,py to dx,dy + mov py,dy + +plotp tjnz pwidth,#wplot 'if width > 0, do wide plot + + mov t1,px 'compute pixel mask + shl t1,#1 + mov mask0,#%11 + shl mask0,t1 + shr t1,#5 + + cmp t1,xlongs wc 'if x or y out of bounds, exit + if_c cmp py,ylongs wc + if_nc jmp #plotp_ret + + mov bits0,pcolor 'compute pixel bits + and bits0,mask0 + + shl t1,#1 'get address of pixel long + add t1,basesptr + mov t2,py + rdword t1,t1 + shl t2,#2 + add t1,t2 + + rdlong t2,t1 'write pixel + andn t2,mask0 + or t2,bits0 + wrlong t2,t1 +plotp_ret +plotd_ret ret +' +' +' Plot wide pixel +' +wplot mov t1,py 'if y out of bounds, exit + add t1,#7 + mov t2,ylongs + add t2,#7+8 + cmp t1,t2 wc + if_nc jmp #plotp_ret + + mov t1,px 'determine x long pair + sub t1,#8 + sar t1,#4 + cmp t1,xlongs wc + muxc jumps,#%01 '(use jumps[1..0] to store writes) + add t1,#1 + cmp t1,xlongs wc + muxc jumps,#%10 + + test jumps,#%11 wz 'if x out of bounds, exit + if_z jmp #plotp_ret + + shl t1,#1 'get base pair + add t1,basesptr + rdword base1,t1 + sub t1,#2 + rdword base0,t1 + + mov t1,px 'determine pair shifts + shl t1,#1 + movs :shift1,t1 + xor :shift1,#7<<1 + add t1,#9<<1 + movs :shift0,t1 + test t1,#$F<<1 wz '(account for special case) + if_z andn jumps,#%01 + + mov pass,#0 'ready to plot slices + mov slice,slicesptr + +:loop rdlong mask0,slice 'get next slice + mov mask1,mask0 + +:shift0 shl mask0,#0 'position slice +:shift1 shr mask1,#0 + + mov bits0,pcolor 'colorize slice + and bits0,mask0 + mov bits1,pcolor + and bits1,mask1 + + mov t1,py 'plot lower slice + add t1,pass + cmp t1,ylongs wc + if_c call #wslice + + mov t1,py 'plot upper slice + test pwidth,#1 wc + subx t1,pass + cmp t1,ylongs wc + if_c call #wslice + + add slice,#4 'next slice + add pass,#1 + cmp pass,passes wz + if_nz jmp #:loop + + jmp #plotp_ret +' +' +' Plot wide pixel slice +' +wslice shl t1,#2 'ready long offset + + add base0,t1 'plot left slice + test jumps,#%01 wc + if_c rdlong t2,base0 + if_c andn t2,mask0 + if_c or t2,bits0 + if_c wrlong t2,base0 + + add base1,t1 'plot right slice + test jumps,#%10 wc + if_c rdlong t2,base1 + if_c andn t2,mask1 + if_c or t2,bits1 + if_c wrlong t2,base1 + + sub base0,t1 'restore bases + sub base1,t1 + +wslice_ret ret +' +' +' Get arc point from args and then move args 5..7 to 2..4 +' +arcmod call #arca 'get arc using first 5 args + + mov arg0,dx 'set arg0,arg1 + sub arg0,xorigin + mov arg1,yorigin + sub arg1,dy + + mov arg2,arg5 'move args 5..7 to 2..4 + mov arg3,arg6 + mov arg4,arg7 + +arcmod_ret ret +' +' +' Get arc dx,dy from arg0,arg1 +' +' in: arg0,arg1 = center x,y +' arg2/t4 = x length +' arg3/t5 = y length +' arg4/t6 = 13-bit angle +' +' out: dx,dy = arc point +' +arca mov t4,arg2 'use args + mov t5,arg3 + mov t6,arg4 + +arcd call #setd 'reset dx,dy to arg0,arg1 + + mov t1,t6 'get arc dx + mov t2,t4 + call #polarx + add dx,t1 + + mov t1,t6 'get arc dy + mov t2,t5 + call #polary + sub dy,t1 +arcd_ret +arca_ret ret +' +' +' Polar to cartesian +' +' in: t1 = 13-bit angle +' t2 = 16-bit length +' +' out: t1 = x|y +' +polarx add t1,sine_90 'cosine, add 90° for sine lookup +polary test t1,sine_180 wz 'get sine quadrant 3|4 into nz + test t1,sine_90 wc 'get sine quadrant 2|4 into c + negc t1,t1 'if sine quadrant 2|4, negate table offset + or t1,sine_table 'or in sine table address >> 1 + shl t1,#1 'shift left to get final word address + rdword t1,t1 'read sine/cosine word + call #multiply 'multiply sine/cosine by length to get x|y + add t1,h8000 'add 1/2 lsb to round up x|y fraction + shr t1,#16 'justify x|y integer + negnz t1,t1 'if sine quadrant 3|4, negate x|y +polary_ret +polarx_ret ret + +sine_90 long $0800 '90° bit +sine_180 long $1000 '180° bit +sine_table long $E000 >> 1 'sine table address shifted right +' +' +' Multiply +' +' in: t1 = 16-bit multiplicand (t1[31..16] must be 0) +' t2 = 16-bit multiplier +' +' out: t1 = 32-bit product +' +multiply mov t3,#16 + shl t2,#16 + shr t1,#1 wc + +:loop if_c add t1,t2 wc + rcr t1,#1 wc + djnz t3,#:loop + +multiply_ret ret +' +' +' Defined data +' +zero long 0 'constants +d0 long $200 +h8000 long $8000 +hFFFFFFFF long $FFFFFFFF +color1 long %%1111111111111111 +color2 long %%2222222222222222 + +fontptr long 0 'font pointer (set before cognew command) + +pcolor long %%1111111111111111 'pixel color +pwidth long 0 'pixel width +passes long 1 'pixel passes +textsx long 1 'text scale x +textsy long 1 'text scale y +textsp long 6 'text spacing +' +' +' Undefined data +' +t1 res 1 'temps +t2 res 1 +t3 res 1 +t4 res 1 +t5 res 1 +t6 res 1 +t7 res 1 + +arg0 res 1 'arguments passed from high-level +arg1 res 1 +arg2 res 1 +arg3 res 1 +arg4 res 1 +arg5 res 1 +arg6 res 1 +arg7 res 1 + +basesptr res 1 'pointers +slicesptr res 1 + +xlongs res 1 'bitmap metrics +ylongs res 1 +xorigin res 1 +yorigin res 1 + +dx res 1 'line/plot coordinates +dy res 1 +px res 1 +py res 1 + +sx res 1 'line +sy res 1 +count res 1 +ratio res 1 + +pass res 1 'plot +slice res 1 +base0 res 1 +base1 res 1 +mask0 res 1 +mask1 res 1 +bits0 res 1 +bits1 res 1 + +DAT ' graphics-heap-ende + +grdatend + +{{ ' lizenz + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} + + + diff --git a/source/triborg/triborg-reg.spin b/source/triborg/triborg-reg.spin new file mode 100644 index 0000000..88c78ec --- /dev/null +++ b/source/triborg/triborg-reg.spin @@ -0,0 +1,405 @@ +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Autor: Ingo Kripahle │ +│ Copyright (c) 2010 Ingo Kripahle │ +│ See end of file for terms of use. │ +│ Die Nutzungsbedingungen befinden sich am Ende der Datei │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Informationen : hive-project.de +Kontakt : drohne235@googlemail.com +System : TriOS +Name : TriBorg - SID-Player +Chip : Regnatix +Typ : Programm +Version : +Subversion : +Funktion : +Komponenten : - +COG's : - +Logbuch : +Kommandoliste : +Notizen : + +}} + +OBJ + ios: "reg-ios" + +CON + +_CLKMODE = XTAL1 + PLL16X +_XINFREQ = 5_000_000 + +SIDMASK = %00000000_00000000_00000000_00010000 + +XMAX = 256 +YMAX = 192 + +x_tiles = 16 +y_tiles = 12 + +bit_base = $2000 +disp_base = $5000 +len_colblk = (x_tiles * y_tiles) * 2 + 256 + +'ademo1 +lines = 5 +thickness = 2 +s_obj = 200 'scale +d_obj = 64 'durchmesser +r_obj = d_obj/2 'radius +rotvar = 14 'rotationsvarianz +arcstep = 16 + +VAR + + 'achtung, folgende reihenfolge darf nicht verändert werden! + word screen[x_tiles * y_tiles] 'tilemap + long colortab[64] 'farbregister + + long heap_len + long heap_use + long dmpreg + + long fcnt + long fpos + long datcnt 'zeiger für dateiliste + byte fl_bye 'flag player beenden +' byte fn[12] 'puffer für dateinamen + + long dmu 'marker des aktuellen userverzeichnisses + +PUB main|i,x,y,n,len + + ios.start + ios.belload(string("triborg.bel")) + ios.g0_dynamic + ios.g0_clear + ios.g0_copy + screenset1 'farben und tiles setzen + heap_len := ios.g0_datlen + ios.g0_datblk(@grdat,0,heap_len) 'heapdaten senden + + ifnot (ios.admgetspec & SIDMASK) + ios.sddmset(ios#DM_USER) 'u-marker setzen + dmu := ios.sddmget(ios#DM_USER) 'usermarker von administra holen + ios.admload(string("triborg.adm")) + ios.sddmput(ios#DM_USER,dmu) 'usermarker wieder in administra setzen + ios.sddmact(ios#DM_USER) 'u-marker aktivieren + + play_count + repeat + ios.sid_sdmpplay(string("triborg.sid")) + \phase1(400) + \phase2(445) + \phase3(250) + \phase4 + play_dir + +PRI esc_key + + if ios.g0_keystat + case ios.g0_keycode + 27: + 'ESC - geordneter rücksturz zur erde... :) + ios.sid_dmpstop 'ok, erstmal die mucke aus + ios.sid_mute(3) + ios.g0_reboot 'grafiksubsystem neu im textmodus starten + ios.admreset 'administra neu starten + waitcnt(cnt+clkfreq*2) 'geben wir den damen noch ein wenig zeit... + reboot + "n": + ios.sid_dmpstop + ios.sid_mute(3) + abort + "p": + ios.sid_dmppause + ios.sid_mute(3) + repeat until ios.g0_keystat + + +PRI screenset1|i,tx,ty + + 'tilescreen setzen + repeat tx from 0 to x_tiles - 1 + repeat ty from 0 to y_tiles - 1 + screen[ty * x_tiles + tx] := disp_base >> 6 + ty + tx * y_tiles + ((ty & $3F) << 10) + + 'farbtabelle füllen + repeat i from 0 to 63 + colortab[i] := $00001010 * (i<<1+4) & $F + $48060D02 + + ios.g0_colortab(@colortab) + ios.g0_screen(@screen) + + +PRI phase1(frcnt) | k + + ios.g0_textmode(5,5,6,0) + ios.g0_colorwidth(2,8) + repeat frcnt + ios.g0_clear + ios.g0_text(15,k,@string4 - @grdat) + ios.g0_copy + k := k + 1 <# 60 + esc_key + +PRI phase2(frcnt) | k + + ios.g0_textmode(5,5,6,0) + ios.g0_colorwidth(2,8) + k := 255 + repeat frcnt + ios.g0_clear + ios.g0_text(15,60,@string4 - @grdat) + ios.g0_text(15,k,@string3 - @grdat) + ios.g0_copy + k := k - 1 #> 60 + esc_key + +PRI phase3(frcnt) | k + + k := 0 + ios.g0_textmode(5,5,6,0) + repeat frcnt + ios.g0_clear + ios.g0_colorwidth(2,8) + ios.g0_text(15,60,@string2 - @grdat) + cores1(400,ymax/2 - 5,cnt>>14,600 + k <# 1600,2) + ios.g0_colorwidth(2,8) + ios.g0_text(15,60,@string1 - @grdat) + ios.g0_copy + k := k + 5 + esc_key + +PRI phase4 | status + + ios.g0_textmode(5,5,6,0) + repeat + dmpreg := ios.sid_dmpreg + status := ios.sid_dmpstatus + ios.g0_clear + ios.g0_colorwidth(2,8) + ios.g0_text(15,60,@string2 - @grdat) + cores2(400,ymax/2 - 5,cnt >> 14,2000,1) + ios.g0_colorwidth(2,8) + ios.g0_text(15,60,@string1 - @grdat) + ios.g0_copy + esc_key + while status + +PRI phase5 | status + + ios.g0_textmode(5,5,6,0) + repeat + dmpreg := ios.sid_dmpreg + status := ios.sid_dmpstatus + ios.g0_clear + ios.g0_colorwidth(2,8) + ios.g0_text(15,60,@string2 - @grdat) + cores2(400,ymax/2 - 5,cnt >> 14,2000,1) + ios.g0_colorwidth(2,8) + ios.g0_text(15,60,@string1 - @grdat) + ios.g0_copy + esc_key + while status + +PRI play(stradr) | status,k + + k := 0 + ios.sid_sdmpplay(stradr) 'sound starten + ios.g0_datblk(@grdat,@fn - @grdat,12) 'dateinamen aktualisieren + repeat + k++ + dmpreg := ios.sid_dmpreg 'sounddaten holen + status := ios.sid_dmpstatus 'playerstatus abfragen + ios.g0_clear + ios.g0_colorwidth(2,8) + ios.g0_textmode(5,5,6,0) + ios.g0_text(15,60,@string2 - @grdat) + cores2(400,ymax/2 - 5,cnt >> 14,2000,1) + ios.g0_colorwidth(2,8) + ios.g0_text(15,60,@string1 - @grdat) + if k < XMAX 'dateiname und zählerstand + ios.g0_textmode(2,1,6,0) + ios.g0_colorwidth(2,0) + ios.g0_text(k,5,@fn - @grdat) + ios.g0_printdec(70,150,fpos,4,@val1,@val1 - @grdat) + ios.g0_printdec(130,150,fcnt,4,@val2,@val2 - @grdat) + + ios.g0_copy + esc_key + while status + ios.sid_dmpstop + +PRI cores1(x,y,angle,radius,col)|x1,y1,x2,y2,x3,y3 + + x1 := x + sin(angle)/(radius) + y1 := y + cos(angle)/(radius) + x2 := x + sin(angle+2730)/(radius) + y2 := y + cos(angle+2730)/(radius) + x3 := x + sin(angle+5460)/(radius) + y3 := y + cos(angle+5460)/(radius) + + ios.g0_colorwidth(0,0) 'startpunkt + ios.g0_plot(x1,y1) + + ios.g0_colorwidth(col, 7) 'kreise + ios.g0_arc(x1, y1, 20, 20, 0, $1fff/(arcstep-1), arcstep, 2) + ios.g0_arc(x2, y2, 20, 20, 0, $1fff/(arcstep-1), arcstep, 2) + ios.g0_arc(x3, y3, 20, 20, 0, $1fff/(arcstep-1), arcstep, 2) + +PRI cores2(x,y,angle,radius,col)|x1,y1,x2,y2,x3,y3,r1,r2,r3,xl,yl + + x1 := x + sin(angle)/(radius - word[dmpreg + 0]>>6) + y1 := y + cos(angle)/(radius - word[dmpreg + 0]>>6) + x2 := x + sin(angle+2730)/(radius - word[dmpreg + 2]>>6) + y2 := y + cos(angle+2730)/(radius - word[dmpreg + 2]>>6) + x3 := x + sin(angle+5460)/(radius - word[dmpreg + 4]>>6) + y3 := y + cos(angle+5460)/(radius - word[dmpreg + 4]>>6) + r1 := byte[dmpreg + 1] >> 3 + r2 := byte[dmpreg + 3] >> 3 + r3 := byte[dmpreg + 5] >> 3 + + ios.g0_colorwidth(2,0) 'startpunkt + ios.g0_plot(x,y) + xl := x + sin(angle+2730)/(500 - word[dmpreg + 0]>>7) + yl := y + cos(angle+2730)/(500 - word[dmpreg + 0]>>7) + ios.g0_line(xl,yl) + + ios.g0_colorwidth(0,0) 'startpunkt + ios.g0_plot(x1,y1) + + ios.g0_colorwidth(col, 7) 'kreise + ios.g0_arc(x1, y1, r1, r1, 0, $1fff/(arcstep-1), arcstep, 2) + ios.g0_arc(x2, y2, r2, r2, 0, $1fff/(arcstep-1), arcstep, 2) + ios.g0_arc(x3, y3, r3, r3, 0, $1fff/(arcstep-1), arcstep, 2) + +PRI cos(angle) : x + x := sin(angle + $800) + +PRI sin(angle) : y + '' Get sine of angle (0-8191) + y := angle << 1 & $FFE ' address + if angle & $800 + y := word[$F000 - y] + else + y := word[$E000 + y] + if angle & $1000 + -y +CON 'sd-player +PRI play_count|stradr,i + + ios.sddir 'kommando: verzeichnis öffnen + datcnt := 0 'zum listenanfang + fcnt := 0 'zähler für dateianzahl + fl_bye := 0 + repeat while (stradr := ios.sdnext) 'dateiliste einlesen + if str_find(stradr,string(".DMP")) + fcnt++ + play_dir_wrlst(stradr) + +PRI play_dir|stradr,len,i 'alle songs auf der sd-card abspielen + + datcnt := 0 'zum listenanfang + repeat i from 0 to fcnt-1 'dateiliste abspielen + fpos := i + 1 + play_dir_rdlst(@fn) + \play(@fn) + + +PRI play_dir_wrlst(stradr)|len,i 'kopiert dateinamen in liste + len := strsize(stradr) + repeat i from 0 to len-1 + ios.ram_wrbyte(ios#usrmod,byte[stradr][i],datcnt++) + ios.ram_wrbyte(ios#usrmod,0,datcnt++) + +PRI play_dir_rdlst(stradr)|i,n 'liest dateinamen aus list + i := 0 + repeat + n := ios.ram_rdbyte(ios#usrmod,datcnt++) + byte[stradr][i++] := n + while n <> 0 + +PRI str_find(string_1, string_2) : buffer | counter 'sys: string suchen + +'' ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +'' │ Searches a string of characters for the first occurence of the specified string of characters. │ +'' │ │ +'' │ Returns the address of that string of characters if found and zero if not found. │ +'' │ │ +'' │ string1 - A pointer to the string of characters to search. │ +'' │ string2 - A pointer to the string of characters to find in the string of characters to search. │ +'' └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + repeat strsize(string_1--) + + if(byte[++string_1] == byte[string_2]) + + repeat counter from 0 to (strsize(string_2) - 1) + + if(byte[string_1][counter] <> byte[string_2][counter]) + buffer~~ + + ifnot(buffer~) + return string_1 + +DAT 'heap-daten + +grdat + +DAT 'stringpuffer + +val1 'stringpuffer für zahlenausgabe +byte "00000000",0 '8 digits +byte 0 'wichtig: auf wortgrenze auffüllen! + +val2 'stringpuffer für zahlenausgabe +byte "00000000",0 '8 digits +byte 0 'wichtig: auf wortgrenze auffüllen! + +strend + + +DAT 'strings + +string1 +byte " rg",0 + +string2 +byte "TriBo",0 + +string3 +byte "Tri",0 + +string4 +byte " Borg",0 + +fn +byte "--------.---",0 + +grdatend + + + +DAT + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}}