Document not found (404)
+This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +From 613691b6e7065330ed0cf37d131e013bce58c5e9 Mon Sep 17 00:00:00 2001 From: Deploy from CI <> Date: Tue, 19 Mar 2024 02:30:22 +0000 Subject: [PATCH] Deploy d94294846a39c3a75a0dfed051df10863a258d9b to gh-pages --- .nojekyll | 1 + asar_19/changelog/index.html | 1894 ++++++ asar_19/index.html | 18 + asar_19/manual/errors-list.html | 234 + asar_19/manual/index.html | 3407 +++++++++++ asar_19/manual/warnings-list.html | 56 + asar_19/shared/common.js | 26 + asar_19/shared/highlight_js/README.txt | 5 + asar_19/shared/highlight_js/highlight.min.js | 421 ++ .../highlight_js_asar/highlight_js_asar.js | 151 + .../highlight_js_asar/styles/default.css | 134 + asar_2_beta/.nojekyll | 1 + asar_2_beta/404.html | 219 + asar_2_beta/FontAwesome/css/font-awesome.css | 4 + asar_2_beta/FontAwesome/fonts/FontAwesome.ttf | Bin 0 -> 165548 bytes .../FontAwesome/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../FontAwesome/fonts/fontawesome-webfont.svg | 2671 +++++++++ .../FontAwesome/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes asar_2_beta/arch-65816.html | 510 ++ asar_2_beta/arch-spc700.html | 487 ++ asar_2_beta/arch-superfx.html | 835 +++ asar_2_beta/arch.html | 309 + asar_2_beta/ayu-highlight.css | 169 + asar_2_beta/binary.html | 371 ++ asar_2_beta/book.js | 697 +++ asar_2_beta/changelog-old.html | 1208 ++++ asar_2_beta/changelog.html | 847 +++ asar_2_beta/checks.html | 260 + asar_2_beta/clipboard.min.js | 7 + asar_2_beta/cmd-index.html | 322 + asar_2_beta/compat.html | 249 + asar_2_beta/conditionals.html | 337 ++ asar_2_beta/css/chrome.css | 606 ++ asar_2_beta/css/general.css | 234 + asar_2_beta/css/print.css | 50 + asar_2_beta/css/variables.css | 279 + asar_2_beta/defines.html | 327 ++ asar_2_beta/elasticlunr.min.js | 10 + asar_2_beta/error-list.html | 416 ++ asar_2_beta/favicon.png | Bin 0 -> 5679 bytes asar_2_beta/favicon.svg | 22 + asar_2_beta/fonts/OPEN-SANS-LICENSE.txt | 202 + asar_2_beta/fonts/SOURCE-CODE-PRO-LICENSE.txt | 93 + asar_2_beta/fonts/fonts.css | 100 + .../open-sans-v17-all-charsets-300.woff2 | Bin 0 -> 44352 bytes ...open-sans-v17-all-charsets-300italic.woff2 | Bin 0 -> 40656 bytes .../open-sans-v17-all-charsets-600.woff2 | Bin 0 -> 44936 bytes ...open-sans-v17-all-charsets-600italic.woff2 | Bin 0 -> 42120 bytes .../open-sans-v17-all-charsets-700.woff2 | Bin 0 -> 44988 bytes ...open-sans-v17-all-charsets-700italic.woff2 | Bin 0 -> 40800 bytes .../open-sans-v17-all-charsets-800.woff2 | Bin 0 -> 44536 bytes ...open-sans-v17-all-charsets-800italic.woff2 | Bin 0 -> 40812 bytes .../open-sans-v17-all-charsets-italic.woff2 | Bin 0 -> 41076 bytes .../open-sans-v17-all-charsets-regular.woff2 | Bin 0 -> 43236 bytes ...source-code-pro-v11-all-charsets-500.woff2 | Bin 0 -> 59140 bytes asar_2_beta/formatting.html | 275 + asar_2_beta/freespace.html | 362 ++ asar_2_beta/functions.html | 490 ++ asar_2_beta/highlight.css | 168 + asar_2_beta/highlight.js | 593 ++ asar_2_beta/includes.html | 311 + asar_2_beta/index.html | 227 + asar_2_beta/intro.html | 227 + asar_2_beta/labels.html | 412 ++ asar_2_beta/macros.html | 331 ++ asar_2_beta/mapping-modes.html | 251 + asar_2_beta/mark.min.js | 7 + asar_2_beta/math.html | 257 + asar_2_beta/namespaces.html | 328 ++ asar_2_beta/print.html | 5199 +++++++++++++++++ asar_2_beta/prints.html | 280 + asar_2_beta/program-counter.html | 368 ++ asar_2_beta/searcher.js | 483 ++ asar_2_beta/searchindex.js | 1 + asar_2_beta/searchindex.json | 1 + asar_2_beta/sectionhack.js | 19 + asar_2_beta/standard-defines.html | 238 + asar_2_beta/standard-includes.html | 237 + asar_2_beta/structs.html | 337 ++ asar_2_beta/tomorrow-night.css | 169 + asar_2_beta/usage.html | 338 ++ asar_2_beta/warning-list.html | 248 + asar_2_beta/warnings.html | 265 + changelog/index.html | 12 + index.html | 12 + manual/index.html | 12 + v1.90/changelog/index.html | 1875 ++++++ v1.90/index.html | 18 + v1.90/manual/errors-list.html | 234 + v1.90/manual/index.html | 3407 +++++++++++ v1.90/manual/warnings-list.html | 56 + v1.90/shared/common.js | 26 + v1.90/shared/highlight_js/README.txt | 5 + v1.90/shared/highlight_js/highlight.min.js | 421 ++ .../highlight_js_asar/highlight_js_asar.js | 151 + .../highlight_js_asar/styles/default.css | 134 + v1.91/changelog/index.html | 1894 ++++++ v1.91/index.html | 18 + v1.91/manual/errors-list.html | 234 + v1.91/manual/index.html | 3407 +++++++++++ v1.91/manual/warnings-list.html | 56 + v1.91/shared/common.js | 26 + v1.91/shared/highlight_js/README.txt | 5 + v1.91/shared/highlight_js/highlight.min.js | 421 ++ .../highlight_js_asar/highlight_js_asar.js | 151 + .../highlight_js_asar/styles/default.css | 134 + 108 files changed, 43320 insertions(+) create mode 100644 .nojekyll create mode 100644 asar_19/changelog/index.html create mode 100644 asar_19/index.html create mode 100644 asar_19/manual/errors-list.html create mode 100644 asar_19/manual/index.html create mode 100644 asar_19/manual/warnings-list.html create mode 100644 asar_19/shared/common.js create mode 100644 asar_19/shared/highlight_js/README.txt create mode 100644 asar_19/shared/highlight_js/highlight.min.js create mode 100644 asar_19/shared/highlight_js_asar/highlight_js_asar.js create mode 100644 asar_19/shared/highlight_js_asar/styles/default.css create mode 100644 asar_2_beta/.nojekyll create mode 100644 asar_2_beta/404.html create mode 100644 asar_2_beta/FontAwesome/css/font-awesome.css create mode 100644 asar_2_beta/FontAwesome/fonts/FontAwesome.ttf create mode 100644 asar_2_beta/FontAwesome/fonts/fontawesome-webfont.eot create mode 100644 asar_2_beta/FontAwesome/fonts/fontawesome-webfont.svg create mode 100644 asar_2_beta/FontAwesome/fonts/fontawesome-webfont.ttf create mode 100644 asar_2_beta/FontAwesome/fonts/fontawesome-webfont.woff create mode 100644 asar_2_beta/FontAwesome/fonts/fontawesome-webfont.woff2 create mode 100644 asar_2_beta/arch-65816.html create mode 100644 asar_2_beta/arch-spc700.html create mode 100644 asar_2_beta/arch-superfx.html create mode 100644 asar_2_beta/arch.html create mode 100644 asar_2_beta/ayu-highlight.css create mode 100644 asar_2_beta/binary.html create mode 100644 asar_2_beta/book.js create mode 100644 asar_2_beta/changelog-old.html create mode 100644 asar_2_beta/changelog.html create mode 100644 asar_2_beta/checks.html create mode 100644 asar_2_beta/clipboard.min.js create mode 100644 asar_2_beta/cmd-index.html create mode 100644 asar_2_beta/compat.html create mode 100644 asar_2_beta/conditionals.html create mode 100644 asar_2_beta/css/chrome.css create mode 100644 asar_2_beta/css/general.css create mode 100644 asar_2_beta/css/print.css create mode 100644 asar_2_beta/css/variables.css create mode 100644 asar_2_beta/defines.html create mode 100644 asar_2_beta/elasticlunr.min.js create mode 100644 asar_2_beta/error-list.html create mode 100644 asar_2_beta/favicon.png create mode 100644 asar_2_beta/favicon.svg create mode 100644 asar_2_beta/fonts/OPEN-SANS-LICENSE.txt create mode 100644 asar_2_beta/fonts/SOURCE-CODE-PRO-LICENSE.txt create mode 100644 asar_2_beta/fonts/fonts.css create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-300.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-300italic.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-600.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-600italic.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-700.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-700italic.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-800.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-800italic.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-italic.woff2 create mode 100644 asar_2_beta/fonts/open-sans-v17-all-charsets-regular.woff2 create mode 100644 asar_2_beta/fonts/source-code-pro-v11-all-charsets-500.woff2 create mode 100644 asar_2_beta/formatting.html create mode 100644 asar_2_beta/freespace.html create mode 100644 asar_2_beta/functions.html create mode 100644 asar_2_beta/highlight.css create mode 100644 asar_2_beta/highlight.js create mode 100644 asar_2_beta/includes.html create mode 100644 asar_2_beta/index.html create mode 100644 asar_2_beta/intro.html create mode 100644 asar_2_beta/labels.html create mode 100644 asar_2_beta/macros.html create mode 100644 asar_2_beta/mapping-modes.html create mode 100644 asar_2_beta/mark.min.js create mode 100644 asar_2_beta/math.html create mode 100644 asar_2_beta/namespaces.html create mode 100644 asar_2_beta/print.html create mode 100644 asar_2_beta/prints.html create mode 100644 asar_2_beta/program-counter.html create mode 100644 asar_2_beta/searcher.js create mode 100644 asar_2_beta/searchindex.js create mode 100644 asar_2_beta/searchindex.json create mode 100644 asar_2_beta/sectionhack.js create mode 100644 asar_2_beta/standard-defines.html create mode 100644 asar_2_beta/standard-includes.html create mode 100644 asar_2_beta/structs.html create mode 100644 asar_2_beta/tomorrow-night.css create mode 100644 asar_2_beta/usage.html create mode 100644 asar_2_beta/warning-list.html create mode 100644 asar_2_beta/warnings.html create mode 100644 changelog/index.html create mode 100644 index.html create mode 100644 manual/index.html create mode 100644 v1.90/changelog/index.html create mode 100644 v1.90/index.html create mode 100644 v1.90/manual/errors-list.html create mode 100644 v1.90/manual/index.html create mode 100644 v1.90/manual/warnings-list.html create mode 100644 v1.90/shared/common.js create mode 100644 v1.90/shared/highlight_js/README.txt create mode 100644 v1.90/shared/highlight_js/highlight.min.js create mode 100644 v1.90/shared/highlight_js_asar/highlight_js_asar.js create mode 100644 v1.90/shared/highlight_js_asar/styles/default.css create mode 100644 v1.91/changelog/index.html create mode 100644 v1.91/index.html create mode 100644 v1.91/manual/errors-list.html create mode 100644 v1.91/manual/index.html create mode 100644 v1.91/manual/warnings-list.html create mode 100644 v1.91/shared/common.js create mode 100644 v1.91/shared/highlight_js/README.txt create mode 100644 v1.91/shared/highlight_js/highlight.min.js create mode 100644 v1.91/shared/highlight_js_asar/highlight_js_asar.js create mode 100644 v1.91/shared/highlight_js_asar/styles/default.css diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..f1731109 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ +This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/asar_19/changelog/index.html b/asar_19/changelog/index.html new file mode 100644 index 00000000..3c3ac1f9 --- /dev/null +++ b/asar_19/changelog/index.html @@ -0,0 +1,1894 @@ + + +
+error
, warn
and assert
commands now support the same functions as the print
command. (RPG Hacker)<...[math]>
, which makes them less ambiguous and helps prevent syntax parsing bugs. (RPG Hacker)freecode
. (p4plus2)check bankcross
now allows disabling checking for half-bank crossings (going from $7FFF to $8000). (p4plus2)pc()
and realbase()
functions: returns the current SNES address that is being written to. (p4plus2, trillian)pushns
and pullns
. (p4plus2)while
loops can now be ended with endwhile
. (p4plus2)incsrc
'd from macros. (trillian)for
loops, used like for i = 1..10
. These are more convenient than while loops in most cases and are the main replacement for rep
. (trillian)incbin
now has a new syntax for including a range of the target file which looks like the for loop range syntax and is less ambiguous. (trillian)spcblock
feature as a replacement for spc-inline
, which allows defining blocks of SPC data, but in a more flexible way that can easily be extended in the future. (p4plus2)--error-limit
option, which allows raising the limit on the number of errors that Asar will print before stopping. (trillian)freespacebyte
command to set the byte value used for searching for freespace and expanding the ROM. (trillian)autoclean jml/jsl
commands, pseudo opcodes like asl #4
, and most other data-writing commands like db/dw/dl
. (spooonsss, RPG Hacker, trillian)'''
and ';'
are now valid can now be used without causing errors. (trillian, RPG Hacker)--version
commandline flag now causes asar to exit afterwards, which is the expected behavior for command-line flags. (Alcaro)optimize address
now works a bit more consistently (new behavior also properly documented in manual). (trillian)JMP.l
: Use JML
instead.sizeof("my_struct")
): Remove the quotes (sizeof(my_struct)
).math round
and math pri
: Use parentheses and explicit rounding where xkas-style math emulation is needed instead.if !condition
to negate conditions: Use if not(condition)
instead.endif
: Use endwhile
instead.check bankcross on
: Use check bankcross full
or check bankcross half
instead.rep
to repeat commands: Use while loops, unrolled loops or for loops instead.incsrc dir\file.asm
): Use cross-platform paths instead (incsrc dir/file.asm
).table
command: Assign characters directly instead, like 'a' = $00
.spc700-inline
: Use spcblock
and endspcblock
instead.spc700-raw
: use arch spc700
and norom
.fastrom
: Has never actually worked and can be removed.header
: Doesn't do anything and can be removed.#
before numbers in db/dw/dl/dd
statements: this was intended for xkas compatibility but was for some reason enabled everywhere without a warning. The #
does nothing and can be removed.;@command
and @command
notation: Use command
instead.if
/endif
pair instead. (Note that you can still have an if statement entirely on one line, you just need to have an : endif
at the end of it.)freespace fixed
: Use freespace static
instead.<math>
syntax for variadic macro parameters: Use <...[math]>
instead.incbin file.bin:0-F
: use incbin file.bin:$0..$10
instead. (note that the new syntax is end-exclusive.)incbin file.bin -> target
: put an org
or freespace
command before the incbin.if a = b
: use if a == b
instead.;[[
: These mark the start of block comments in Asar 2.0, so either remove the [[
for the time being, or make the commented line end with a ]]
.A
as a label name ambiguously, e.g. lda a
: in Asar 2 this will be interpreted as trying to use the "A" addressing mode, and will give an error. You can use A+0
if you really must refer to the label as is.startpos
command of the spc700-inline
architecture: use the optional execute
argument to endspcblock
instead.freecode $ff
. Use the new separate freespacebyte
command for it.warnpc
command: use assert pc() <= $xxxxxx
instead.print
command has a new bin()
function. Also, bin()
, dec()
and hex()
now take an optional width argument that allows padding the number with zeroes. (randomdude999)assert
command. (randomdude999)sizeof()
and objectsize()
functions don't need quotes around the struct name anymore. For backwards compatibilty, quotes are still allowed. (p4plus2)dpbase
, optimize dp
and optimize address
. (p4plus2)floor()
and ceil()
functions have been added. (randomdude999)bank()
and <:
function and math operator have been added (p4plus2)datasize()
function has been added. (p4plus2)fill align
and skip align
(randomdude999)undef
command inside an unexecuted if
statement would still get executed. This is now fixed. (randomdude999)assert
command would give strange errors when the condition to test contained a comma. This is now fixed. (randomdude999)warnings enable
command no longer prints the error message three times. (randomdude999)!assembler_ver
) are now always present. Previously they were missing when the DLL was called without specifying a standard defines file. (randomdude999)LSR #4
don't accept labels in their argument anymore. (randomdude999)arch spc700
. Now adc.w a, $0
properly assembles to 85 00 00
instead of 84 00
. (randomdude999)@
(randomdude999)arch superfx
ignoring all commands with more than 2 words (randomdude999)canread()
functions returned true for the first byte after the end of the ROM. That has been fixed.check bankcross off
now should mess with the PC less (i.e. no forced fastrom addressing).error
command now doesn't print the line of code that caused the error. All warnings except for the one from the warn
command should now print the line of code that caused the warning. (note that not all warnings or errors are associated with a specific line of code.)fullsa1rom
mapper now supports automatic freespace searching. (randomdude999)incbin
ranges can now use math as an alternative to unprefixed hex. To use this, surround the math with parentheses. For example, incbin file:(4+2)-($F+$10)
. Note that in math statements, unprefixed numbers are decimal, not hex! (randomdude999)dd
should work better (randomdude999)prot
(randomdude999, found by WhiteYoshiEgg)"
.if !condition
was removed without a proper deprecation process. It has been re-added with a deprecation warning, because a few patches still managed to use it.defined()
function (randomdude999) and undef
command. (zetaPRIME):
now for consistency. (RPG Hacker)?
. (RPG Hacker)macro my_macro()
+ ?MacroLabel = $008000
+
+ dl ?MacroLabel ; Valid
+endmacro
+
+%my_macro()
+
+dl ?MacroLabel ; Error!
#
will create that label without modifying existing label hierarchies. This is mainly intended for hacky incsrc/routine macros as used by GPS, which can break existing label hierarchies when used. (RPG Hacker)
+ macro my_new_routine()
+ jsl MyNewRoutine
+
+ !macro_routine_defined ?= 0
+
+ if !macro_routine_defined == 0
+ pushpc
+
+ freecode cleaned
+
+ #MyNewRoutine:
+ incsrc routines/mynewroutine.asm
+
+ pullpc
+
+ !macro_routine_defined = 1
+ endif
+endmacro
+
+Main:
+ %my_new_routine()
+.Sub
+
+ ; Both of these are found
+ dl MyNewRoutine
+ dl Main_Sub
+ Note that while it's technically possible to use this feature on sub labels, macro labels and macro sub labels, I don't think there's any reasonable use case for this. In most cases, regular macro labels and regular macro sub labels are recommended for usage inside macros.
+ !
by doing \!
(useful for preventing Asar to replace defines in certain places). (randomdude999)\
by doing \\
. (RPG Hacker)pushbase
/pullbase
. (randomdude999, RPG Hacker)check title
command which makes Asar verify the title of the output ROM (to assure a patch is applied to the correct ROM). (randomdude999)namespace nested on
+ check bankcross off
which disables checking for bank crossing. Use with caution; might not work with certain features or outright break some stuff. (randomdude999, RPG Hacker)filesize()
function and a getfilestatus()
function. (randomdude999)stdincludes.txt
and stddefines.txt
- see manual for details. (RPG Hacker, randomdude999)includeonce
command for shared files which may be included multiple times, but should only be assembled once. (RPG Hacker).b
, .w
and .l
) are now also supported for the SPC700 architecture. (KungFuFurby)stringsequal()
and stringsequalnocase()
functions. (RPG Hacker)!assembler
and !assembler_ver
are supported. Trying to modify those defines will throw an error. (RPG Hacker);@
works. This command was really meant for backwards-compatibility with xkas and is supposed to assemble everything following it, which it now does again. (RPG Hacker);@asar 1.60
+@asar 1.60
+asar 1.60
+ When Asar finds an unknown command on a line starting with ;@
or @
, it only throws a warning instead of an error. You can make use of this fact by using optional features from newer Asar versions and still have your patch assemble in old Asar versions, where those features are ignored (don't know how practicable and useful this really is, but in theory, it should be possible). And of course it can be used for patches that are compatible with both xkas and Asar, not that that's particularly useful anymore in this day and age.$70
to $7D
can be used correctly. (randomdude999)freedata align
. (TheBiob)pad
command that made it not update the pc correctly and also made it trigger a bank cross error before actually writing any data into a new bank. (RPG Hacker)readfile()
/canreadfile()
crashing when reading from more than 16 different files in the same patch. (randomdude999)math round on
making the values negative sometimes. (randomdude999)print double()
not working when any of their arguments contain commas or parentheses. (randomdude999)readfile1()
via user-defined functions. Previously, this failed as Asar expected all parameters in user-defined functions to be numbers. (KevinCathcart)ab
and a paramater abc
, Asar occasionally returned the parameter abc
when referencing the paramter ab
. (KevinCathcart)autoclear
, which is an alias of autoclean
. Please use only autoclean
from now on.freespace fixed
, which is an alias of freespace static
. Please use only freespace static
from now on.if !condition
. This feature was planned for deprecation as it bites with Asar's define syntax, but it was removed earlier than planned because, after inspecting the code, it was determined that it didn't work properly and probably wasn't even usable in versions prior to 1.60 at all. The only way to use it in Asar 1.60 was by using the new escape sequence for !, which didn't exist in earlier Asar versions. Thus it can be assumed that the feature wasn't used in previous Asar versions and can be removed safely. To negate conditions in Asar 1.60, either use the built-in logic functions (if !(a == 0 && b == 0)
becomes if not(and(equal(a, 0),equal(b, 0)))
) or directly negate the condition (if !(a == 0 && b == 0)
becomes if a != 0 || b != 0
).freedata
, for example), but I can't verify this.pushtable
and pulltable
commands, which let you back-up or restore the current character table to the/from the stack.ext\notepad-plus-plus\syntax-highlighting.xml
. This file can be imported into Notepad++ as a user-defined language to add syntax highlighting for Asar patches, featuring all commands currently supported by Asar. By default, this syntax highlighting is enabled on all files with an extension of .asm
or .asr
, but this can be customized via Notepad++.@
or ;@
that don't map to a recognized special command now only throw warnings at best and no errors.readfile
functions: readfile1(filename, offset)
, readfile2(filename, offset)
, readfile3(filename, offset)
, readfile4(filename, offset)
- similiar to read1()
etc. functions, except data is read from another file instead of from the ROM (note that offset is a PC offset, not a SNES offset). You can pass an optional third value which is returned if the read fails. These functions are primarily intended for reading bytes from another file and then doing math with them. For example: reading bytes from a Lunar Magic .pal file, converting them into a different format and then writing them to the ROM as a table that can directly be DMA'd to CGRAM without further conversions (all conversions happen at compile-time). As an additional bonus, all of those functions cache any file passed to them (up to 16 simultanous files), which means that multiple readfile() calls on the same file will keep the file open rather than repeatedly opening and closing the file.canreadfile
functions: canreadfile1(filename, offset)
, canreadfile2(filename, offset)
, canreadfile3(filename, offset)
, canreadfile4(filename, offset)
, canreadfile(filename, offset, length)
- basically the readfile()
equivalents of canread1()
etc.snestopc(address)
and pctosnes(address)
functions: for manually converting addresses (note that those functions are dependent on the ROM's current mapping mode, so use them with caution - chances are you will never need them at all).max(a, b)
, min(a, b)
and clamp(value, min, max)
functions: max()
/min()
return the maximum/minimum of two values, whereas clamp()
makes sure that that value is >= min
and <= max.
safediv(dividend, divisor, exception)
function: divides dividend
by divisor
, unless divisor
is 0, in which case exception
is returned.select(statement, true, false)
function: if statement
is 0, false
is returned, in any other case, true
is returned. Basically, a mathematical version of "if/else". Please note that unlike if/else blocks, function arguments in Asar are always evaluated before a function returns. In other words: if you do select(1, 1/1, 1/0)
, Asar will throw a "division by zero" error, even though the function would return 1/1. In this particular case, it's recommended to simply use the safediv()
function in place of a regular division.not(value)
function: returns 1 if value
is 0 and 0 in any other case.equal(a, b)
, notequal(a, b)
, less(a, b)
, lessequal(a, b)
, greater(a, b)
, greaterequal(a, b)
- rather self-explanatory, return 1 if the respective comparison is true and 0 otherwise. Primarily intended to be passed as statement to select()
function.and(a, b)
, or(a, b)
, nand(a, b)
, nor(a, b)
, xor(a, b)
- also self-explanatory, return 1 if the respective logical operation is true and 0 otherwise. Primarily intended to be passed as statement to select()
function.while
loops: Added compile-time while
loops to Asar. Those work similar to if conditionals, with the difference that their blocks are assembled repeatedly until their condition becomes false. For easier implementation and higher compatibility, while loops are terminated with endif
, just like if
conditionals. When using while loops, be careful not to cause an infinite loop. Asar won't make any effort to detect those.\
at the end of any line of source code and Asar will append the next line to it. This is similar to putting a ,
at the end of a line, with the difference, that the \
itself does not appear in the concatenated string, whereas the ,
would. This is useful to split long function definitions into multiplie lines, for example. Note that all whitespace following the \
is ignored, whereas whitespace preceeding the \
isn't. Therefore
+ db\
+$FF
+ turns into
+ db$FF
+ for example, whereas
+ db \
+$FF
+ turns into
+ db $FF
+ double(num)
print function: Can be passed to print to print a double variable with its fractional part. Has a default precision of 5 decimal places, but can be passed an optional second argument to override the precision.round(num, precision)
function: Rounds the double variable num
to precision
decimal places. Pass 0 as precision to round to the nearest integer.read1()
to read4()
, but always threw "Wrong number of parameters to function" errors when actually using those overloaded versions.canread()
when passing 2 arguments to it, because it actually treated it as canread1()
due to an internal error in string comparison.dd $FFFFFFFF
+ would assemble to 00 00 00 80
($80000000
) in Asar 1.37, it now assembles to FF FF FF FF
elseif
conditionals now get properly resolved.#=
define operator now doesn't truncate its value when using math round off
, making it possible to do double-precision math with it.@xkas : @asar 1.37
on the first line would previously lead to an error, whereas putting @asar 1.37 : @xkas
there would not. Both variations lead to an error message now, since it really doesn't make much sense to use them together in any combination.@asar
or @include
could previously be used on the first line only and needed to be chained with a :
inbetween. They can now be used on any line as long as no other command comes before or inbetween them.src/test/arch-superfx.asm
was never edited to acknowledge this fix, so the test always failed=
, +=
, :=
, #=
, ?=
) to manual.txt. Those have been in Asar for quite a while, but were never documented yet, although they can be quite useful.freespace
argument added; a $xx
byte that will search the ROM for contiguous sections of that byte. Before it was hardcoded to only search for $00
. Default is still $00
if not supplied, so past patches should not be broken.autoclean
was hardcoded to clean using $00
. This was fixed also to clean with the byte supplied by freecode, or $00
if not supplied.lms r0,($00D4)
used to output 3D A0 D4
, which is actually incorrect because short addressing doubles the byte supplied by the instruction to give a range from $0000
-$01FE
with just one byte (since Super FX reads words). This now outputs 3D A0 6A
which is correct. Also, asar now throws an error for anything outside $0000
-$01FE
as well as all odd-numbered addresses for both sms
and lms
instructions. (Odd-numbered addresses cannot be accessed using short addressing due to the multiplying by 2.)0xFFFFFF
because freespace addresses use a high byte to indicate that they're freespace.Error name | +Error message | +
---|---|
Elimit_reached | Over %d errors detected. Aborting. |
Ewerror | One or more warnings was detected with werror on. |
E16mb_rom_limit | Can't create ROMs larger than 16MB. |
Ebuffer_too_small | The given buffer is too small to contain the resulting ROM. |
Eparams_null | params passed to asar_patch_ex() is null. |
Eparams_invalid_size | Size of params passed to asar_patch_ex() is invalid. |
Ecmdl_define_invalid | Invalid define name in %s: '%s'. |
Ecmdl_define_override | %s '%s' overrides a previous define. Did you specify the same define twice? |
Ecreate_rom_failed | Couldn't create ROM. |
Eopen_rom_failed | Couldn't open ROM. |
Eopen_rom_not_smw_extension | Doesn't look like an SMW ROM. (Maybe its extension is wrong?) |
Eopen_rom_not_smw_header | Doesn't look like an SMW ROM. (Maybe it's headered?) |
Estddefines_no_identifier | stddefines.txt contains a line with a value, but no identifier. |
Estddefine_after_closing_quote | Broken std defines. (Something after closing quote) |
Efailed_to_open_file | Failed to open file '%s'. |
Efile_not_found | File '%s' wasn't found. |
Ereadfile_1_to_4_bytes | Can only read chunks of 1 to 4 bytes. |
Ecanreadfile_0_bytes | Number of bytes to check must be > 0. |
Efile_offset_out_of_bounds | File offset %s out of bounds for file '%s'. |
Erep_at_file_end | rep at the end of a file |
Emismatched_parentheses | Mismatched parentheses. |
Einvalid_hex_value | Invalid hex value. |
Einvalid_binary_value | Invalid binary value. |
Einvalid_character | Invalid character. |
Estring_literal_not_terminated | String literal not terminated. |
Emalformed_function_call | Malformed function call. |
Einvalid_number | Invalid number. |
Egarbage_near_quoted_string | Garbage near quoted string. |
Emismatched_quotes | Mismatched quotes. |
Erom_too_short | ROM is too short to have a title. (Expected '%s') |
Erom_title_incorrect | ROM title is incorrect. Expected '%s', got '%s'. |
Ebank_border_crossed | A bank border was crossed, SNES address $%06X. |
Estart_of_file | This command may only be used at the start of a file. |
Exkas_asar_conflict | Using @xkas and @asar in the same patch is not supported. |
Einvalid_version_number | Invalid version number. |
Easar_too_old | This version of Asar is too old for this patch. |
Erelative_branch_out_of_bounds | Relative branch out of bounds. (Distance is %s). |
Esnes_address_doesnt_map_to_rom | SNES address %s doesn't map to ROM. |
Esnes_address_out_of_bounds | SNES address %s out of bounds. |
Einvalid_tcall | Invalid tcall. |
Euse_xplus | Use (x+) instead. |
Eopcode_length_too_long | Opcode length is too long. |
Esuperfx_invalid_short_address | Invalid short address parameter: $%s. (Must be even number and $0000-$01FE) |
Esuperfx_invalid_register | Invalid register for opcode; valid range is %d-%d. |
Einvalid_opcode_length | Invalid opcode length specification. |
Einvalid_mapper | Invalid mapper. |
Enan | NaN encountered. |
Edivision_by_zero | Division by zero. |
Emodulo_by_zero | Modulo by zero. |
Eunknown_operator | Unknown operator. |
Einvalid_input | Invalid input. |
Einvalid_function_name | Invalid function name. |
Efunction_not_found | Function '%s' wasn't found. |
Efunction_redefined | Function '%s' redefined. |
Ebroken_function_declaration | Broken function declaration. |
Ewrong_num_parameters | Wrong number of parameters to function. |
Einvalid_param_name | Invalid parameter name. |
Emath_invalid_type | Wrong type for parameter %s, expected %s. |
Einvalid_label_name | Invalid label name. |
Elabel_not_found | Label '%s' wasn't found. |
Elabel_redefined | Label '%s' redefined. |
Ebroken_label_definition | Broken label definition. |
Elabel_cross_assignment | Setting labels to other non-static labels is not valid. |
Emacro_label_outside_of_macro | Macro label outside of a macro. |
Elabel_on_third_pass | Internal error: A label was created on the third pass. Please create an issue on the official GitHub repository and attach a patch which reproduces the error. |
Elabel_moving | Internal error: A label is moving around. Please create an issue on the official GitHub repository and attach a patch which reproduces the error. |
Einvalid_namespace_name | Invalid namespace name. |
Einvalid_namespace_use | Invalid use of namespace command. |
Einvalid_struct_name | Invalid struct name. |
Estruct_not_found | Struct '%s' wasn't found. |
Estruct_redefined | Struct '%s' redefined. |
Estruct_invalid_parent_name | Invalid parent name. |
Einvalid_label_missing_closer | Invalid label name, missing array closer. |
Emultiple_subscript_operators | Multiple subscript operators is invalid. |
Einvalid_subscript | Invalid array subscript after first scope resolution. |
Elabel_missing_parent | This label has no parent. |
Earray_invalid_inside_structs | Array syntax invalid inside structs. |
Estruct_without_endstruct | struct without matching endstruct. |
Enested_struct | Can not nest structs. |
Emissing_struct_params | Missing struct parameters. |
Etoo_many_struct_params | Too many struct parameters. |
Emissing_extends | Missing extends keyword. |
Einvalid_endstruct_count | Invalid endstruct parameter count. |
Eexpected_align | Expected align parameter. |
Eendstruct_without_struct | endstruct can only be used in combination with struct. |
Ealignment_too_small | Alignment must be >= 1. |
Einvalid_define_name | Invalid define name. |
Edefine_not_found | Define '%s' wasn't found. |
Ebroken_define_declaration | Broken define declaration. |
Eoverriding_builtin_define | Trying to set define '%s', which is the name of a built-in define and thus can't be modified. |
Edefine_label_math | !Define #= Label is not allowed with non-static labels. |
Emismatched_braces | Mismatched braces. |
Einvalid_macro_name | Invalid macro name. |
Emacro_not_found | Macro '%s' wasn't found. |
Emacro_redefined | Macro '%s' redefined. |
Ebroken_macro_declaration | Broken macro declaration. |
Einvalid_macro_param_name | Invalid macro parameter name. |
Emacro_param_not_found | Macro parameter '%s' wasn't found. |
Emacro_param_redefined | Macro parameter '%s' redefined |
Ebroken_macro_usage | Broken macro usage. |
Emacro_wrong_num_params | Wrong number of parameters to macro. |
Ebroken_macro_contents | Broken macro contents. |
Erep_at_macro_end | rep or if at the end of a macro. |
Enested_macro_definition | Nested macro definition. |
Emisplaced_endmacro | Misplaced endmacro. |
Eunclosed_macro | Unclosed macro. |
Elabel_in_conditional | Non-static label in %s command. |
Ebroken_conditional | Broken %s command. |
Einvalid_condition | Invalid condition. |
Emisplaced_elseif | Misplaced elseif. |
Eelseif_in_while | Can't use elseif in a while loop. |
Eelseif_in_singleline | Can't use elseif on single-line statements. |
Emisplaced_endif | Misplaced endif. |
Emisplaced_else | Misplaced else. |
Eelse_in_while_loop | Can't use else in a while loop. |
Eunclosed_if | Unclosed if statement. |
Eunknown_command | Unknown command. |
Ecommand_disabled | This command is disabled. |
Ebroken_incbin | Broken incbin command. |
Eincbin_64kb_limit | Can't include more than 64 kilobytes at once. |
Erecursion_limit | Recursion limit reached. |
Ecommand_in_non_root_file | This command may only be used in the root file. |
Ecant_be_main_file | This file may not be used as the main file.%s |
Eno_labels_here | Can't use non-static labels here. |
Einvalid_freespace_request | Invalid freespace request. |
Eno_banks_with_ram_mirrors | No banks contain the RAM mirrors in hirom or exhirom. |
Eno_freespace_norom | Can't find freespace in norom. |
Estatic_freespace_autoclean | A static freespace must be targeted by at least one autoclean. |
Estatic_freespace_growing | A static freespace may not grow. |
Eno_freespace_in_mapped_banks | No freespace found in the mapped banks. (Requested size: %s) |
Eno_freespace | No freespace found. (Requested size: %s) |
Efreespace_limit_reached | A patch may not contain more than %d freespaces. |
Eprot_not_at_freespace_start | PROT must be used at the start of a freespace block. |
Eprot_too_many_entries | Too many entries to PROT. |
Eautoclean_in_freespace | autoclean used in freespace. |
Eautoclean_label_at_freespace_end | Don't autoclean a label at the end of a freespace block, you'll remove some stuff you're not supposed to remove. |
Ebroken_autoclean | Broken autoclean command. |
Epulltable_without_table | Using pulltable when there is no table on the stack yet. |
Einvalid_table_file | Invalid table file. Invalid entry on line: %i |
Epad_in_freespace | pad does not make sense in a freespaced code. |
Eorg_label_invalid | org Label is not valid. |
Eorg_label_forward | org Label is only valid for labels earlier in the patch. |
Eskip_label_invalid | skip Label is not valid. |
Espc700_inline_no_base | base is not implemented for architecture spc700-inline. |
Ebase_label_invalid | base Label is not valid. |
Erep_label | rep Label is not valid. |
Epushpc_without_pullpc | pushpc without matching pullpc. |
Epullpc_without_pushpc | pullpc without matching pushpc. |
Epullpc_different_arch | pullpc in another architecture than the pushpc. |
Epullbase_without_pushbase | pullbase without matching pushbase. |
Einvalid_math | Invalid math command. |
Einvalid_warn | Invalid warn command. |
Einvalid_check | Invalid check command. |
Ewarnpc_in_freespace | warnpc used in freespace. |
Ewarnpc_broken_param | Broken warnpc parameter. |
Ewarnpc_failed | warnpc failed: Current position (%s) is after end position (%s). |
Ewarnpc_failed_equal | warnpc failed: Current position (%s) is equal to end position (%s). |
Eassertion_failed | Assertion failed%s |
Eerror_command | error command%s |
Einvalid_print_function_syntax | Invalid printable string syntax. |
Eunknown_variable | Unknown variable. |
Einvalid_warning_id | Invalid warning ID passed to %s. Expected format is WXXXX where %d <= XXXX <= %d. |
Epushwarnings_without_pullwarnings | warnings push without matching warnings pull. |
Epullwarnings_without_pushwarnings | warnings pull without matching warnings push. |
Efailed_to_open_file_access_denied | Failed to open file '%s'. Access denied. |
Efailed_to_open_not_regular_file | Failed to open file '%s'. Not a regular file (did you try to use a directory?) |
Ebad_dp_base | The dp base should be page aligned (i.e. a multiple of 256) |
Ebad_dp_optimize | Bad dp optimize value %s, expected: [none, ram, always] |
Ebad_address_optimize | Bad dp optimize value %s, expected: [default, ram, mirrors] |
Ebad_optimize | Bad optimize value %s, expected: [dp, address] |
Erequire_parameter | Missing required function parameter |
Eexpected_parameter | Not enough parameters in calling of function %s |
Eunexpected_parameter | Too many parameters in calling of function %s |
Eduplicate_param_name | Duplicated parameter name: %s in creation of function %s |
Einvalid_alignment | Invalid alignment. Expected a power of 2. |
Ealignment_too_big | Requested alignment too large. |
Enegative_shift | Bitshift amount is negative. |
Emacro_not_varadic | Invalid use of sizeof(...), active macro is not variadic. |
Evararg_sizeof_nomacro | Invalid use of sizeof(...), no active macro. |
Emacro_wrong_min_params | Variadic macro call with too few parameters |
Evararg_out_of_bounds | Variadic macro parameter requested is out of bounds. |
Evararg_must_be_last | Variadic macro parameter must be the last parameter. |
Einvalid_global_label | Global label definition contains an invalid label [%s]. |
Espc700_addr_out_of_range | Address %s out of range for instruction, valid range is 0000-1FFF |
Elabel_ambiguous | Label (%s) location is ambiguous due to straddling optimization border. |
Efeature_unavaliable_in_spcblock | This feature may not be used while an spcblock is active |
Eendspcblock_without_spcblock | Use of endspcblock without matching spcblock |
Emissing_endspcblock | Use of endspcblock without matching spcblock |
Espcblock_bad_arch | spcblock only valid inside spc700 arch |
Espcblock_inside_struct | Can not start an spcblock while a struct is still open |
Espcblock_too_few_args | Too few args passed to spcblock |
Espcblock_too_many_args | Too many args passed to spcblock |
Eunknown_spcblock_type | Unknown spcblock format |
Ecustom_spcblock_missing_macro | Custom spcblock types must refer to a valid macro |
Espcblock_macro_doesnt_exist | Macro specified to custom spcblock was not found |
Eextra_spcblock_arg_for_type | Only custom spcblock type takes a fourth argument |
Espcblock_macro_must_be_varadic | Custom spcblock macros must be variadic |
Espcblock_macro_invalid_static_args | Custom spcblock macros must have three static arguments |
Espcblock_custom_types_incomplete | Custom spcblock types are not yet supported. One day. |
Estartpos_without_spcblock | The startpos command is only valid in spcblocks |
Einvalid_endspcblock_arg | Invalid argument to endspcblock: "%s" |
Eunknown_endspcblock_format | Unsupported endspcblock format. Currently supported formats are "endspcblock" and "endspcblock execute [label]" |
Einternal_error | An internal asar error occured (%s). Send help. |
Epushns_without_pullns | pushns without matching pullns. |
Epullns_without_pushns | pullns without matching pushns. |
Elabel_forward | The use of forward labels is not allowed in this context. |
Eunclosed_vararg | Variadic macro parameter wasn't closed properly. |
Einvalid_vararg | Trying to use variadic macro parameter syntax to resolve a non variadic argument. |
Emacro_param_outside_macro | Reference to macro parameter outside of macro |
Ebroken_for_loop | Broken for loop declaration. |
Ebad_single_line_for | Single-line for loop not allowed here. |
A multi-architecture SNES assembler by Alcaro, modelled after xkas v0.06 by byuu.
+ This manual was written by RPG Hacker, so if you find something that is wrong or weird, make sure to blame me instead of Alcaro.
As a general rule, the manual uses {}
to denote required parameters and []
to denote optional parameters (where optional parameters ending in ...
mean "zero or more" of that parameter). Everything else refers to keywords/names.
asar.exe [options] {asm_file} [rom_file]
+ Argument | +Type | +Details | +Examples | +
---|---|---|---|
[options] |
+ + | List of optional arguments. The following options are supported: | ++ |
--version |
+ Input | +Displays Asar version information. | +
|
+
-v --verbose |
+ Input | +Enables verbose mode. | +
+
|
+
--no-title-check |
+ Input | +Disables input ROM title and checksum verification when using Asar to apply a patch to an existing ROM file. Note that irresponsible use of this option will likely corrupt your ROM. | +
|
+
--pause-mode={mode} |
+ Input | +Sets Asar's pause mode, specifying when Asar should pause the application before exit, where {mode} can be one of the following:+ never : Don't pause the application (default).+ on-error : Pause the application if an error was thrown.+ on-warning : Pause the application if an error or a warning was thrown.+ always : Always pause the application. |
+
|
+
-I{path} --include {path} |
+ Input | +Adds an include search path for file-based commands to Asar. Normally, commands like incsrc, incbin etc. look for files relative to the ASM file that is currently being compiled. If those files aren't found, an error is thrown, unless you specify include search paths, in which case Asar will look for the file in each respective directory before throwing an error. For example, imagine you compiled the file
+
+ with Asar, adding the include search path
+
+ and the ASM file included the line:
+
+ Asar would now look for a file:
+
+ If this file didn't exist, it would then look for a file:
+
+ If this file didn't exist, Asar would throw an error, otherwise Asar would include it. See section Includes for details on Asar's handling of file names.
+ |
+
+
|
+
-D{identifier}[=value] --define {identifier}[=value] |
+ Input | +Adds a define to Asar. When no value is provided, the define is set to an empty string. See section Defines for details. | +
+
|
+
--symbols={format} |
+ Input | +Specifies the format of the symbols output file generated by Asar. The following values are supported for {format}: + none : Don't generate a symbols file (default).+ wla : Generate a symbols file in the WLA format. This format additionally includes an address-to-line mapping which can be used by some debuggers to provide source-level debugging.+ nocash : Generate a symbols file in the no$sns format. |
+
|
+
--symbols-path={path} |
+ Output | +Specifies the path and file name to use for generating the symbols output file. By default, the path is the path of [rom_file] and the file name is the base name of [rom_file] with an extension of .sym . Ignored when --symbols is set to none .+ Note that relative paths here are relative from the current working directory, not relative from {asm_file} or [rom_file] . |
+
|
+
-w{name} |
+ Input | +Enables the warning with the specified name. See section Warnings for details. | +
|
+
-wno{name} |
+ Input | +Disables the warning with the specified name. See section Warnings for details. | +
|
+
--fix-checksum={on/off} |
+ Input | +Overrides Asar's default behavior of enabling or disabling checksum generation based on context. When set to on , Asar always generates a checksum. When set to off , Asar never generates a checksum.+ |
|
+
--error-limit={n} |
+ Input | +Sets the maximum number of errors that Asar will print before stopping. The default is 20. | +
|
+
+ | + | + | + |
{asm_file} |
+ Input | +Path to the ASM source file. | +
|
+
[rom_file] |
+ Input, output | +Path to the ROM file that is modified by Asar. If this file doesn't exist yet, Asar creates a new ROM file instead. When omitted, Asar checks if asm_file_name.sfc or asm_file_name.smc exists and uses the one it finds. When zero or two ROMs with that filename are found, Asar defaults to the .sfc extension. As a convention, Asar always treats .smc files as headered and .sfc files as unheadered ROMs. This means that headered .sfc files or unheadered .smc files cannot be used with Asar unless their extension is changed. This is by design and meant to encourage compliance with the convention. | +
+
+
|
+
stdincludes.txt
. When a file with this name exists next to the Asar executable, Asar automatically opens it and adds every line in it as an include search path (trailing and leading whitespace on a line is ignored, as are lines containing only whitespace). Absolute and relative paths are supported. Relative paths are considered relative to the TXT file. The purpose of this file is to make it easier to distribute standard code libraries for use with Asar by making it possible to just unpack the contents of a ZIP file or similar directly into the Asar directory. Note that include search paths passed in via the command line get priority over paths parsed from this TXT file. See section Includes for details on include search paths.C:/asm/stdlib
+
+ ./debug
+../../my_game/libraries
+ test/
+ stddefines.txt
. When a file with this name exists next to the Asar executable, Asar automatically opens it and adds every line in it as an additional define. The syntax is similar to Asar's regular define syntax, with a few notable differences. There are no spaces required around the =
, the !
of the identifier is optional, whitespace around the identifier is ignored, so is whitespace around the value (unless the value is delimited by double quotes, in which case any whitespace inside is kept in the define), the value itself is optional (when left out, it is set to an emptry string). Lines containing only whitespace are ignored. The purpose of this file is to make it easier to distribute standard code libraries for use with Asar by making it possible to just unpack the contents of a ZIP file or similar directly into the Asar directory. See section Defines for details on defines.!stddefined1=1
+ stddefined2=1
+
+stddefined3
+stddefined4 = 1
+stddefined5 = " $60,$50,$40 "
+ Return to top
+ arch {name}
. Going into detail on any of the supported architectures is beyond the scope of this manual. For that, it's recommended to check the SNES Dev Manual or other specialized resources. Asar tries as much as possible to always stick to the known conventions and specifications of each respective architecture (with a few notable exceptions that are hopefully all covered somewhere in this manual).
+ Architecture | +Command | +Supported Opcodes | +Details | +
---|---|---|---|
65c816 | +arch 65816 |
+ + [+] Expand + + | +Default setting. Compiles code for the 65c816 architecture. | +
SPC700 | +arch spc700 |
+ + [+] Expand + + | +Compiles code for the SPC700 architecture. Follows the format the SNES Dev Manual recommends, with the exception of mov (x)+,a and mov a,(x)+, which are moved to mov (x+),a and mov a,(x+). See the spcblock section for an alternative way of assembling SPC700 code. | +
Super FX | +arch superfx |
+ + [+] Expand + + | +Compiles code for the Super FX architecture. | +
LDA
and lda
will be treated equally.
+ arch 65816
+lda $00
+
+arch spc700
+mov a,$00
+ $
as a prefix, binary literals use %
as a prefix. Number literals can be made positive or negative by prefixing a +
or a -
(without a sign, positive is assumed). They can also be prefixed with a ~
to get their unary complement (a 32-bit integer with all the bits inverted).
+ lda $00
+clc
+adc #-10
+and #%01111111
+lda #~$80 ; Equal to lda #$FFFFFF7F
+ Aditionally, Asar supports character literals by delimiting a single ASCII character with '
. Asar will automatically convert them to the integer value currently mapped to them (by default their ASCII value). They can be used in all places where number literals can be used. See section Tables for details on ASCII character mapping.
+ lda #'a'
+sta $00
+
+db 'x','x'+1,'x'+2
+ .b
, .w
or .l
to an opcode, you can specify that opcode's length. This is recommended in cases where the length could be ambiguous.
+ lda #0 ; Could be either lda #$00 or lda #$0000
+lda.b #0 ; Always lda #$00
+lda.w #0 ; Always lda #$0000
+ When no length is specified, Asar tries to guess the length based on the operand. Note that Asar does not use the standard <>
for length specifications to avoid ambiguity with other uses of these symbols (such as in macros or math statements). Opcode length specifications are currently supported for the 65c816 and SPC700 architectures.
+ {opcode} #{num}
+ This assembles opcode
num
times in succession. This means that
+ nop #3
+
+inx #2
+ is the same as
+ nop
+nop
+nop
+
+inx
+inx
+ rep {num} : {code}
+
+ Warning: this command is deprecated due to conflicting with the 65816 assembly REP
instruction. In most cases, you can achieve the same effect with a for loop.
num
times. It functions similarly to pseudo opcodes, but without being limited to certain opcodes only. Instead, you can repeat almost any bit of code supported by Asar, even macro calls. Note that in xkas compatibility mode, rep 0
will compile the following code once, whereas rep -1
(or any number < 0
) won't compile the following code at all. This is to keep compatibility with old patches, which occasionally used the rep command as a replacement for conditionals. Also note that stacking multiple rep commands is not supported (only the most recent rep will take effect).
+ macro writeval(val)
+ db <val>
+endmacro
+
+macro memset(val, num)
+ rep <num> : %writeval(<val>)
+endmacro
+
+rep 16 : %memset($00, 16)
+
+ N-SPC
engine is supported). The general format looks like this:
+ spcblock {target_address} [{engine_type}]
+ [spc700_instruction...]
+endspcblock [execute {execution_address}]
+ Inside an spcblock
, arch spc700
is automatically active (see section Architectures for details).
+ The target_address
parameter specifies the target address (in ARAM) for the command data. The optional execute
parameter tells Asar to generate a "start execution" command immediately after this SPC block, with execution_address
as the ARAM address to start execution at. The engine_type
parameter specifies which SPC engine to use. When omitted, the default value of nspc
is used. The following engine types are supported:
+ Parameter | +Engine Type | +Description | +Output Format | +Example Code | +Example Output | +
---|---|---|---|---|---|
nspc |
+ N-SPC | +Implements the format used by the N-SPC engine found in most Nintendo games, as well as by the SPC700's initial program loader. | +
|
+
+ |
|
+
Wmapper_already_set
.Command | +Details | +
---|---|
lorom |
+ Switch to LoROM mapping mode. | +
hirom |
+ Switch to HiROM mapping mode. | +
exlorom |
+ Switch to ExLoROM mapping mode. | +
exhirom |
+ Switch to ExHiROM mapping mode. | +
sa1rom [bank_1,bank_2,bank_3,bank_4] |
+ Switch to hybrid SA-1 mapping mode. To tell which banks are mapped in (maximum is 7) use the optional parameter, like so:
+
+ The default is 0,1,2,3. |
+
fullsa1rom |
+ Switch to full SA-1 mapping mode. | +
sfxrom |
+ Switch to Super FX mapping mode. | +
norom |
+ Disable Asar's address translation; the SNES address is equal to the PC address. Can be combined with base and macros to implement your own address translation. |
+
lorom
+org $008000
+db $FF ; Will write to PC address 0x000000
+
+hirom
+org $008000
+db $FF ; Will write to PC address 0x008000
+ Return to topxkas
command enables Asar's xkas compatibility mode. In this mode, Asar tries to replicate the behavior of xkas as much as possible and throws warnings whenever it detects the usage of Asar-specific features that are not compatible with xkas. This command has to be used before any other command in the same patch. The intended purpose of the xkas command is to use it in conjunction with ;@
to write patches that can be assembled with both, Asar and xkas. See section Comments for details. Note that as of Asar version 1.40, xkas backwards compatibility is officially deprecated and is no longer guaranteed to work as expected. New features introduced into Asar since then might not throw warnings when attempted to use in xkas compatibility mode and old xkas patches might not assemble correctly with Asar anymore, even when xkas compatibility mode is used. xkas compatibility mode will be fully removed in Asar 2.0, along with the ;@
comments.
+ ;@xkas
+
+!is_asar=0
+;@!is_asar=1
+
+;@if !is_asar == 0
+ macro do_something()
+ ; Do something xkas-specific here
+ endmacro
+;@else
+;@ macro do_something()
+;@ ; Do something asar-specific here
+;@ endmacro
+;@endif
+
+do_something()
+ asar {ver}
+ The asar
command can be used to specify the minimum Asar version your patch is compatible with. The ver
parameter specifies the minimum required Asar version. When a user tries to assemble the patch in an older version of Asar, an error will be thrown, stating that the used Asar version is too old. This should be the first command in your patch, otherwise an error will be thrown.
+ ; This patch uses features from Asar 1.40, so it makes sense to require it as a minimum.
+asar 1.40
+
+if readfile1("data.bin", 0) == 1
+ ; Do something
+else
+ ; Do something else
+endif
+ warn xkas {on/off}
+ The warn xkas
command determines whether Asar should throw warnings on behavior that is known to be different between xkas and Asar. Use warn xkas on
to enable these warnings and warn xkas off
to disable them. It's recommended to only enable them when not using xkas compatibility mode. Note that some behavioral changes throw warnings or errors regardless of this setting.
+ math pri {on/off}
+ The math pri
command tells Asar which order of operations to use in math experssions. When set to off (default), Asar uses left-to-right math, just like xkas v0.06. When set to on, Asar follows the conventional order of operations (exponentiation before multiplication & division, multiplication & division before addition & subtraction etc.). Parentheses can be used in either mode and tell Asar to calculate the expression inside first. For most intents and purposes, working with this flag enabled is more practical and predictable. It is disabled by default solely for the purpose of xkas backwards compatibility, unless asar 1.9
or a higher version is specified at the start of the file, which will change the default to math pri on
. This command will be removed in Asar 2.0, after which math pri on
will be the only possible behavior.
+ math pri off
+db 1+(6/3)*5 ; db 15
+
+math pri on
+db 1+(6/3)*5 ; db 11
+ math round {on/off}
+ The math round
command tells Asar which rounding behavior to use in math expressions. When set to on (default), Asar truncates all numbers immediately, whereas when set to off, Asar only truncates numbers whenever they need to be cast to an integer type. Note that having this flag enabled will make it practically impossible to work with floating point numbers since calculations will lead to unexpected and impractical results. It is the default setting solely for the purpose of xkas backwards compatibility. When writing patches specifially for Asar, it is recommended to always disable this flag. If you specify a minimum supported Asar version using asar 1.9
or higher, the default will be math round off
. This command will be removed in Asar 2.0, after which math round off
will be the only possible behavior.
+ math round on
+; 1.75 is immediately truncated to 1, resulting in (3/4)+1
+; 3/4 would result in 0.75, which is immediately truncated to 0, resulting in 0+1
+; Thus leading to the final result of "db 1"
+db (3/4)+1.75
+
+; !some_number contains 0 after this line
+!some_number #= 0.75
+
+math round off
+; As expected will result in 0.75+1.75, which will result in 2.5
+; 2.5 is truncated to 2
+; Thus leading to the final result of "db 2"
+db (3/4)+1.75
+
+; !some_number contains 0.75 after this line
+!some_number #= 0.75
+ namespace nested {on/off}
+ The namespace nested
command enables (on
) or disables (off
) nested namespaces. The default is off
. See section Namespaces for details. lda $00 ; Asar only sees the lda $00 and ignores everything else
+ Previously, there were exceptions to this rule in the form of lines starting with ;@
, which are assembled normally. This functionality is deprecated, Asar 2.0 will treat these lines as regular comments. The purpose of this feature was to add code to your patch that can be used with both xkas and Asar. In xkas, lines with ;@ will simply be ignored, whereas in Asar, they would be assembled. If you don't care about backwards-compatibility, you can also just use @
, which will make your patch only assemble with Asar and fail with xkas. (This too is deprecated.) Aditionally, when Asar finds any unknown command on a line starting with ;@ or @, it will only throw a warning instead of an error. This can, at least in theory, be used to include optional features from newer Asar versions and still have your patch be compatible with older Asar versions. See section Compatibility Settings for details on xkas compatibility.
+ Also note that comments starting with ;[[
are reserved for multi-line comments in a future version of Asar. You should either change the beginning or make sure that there's a ]]
(the comment terminator) at the end of the line.
lda $00
+ beq .IsZero
+
+.GreaterThanZero
+ {
+ dec $00
+ }
+
+.IsZero
+ rts
+ ,
and the \
operator are formatting operators which make it possible to split commands in Asar into multiple lines. Both are put at the end of a line and work very similarly with only one key difference. During execution, Asar will concatenate subsequent lines to lines ending with either operator and treat them as a single line. When using the comma operator, the comma itself will actually remain a part of the concatenated string, whereas when using the backslash operator, the backslash itself will be removed from the concatenated string. When using the backslash operator, please note that all whitespace following it is ignored, whereas all whitespace preceeding it is preserved. This is by design, since some commands in Asar require spaces to work, whereas other commands (like math commands) only work without spaces.
+ %some_macro(!arg1, !arg2, !arg3,
+ !arg4, !arg5, !arg6)
+; This will be treated as "%some_macro(!arg1, !arg2, !arg3, !arg4, !arg5, !arg6)"
+
+lda \
+ $7F0000
+; This will be treated as "lda $7F0000"
+
+function func(param) = ((param*param)+1000)\
+ /256
+; This will be treated as "function func(param) = ((param*param)+1000)/256"
+
+ :
is a formatting operator which makes it possible to treat a single line of code as multiple lines. It requires a space before and after usage to differentiate it from the : used with certain commands. When used between different commands, Asar interprets it similarly to a new line and treats each command as being on a separate line. This can be used to link multiple commands together into functional blocks and make the code more readable.
+ lda #$00 : sta $00
+
+; Treated as:
+lda #00
+sta $00
+ Return to toporg {snes_address}
+ The org command directly sets the pc to snes_address
. Most commonly used inside patches to specify which code to hijack or which data to overwrite.
+ org $008000
+MainEntryPoint:
+ ; ...
+ base {snes_address/off}
+ The base command makes Asar act as though the pc was currently set to snes_address
without actually setting it; base off
deactivates this behavior. This can be useful for writing code that you plan to execute from another location (such as RAM).
+ org $008000
+MainEntryPoint:
+ ; Some code which copies SomeRamRoutine to $7E0000 goes here
+ ; ...
+ jsl $7E0000
+ ; ...
+
+SomeRamRoutine:
+base $7E0000
+ ; ...
+base off
+ rtl
+ skip {num_bytes}
+skip align {alignment} [offset {offset}]
+ The skip command moves the pc by num_bytes
bytes. By specifying a negative value, the pc can be moved backwards. When alignment
is given, skips to the next multiple of alignment
, plus offset
if it is specified. Note that the alignment must be a power of 2, if specified. Offset can also be negative, in that case it's treated exactly like alignment+offset
. The seeked-to position will always be after the current SNES position, but it might be before the next multiple of alignment
: see the last example.
+ org $008000
+skip 5
+; pc is now at $008005
+skip -1
+; pc is now at $008004
+skip align 16
+; pc is now at $008010
+skip align 16 offset 5
+; pc is now at $008015
+skip align $20 offset $17
+; pc is now at $008017
+
+ warnpc {snes_address}
+ DEPRECATED: you can achieve the exact same thing with assert pc() <= {snes_address}
.> snes_address
. If that's the case, it throws an error. This is useful for detecting overflow errors.
+ org $008000
+incbin datafile.bin
+warnpc $008100 ; Throws an error if datafile.bin is larger than $100 bytes.
+ bank {data_bank/noassume/auto}
+ The bank command makes Asar's label optimizer act as though the current data bank was set to data_bank
. Consider the following example:
+ bank $FF
+
+lda DataTable,x
+
+DataTable:
+ db $01,$02,$03,$04
+ Asar will always assemble the lda DataTable,x
with 24-bit addressing, unless the current pc (or base address) is inside bank $FF
itself. This is intended for code that uses a data bank register different from the code bank register. You can use bank noassume
to make Asar act as though the data bank was always in a different bank. Using bank auto
restores the default behavior of assuming that the data bank register and the code bank register are the same. Note that the bank command can't point to freespace areas.
+ org $008000
+phb
+lda #$FF
+pha
+plb
+
+bank $FF
+; ...
+bank auto
+
+plb
+ dpbase {snes_address}
+ The dpbase
command makes Asar's label optimizer assume the Direct Page register is set to the specified address. When used with the optimize dp
command, this will cause Asar to use 8-bit addressing where possible. For example, in the following code Asar can assemble lda SpriteTable,x
as a direct page address.
+
+ SpriteTable = $7E0200
+dpbase $0200
+optimize dp ram
+
+org $008000
+lda SpriteTable,x
+
+ optimize dp {none/ram/always}
+ This command changes how aggressive Asar's direct page access optimizer is. With optimize dp none
(the default), the direct page optimizer is disabled and direct page accesses will only be done with the .b
instruction suffix or with explicit addresses like lda $42
. With optimize dp ram
, direct page optimization will be performed according to the dpbase setting, but only on labels in bank $7E
. With optimize dp always
, direct page optimization will be performed on all labels in banks that have RAM mirrors, i.e. 00-3F and 80-BF, and also on labels in bank 7E.
+
+ optimize address {default/ram/mirrors}
+ This command changes how aggressive Asar's label optimizer is. With optimize address default
, references to labels will be shortened to 2 bytes only if the label is in the current data bank. With optimize address ram
, additionally labels between $7E:0000-$7E:1FFF
will be shortened to 2 bytes if the current data bank has RAM mirrors ($00-$3F
and $80-$BF
). With optimize address mirrors
, additionally labels between $00-3F:2000-7FFF
(that is, $00:2000-$00:7FFF
all the way up to $3F:2000-$3F:7FFF
) will be shortened to 2 bytes whenever the current data bank has RAM mirrors. Note that in freespace, the current bank will be assumed from whether the freespace was started as freecode
or freedata
, not where the freespace was actually placed in the end.
+
+ org $008000
+
+Main:
+ jsl CodeInAnotherBank
+
+pushpc
+org $018000
+
+CodeInAnotherBank:
+ ; ...
+ rtl
+
+pullpc
+
+bra Main
+ base $7E2000
+
+InsideRam:
+ jsl OutsideOfRam
+ ; ...
+
+pushbase
+pushpc
+base off
+
+freecode
+
+OutsideOfRam:
+ ; ...
+ jsl InRamAgain
+ rtl
+
+pullpc
+pullbase
+
+InRamAgain:
+ ; ...
+ rtl
+
+base off
+ Return to toplda #6*2+5 ; the same as "lda #17"
+lda #5+6*2 ; the same as "lda #22"
+ This behavior can be changed by using the command math pri on
, which makes Asar apply conventional prioritization rules in all math statements. (See math pri)
+ math pri on
+
+lda #6*2+5 ; the same as "lda #17"
+lda #5+6*2 ; the same as "lda #17"
+ In both modes, Asar supports parentheses for explicit control over the order of operations.
+ math pri on
+
+lda #5+6*2 ; the same as "lda #17"
+lda #(5+6)*2 ; the same as "lda #22"
+ Math statements in Asar support the following operators:+ : Addition (Also valid as prefix, but a no-op)
+- : Subtraction (Or negation prefix)
+* : Multiplication
+/ : Division
+% : Modulo (the remainder of a division, fmod() in C)
+<< : Left-shift ( x << y formula: x = x * 2^y )
+>> : Right-shift ( x >> y formula: x = x / 2^y )
+& : Bitwise AND
+| : Bitwise OR
+^ : Bitwise XOR (Note: not exponentials)
+~ : Bitwise NOT (Prefix)
+<: : Bitshift right 16, shorthand for isolating address bank (Prefix)
+** : Exponentials (2**4 = 2*2*2*2 = pow(2, 4) in C)
lda .Data+3 ; Will load $03 into A
+
+.Data
+ db $00,$01,$02
+ db $03,$02,$03
+ Return to top[#]{identifier}:
+ Main labels are the top-most level of labels supported by Asar. They're global and thus can be directly acessed from anywhere. Their identifier can contain any of the following charactersa-z A-Z 0-9 _
+ org $008000
+
+Main:
+ %do_frame()
+ jmp Main ; Equal to jmp $8000
+ An alternate form of defining main labels is by directly assigning a value to them. A common use-case for this is to make a label point to an existing address inside a ROM. Syntax:
+ {identifier} = {snes_address}
+ where snes_address
can be a number or any math statement evaluating to an SNES address. Note that defining a main label this way does not start a new sub label group.
+ Main:
+; ...
+
+SomewhereInRom = $04CA40
+
+.Sub:
+; ...
+
+Table:
+ dl Main_Sub ; Okay!
+ dl SomewhereInRom_Sub ; Error, label not found
+ Prefixing a label definition (except label assignments) with a #
will define the label without modifying existing label hierarchies. This can be useful for defining global routines inside call-anywhere macros without having them break existing label hierarchies.
+ macro my_new_routine()
+ jsl MyNewRoutine
+
+ !macro_routine_defined ?= 0
+
+ if !macro_routine_defined == 0
+ pushpc
+
+ freecode cleaned
+
+ #MyNewRoutine:
+ incsrc routines/mynewroutine.asm
+
+ pullpc
+
+ !macro_routine_defined = 1
+ endif
+endmacro
+
+Main:
+ %my_new_routine()
+.Sub
+
+ ; Both of these are found
+ dl MyNewRoutine
+ dl Main_Sub
+ Asar includes a label optimizer which attempts to optimize performance by shortening opcodes accessing labels from 24-bit to 16-bit whenever possible. See section Program Counter for details.
+ [#].{identifier}[:]
+ Sub labels are the second-most level of labels supported by Asar. They're local to the last main label declared and their identifiers can contain the same characters as main labels.
+ Proc1:
+ nop
+.Sub
+ bra .Sub
+
+Proc2:
+ nop
+.Sub: ; Note that the colon is optional
+ bra .Sub
+ Sub labels allow you to reuse redundantly named labels such as Loop, End, etc. without causing label redefinition errors. A new sub label group is automatically started after a main label is declared. Internally, sub labels are converted to MainLabel_SubLabel
, which can be used to access them from anywhere.
+ Main1:
+ ; ...
+.Sub1:
+ ; ...
+.Sub2:
+ ; ...
+
+Main2:
+ ; ...
+.Sub1:
+ ; ...
+.Sub2:
+ ; ...
+
+Table:
+ dl Main1_Sub1
+ dl Main1_Sub2
+ dl Main2_Sub1
+ dl Main2_Sub2
+ Sub labels can themselves contain sub labels to an arbitrary depth by prepending additional dots.
+ Main1:
+; ...
+.Sub:
+; ...
+..Deeper:
+; ...
+...TheEnd:
+; ...
+
+Table:
+ dl Main1_Sub_Deeper_TheEnd
+ Prefixing a sub label definition with a #
will define the sub label without modifying existing label hierarchies, but there is probably no practical use for this and it's unintuitive, so it should be avoided.
+ +[+...][:]
+ -[-...][:]
+ +/- labels are a special type of labels that are different from both main labels and sub labels in that they don't refer to a specific location in code, but rather to a location relative from where they are used. When used inside opcodes etc., +
always refers to the next + label and -
always refers to the previous - label. You can also chain an arbitrary number of + or an arbitrary number of - to create unique +/- labels that don't overwrite labels with a different number of +/-, for example +++
or -----
.
+ ldx.b #4
+
+-- ; A
+ lda $10,x
+ beq + ; Branches to "C"
+
+ ldy.b #8
+
+- ; B
+ %do_something()
+
+ dey
+ bne - ; Branches to "B"
+
++: ; C - note that +/- labels can also include an optional colon in their declaration
+ dex
+ bpl -- ; Branches to "A"
+ +/- labels are useful in a number of situations. For example: inside a long routine with multiple short loops, where even a sub label like .Loop
would get repetitive. +/- labels aren't bound to any scope, which means they can technically be used across different scopes. Just like sub labels, +/- labels are converted to main labels internally. Unlike sub labels, they can not be referenced from code directly since their names depend on where in the code they're used, making it impractical to directly refer to them. This is by design. They can, however, be accessed via the Asar DLL API, and their full name may appear in error messages printed by Asar. The naming format used for them is:pos_x_y
:neg_x_y
x
= number of chained +/-y
= instance of this label within all +/- labels of the same name (starting from 0 for + labels and from 1 for - labels).
+ lorom
+org $008000
+
+--- ; :neg_3_1
+- ; :neg_1_1
+ bra -
+-- ; :neg_2_1
+- ; :neg_1_2
+ bra ---
+ bra --
+ bra -
+
+ bra ++
+ bra +
+ bra +++
+
+++ ; :pos_2_0
++ ; :pos_1_0
+ bra ++
+++ ; :pos_2_1
++++ ; :pos_3_0
+ [#]?{identifier}:
+ ?{identifier} = {snes_address}
+ [#]?.{identifier}[:]
+ ?+[+...]
+ ?-[-...]
+ Macro labels are special variations of the labels mentioned in the previous sections. Functionally, they behave the same as the other labels with the exception of being local to the macro they're used in. This means they can't be referenced from outside the respective macro. Macro labels are created by prefixing any of the other label types with a ?
.
+ macro do_something()
+ ?MacroMainLabel:
+ ?.MacroSubLabel
+ ?-
+ ; All of these are fine!
+ dl ?MacroMainLabel
+ dl ?.MacroSubLabel
+ dl ?-
+ dl ?+
+ dl ?MacroMainLabel_MacroSubLabel
+ ?+
+endmacro
+
+%do_something()
+
+; ERROR! ?MacroMainLabel is undefined, because we're not inside %do_something() anymore.
+dl ?MacroMainLabel
+ Prefixing a macro label definition (except for macro label assignments and macro +/- labels) with a #
will define the macro label without modifying existing label hierarchies, but there is probably no practical use for this, so it should be avoided.
+ Like all other labels, macro labels are converted to main labels internally and prefixed with an identifier of:macro_x_
x
= total macro call instance. They can't be referenced in code directly, except inside their respective macro and using the respective macro label syntax seen above. They can, however, be accessed via the Asar DLL API, and their full name may appear in error messages printed by Asar.
+ struct {identifier} {snes_address}
+ [label...]
+endstruct [align num]
+ where identifier
can contain any of the following characters:a-z A-Z 0-9 _
snes_address
parameter can be any number literal or math statement evaluating to an SNES address. This address marks the start of the struct. The label
parameter should be any number of labels, ideally coupled with skip commands. These labels become offsets into the struct. Internally, the struct command will do something similar to this
+ pushpc
+base snes_address
+ whereas the endstruct command will do something similar to this
+ base off
+pullpc
+ Take a look at the simple example below:
+ struct ObjectList $7E0100
+ .Type: skip 1
+ .PosX: skip 2
+ .PosY: skip 2
+ .SizeX: skip 1
+ .SizeY: skip 1
+endstruct
+ This defines a struct called ObjectList
at location $7E0100
with a size of 7
(the sum of all skip commands). You can access into this struct like so:
+ lda ObjectList.PosY
+ This is equal to:
+ lda $7E0103 ; $7E0100+1+2
+ The final address is calculated by taking the start of the struct ($7E0100
) and adding to that all the skips preceding the .PosY
label (1
and 2
). Aside from accessing structs directly, it's also possible to access them as arrays. A simple example:
+ lda ObjectList[2].PosY
+ The final address in this case is calculated by the equation:struct_start + (array_index * struct_size) + label_offset
$7E0100 + (2 * 7) + (1 + 2) = $7E0111
. When using structs this way, the optional align
parameter becomes relevant. This parameter controls the struct's alignment. Simply put, when setting a struct's alignment, Asar makes sure that its size is always a multiple of that alignment, increasing the size as necessary to make it a multiple. Let's take another look at the example above with an added alignment:
+ struct ObjectList $7E0100
+ .Type: skip 1
+ .PosX: skip 2
+ .PosY: skip 2
+ .SizeX: skip 1
+ .SizeY: skip 1
+endstruct align 16
+ With an alignment of 16 enforced, this struct's size becomes 16 (the first multiple of 16 that 7 bytes fit into). So when accessing the struct like this
+ lda ObjectList[2].PosY
+ the final address becomes $7E0100 + (2 * 16) + (1 + 2) = $7E0123
. If we add some data into the struct
+ struct ObjectList $7E0100
+ .Type: skip 1
+ .PosX: skip 2
+ .PosY: skip 2
+ .SizeX: skip 1
+ .SizeY: skip 1
+ .Properties: skip 10
+endstruct align 16
+ its original size becomes 17. Since a final size of 16 would now be too small to contain the entire struct, the alignment instead makes the struct's final size become 32 (the first multiple of 16 that 17 bytes fit into), so in our example of
+ lda ObjectList[2].PosY
+ we now end up with a final address of $7E0100 + (2 * 32) + (1 + 2) = $7E0143
.struct {extension_identifier} extends {parent_identifier}
+ [label...]
+endstruct [align num]
+ This adds the struct extension_identifier
at the end of the previously defined struct parent_identifier
. Consider the following example:
+ struct ObjectList $7E0100
+ .Type: skip 1
+ .PosX: skip 2
+ .PosY: skip 2
+ .SizeX: skip 1
+ .SizeY: skip 1
+endstruct
+
+struct Properties extends ObjectList
+ .Palette: skip 1
+ .TileNumber: skip 2
+ .FlipX: skip 1
+ .FlipY: skip 1
+endstruct
+ The struct ObjectList
now contains a child struct Properties
which can be accessed like so:
+ lda ObjectList.Properties.FlipX
+ Since extension structs are added at the end of their parent structs, the offset of .FlipX
in this example is calculated asparent_struct_start_address + parent_struct_size + extension_struct_label_offset
,$7E0100 + 7 + (1 + 2) = $7E0109
. Note that extending a struct also changes its size, so in this example, the final size of the ObjectList
struct becomes 12. Extended structs can also be accessed as arrays. This works on the parent struct, as well as the extension struct.
+ lda ObjectList[2].Properties.FlipX
+ lda ObjectList.Properties[2].FlipX
+ In the first example, our final address is calculated asparent_struct_start_address + (combined_struct_size * array_index) + parent_struct_size + extension_struct_label_offset
,parent_struct_start_address + parent_struct_size + (extension_struct_size * array_index) + extension_struct_label_offset
,$7E0100 + (12 * 2) + 7 + (1 + 2) = $7E0122
and $7E0100 + 7 + (5 * 2) + (1 + 2) = $7E0114
._
. Namespaces can be stacked if desired by enabling the namespace nested setting. When you try to access a label from within a namespace and Asar doesn't find it in there, it automatically looks in the upper namespaces (up to the global namespace). Usenamespace {identifier}
+ to enter a namespace, where identifier
can contain any of the following characters:a-z A-Z 0-9 _
namespace off
+ to leave the current namespace (or immediately return to the global namespace when nested namespaces are not enabled).
+ ; All of the below is valid
+
+namespace nested on
+
+Main: ; Main
+Main2: ; Main2
+
+namespace Deep
+
+ Main: ; Deep_Main
+
+ namespace Deeper
+
+ Main: ; Deep_Deeper_Main
+ Main3: ; Deep_Deeper_Main3
+
+ namespace Deepest
+
+ Main: ; Deep_Deeper_Deepest_Main
+
+ dl Main ; Deep_Deeper_Deepest_Main
+ dl Main2 ; Main2
+ dl Main3 ; Deep_Deeper_Main3
+
+ namespace off
+
+ dl Main ; Deep_Deeper_Main
+
+ namespace off
+
+ dl Main ; Deep_Main
+
+namespace off
+
+
+namespace nested off
+
+namespace TheFirst
+
+ Main: ; TheFirst_Main
+
+ dl Main ; TheFirst_Main
+
+namespace TheSecond
+
+ Main: ; TheSecond_Main
+
+ dl Main ; TheSecond_Main
+
+namespace TheThird
+
+ Main: ; TheThird_Main
+
+ dl Main ; TheThird_Main
+
+namespace off
+
+
+dl Main ; Main
+dl Deep_Main ; Deep_Main
+dl Deep_Deeper_Main ; Deep_Deeper_Main
+dl Deep_Deeper_Deepest_Main ; Deep_Deeper_Deepest_Main
+
+dl TheFirst_Main ; TheFirst_Main
+dl TheSecond_Main ; TheSecond_Main
+dl TheThird_Main ; TheThird_Main
+ pushns
saves the current namespace. pullns
restores the last-pushed value of the namespace.
+
+ global
to define labels outside all namespaces. The syntax is global [#]{identifier}:
. For example:
+
+namespace NS
+global GlobalLabel:
+.Sub: ; this is a sublabel of GlobalLabel
+
+LocalLabel:
+
+global #AnotherGlobal: ; this global won't modify the sublabel hierarchy
+
+.Sub: ; this is a sublabel of LocalLabel
+namespace off
+
+; these are all valid:
+dl NS_LocalLabel
+dl NS_LocalLabel_Sub
+dl GlobalLabel
+dl GlobalLabel_Sub
+dl AnotherGlobal
+
+ Note that #
acts the same way as it does for regular labels.global
command with sublabels or macro labels. Outside of a namespace, global
acts just like a regular label definition.!
and declared as follows:!{identifier} = {value}
+ !{identifier} = "{value}"
+ where identifier
is a unique identifier that can contain any of the following characters:a-z A-Z 0-9 _
!identifier=value
will not work. Since defines are really just placeholders for text, they can contain anything - labels, math formulas, even other defines.
+ !x = $00
+
+lda !x ; Treated as "lda $00"
+lda #!x ; Treated as "lda #$00"
+lda [!x],y ; Treated as "lda [$00],y"
+
+!y = $12
+!x = !y$34
+
+lda !x ; Treated as "lda $12$34" (will throw error)
+
+!phr = "pha : phx : phy"
+
+!phr ; Treated as "pha : phx : phy"
+ To assign text containing whitespace to a define, you must delimit it with two "
as shown above with !phr. Besides the regular define operator =
, Asar also supports a number of additional define operators with slightly different functionality.Operator | +Functionality | +Example | +
---|---|---|
= |
+ The standard define operator. Directly assigns text to a define. | +
|
+
+= |
+ Appends text to the current value of a define. | +
|
+
:= |
+ Equal to the standard = , but resolves all defines in the text to assign before actually assigning it. This makes recursive defines possible. |
+
|
+
#= |
+ Evalutes the text as though it was a math expression, calculates its result and assigns it to the define. The math is done in-place on the same line the operator is used on and is affected by all of Asar's math settings (such as prioritization rules and rouding behavior). | +
|
+
?= |
+ Equal to the standard = , but only assigns text to a define that doesn't exist yet, otherwise does nothing. |
+
|
+
defined("{identifier}")
function and to delete a define using the undef "{identifier}"
command. Make sure to leave the !
out of the identifier when using these functions, as Asar would otherwise try to resolve the defines.
+ !define = "hello"
+
+if defined("define")
+ print "This will be printed!"
+endif
+
+undef "define"
+
+if defined("define")
+ print "This won't be printed!"
+endif
+ Note that Asar tries to replace defines wherever possible, even inside strings. In some occasions, this might be undesirable. See section Tables for details on how to escape certain characters.
+ {}
operator makes it possible to still use those defines by resovling everything inside the braces immediately.
+ !hex = $
+
+db !hexFF ; Error - define !hexFF not found
+db !{hex}FF ; OK
+ Perhaps the more useful feature of this operator is that it can also be nested to allow for the creation of dynamic define names.
+ ; Please specifiy a mode from 0 to 3
+!mode = 1
+
+assert !mode >= 0 && !mode <= 3, "Please specify a mode from 0 to 3!"
+
+!modename0 = "Default"
+!modename1 = "Debug"
+!modename2 = "Fast"
+!modename3 = "Small"
+
+!modenamestring = !{modename!{mode}}
+
+print "Building in mode: !modenamestring"
+ Define | +Details | +Example | +
---|---|---|
!assembler |
+ Contains the value asar . Theoretically can be used to differentiate between different assemblers if other assemblers use this define and a syntax similar to Asar. |
+
|
+
!assembler_ver |
+ Contains the version number of Asar in the format (major_version * 10000) + (minor_version * 100) + revision . For Asar version 1.60, this contains 10600. |
+
|
+
macro {identifier}([parameter1_identifier[, parameter2_identifier...]][variadic_token])
+ [command1]
+ [command2...]
+endmacro
+ where all the identifiers can contain any of the following characters:a-z A-Z 0-9 _
<parameter_identifier>
to expand a parameter inside a macro. This works just like placing a !define_identifier
anyhwere else in the code. Macros can be recursive (macros calling themselves) and/or nested up to 512 levels deep. This limit only serves the purpose of preventing infinite recursion. The first and last line of the macro definition need to go on their own lines (the single-line operator is not supported here). To call a macro that has already been defined, use the syntax
+ %{identifier}([parameter1[, parameter2...]])
+ where each individual parameter may be wrapped in double quotes (which is required for parameters that contain any whitespace).
+ macro mov(target, source)
+ lda <source>
+ sta <target>
+endmacro
+
+macro swap(first, second)
+ %mov($00, <first>)
+ %mov(<first>, <second>)
+ %mov(<second>, $00)
+endmacro
+
+macro use_x_safely(code)
+ phx
+ <code>
+ plx
+endmacro
+
+%swap($01, $02)
+%use_x_safely("ldx $10 : stx $11 : ldx #10 : stx $12")
+
+
+In addition to named substitutions if the variadic token ...
is specified as the last parameter asar will allow an arbitrary number of parameters after all prior parameters have been satisfied.
+To access unnamed parameters of a variadic macro, use the syntax <...[{math}]>
, where math
is any math expression evaluating to the index of a variadic parameter. These are declared numerically starting from 0 up to the number of provided parameters. To access the number of provided variadic arguments one may use sizeof(...)
.
+Lastly, it is important to note that while traditionally macros do not parse defines at their creation variadic macros will. This is to allow iteration of arguments by using defines.
+
+
+macro example0(...)
+ db sizeof(...), <...[0]> ;04 01
+endmacro
+
+macro example1(...)
+ !a #= 0
+ while !a < sizeof(...)
+ db <...[!a]> ;01 02 03
+ !a #= !a+1
+ endif
+endmacro
+
+macro example2(named_parameter, ...)
+ !a #= 0
+ while !a < sizeof(...)
+ db <...[!a]> ;02 03 04 05 06 07
+ !a #= !a+1
+ endif
+ db <named_parameter> ;01
+endmacro
+
+macro macro_with_optional_arguments(required, ...)
+ db <required>
+ if sizeof(...) > 0
+ db <...[0]>
+ end
+end
+
+%example0(1,2,3,4)
+%example1(1,2,3)
+%example2(1,2,3,4,5,6,7)
+%macro_with_optional_arguments(1)
+%macro_with_optional_arguments(2, 3)
+
+ Return to topfunction {identifier}([parameter1_identifier[, parameter2_identifier...]]) = {some_math_statement}
+ where all the identifiers can contain any of the following charactersa-z A-Z 0-9 _
some_math_statement
can be any math statement supported by Asar (including the use of other functions). Use a parameter's name to expand it inside a function.
+ function kilobytes_to_bytes(kb) = kb*1024
+function megabytes_to_kilobytes(mb) = mb*1024
+function megabytes_to_bytes(mb) = kilobytes_to_bytes(megabytes_to_kilobytes(mb))
+
+; Will print "4 MB = 4194304 bytes."
+print "4 MB = ",dec(megabytes_to_bytes(x))," bytes."
+
+
+function data_index_to_offset(index) = index*2
+
+lda .Data+data_index_to_offset(2) ; Will load $0002 into A
+
+.Data
+ dw $0000
+ dw $0001
+ dw $0002
+
+ Function definitions must be on a single line and can't include whitespace in their math statements, except when using the multi line operator \, which can be used to split long function definitions into multiple lines.
+ Function | +Details | +Example | +
---|---|---|
read1(pos[, default])
+read2(pos[, default])
+read3(pos[, default])
+read4(pos[, default]) |
+ Read one/two/three/four byte(s) from the output ROM at SNES location pos. Mainly intended for detecting the presence of certain hijacks/patches in a ROM. Throws an error when given an invalid address, unless the optional parameter default is provided in which case it is returned. | +
|
+
readfile1(filename, pos[, default])
+readfile2(filename, pos[, default])
+readfile3(filename, pos[, default])
+readfile4(filename, pos[, default]) |
+ Read one/two/three/four byte(s) from file filename at position pos (see section Includes for details on Asar's handling of file names). Throws an error when the referenced file doesn't exist or the given position is out-of-bounds, unless the optional parameter default is provided in which case it is returned. | +
|
+
canread1(pos)
+canread2(pos)
+canread3(pos)
+canread4(pos)
+canread(pos, num) |
+ Returns 1 if reading one/two/three/four/num bytes from the output ROM at SNES location pos would succeed and 0 otherwise. | +
|
+
canreadfile1(filename, pos)
+canreadfile2(filename, pos)
+canreadfile3(filename, pos)
+canreadfile4(filename, pos)
+canreadfile(filename, pos, num) |
+ Returns 1 if reading one/two/three/four/num bytes from file filename at position pos would succeed and 0 otherwise (see section Includes for details on Asar's handling of file names). | +
|
+
filesize(filename) |
+ Returns the size of file filename. Throws an error if the file doesn't exist. | +
|
+
getfilestatus(filename) |
+ Checks the status of file filename. Returns 0 if the file exists and can be read from, returns 1 if the file doesn't exist and returns 2 if the file exists, but can't be read from for any other reason (like being read-protected, being locked etc.). | +
|
+
sqrt(x) |
+ Computes the square root of x. | ++ |
sin(x)
+cos(x)
+tan(x)
+asin(x)
+acos(x)
+atan(x)
+arcsin(x)
+arccos(x)
+arctan(x) |
+ Various trigonometric functions. Units are in radians. | ++ |
log(x)
+log2(x)
+log10(x) |
+ Logarithmic functions (base-e, base-2 and base-10 respectively). | ++ |
snestopc(address)
+pctosnes(address) |
+ Functions for converting between SNES and PC addresses. Affected by the current mapping mode. | +
|
+
min(a, b)
+max(a, b) |
+ Return the minimum/maximum of two numbers. | +
|
+
clamp(value, minimum, maximum) |
+ Makes sure that value stays within the bounds set by minimum and maximum. Equal to min(max(value, minimum), maximum) . |
+
|
+
safediv(a, b, exception) |
+ Returns a/b unless b is 0 in which case exception is returned. Intended for avoiding division by zero errors in functions. |
+
|
+
select(statement, true, false) |
+ Returns false if statement is 0 and true otherwise. Can be considered an if/else conditional that is usable within functions. + NOTE: Asar always evaluates all parameters of a function before calling it, so if, for example, you pass an expression that divides by zero to select() as true, Asar will throw a division by zero error even if statement evalutes to 0 and thus false would be returned. To work around this, you can use the safediv() function in place of a regular division. |
+
|
+
not(value) |
+ Returns 1 if value is 0 and 0 in any other case. Useful for negating statements in the select() function. |
+
|
+
bank(value) |
+ Returns value>>16 |
+
|
+
equal(value, comparand)
+notequal(value, comparand)
+less(value, comparand)
+lessequal(value, comparand)
+greater(value, comparand)
+greaterequal(value, comparand) |
+ Comparison functions. Return 1 if the respective comparison is true and 0 otherwise. Useful as statements in the select() function. |
+
|
+
and(a, b)
+or(a, b)
+nand(a, b)
+nor(a, b)
+xor(a, b) |
+ Perform the respective logical operation with a and b. Useful for chaining statements in the select() function. |
+
|
+
round(number, precision) |
+ Rounds number to precision decimal places. Pass 0 as precision to round to the nearest integer. | +
|
+
floor(number)
+ceil(number) |
+ Rounds a number up (in the case of ceil ) or down (in the case of floor ) to the nearest integer. |
+
|
+
defined(identifier) |
+ Takes an identifier as a string parameter and returns 1 if a define with that identifier exists, 0 otherwise. + NOTE: Don't include the ! in the identifier as Asar will otherwise try to expand it as a define before calling the function. |
+
|
+
sizeof(identifier) |
+ Takes the identifier of a struct as a parameter and returns the base size of that struct (without any extension structs). Throws an error if a struct with that name doesn't exist. For backwards compatibility, the identifier can be surrounded with quotes. | +
|
+
objectsize(identifier) |
+ Takes the identifier of a struct as a parameter and returns the object size of that struct. In the case of an extended struct, this will be the base size of the struct plus the size of its largest extension struct. Throws an error if a struct with that name doesn't exist. For backwards compatibility, the identifier can be surrounded with quotes. | +
|
+
datasize(label) |
+ Takes a given label and calculates the distance between it and the next label. It will throw a warning if the distance exceeds 0xFFFF or is the last label in the targeted assembly. | +
|
+
stringsequal(string1, string2) |
+ Returns 1 if the given string parameters are equal and 0 otherwise. | +
|
+
stringsequalnocase(string1, string2) |
+ Returns 1 if the given string parameters are equal and 0 otherwise. The comparison is case-insensitive. | +
|
+
pc() |
+ Returns the current SNES address. This is a shorthand for placing a label right before the current command. | +
|
+
realbase() |
+ Returns the current address in the ROM being written to. This is not the same as the value of a nearby label when the base command is active: it returns the actual address the code will end up at. |
+
_read1()
) leads to the original function, which can be used to make an overridden function call its original function.
+ function read1(x) = _read1(x+$010000)
+ While user-defined functions can't use string parameters themselves, passthrough of string parameters to built-in functions is supported.
+ function readfilenormalized(filename, pos) = readfile4(filename, pos)/2147483648.0
+db readfilenormalizd("datafile.bin", 0)
+ Return to topif {condition}
+ {codeblock}
+endif
+ To construct condition statements, you can also make use of a number of comparison operators specific to conditionals. They return 1 if their respective comparison is true and 0 otherwise.Operator | +Details | +
---|---|
a == b |
+ Returns 1 if a is equal to b |
+
a != b |
+ Returns 1 if a is not equal to b |
+
a > b |
+ Returns 1 if a is greater than b |
+
a < b |
+ Returns 1 if a is less than b |
+
a >= b |
+ Returns 1 if a is greater than or equal to b |
+
a <= b |
+ Returns 1 if a is less than or equal to b |
+
!a |
+ Returns 1 if a is 0, and 0 otherwise. Note: this is deprecated and will be removed in a future version. Please use if not(a) instead. |
+
Operator | +Details | +
---|---|
a || b |
+ Returns 1 if at least one of a and b evaluates to 1 |
+
a && b |
+ Returns 1 if both of a and b evaluate to 1 |
+
0 && my_function()
, my_function() will never be called). Note that only one kind of logical operator can be used in a single condition, but conditionals themselves can be nested to an arbitrary depth, which can be used as a workaround here. Optionally, if conditionals can contain an arbitrary number of elseif branches as well as a single else branch. The compiler checks the if and all elseif branches in succession until a single condition evaluates to > 0
- if none does, the code inside the else branch is compiled.
+ !mode = 0 ; Supported modes: 0, 1, 2, 3
+!verbose = 0 ; Set to 1 to enable verbose mode
+
+if !mode == 0
+ ; ...
+elseif !mode == 1
+ ; ...
+elseif !mode == 2
+ ; ...
+elseif !mode == 3
+ if !verbose != 0
+ print "Oh boy, so you're going with mode 3 today!"
+ endif
+ ; ...
+else
+ error "Unsupported mode! Please choose 0, 1, 2 or 3!"
+endif
+ Alternatively, if conditionals can also be constructed on a single line via the following syntax:
+ if {condition} : {codeblock}[ : codeblock...] : endif
+ Note that else or elseif are unsupported when using this syntax. The endif
used to be optional, but this functionality is deprecated. It's recommended to always add the endif at the end of the line.
+ PressedY:
+ if !fireballs_enabled : %PlaySoundEffect(!fireball_sfx) : jsr ShootFireball : endif
+ rtl
+
+ If you plan to use labels in if commands, note that there's certain restrictions that apply. More specifically, only static labels can be used. That is, only labels whose address can't change between Asar's passes, as demonstrated by the following example:
+ FirstLabel = $018000
+
+freecode
+ lda SecondLabel,x
+
+SecondLabel:
+ db $00,$01,$02,$03
+
+; All good. FirstLabel was statically defined.
+if FirstLabel == 0
+endif
+
+; Error. The label could move between passes.
+if SecondLabel == 0
+endif
+ <= 0
. Typically, this would be used with a define that is modified inside the loop. This can be useful for generating data tables.
+ !counter = 0
+
+while !counter < $10
+ db (!counter<<8)|$00,(!counter<<8)|$01,(!counter<<8)|$02,(!counter<<8)|$03
+ db (!counter<<8)|$04,(!counter<<8)|$05,(!counter<<8)|$06,(!counter<<8)|$07
+ db (!counter<<8)|$08,(!counter<<8)|$09,(!counter<<8)|$0A,(!counter<<8)|$0B
+ db (!counter<<8)|$0C,(!counter<<8)|$0D,(!counter<<8)|$0E,(!counter<<8)|$0F
+
+ !counter #= !counter+1
+endwhile
+ Note that while loops can also end with endif
, but this is deprecated. Be warned as improper use of while loops can lead to infinite loops and thus a dead-lock of the compiler, as Asar won't attempt to detect those.for i = 1..5
+ db !i
+ db 2*!i
+endfor
+ This will write the bytes 01 02 02 04 03 06 04 08.
+You can also put for loops on a single line, however in this case due to the order in which Asar parses defines, you will not be able to use the loop counter. E.g. for i = 0..10 : nop : endfor
.
db {value}[,value...]
+ dw {value}[,value...]
+ dl {value}[,value...]
+ dd {value}[,value...]
+ Table commands let you insert a number or a list of numbers directly into the ROM as raw bytes. Use db for 8-bit numbers, dw for 16-bit numbers, dl for 24-bit numbers and dd for 32-bit numbers respectively, where value
can be a number literal, a math statement, a label or an ASCII string delimited by double quotes. When using dw, dl or dd, each number is converted to little-endian. Big numbers are truncated to smaller integers as needed.
+ org $0189AB
+Label:
+
+; This will write the following data to the ROM:
+; $01 $03 $07 $AB $41 $42 $43
+db $01,$0203,$04050607,Label,"ABC"
+; This will write the following data to the ROM:
+; $01 $00 $03 $02 $07 $06 $AB $89 $41 $00 $42 $00 $43 $00
+dw $01,$0203,$04050607,Label,"ABC"
+; $01 $00 $00 $03 $02 $00 $07 $06 $05 $AB $89 $01 $41 $00 $00 $42 $00 $00 $43 $00 $00
+dl $01,$0203,$04050607,Label,"ABC"
+; $01 $00 $00 $00 $03 $02 $00 $00 $07 $06 $05 $04 $AB $89 $01 $00 $41 $00 $00 $00 $42 $00 $00 $00 $43 $00 $00 $00
+dd $01,$0203,$04050607,Label,"ABC"
+ By default, each character in an ASCII string used in in a table maps onto the respective ASCII value. This mapping can be customized via the table command:
+ table {filename}[,rtl/ltr]
+ Note: this command is deprecated and will be removed in Asar 2.0, see below (direct character assignment syntax) for the replacement.
+ Wherefilename
specifies the path to a table file (enclose in double quotes to use file names with spaces, see section Includes for details on Asar's handling of file names) and ltr/rtl specifies whether that file is in left-to-right or right-to left format (default: left-to-right).{character}={value}
+[character=value...]
+ Format of right-to-left table files:
+ {value}={character}
+[value=character...]
+ where character
represents an ASCII character and value
represents a hexadecimal number literal (without a prefix) to map to that ASCII character. Note that the table command initializes the mapping to garbage, so when using it, it's recommended to provide mappings for all ASCII characters. It's also possible to directly map characters inline without using a table file by using the syntax
+ '{character}' = {value}
+ where value
can be any number literal or math statement. This will be the only way to set the table in Asar 2.0.cleartable
. Additionally, the pushtable
command lets you push the current table mapping to the stack, whereas the pulltable
command lets you restore the mapping from the stack.
+ ; Contents of table1.txt:
+;A=1A
+;B=1B
+;C=1C
+
+; Contents of table2.txt:
+;1D=A
+;1E=B
+;1F=C
+
+; This writes $41 $42 $43
+db "ABC"
+
+table "table1.txt",ltr
+
+; This writes $1A $1B $1C
+db "ABC"
+
+pushtable
+table "table2.txt",rtl
+
+; This writes $1D $1E $1F
+db "ABC"
+
+pulltable
+
+; This writes $1A $1B $1C
+db "ABC"
+
+cleartable
+
+; This writes $41 $42 $43
+db "ABC"
+
+'A' = $20
+'B' = $20+1
+'C' = $20+2
+
+; Those both write $20 $21 $22
+db "ABC"
+db 'A','B','C'
+ Note that Asar tries to replace defines wherever possible - even inside strings. Sometimes, this might be undesired. In those cases, you can prefix the !
with a \
to escape it. The \
itself can be escaped with another \
. In the case of a "
it can be escaped with an additional "
+ !define = "text"
+
+; This writes "text" to the ROM
+db "!define"
+
+; This writes "!define" to the ROM
+db "\!define"
+
+; This writes "\text" to the ROM
+db "\\!define"
+; This writes 'something "cool"' to the ROM
+db "something ""cool"""
+ fillbyte {byte}
+ fill {num}
+fill align {alignment} [offset {offset}]
+ The fillbyte and fill commands let you write a specific byte value to the ROM multiple times. The byte
parameter of fillbyte specifies which value to write, wheres fill writes that value to the output ROM num
times. If alignment
is specified, the value will be written repeatedly until the SNES address has the specified alignment, similar to skip align
.
+ fillbyte $FF
+; This writes $FF $FF $FF $FF $FF $FF $FF $FF
+fill 8
+org $008005
+; this writes $FF until SNES address $00800A (=$8008 + 2)
+fill align 8 offset 2
+
+ It's also possible to write 16-bit, 24-bit or 32-bit values with the fill command by using fillword
, filllong
or filldword
instead of fillbyte. Note that the num
parameter of fill still specifies the number of bytes to write in those cases. Values might get truncated as needed to exactly reach the specified number of bytes to write.
+ padbyte {byte}
+ pad {snes_address}
+ The padbyte and pad commands let you write a specific byte value to the ROM until the pc reaches a certain SNES address. The byte
parameter of padbyte specifies which value to write, wheres pad writes that value to the output ROM until the pc reaches snes_address
.
+ org $008000
+padbyte $FF
+; This writes $FF $FF $FF $FF
+pad $008004
+ It's also possible to write 16-bit, 24-bit or 32-bit values with the pad command by using padword
, padlong
or paddword
instead of padbyte. Note that the snes_address
parameter of pad still specifies the end offset of the write in those cases. Values might get truncated as needed to exactly reach the specified end offset.
+ incbin {filename}[:range_start..range_end]
+ The incbin command copies a binary file directly into the output ROM. The filename
parameter specifies which file to copy (enclose in double quotes to use file names with spaces, see section Includes for details on Asar's handling of file names) and the optional range_start
and range_end
parameters are math expressions which specify a range of data to copy from the file (a range_end of 0 copies data until the end of the file; not specifying a range copies the entire file). The older form of this command used -
as the separator of start and end, which caused ambiguities and was thus deprecated.
+ ; datafile.bin contains the following bytes:
+; $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F
+
+; This writes $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F
+incbin "datafile.bin"
+
+; This writes $09 $0A $0B $0C $0D $0E
+incbin "datafile.bin":$9..$F
+; Alternatively with the deprecated syntax:
+incbin "datafile.bin":9-F
+
+; This writes $01 $02 $03 $04
+incbin "datafile.bin":$F-$E..2+3
+; Old deprecated syntax:
+incbin "datafile.bin":($F-$E)-(2+3)
+
+ Another now-deprecated form is incbin {filename}[range] -> {location}
, where location is either a label or a SNES address. If it is specified, the contents of the file are written to another location in the ROM. When set to a label identifier, this code behaves identically to
+ pushpc
+freedata align
+
+{label_identifier}:
+incbin {filename}
+
+pullpc
+ with the exception that incbin gains special permission to cross bank borders. In this case, the size limit of the included file is 65536 bytes due to how freespace works. For files with a size of 32767 or lower, no alignment is enforced. When label_name_or_sness_address
is set to an SNES address, the code behaves identically to
+ pushpc
+
+org {snes_address}
+incbin {filename}
+
+pullpc
+ In this case there is no hard limitation on the size of your file.incsrc {filename}
+ The incsrc command makes Asar assemble the file referenced by the filename
parameter (enclose in double quotes to use file names with spaces, see section Includes for details on Asar's handling of file names). The file is assembled in-place which means that Asar instantly switches to the new file and only returns to the previous file once assembling the new file has finished. All of Asar's state (labels, defines, functions, pc etc.) is shared between files. When including other files, there is a recursion limit of 512 levels. This limit only serves the purpose of preventing infinite recursion. For an easier understanding of incsrc, you can visualize it as a command which pastes the contents of another file directly into the current file (although that's not actually how it's implemented and there are differences in the way relative file paths are handled).
+ ; Contents of routine.asm:
+;AnotherRoutine:
+; lda #$FF
+; sta $00
+; rts
+
+Main:
+ jsr AnotherRoutine
+ bra Main
+
+incsrc "routine.asm"
+
+ include
+ includefrom {filename}
+ The include and includefrom commands specify that a file is only to be included in another file and not to be assembled directly. When a user tries to assemble a file containing include or includefrom directly, an error is thrown. The includefrom command behaves identically to the include command with the exception that it is passed the name of the file it is meant to be included from (note that Asar doesn't verify whether it's actually included from that file, it only checks whether it's included from another file at all). When making use of include or includefrom, they must be the first command within their respective file and can't be used in combination with the asar or xkas command in the same file.
+ ; Contents of shared.asm:
+;includefrom main.asm
+;
+;if read1($00FFD5) == $23
+; !is_sa1_rom = 1
+;else
+; !is_sa1_rom = 0
+;endif
+
+
+asar 1.37
+
+incsrc "shared.asm"
+
+if !is_sa1_rom
+ ; ...
+endif
+ includeonce
+ The includeonce command places an include guard on the file that is currently being assembled. This prevents it from being assembled again in the same pass. This is intended for shared files which may be included from multiple source files, but should only be assembled once to prevent redefinition errors etc.
+ ; Contents of shared.asm:
+;
+;includeonce
+;
+;MyRoutine = $018000
+;MyOtherRoutine = $028000
+
+
+; Note that the second include does not throw
+; redefinition errors, thanks to the "includeonce".
+incsrc "shared.asm"
+incsrc "shared.asm"
+
+jsl MyRoutine
+jsl MyOtherRoutine
+
+ Return to topfreespace {ram/noram}[,align][,cleaned][,static][,value]
+ freecode [align][,cleaned][,static][,value]
+ freedata [align][,cleaned][,static][,value]
+ The freespace command makes Asar search the output ROM for a freespace area large enough to contain the following section of code/data. If such an area is found, the pc is placed at its beginning and a RATS tag automatically written. If no such area is found, an error is thrown. The parameters control what kind of freespace to look for.
+ Parameter | +Details | +
---|---|
ram |
+ The freespace finder searches for an area where RAM mirrors are available (banks $10 to $3F). Recommended when inserting code. | +
noram |
+ The freespace finder searches for an area where RAM mirrors aren't available (banks $40 to $6F and $F0 to $FF). If no such area is found, it searches in the remaining banks ($10 to $3F). Recommended when inserting data. | +
align |
+ The freespace finder searches for an area at the beginning of a bank. | +
cleaned |
+ Suppresses the warning about freespace leaking. Useful when Asar's leak detection misbehaves on an autoclean with a complicated math statement or similar. | +
static |
+ Prevents the freespace area from moving once assigned. This also prevents it from growing (an error is thrown if the area would need to grow). Useful in situations where data needs to remain in a certain location (for example: when another tool or another patch needs to access it). | +
value |
+ A number literal or math statement specifying the byte value to look for when searching for freespace (default: $00). To find freespace, Asar will look for continuous areas of this value. When using autoclean on this freespace, this is also the value the area will be cleaned to. Note that specifying the byte like this is deprecated. You should use the separate freespacebyte command instead. |
+
freespace ram
, whreas the freedata command is an alias of freespace noram
. There are a few things to note when working with freespace in Asar. First of all, if Asar places two freespace areas within the same bank, it will use 24-bit addressing in cases where they reference each other, despite 16-bit addressing being possible in theory. This can be worked around by only using a single freespace area instead. It's not recommended to explicitly use 16-bit addressing in these cases as the two freespace areas are not guaranteed to always end up in the same bank for all users. Secondly, when Asar places two freespace areas close to each other, a few bytes will be wasted between them for technical reasons. In most practical scenarios, the amount of wasted space should be reasonably small (< 1% of the code size), nevertheless this once again can be worked around by only using a single freespace area instead. Lastly, the number of freespace areas a single Asar patch can place is limited to a maximum of 125.
+ ; Let's assume this to be some location in the ROM originally containing
+;lda #$10
+;sta $1F
+org $01A56B
+ autoclean jsl MyNewCode
+
+freecode
+
+MyNewCode:
+ ; Do something here
+ ; ...
+
+.Return:
+ ; We overwrote some code from the original ROM with our org, so we have to restore it here
+ lda #$10
+ sta $1F
+
+ rtl
+ freespacebyte {value}
+ This command sets the byte which Asar considers to be free space. This value will be used for searching for freespace, as padding when resizing the ROM, or when cleaning up old freespaces.
+ autoclean jml/jsl/dl {label}
+ autoclean {snes_address}
+ The autoclean command makes it possible for Asar to automatically clean up and reuse all of the freespace allocated by a patch when applying that patch again. The purpose of this is to prevent freespace leaks. Normally, applying a patch including a freespace (or similar) command to the same ROM multiple times would allocate a new freespace area each time. Since Asar automatically protects allocated freespace via RATS tags, all freespace areas previously allocated by the same patch would leak and become unusable, making the output ROM run out of freespace eventually. The autoclean command can prevent this by freeing up freespace areas previously allocated by the patch before allocating new ones. How it accomplishes this depends on how it is used:
+ jml
or jsl
:label
parameter must be a label pointing to inside a freespace area. When the patch is applied and the autoclean is encountered, Asar checks whether the output ROM contains a jml/jsl at the current pc. If it does, Asar checks whether the jml/jsl points to the expanded area of the ROM (banks $10+). If it does, Asar checks whether the jml/jsl points to an area protected by a RATS tag (including the RATS tag itself). If it does, Asar cleans up that area and removes the RATS tag.
+ dl
:label
parameter must be a label pointing to inside a freespace area. When the patch is applied and the autoclean is encountered, Asar checks whether the output ROM contains an address pointing to the expanded area of the ROM (banks $10+) at the current pc. If it does, Asar checks whether that address points to an area protected by a RATS tag (including the RATS tag itself). If it does, Asar cleans up that area and removes the RATS tag.
+ snes_address
parameter must be any label, number literal or math statement evaluating to an SNES address pointing to inside a freespace area. When the patch is applied and the autoclean is encountered, Asar checks whether that address points to the expanded area of the ROM (banks $10+). If it does, Asar checks whether it points to an area protected by a RATS tag (including the RATS tag itself). If it does, Asar cleans up that area and removes the RATS tag.
+ ; Let's assume this to be some location in the ROM containing a function pointer table or similar
+org $00A5F2
+ autoclean dl MyNewFunction1
+ autoclean dl MyNewFunction2
+
+freecode
+
+MyNewFunction1:
+ ; ...
+ rtl
+
+MyNewFunction2:
+ ; ...
+ rtl
+ prot {label}[,label...]
+ The prot command makes it possible for Asar to automatically clean up a freespace area that is only referenced within another freespace area and thus can't be cleaned via an autoclean directly. It must be used at the beginning of a freespace area (right after the freespace command), where the label
parameter must be a label pointing to inside a freespace area (you can pass up to 85 labels separated by commas to a single prot). When a freespace area containing a prot is cleaned by an autoclean, all freespace areas referenced by the prot are also cleaned up.
+ org $0194BC
+ autoclean jsl MyNewFunction
+
+
+freecode
+prot SomeLargeData
+
+MyNewFunction:
+ ldx.b #0
+
+.Loop:
+ {
+ lda SomeLargeData,x
+ cmp #$FF
+ beq .Return
+
+ ; ...
+
+ inx
+
+ bra .Loop
+ }
+
+.Return:
+ rtl
+
+
+freedata
+
+SomeLargeData:
+ db $00,$01,$02,$03
+ ; ...
+ db $FF
+ Return to topprint {text_or_function}[,text_or_function...]
+ where text_or_function
can be either a string delimited by double quotes or one of the print-specific functions below:Function | +Details | +
---|---|
bin(x[, width]) |
+ Prints x as a binary (base-2) integer, where x can be any math statement. If width is provided, the output is padded to at least this many digits using zeroes. | +
dec(x[, width]) |
+ Prints x as a decimal (base-10) integer, where x can be any math statement. If width is provided, the output is padded to at least this many digits using zeroes. | +
hex(x[, width]) |
+ Prints x as a hexadecimal (base-16) integer, where x can be any math statement. If width is provided, the output is padded to at least this many digits using zeroes. | +
double(x[, precision]) |
+ Prints x as a decimal number with precision decimal places (default: 5), where x can be any math statement. Affected by the math round setting. | +
pc |
+ Prints the current PC. | +
freespaceuse |
+ Prints the total number of bytes used by commands that acquire freespace (such as freespace, freecode, freedata etc.). + You can use the command +
+ to reset this value. |
+
bytes |
+ Prints the total number of bytes written to the output ROM. + You can use the command +
+ to reset this value. |
+
warn [text_or_function...]
+ where custom_warning_text
can be a custom warning text and uses the same format as the print
command. A warning does not cause compilation to fail, so it can be used to inform the user about potential dangers. Warning messages are printed to stderr by default, but are printed to stdout if the Asar executable is renamed to xkas.exe. This is intended for compatibility purposes.
+ if read1($00FFD5) == $23
+ warn "SA-1 compatibility of this patch is untested, use with caution!"
+endif
+ error [text_or_function...]
+ where custom_error_text
can be a custom error text and uses the same format as the print
command. An error causes compilation to fail, so it should be used to inform the user about irrecoverable error states. Error messages are printed to stderr by default, but are printed to stdout if the Asar executable is renamed to xkas.exe. This is intended for compatibility purposes.
+ if read1($00FFD5) == $23
+ error "This patch is not SA-1 compatible!"
+endif
+ if {condition}
+else
+ error [text_or_function...]
+endif
+ and is used via the syntax
+ assert {condition}[,text_or_function...]
+ where custom_error_text
can be a custom error text and uses the same format as the print
command. If condition
evaluates to <= 0
, an error is thrown, otherwise nothing happens.
+ assert read1($00FFD5) != $23,"This patch is not SA-1 compatible!"
+ Return to topcheck title "{title}"
+ The check title command verifies that the title stored in the output ROM is identical to title
. If it isn't, an error is thrown (unless --no-title-check
is passed to the application, in which case only a warning is thrown - see section Usage for details). The purpose of this command is to assure that patches are applied to the correct output ROM.
+ ; This patch is only for a Super Mario World ROM
+check title "SUPER MARIOWORLD "
+
+; Remove small bonus stars from game
+org $009053
+ nop #3
+
+org $009068
+ nop #3
+ check bankcross {off/half/full}
+ The check bankcross
command enables (full
or half
) or disables (off
) throwing errors when a bank border is crossed while assembling a file. The default is full
, which checks whether the code crosses from pc $FFFF to $0000 in the next bank, and throws an error if that happens. With half
, Asar will additionally check crossings from $7FFF to $8000. Use off
with caution as some features may not behave correctly with bank border checking disabled and some places may still check for bank borders, anyways.
+ check bankcross off
+
+org $80FFFF
+
+ db $00,$00
+
+check bankcross on
+
+print pc ; Will print 818001 when using LoROM mapper
+ Return to topWarning Name | +Details | +
---|---|
Wimplicitly_sized_immediate |
+ Thrown when opcodes are sized implicitly and Asar has to assume a size. An opcode is considered to be sized explicitly when either a length specifier is used or a simple hex constant that can be assumed to be of a specific size (that is, a hex constant with either two or four digits). Opcodes that don't support multiple sizes are always considered to be sized explicitly. Everything else is considered to be sized implicitly and will throw this warning when enabled. | +
Wcheck_memory_file |
+ Only relevant for the DLL API. Thrown when a file is accessed that was either not provided as a memory file or that isn't found in memory. Mainly intended for debugging purposes and can be used to assure that files are actually read from the correct location. | +
warnings {push/pull}
+ The warnings push
command pushes the current state of enabled and disabled warnings to the stack. The warnings pull
command pulls it back from the stack.
+ warnings push
+; Disable "freespace leaked" warning
+warnings disable Wfreespace_leaked
+
+freecode
+
+; [...]
+
+warnings pull
+ warnings {enable/disable} {name}
+ The warnings enable
command enables the warning with the specified name, the warnings disable
command disables it. Warnings enabled or disabled via this command override warnings enabled or disabled via the command line (see section Usage for details). When using these commands inside shared code, it's recommended to do so in conjunction with warnings {push/pull} to prevent the modified settings from leaking into other files.
+ warnings disable Wwarn_command
+
+warn "This text in invisible!"
+
+warn enable Wwarn_command
+
+warn "This text in visible!"
+ Here is a list of all warnings Asar can emit (useful to know what to disable).
+Here is a list of all errors Asar can emit. This is really only useful when using the DLL API, where you can use the names to check which exact error was thrown. Note that error names might change between Asar versions.
+ Return to topWarning name | +Warning message | +Enabled by default | +
---|---|---|
Wrelative_path_used | Relative %s path passed to asar_patch_ex() - please use absolute paths only to prevent undefined behavior! | True |
Wrom_too_short | ROM is too short to have a title. (Expected '%s') | True |
Wrom_title_incorrect | ROM title is incorrect. Expected '%s', got '%s'. | True |
W65816_yy_x_does_not_exist | ($yy),x does not exist, assuming $yy,x. | True |
W65816_xx_y_assume_16_bit | %s $xx,y is not valid with 8-bit parameters, assuming 16-bit. | True |
Wspc700_assuming_8_bit | This opcode does not exist with 16-bit parameters, assuming 8-bit. | True |
Wcross_platform_path | This patch may not assemble cleanly on all platforms. Please use / instead. | True |
Wmissing_org | Missing org or freespace command. | True |
Wset_middle_byte | It would be wise to set the 008000 bit of this address. | True |
Wunrecognized_special_command | Unrecognized special command - your version of Asar might be outdated. | True |
Wfreespace_leaked | This freespace appears to be leaked. | True |
Wwarn_command | warn command%s | True |
Wimplicitly_sized_immediate | Implicitly sized immediate. | False |
Wxkas_deprecated | xkas support is being deprecated and will be removed in a future version of Asar. Please use an older version of Asar (<=1.50) if you need it. | True |
Wxkas_eat_parentheses | xkas compatibility warning: Unlike xkas, Asar does not eat parentheses after defines. | True |
Wxkas_label_access | xkas compatibility warning: Label access is always 24bit in emulation mode, but may be 16bit in native mode. | True |
Wxkas_warnpc_relaxed | xkas conversion warning : warnpc is relaxed one byte in Asar. | True |
Wxkas_style_conditional | xkas-style conditional compilation detected. Please use the if command instead. | True |
Wxkas_patch | If you want to assemble an xkas patch, add ;@xkas at the top or you may run into a couple of problems. | True |
Wxkas_incsrc_relative | xkas compatibility warning: incsrc and incbin look for files relative to the patch in Asar, but xkas looks relative to the assembler. | True |
Wconvert_to_asar | Convert the patch to native Asar format instead of making an Asar-only xkas patch. | True |
Wfixed_deprecated | the 'fixed' parameter on freespace/freecode/freedata is deprecated - please use 'static' instead. | True |
Wautoclear_deprecated | 'autoclear' is deprecated - please use 'autoclean' instead. | True |
Wcheck_memory_file | Accessing file '%s' which is not in memory while W%d is enabled. | False |
Wif_not_condition_deprecated | 'if !condition' is deprecated - please use 'if not(condition)' instead. | True |
Wfunction_redefined | Function '%s' redefined. | True |
Wdatasize_last_label | Datasize used on last detected label '%s'. | True |
Wdatasize_exceeds_size | Datasize exceeds 0xFFFF for label '%s'. | True |
Wmapper_already_set | A mapper has already been selected. | True |
Wfeature_deprecated | DEPRECATION NOTIFICATION: Feature "%s" is deprecated and will be REMOVED in the future. Please update your code to conform to newer styles. Suggested work around: %s. | True |
Wbyte_order_mark_utf8 | UTF-8 byte order mark detected and skipped. | True |
Woptimization_settings | In Asar 2.0, the default optimization settings will change to `optimize dp always` and `optimize address mirrors`, which changes this instruction's argument from %d to %d bytes. Either specify the desired settings manually or use explicit length suffixes to silence this warning. | True |
This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +W~^` zjDf-D4fq2U%2=tnQ*LW*>*Q@NeQ=U48Xk01IuzADy1ym0rit^WHK~^SwU449k4??k zJX|$cO-EBU&+R{a*)XQ6t~;?kuP)y%}DA(=%g4sNM$ z8a1k^e#^m%NS4_=9;HTdn_VW0>ap!zx91UcR50pxM}wo(NA}d;)_n~5mQGZt41J8L zZE5Hkn1U{CRFZ(Oxk3tb${0}UQ~92RJG;|T-PJKt>+QV$(z%hy+)J z~xmNJS#48TFsM{-?LHd-bxvg|X{pRq&u74~nC4i>i16LEAiprfpGA zYjeP(qECX_9cOW$*W=U1YvVDXKItrNcS$?{_zh2o=MDaGyL^>DsNJtwjW%Do^}YA3 z 3HS=f@249Y h{jnme5ZRV>tcdeh+=o(;eXg_-64c@tJ&As=oIrFZ& z*Gx&Lr>wdAF8POg_#5blBAP!&nm-O!$wspA>@;>RyOdqWZe?F%--gC9nTXZ%DnmK< z`p0sh@aOosD-jbIo je0ec`&&fWsK?xPdf*L)Qp(MwKKIOtB+EDn(3w-9Ns9O~i z7MwnG8-?RZlv&XIJZUK*;)r!1@Bh4bnRO*JmgwqANa8v4EvHWvBQYYGT?tN4>BRz1 zf1&5N7@@!g89ym5LO{@=9>;Y8=^ExA9{+#aKfFGPwby8wn)db@o}%Z_x0EjQWsmb6 zA9uX(vr-n8$U~x9dhk~VKeI!h^3Z2NXu;>n6BHB%6e2u2VJ!ZykHWv-t19}tU-Yz$ zHXl2#_m7V&O!q(RtK+(Yads868*Wm*!~EzJtW!oq)kw}`iSZl@lNpanZn&u|+px84 zZrN7t&ayK4;4x_@`Q;;XMO4{VelhvW%CtX7w;>J6y=346)vfGe)zJBQ9o$eAhcOPy zjwRa6$CvN-8qHjFi;}h1wAb{Kcnn{;+ITEi`fCUk^_(hJ&q1Z=yo*jRs<94E#yX67 zRj)s)V&gd0VVZGcLALQ|_Lp<4{XEBIF-*yma#;%V*m^xSuqeG?H- 7=M0Cq%%W9`2Oe>Ov)OMv8yKrI^mZ$ql{A!!3mw_27Y zE=V#cA@HopguAWPAMhKDb__-Z_(TN7;*A` XxrMefxoz4{Seu)$%$=sPf{vT@Pf_T`RlrC#CPDl$#FnvU|VBC$0(E>+3EG z&3xsml}L_UE3bNGX6T~2dV6S%_M9{`E9kgHPa+9mas{tj$S<&{z?nRzH2b4~4m^Wc zVF+o4`w9BO_!IohZO_=<;=$8j?7KUk(S5llK6wfy9m$GsiN5*e{q(ZS6vU4l6&{s5 zXrJJ@giK>(m%yKhRT;egW||O~pGJ&`7b8-QIchNCms)}88aL8Jh{cIp1uu`FMo!ZP z1fne;+5#%k3SM7Kqe|`%w1JI=6hJJrog4j?5Iq!j=b=0AJS5%ev_9?eR!_H>OLzLM z_U#QLoi=0npY1+gHmde37Kgp)+PKl=nC>pM|EJCAEPBRXQZvb74&LUs*^W CT5Q%L-{O+y zQKgd4Cek)Gjy~OLwb&xJT2>V%wrprI+4aOtWs*;<9pGE>o8u|RvPtYh;P$XlhlqF_ z77X`$AlrH?NJj1CJdEBA8;q*JG-T8nm>hL#38U9ZYO3UTNWdO3rg-pEe5d= zw3Xi@nV)1`P%F?Y4 s9yVPgPYT9d#3SLD{*L0U{ z;TtVh?Wb0Lp4MH{o@L6GvhJE=Y2u>{DI_hMtZgl~^3m3#ZUrkn?-5E3A!m!Z>183- zpkovvg1$mQawcNKoQ*tW=gtZqYGqCd)D#K;$p113iB1uE#USvWT}QQ7kM7!al-C^P zmmk!=rY+UJcJLry#vkO% BuM>pb)46x!{DkRYY7wGNK$v=np_sv7nfHZO_=eyqLSK zA6ebf$Bo&P&CR_C*7^|cA>zl^hJ7z0?xu#wFzN=D8 zxm(>@s?z1E;|!Py8HuyHM}_W5*Ff>m5U0Jhy?txD x{jjLGNXs}(CVxgu9Q4tPgE+Hm z*9ll7bz80456xzta(cX+@W!t7xTWR-OgnG_>YM~t&_#5vzC`Mp5aKlXsbO7O0HKAC z2iQF2_|0d6y4$Pu5P-bfZMRzac(Yl{IQgfa0V>u;BJRL(o0$1wD7WOWjKwP)2-6y$ zlPcRhIyDY>{PF LvIr0!VoC e;c_}dp>U-X z`pii$Ju=g+Wy~f|R7yuZZjYAv4AYJT}Ct-OfF$ZUBa> zOiKl0HSvn=+j1=4%5yD}dAq5^vgI~n>UcXZJGkl671v`D74kC?HVsgEVUZNBihyAm zQUE~mz%na<71JU=u_51}DT92@IPPX)0eiDweVeDWmD&fpw12L;-h=5Gq?za0HtmUJ zH@-8qs1E38^OR8g5Q^sI0)J}rOyKu$&o1s=bpx{TURBaQ(!P7i1=oA@B4P>8wu#ek zxZHJqz$1GoJ3_W^(*tZqZsoJlG*66B5j&D6kx@x^m6KxfD?_tCIgCRc?kD~(zmgCm zLGhpE_YBio<-2T9r;^qM0TO{u_N5@cU&P7is8f9-5vh4~t?zMqUEV!d@P{Y)%APE6 zC@k9|i%k6)6t2uJRQQTHt`P5Lgg%h*Fr*Hst8>_$ J{ZI{mNBjN$^2t?KP8*6_xXu5xx8ufMp5R?P(R-t`{n6c{!t+*z zh;|Ek#vYp1VLf;GZf>~uUhU}a<>y*ErioacK@F{%7aq0y(Ytu@OPe;mq`jlJD+HtQ zUhr^&Zeh93@tZASEHr)@YqdxFu69(=VFRCysjBoGqZ!U;W1gn5D$myEAmK|$NsF>Z zoV+w>31}eE0iAN9QAY2O+;g%zc>2t#7Dq5vTvb&}E*5lHrk rj!I1b0=@+&c(qJcmok6 zS ZAuQ496j<&@a6?K6ox1vRks+RqYD< zT9On_zdVf}IStW^#13*WV8wHQWz$L;0cm)|JDbh|f~*LV8N$;2oL|R99**#AT1smo zob=4dB_WB-D3}~I!ATFHzdW%WacH{qwv5Go2WzQzwRrv)ZajWMp{13T_u;Rz^V-VF z@#62k@#FD#t@v9ye*A%@ODWm-@oM_$_3Cy1BS+(+ujzNF@8a7?`$B^{iX2A-2_nA? zfi2=05XV^;D_2G}Up$eFW|Ofb^zuE)bWHkXR4Jm!Sz0O?)x6QD^kOuf R`*v0=|sS?#*ZCvvr^VkV!zhLF3}FHf%+=#@ae1Qq<4~Y1EGYK$Ib1 zg!s~&&u27X&4Ks^(L3%}Npx!_-A)We=0v#yzv03fzxKZ8iV6KIX5U&?>^E?%iIUZ4 z2sD^vRg%kOU!B5@iV{&gBNc9vB)i{Wa@joIa2#4=oAl|-xqj_~$h33%zgk*UWGUV# zf3>{T#2buK?AZH?)h>10N)#VHvOV}%c|wR%HF|pgm8k`*=1l5P8ttZ1Ly@=C5?d9s z)R>B@43V`}=0??4tp?Y}Ox0$SH)yg(!|@V7H^}C-GyAXHFva04omv@`|LCuFRM2`U zxCM>41^p9U3cR>W>`h`{m^VWSL0SNz27{ske7TN 1dTpM|P6Hn!^*}+fr>rJ*+GQN{ ziKp9Zda}CgnbNv#9^^&{MChK=E|Wr}tk?tP#Q?iZ%$2k;Eo9~}^tmv?g~ PW^C$`N)|awe=5m{Xqd!M=ST?2~(mWjdOsXK#yVMN(qP6`q#tg+rQexf|*BeIU)a z^WuJyPR4WVsA Tp2E{*y77*kZ9 zEB{*SRHSVGm8ThtES`9!v{E``H)^3d+TG_?{b|eytE1cy^QbPxY3KFTWh&NZi`C?O z;777FMti@+U+IRl7B{=SCc93nKp`>jeW38muw(9T3AqySM#x@9G|p?N;IiNy(KN7? zMz3hIS5SaXrGqD(NIR0ZMnJT%%^~}|cG(Ez!3#)*o{{QjPUIVFOQ%dccgC0*WnAJW zL*1k^HZ5-%bN;%C&2vpW`=;dB5iu4SR48yF$;K8{S Y`7mu6c z@q{10W=zwHuav3wid&;5tHCUlUgeVf&>wKuUfEVuUsS%XZ2RPvr>;HI=<(RACmN-M zR8(DJD^lePC9|rUrFgR?>hO#VkFo8}zA@jt{ERalZl$!LP4-GTT`1w}QNUcvuEFRv z`)NyzRG!e-04~~Y1DK>70lGq9rD4J}>V(1*UxcCtBUmyi-Y8Q$NOTQ&VfJIlBRI;7 z5Dr6QNIl|8NTfO>Jf|kZVh7n>hL^)`@3r1BaPIKjxrLrjf8A>RDaI{wYlKG)6-7R~ zsZQ}Kk{T~BDVLo#Zm@cc<&x{X<~boVS5(zfvp1s3RbASf6EKpp>+IFV9s`#Yx#+I& zMz5zL9IUgaqrnG*_=_qm|JBcwfl`bw=c=uU^R>Nm%k4_TeDj y|&K2eKwx!u8 z9&lbdJ?yJ@)>!NgE_vN8+*}$8+Uxk4EBNje>!s2_nOCtE+ie>zl!9&!!I)?QPMD&P zm$5sb#Le|%L<#tZbz%~WWv&yUZH6NLl>OK#CBOp{e~$&fuqQd03DJfLrcWa}IvMu* zy;z7L)WxyINd`m}Fh=l&6EWmHUGLkeP{6Vc;Xq->+AS`1T*b9>SJ#<2Cf!N<)o7Ms z!Gj)CiteiY$f@_OT4C*IODVyil4|R)+8nCf&tw%_BEv!z3RSN|pG(k%hYGrU_Ec^& zNRpzS-nJ*v_QHeHPu}Iub>F_}G1*vdGR~ZSdaG(JEwXM{Df;~AK)j(<_O<)u)`qw* zQduoY)s+$7NdtxaGEAo-cGn7Z5yN#ApXWD1&-5uowpb7bR54QcA7kWG@gybdQQa&cxCKxup2Av3_#{04Z^J#@M&a}P$M<((Zx{A8 z!Ue=%xTpWEzWzKIhsO_xc?e$$ai{S63-$76>gtB?9usV&`qp=Kn*GE5C&Tx`^uyza zw{^ImGi-hkYkP`^0r5vgoSL$EjuxaoKBh2L;dk#~x%`TgefEDi7^(~ cmE)UEw*l#i+5f-;!v^P%ZowUbhH*3Av)CifOJX7KS6#d|_83fqJ#8VL=h2KMI z GYTbGm=Q=0lfc{$IDTn;IxIgLZ(Z?)#!mln$0r3A(um zzBIGw6?zmj=H#CkvRoT+C{T=_kfQQ!%8T;loQ5;tH?lZ%M{aG+z75&bhJE`sNSO`$ z`0eget1V7SqB@uA;kQ4UkJ-235xxryG*uzwDPikrWOi1;8WASslh$U4RY{JHgggsL zMaZ|PI2Ise8dMEpuPnW`XYJY^W$n>4PxVOPCO#DnHKfqe+Y7BA6(=QJn}un5MkM7S zkL?&Gvnj|DI!4xt6BV*t)Zv0YV-+(%$}7QcBMZ01jlLEiPk>A3;M^g%K=cNDF6d!7 z zq1_(l4SX+ekaM;bY|YgEqv2RAEE}e-Im8<@oEZ?Z81Y?3(z-@nRbq?!xD9Hyn|7Gx z-NUw`yOor_DJLC1aqkf2(!i=2$ULNfg|s8bV^xB!_rY+bHA;KsWR@aB=!7n&LJq(} z!pqD3Wkv o-Goy zx1edGgnc}u5V8cw&nvWyWU+wXqwinB#x7(uc>H44lXZQkk*w_q#i2O!s_A?a*?`Rx zoZW6Qtj)L1T^4kDeD7;%G5dS816OPqAqPx~(_- jZ`bo-MR_kd&sJv{A^ zs @18qv!kD;U z5Evv$ C*bD~m z+x@>Oo>;7%QCxfp-rOkNgx4j-(o*e5`6lW^X^{qpQo~SMWD`Gxyv6)+k)c@o6j`Yd z8c&XSiYbcmoCKe+82}>^CPM+?p@o&i(J*j0zsk}!P?!W%T5`ppk%)?&GxA`%4>0VX zKu?YB6Z)hFtj@u-icb&t5A1}BX!;~SqG5ARpVB>FEWPLW+C+QOf~G-Jj0r`0D6|0w zQUs5sE6PYc)!HWi))NeRvSZB3kWIW|R ^A%RfamB2jCbVX(Fn>y%#b1W%}W%qc)XVrwuvM!>Qur!Ooy2`n@?qMe3$`F2vx z9<=L}wP7@diWhCYTD?x)LZ>F6 F?z8naL18P%1T9&P_d4p;u=(XW1LO3-< z`{|5@&Y=}7s x3t1Zs zr9ZBmp}Yp HLq7lwu?CXL8 $Q65$Q29AlDCBJSxu5 ;p0({^4skD z+4se#9)xg8qnEh|WnPdgQ&+te7@`9WlzAwMit$Julp+d80n+VM1JxwqS5H6*MPKA` zlJ*Z77B;K~;4JkO5eq(@D}tezez*w6g3ZSn?J1d9Z~&MKbf=b6F9;8H22TxRl%y1r z<-6(lJiLAw>r^-=F-AIEd1y|Aq2MggNo&>7 Ln)S~iAF1;-4`A*9KlL*vleLO3vhEd(@RsIWp~O@>N4p91SI zb~+*jP?8B~MwmI0W$>ksF8DC*2y8K0o#te?D$z8nrfK{|B1L^TR5hlugr|o=-;>Yn zmL6Yt=NZ2%cAsysPA)D^gkz2Vvh|Z9RJdoH$L$+6a^|>UO=3fBBH0UidA&_JQz9K~ zuo1Z_(cB7CiQ}4loOL3DsdC<+wYysw@&UMl21+LY-(z=6j8fu5%ZQg-z6Bor^M}LX z9hxH}aVC%rodtoGcTh)zEd=yDfCu5mE)qIjw~K+zwn&5c!L-N+E=kwxVE ewN#vvx2WGCf^;C9 ^mmTlYc*kz$NUdQ=gDzLmf z!LXG7{N$Mi3n}?5L&f9TlCzzrgGR*6>MhWBR=lS)qP$&OMAQ2 z`$23{zM%a@9EPdjV|Y1zVVGf?mINO)i-q6;_Ev|n_JQ^Zy&BnUgV>NbY9xba1DlY@ zrg$_Kn?+^_+4V4^xS94tX2oLKAEiuU0<2S#v$WSDt0P^A+d-+M?XlR**u_Xdre&aY zNi~zJk9aLQUqaFZxCNRmu*wnxB_u*M6V0xVCtBhtpGUK)#Dob6DWm-n^~Vy)m~?Yg zO0^ +v~`x6Vqtjl4I5;=^o2jyOb~m+ER;lNwO$iN ziH4vk>E`OTRx~v#B|ifef|ceH)%hgqOy|#f=Q|VlN6i{!0CRndN~x8wS6Ppqq7NSH zO5hX{k5T{4ib@&8t)u=V9nY+2RC^75jU%TRix}FDTB%>t;5jpNRv;(KB|%{AI7Jc= zd%t9-AjNUAs?8m40SLOhrjbC_yZoznU$(rnT2);Rr`2e6$k!zwlz!d|sZ3%x@$Nw? zVn?i%t!J+9S F@^ zO&TGun2&?VIygfH5ePk|!e&G3Zm-GUP(imiWzZu$9JU)Wot`}*RHV<-)vUhc6J6{w&PQIaSZ_N<(d>`C$yo#Ly&0Sr5gCkDY(4f@fY5!fLe57sH54#FF4 zg&hda`KjtJ8cTzz;DwFa#{$!}j~g$9zqFBC@To^}i#`b~xhU;p{x{^f1krbEFNqV^ zEq5c!C5XT0o_q{%p&0F@!I;9ejbs#P4q?R!i$?vl3~|GSyq4@q#3=wgsz+zkrIB<< z=HMWEBz?z??GvvT54YsDSnRLcEf!n>^0eKf4(CIT{qs4y$7_4e=JoIkq%~H9$z-r* zZ?`xgwL+DNAJE`VB;S+w#NvBT{3;}{CD&@Ig*Ka2Acx)2Qx zL)V#$n@%vf1Zzms4Th~fS|(DKDT`?BKfX3tkCBvKZLg^hUh|_Gz8?%#d(ANnY`5U1 zo;qjq=5tn!OQ*-JqA&iG-Tg#6Ka|O64eceRrSgggD% %QBX$t=6?hPEK2|lL1{?|>I^Toc>rQU7a_`RSM^EPVl{_&OG-P;|z0?v{3o#pkl zC6Y;&J7;#5N#+H2J-4RqiSK^rj<_Z6t%?`N$A_FUESt{TcayIew5oWi=jxT*aPIP6 z?MG`?k5p%-x>D73irru{R?lu7<54DCT9Q}%=4%@wZij4+M=fzzz`SJ3I%*#AikLUh zn>k=5%IKUP4TrvZ!A{&Oh;BR}6r3t3cpzS(&|cEe&e{MQby|1#X`?17e9?|=i`sPG zL|OOsh`j@PD4sc6&Y3rT`r?-EH0QPR*IobE@_fkB8*(886ZkjkcO{K8Sz$H`^D-8P zjKG9G9A`O!>|!ivAeteRVIcyIGa#O<6I$^O7}9&*8mHd@Gw!WDU*@;*L;SYvlV#p( zzFSsPw&^UdyxO}%i)W8$@f}|84*mz&i2q@SlzMOd%B!BHOJ<(FYUTR(Ui$DuX>?85 zcdzl5m3hzFr2S@c_20C2x&N)|$<=RhzxI!}NN+yS16X^(_mtqY)g*Q%Fux5}bP3q$ zxQD|TB{+4C1gL>zI>g~-ajKMb{2s_cFhN2(I(q^X!$H(GFxpc6oCV9#maj|OhFZaI z;umX6E*fQVTQ@lyZauuv>%E)5z-?zQZne18V5A}}JEQmCz>7^h 0r)!zhinBG6 zMQghGt!Do5h%HmAQl~%m+!pr-&wlrcwW;qw)S$6*f}ZvXd ;cHw=xm|y~mHbT3yX>?hoYKfy--h +6w9%@_4ukf0Et^zr-DbPwFdyj0VJHi}4bqRetSNR`DoWd( z(%n5>8MQl+>3SeL-DB@IaM{NDwd{{v_HMIO)PKO}v{{##c@ihB0w$aaPTSP4^>n3Z zC8Il%(3dCLLX$-|SwWx1u7KVztXpzNhrOZQ78c$jd{B9lqsNHLr*9h;N9$i+vsrM1 zKzLB_gVdMCfxceejpIZat!MbR)GNZ%^n|fEQo?Xtq#Qa_gEWKTFxSL4b{g} kJNd{QcoQ}HUP-A)Rq;U(***IA*V_0B5mr}Xp$q{YSYs-b2q~DHh z?+muRGn~std!VXuT>P9TL_8Km9G{do qRb-W0B&%d> z^3@hs6y5jaEq%P}dmr(8
=f}x~^ z*{I{tkBgYk@Td|Z{csd23pziZlPYt2RJW7D_C#&)OONEWyN`I19_cM;`Aa=y_)ldH z^co(O-xWIN0{y|@?wx@Y!MeVg3Ln%4ORu5~Dl6$h>AGSXrK3!pH%cpM?D|6#*6+A# zlsj;J0_~^?DHIceRC~0iMq)SJ&?R&if{fsdIb>y;H@M4AE`z8~dvz)(e}BqUWK^U~ zFy`PX+z*Bmv9VxAN;%CvMk(#kGBEMP;a-GgGZf~r$(ei(%yGqHa2dS3hxdTT!r>La zUrW2dCTZ!SjD_D(?9$SK02e_#ZOxdAhO%hgVhq54U=2$Hm+1^O^nH<>wS|&<)2TtD zN_MN@O>?A@_&l;U)*GY*5F_a~cgQb_3p`#77ax1iRxIx!r0HkDnA2G*{l|*}g_yI% zZdHt2`Hx^MA#VH7@BEN68Y_; sAcCNgCY7S&dcQsp*$+uW7Dm@$Vl7!YA^51bi} z*Vy8uTj{neIhI L|PhditfC1Jeub(uy}w|wV5 zsQz)04y;BY2$7U4$~P{k)b`hZb>gv1RkD)L#g~$*N^1N1GfNMS)4r|pT*V<&KE1M9 zTh}rzSW#Kcci_#(^qf0gTW3&QN&zsW%VAQ+AZ%-3?E)kMdgL)kY~@mC>l?RH28u;Y zt-@_u^5(W>mDdt qoe){#t;3NA7c@ {WoY9bYFNoq+sj&ru;Z`x>4ddY0y*`HRtHFEN% z@mFkp=x0C6zDGgA0s|mP^WNEwE4O}S?%DOtce3At%?ThxRp@`zCH6MyzM)dA9C7IP zI}t;YUV(Jcnw$4LoD4H(EM#!{L-Z|&fhNYnBlKcQ$UScR#HH>scYBTf2u|7Fd8q$R zy5Cbt=Pvf^e}m4?VVL@#Pi3z*q-Q0MG8pGTcbS|eeW%R5bRzKsHSH#G (#$9hj9}0O7lXsC zbZ7#UjJM^FcvdKK3MOEl+Pb-93Px}F$ID&jcvZdJ{d(D)x|*`=vi%1hdg(dd-1E>& zoB4U&a${9!xyxoT%$ 7gFp{M<_q z9oVnk*Dcp$k#jA#7-pZbXd=L8nDhe<*t_*%gj^Vx>(~KyEY~i&(?@R~L_e^txnUyh z64-dU=Lc;eQ}vPX;g{GitTVZben7||wttapene^dB|oSGB~tmAGqE^`1Jxt$4uXUL zz5?7GEqvmLa{#mgN6la^gY O#}`eXyUJ)lFyTO8*iL~P z$A`A_X^V#!SJyU8Dl%J*6&s9;Jl54CiyfA`ExxmjrZ1P8E%rJ7hFCFo6%{5mRa|LY z k^x76W8M0tQBa1Q(&L`|!e zrczv>+#&b 2bt zuD1Bfoe>oW0&!ju$-LI)$URptI!inJ^Dz|<@S1hk+!(n2PWfi-AMb5*F03&_^29MB zgJP7yn#Fw4n&Rod*>LlF+qPx5ZT$80;+m*0X5ffa3 d-;F72#5un;L$}RfmR5&xbOf(KNeD|gT1x6bw5t;~j}(oMHcSzkCgcpbd>5UN z7e8CV*di9kpy JAo1YyE9XtfV1Q8^?ViwrKgtK$H60 z%~xgAifVV#>j>4SN10>bP9OV9 m`EA-H{bzMimEQ_3@VZH%@KZzjDu` zRCG*Ax6B^%%dyLs2Cw{bePFWM97 50@SIoZoff4mJvyxIeIjeZ{tYpbmTk4_{wy!_uygk4J;wwSiK&OpZWguG$O082g z^a3rw)F1Q!*)rNy!Sqz9bk0u-kftk^q{FPl4N+eS@ 0p1= zhaBFdyShSMz97B%x3GE|Sst~8Le6+?q@g6HwE1hJ#X)o^?{1!x-m`LlQ+4%?^IPIo zHATgqrm-s`+6SW3LjHB>=Pp{i<6FE#j+sX(Vl-kJt6sug<4UG9SH_| ( zOb(+Vn|4R4lc8pHa-japR|c0ZAN$KOvzss6bKW^uPM$I$8eTr{EMN2N%{Yrl{Z`Y^ zaQ`-S_6omm((Fih26~Bjf^W$wm1J`8N+(=0ET@KFDy;S%{mF@!2&1UMxk>jTk49;@ z*g#0?*iga;P7abx1bh^d3MoAy*XQp{Hl*t(buU@DamDmvcc;5}`ihM!mvm36|GqRu zn*3}UmnOSUai6mM*y&f#XmqyBo>b=dmra`8;%uC8_33-RpM6;x`Rrc0RM~y9>y~ry zVnGanZLDD_lC%6!F%Jzk##j%?nW>JEaJ#U89t`?mGJS_kO5+5U1Gh;Lb3`{w<-DW; z;USPAm%*aQJ)UeYnLVb2V3MJ2vrxAZ@?W$vW)7$+L7~7HSzuF&0V95FC4H6Dy<( z!#o7mJKLMHTNn5)Lyn5l4oh2$s~VI~tlIjn09jE~8C#Ooei=J?K;D+-<8Cb>8RPx8 z-~O0ST{mOeXg+qjG~?}E8@JAo-j?OJjgF3nb^K5v>$yq#-Ybd8lM^jdru2WE-*V6W z>sL(7?%-Qu?&?wZNmmqdn?$FXlE!>2BAa^bWfD69lP0?L3kopYkc4>{m#H6t2d LIEE47|jcI$tEuWzwjmRgqBPkzk zM+(?6)=);W6q<2z95fHMDFKxbhPD-r0IjdX_3EH*BFL|t3 ))c7d~8v; {wU5p8nHUz9I?>l zVfn$bENo_I3JOh 1^^ z+un~MSwCyixbj%C?y{G@G7mSZg_cf~&@djVX_vn8;IF&q?ESd=*AJHOJ(!-hbKPlb zYi-r+me!ezr_eCiQ&SetY;BocRokkbwr=ONG zW2U@X=AUvS^E9eM^w~aztd4h$Q&kF;6EJ1O*M7tJfFi}R1 z6X@asDjL5w+#QEKQE 5V48#ASm?H7u5j%nDqi)iO@ a1@F z*^R+bGpEOs#pRx9CBZQ}#uQa|dCH5EW%a3Xv1;ye-}5|Yh4g~YH5gI1(b#B|6_ZI; zMkxwTjmkKoZIp~AqhXp+k&SSQ)9C=jCWTKCM?(&MUHex;c3Knl(A%3UgJT_BEixIE zQh!;Q(J<0)C`q0-^|UdaGYzFqr^{vZR~Tk?jyY}gf@H+0RHkZ{OID|x;6>6+g)|BK zs6zLY0U>bcbRd6kU;cgkomCZdBSC8$a1H`pcu;XqH=5 z+$oO3i&T_WpcYnVu*lchi>wxt#iE!!bG#kzjIF qb)`s?|OclRAnzUyW5*Py!P@srDXI}&s2lVYf2ZCG`F`H-9;60 zb<=6weckNk=DC&Q6QxU*uJ9FkaT>}qb##eRS8n%qG`G9WrS>Xm+w)!AXSASfd%5fg z#fqxk(5L9@fM};~Gk^Sgb;7|krF-an$kIROPt4HLqq6+EL+62d@~4Hsy9nIU?=Ue4 zJ69;q+5+73nU|TQu}$>#v(M&Vx1RD=6Lu`d?>zHN?P7J&XWwsvwJt|rr?CZu+l>m4 zTi^VLh6Uu2s392u(5DLaM%)Dr$%h3hRB>V7a9XG`B{ZsWgh4IyTO9R~TAR^h^~>ko z(k|Hy#@bP}7OyN92TKE%qNZfyWL32p-BJf1{jj0QU0V`yj=tRospvSewxGxoC=C|N zve$zAMuSaiyY)QTk9!VmwUK&<#b2fxMl_DX|5x$dKH3>6sdYCQ9@c)^A-Rn 9vG?s)0)lCR76kgoR>S;B=kl(v zzM}o+G41dh)%9=ezv$7*a9Mrb+S@13nK-B6D!%vy(}5dzbg$`-UUZJKa`_Z{*$rCu zga2G}o3dTHW|>+P_>c8UOm4Vk-ojaTeAg0-+<4#u-{>pGTYz(%ojZ`0e*nHo=)XZS zpp=$zi4|RBMGJDX{Db?>>fq71rX3t$122E;cJ(9elj+kBXs>3?(tq=s*PeL^<(M$8 zUl;u9e6|EP5Us-A>Lzvr+ln|?*}wt;+gUmd>%?@Wl@m%Qm{>Q0JqTcxtB`ROhd6TB z$VY<7t$^N6IC(s* Z@x2?Gi%eB8%(hYaC zKfY5M -9MeR-@5h zZ?V`qr%%FlPQlW5v_Bp^Q?^)S*%Y#Z$|{!Lpju=$s702T z(P}foXu(uuHN!cJRK*W-8=F*QlYB*zT#WI-SmQ_VYEgKw+>wHhm`ECQS`r3VKw`wi zxlcnn26L*U;F-BC9u{Csy#e%+2uD$He5?mc55)ot>1w`?lr$J zsrI^qGB@!5dglADaHlvWto@|S>kF5>#i#hCNXbp*ZkO$*%P-Sjf3Vc+tuFaJ-^|Ou zW8=}1TOlafUitnrTA2D0<3}&zZz^%y5+t2`Tk`vBI93FqU`W!zY;M%AUoN1V1-I2I zPTVFqaw3Pr-`5HcEFWuD?!8Ybw)Y>g7c0tt=soTHiEBxlY;RlQ`iYY-qdd94zWjyD zFcskM^S{_!E?f3mEh9waR7tb6G&yl%GW%e&Sc5i;y@N)U5ZFLcAsma^K?Cg^%d{PO z=SHQq4a| l`AakzEY;A{n6Rn1u`7v~#ufV *6GZ$`Ef)d2%6apsU6^>QJl0@U& zq|wIBlBAgf0j!YaozAgmhAy0uy;AjRA2%(!` #&e>`V` zg`MfSf5gWvJY#?8%&|`Aj0<@aZ;-q#tCx=-zkGE|_C4)TqKjr-SE6po?cX?Z^B%62 zdA!75;$my<*q)n@eB<^dfFGwRaWB25UL#~PNEV>F^c+e2Be*Df(-rIVB Jo2o*an$1*1 zD$bsU C-BvObdmkKlhW<59G9{d=@bAu8a05VWCO=@_~oP=G3SmO91AK_F`#5 zwXLRV ay<~JYok|rdQM-~C?dcq?Yfz_*)fIte zkE_g4CeLj1oza=9zH!s!4k%H@-n{6aB&Z;Cs8MK?#Jxl`?wD>^{fTL&eQHAQFtJ_% zNEfs|gGYh+39S{-@#MrPA!XpgWD;NLlne0-Vey1n0 ?=ww18{L)7G|$1kjI(sjsz@|a lUMcx*04*>=BWHv_W-t=rCAy0q6&* ;kW&ImkwWTe$lzHJRZJ{-{ zl-mK6+j}V`wobm^^B&2Tl?1r=yWbz;v-F<#y!(CT?-4K(($wWtmD6 31MN9?trDG zMI7;9U7|UsC;urLP%eH1h%U`LJxT3oM4=gpi%X@lpVR9N6Q(uhJ00RWXeL-Z*V(O8 zsIyyVUvf=RXLBKX`!peifjIMvMs1YT0n$0*B;K^yZf&HN8$N%e=EgOejqihLPBT|< zs)z`nNU}BOdT7wYLy}R10eXUksn9o)jG )&=qteGc|XNI~h5R6UBfaPeIHbA32@*>orZsCB4`Q79}A =z@najfekt-_eTg7a}Mcas^D1ELl N6(y28c{ur|tmueFvIDOQxXs1)_lKrA`L2-^^VNC#miFvO%l6w5uK2b Fyu?hyNLCjTCNRRVW^i+GX``giwc&TpV~OHu(yN&o)r2$K$1kjh@>iP z^&`?sCk#?xdFX+ilAb(;I7<$BQ#6j*jKsu%LEhQKe=>ki^ZICepr3#_2#pE`32i4Z zu%eXsgL)3x3Q-^OPPRhm<^!TEPoek6?O^j+qLQ*~#TBw4Aq~M2>U{>{jfojVPADAi zurKpW{7Ii5yqy6_1iXw3$aa!GLn|$~cnvQnv7{LMIFn!&d6K=3kH8+e90 Zq5K%6YfdLv}ZdQmTk7SZ7}>rJ9TW)6>NY{uEZ zY^9PI1UqUFm|h0Vqe60Ny =wCFBtKb zXtqOa3M?2OEN=zDX7z}2$Y{2@WJjr?N`au MDVG9kSH~FjfJRNfsR@yJQp4cQ8zaFkT4>5XQqSVt5c}`-A#Z=3-_mGZ^)Hqayei zhJ}wgZ5UDln%)!;Wz@u=m(6C_P@r9*IMPe7Db`CSqad3ky-5-EcG=*v8J&{RtLJ(E zw2h-ghGYcDtqj4Z^nU7ChgEXO0kox=oGaY;0EPqeW89T6htbZg4z!uU1hi;omVj+3 z0B%$+k$`oH5*SeoG`Ay&BAA%nAUjQxsMlNdq8%;SbEAPVC#qm!r7j75W=A)&a6)3% zdQq$fCN;@RqI!KPf l9l=vmBFSFpD1cAxb@~K-$ZIlIL3W}?#3+|2p{|vZVq`YA zMbx|Xl57kJVwoetAo+opiewCkCIO=uBLEaG+!0U$MRdReNsx>+PIJWN6dW)pfeZ(u zQ8ei-Ht69)ZV`qv=vmorhOkF)Squ;)8AUfh<7A_xI8FGHMRW>~%o`1Wt3|8IMrM%& z8)|@=#ssro9=f9HtN0F#O085{Bf6PJnurfzS_yg?qqszmnQIYDP{N=xqPfvl;VN sK^qpoy2&App~Fe (MB7KCI)$p1!&YEB&%$9gTk zmvlt?t7!>_paNt_fYJvw^~LCqX{4opLy!n)md7}<_s?`gytfSAdoScQWTy&Tbr&~( zg9myGVv)l|4-umFBL0)Y(d}Rvt11)(O4ij#zeao~K$vh~JDn0_@3RjP2M0|79T&9+ z?>Vx&M30Sb15&<{RtpeYUf|n7n5GHyc+-FtA=7H$p6Mh=&M0O!so)tze7#WT>pp|x zfWae>0++DfscU2%>|@oiCQj+6O827)1}KsN^a> NSI*4?#ylfG-{q?3MMXX$dUH^S6Ni=Ve1d0(janpz@WqGJ?cG&sewpq294Qa zL{huwuoARdt5F4Dbh#?<2ruzSS{VeDAOtY+52t^xJW=!(0f3P&G3Cs^%~Q~~Wq{YA z!QrEk#>oXK{sc&Z7VB1_>fA1^#YyU1Ff<^9G(!V0!JW`n@EDdj$$2SVK6*7$!BvXP zmAC;h-W75(Nnzpro3CE9eV=~Lp7yS(vXnk@$ g3{R`!(UG013==W*Hj{-*F!ujl+np%IX?E0*I&-K^u zY1z1I!`iOu+Ll`UtL|F6Vb?~vk=x9w6}eE^*< )O?pZQ#8YKE#b($x>w$3E*F0Kfk zfnyCo#zOpX1(P2yeHG@fP7}}~GB|&S27%6=@G^V=rmeTB$(w9rC6J@uQmcAMq zQ=Ce?Z0RkF_gu30<;5#jEW32il2?}$-6PZ?au16Y)?kUFy3L?ia1A@%S3G-M`{qn8 ze+|6jh0vqfkhdSb0MvIr!;;*AL}QX^gkc+q0RJ4i9IyOo+qAyHbl I+$VuZ3UT7&iIG7640a)fe&>NOVU@xZ*YE`oy!JGMY%j}bGq!= z`R5xY(8TK&AH4b6WoKCo>lPh6vbfu1yYy0 2g^t9bDbexN!A`*$M5`u&}WqF?+*m?ZoW85&MFmXqQ1J{i;_Oz>3*#0?lWa zf?{tv`_JzP7D3x2gX&ICRn(aR$#>;ciH#pO?<*}!<}cYh_r{hb6*kkXSteV>l9n6i zwx63=u%!9MdE>@2X)3$YXh=DuRh~mN2b QFEH&_nHWfU{q+4 =t07pt+Jfj90Or;6JX{BCQrE8bZe&wi3fwEXHRp zz8{VAmxsWU)3nT;;77X7@GCm7_fL1p_xKEG&6G~luO;Bc3ZIa?2b(*uH7qJ!es71c z{Buj4(;Jds$o78u<3df_2~DLq`e9*$SGmrR9p 2OoVB5Q(KL3M{1>eq+;+lHK9N?xvyBPHni<#j$sZK{QrKEcdR9+eQD0V? zGPaq!#<-c#a>t4bt+R#Hu_|}dlIGeve@SR!d((u)Ga45+BuhHfA88G0cPrw>>(`ID zZ;aIyn|qmhuDXBthoW{J(WN+`Yud=y(wvd0rm&1*4 >6?#8&)Fz z&@V=a0w4)F{^!&W_l6<5xg|-0F!~>aCALbeVsZTd*)M*^tr*!)O8w)mzKThWyQW@X zw%BFs5_@CIic5EPcTJu8=CmynV;``)3}gJ`Vl#VY_3Yib@P-KvBk_%!9OVu #8tG|Nc4I~A>8ch-~X%M@!>yk~ERI|QEcwzgI66IaaY>gx0~lm<@f z5-k^OY#SGC80Yr-tDRP(-FEJ{@_4LHsGJ=)PKZ@`eW75-r0ylN%0Q>&*M;@uZLdJ$ z)rw7Dt5ajr;P;~1P>jID!><(7R;w|Yf}qI&8klT?1dTfc@us5mKEe;qw;YKR(cp-D z6NmUMP8x7cM%~ytE@l*Mp^oN*mCF`gRNhw3gpO1PVi_^JzCJo>#mX(q+iJ(Ts$5=! z13b4 5gILEULS!=)SmZ{ qsC1)$8-4eADGR?v z>~4k_SvdvPHAC}=4(!I^OLgQ@9EMDE7d$PvJbi+K%-H Th`P0#Ea|Jm6zj> z?R) (YWtZoIRx>AqzlG1UjT@6ba>yE z{Wf<5moh^-hu;ptAtPG}`h$4PWcOn>vy`#bH#Ss>OoAEE1gIbQwH#eG8+RHG0~TJ$ z>`C`c7KyM^gqsVNDXxT|1s;nTR&cCg6kd<-msrdE5Ofk=1BGDMlP2!93%0c@rg~4` zq)UFVW%s|`xb>;aR@L^*D>nkSLGNmM?cv)WzHZy3*>+ *xAJSX;>))*XRT0r9<#zIpug(}{rSC9T$42@gb zy8eb6)~}wl<=or)2L}4T{vum>-g)QaKjtnp5fyd^;|BxHt x~2W^YbKq1HfB7@>Hw@U5)?b^H=uNOpli?w6O#~V`eG;`irLcC(&Uxz`L_Cl zS8r24e*U71o@dV6Soupo-}Ttu*Dk&EwY`h4KdY-k55DSqR&o7nufO)%>%s-Es^5Q_ z60#cReEy=$4|nW)bLh=|4bxW4j}A?qOle+wjn88oAeYb~!eA+EQ;8Ggp-UldAt$3M z7*E590amz>YB9L(z?Xx&?I37XYw?Os-t+05x6Z4vkzBE6-hrbB=GAB?p{DQXV4CKg zls@_wh*&XC<3R(CEZxg8*Y(6a>cIOq9Nss7{=UQ7Nv%O_WxSyBqnH{@(<>A&2on@z zn57W4Dh*E)o#rJ2#tyxV2;C5#rl8%%As$4qB=IbMt-z|jnWi>>7Ymq37;AW!6Y4nx z1Ogx#!WVdA92mEipgUxzy_?ddg|x)KOCyK)P5v@usc;0sN3{=0slt4CuwaxK@20eO zhdp~Z8iJ7GWrkq_-X`~(eBpthn9|`tZEUCIG iFpJjjxPVE9I)#z3Q$3tw`a69qxjuf+~ z*?v>d5~pcH-AQ~0)8PyIjumD^?SM8! Wb>KZoD7hOlc2nA0_(eG!i n>}Ru}>6)>5 z@*}T`Hw{I^-?PS9>( #UFBQpW72* zsfj(2+_9@5x+57aN!`e`f(Mp_I(D>}p8) @&g^g+X1%d{ z%X5boE ?hEoj0CiwTh9)#8^?~;|wgor_=Z1BI9_dI{ z&t*f95n?ZgZ5CnQa!v(p|JT?y0%KKgi`Smi9k5r!+!Mkz=&Z$%CFl;?AOzV`YBKrY z0#Y6~J6&dA=m>T@TYb8ukaV4z^Z?V X*MCKcp13-ye1*`gAj_Tm@r{fpm?K!U@Xg2AfndEo6jZN} z=XK0GRNXVLW2c?}B)rH^yR>u}b?|p(W$!TkQ TAgu1AIG>MFfNchMQB_^-AQxRE$Th5-E_tBP@v(Cy|ojjP5LEU|JrM8 zVF5;$> Hl^jlHWDPChrTH(vh%bARyj5#TPb>omAs-)4zN z9?9(wybd0$Z5s+}F iytv}-8U`IC<{6U2_NqEAkv;7lys5Qcq3EKt z0-!^Xy3idllgZ~qX^QTe=i*oGUCJNk>Y26?+9U(Ks|C81S{-v+6ebc`c(yibQbuB% zxM7mk>}dI-TfUi5Jqdu6b`4SqF)y5humuCaHhssdcR(jKf5ZGprx;Oe7VG#G6TA1+ z8oZLl<+ey(L+$Qsck^4fi{I|)p15MX73gHFUU!l${lN{)Ht_Wb%j# UE6cZ9}Wq^>+1wz z9TBA@%f~tby^0YWafmn&8Ppjn1Ng{d;S01WImtMzV<`!zU7;+8e-Xko>qM^OfOZ`Y zEZG#vcm>EGF??&G6+v(3l`X(xMn8ESv=@LdMfdcxFi%g1?0HDPG>blldR`OLlWN80 zz<$t+MM9%1K~JT@#aBZjOu9*G{W$u7cqTM|&a1)0wR8R^*r$<&AhuCq1Z{-aUhc5P zdyaaK{$P=Y6R{40FrWmLbDOCijqB(1PrKlnL)Tm|t=l}toVLAZOXJ*~-dx|_A&o65 zskcpT@bs+d@ia`f)t8ivl{(t%H?O?;=^s3O^GXqopx7E3kz06f^UQq<>gyNmo4Ij; zrOxuzn{WOqP75~PwPXC;3mZ#YW1xy&DEXsl~)u4`-v_{*B %R6xNH3* zJElz8@d#i4`#JV(ko%x;u{LMqLEEDmwD*(ccB9Wp;u*9I?=sC7g>%L{%$4m#zhbjm z)gK{LWQvE1>_yl|4T$nYKNVZ<)vza7FKU5*W~4)KNgN@;SA<9&ERxIfA&UZnB=r%N z5YD4fY$9Mkzy}!G+`KUy>3l(FS i1 zw)t)*w$E4#ZSxfm3cZLC(o3aQQ7uHk>_@fMTHoM0=quh%mfN6%{`O($pyzg0kPf=2 zjA%M7bRl4BhV5{{d4HbnTh`HM& YKw@N~47e7NFGr*9 Yzi(7XQl-FJb4hPEKOC!K2x$nWy>8=PJYE)T$=Cqe(n*ChZE zklF{Ms}h0Jd|@o;Gz(~b;9d&c#0O^j{1?tF5dtMj9dG`|j0qZi^aF1r{<7KC5hZ`E zNX2nxJYEr@>u86|tPjTDet;fLn1R+IOm6&3b*}TOyN pIaid@W9c9!jIfiJOgK-aw=xb5Kpb)`E9x%CU82 zEQg_v`e+tWYClJHl=_EsSW?LZO3)o#ox(#2UW9|V7I8fYnz5fRtph`u)dywWL9}UV z*hdU9-BBK5G&}j~O6&dSdWDIpFX;&Or5wNbm^Y+A-x6(K$$Of6JTVl9n0gFY&=T5p zZX?pCxA&w{J)eDSfb?Zh*LT#AdiPlB;A%p |-`Aw6RP2mYTh zLmL~zM^VS0V@*4LkOEG~nQR) HyRB+;*KWli%QqKt&%16HWyMXRhtwdCgyoTm*5#itgp(Wap66 zyr-dgKgjl&t?JLMuw}!Boz)TOa2|37p^FAcPmxX0apWmfp$B1WF_@-dsK+?1F6~yY zEwi!-))Q_CbOP%?p%bx|=d^nLBig-_$e!nh19^Ps`s{SNq{nnW)V-qnz3y+Ipd7HS zsb}z %!+}y8izoy>Nyyj4m_br&8TGFcze#gP4?v*NEdl zzGBLM4qpvdu;5vC Fi9^zXU;sW`>pPi|NFD# ze= $xI@7q9B4WPsw4CAO~UJ(S)s@u41E>#9 D>!? =*N5m$%^0E` z<0RjkAj 02TN9RLX3Js+GArg=Nu>E5z zPa!vMuMV06#7$1dLbwv+VGT(5V_&A~Uy3T^+|y~Q2>lA|=hZZ)ex%G`rhkN54C5gq z>w?qN=A+LgB0-@s{OJs7Da|z%dK)uDH4?m5Y=K(N5KWL)uqDxwBt>QmOk(h~1u6_s z>9x>G_+@bJhBQ;(Rr?20>Tjn}^Y`|rQvI3Ua5$aGq{HFf4BhwAFVk2oHNbk)hmAri zjQ_!g*-c^AKM>A@je&H)i1PsJ5929F<8bLXvONK 4;-n6d;Zm7Q=G|k6Fp*AY!b1a`eoS*c zF413z6`x;!NZV1k5)sv ;-Dqjt?t&|JLNGSA2yWhU-RYC^oiWI1+idw;6*>m1&Io`^iPgF6c$sN zw9j3KFYs@%*HNz1Jr?F^RiLV%@DyQ^Dnc1h&59pWKhD#AMQV~3k7}>c@gdw=dyRf5 zHGNU7bA_hHWUnI-9SXt jM~LT >U5!uS#{ zKSOhB>l^nUa&S8kEFoAUIDG}(Lr#|uJCGb%29Xr>1S4yk0d)9hoJ7#4xNbi?5Dt?N zBp45evje1L) A;&Smy9J8MJe@ 1#HwBFoYPv$=k%GOaq!kd58)tzBI~EkGG3Rqy>GOTce-p>jH0rb~c(K z1|9q=$3)Vdgcwyvy&>S3p(f~O;~?XK{)Kch&2!gs=%kNH#-Ee-i}S+a@DNWR(Xnv< zv7kIUUD(c? RS|JmPeXBC6cbxUl6qRxl;fFAiK%!>EzFa zJ$-mz?G%WqC+P-l!DLX&nfxzGAnLaFsOg^Vq~gaW2QQ<(qixj#J=;Y{m`?kHkfO)i zdxQ*`2Jr3iXdj4QE%|AlQ;| Wx~pKrr7xu NnTe=t-AO)iha6xDYpH}>yZ z+FD^H2VS0x4us;Wo_95^kElZ$>j2HW@wyeLi3i%Q28NXxQT7V1{iHY}Llc~!Dkv8* zM><6X$}-pv0N#?+N%W`5%}K0Is%8kCOC~LuR6+;gtHYPi9=dqUoin~Q
^MhE;TSIe$6dEI=Xs(`oTlj_C-3c4KT+wJvpu4Kkn_RZVg5jE+RF`XNx?0xmaV~bW?v}wVTXn4{5 zO&2X+*pF%!%qu@3SLRk-npU5?`f_cV9;|pa#ktlD9VuvRx;TK+fWUv_$vC8-@TcO4 zN_-D6?7|-4!VWMEgQ}TUe(c3w4{eyxe8C5t7pS0MFe;X@U&B?sVDIGR;u>?mPyb2F zV5WLiQ2mX&1v=E#B`oe9yk4Y2^CFRk8*rV6 k1!uW{m47&7E!m%(ANz&+i
xrB^ng(;#RLHnX%tfsjJWM- zyBo5Of=eNl8*;gm`o zE0weGdP7~Iz5$$pI`$C5 z`U46T|8cnpt;J+VO?%~H_`Ph??bcn%Jzu`2`z~tc^ PoA?r znJlfFuxIeRC?a>J?C!EC2Bn;dnhn3XeZ}sbjb-10*a7A?aS00$P{m0wm zO_v_`nJOwO*k6S$tHR@xmt`N`;fR%l>^^ZvbfRm}PUBtryK5pTwRdIZgj<#_irORP zr7I?yj7m&+KkD(;PKtLXmF-s9=>`j_AFjI$YN7_w1g7hD(md1~ysZj9;u_Y4i3Ssz zgRH~g_UH9AHR4A!67Z@2zch=Odh*4WzWc2=ekK0-ueW&=xy{z7Gz9CSbv}Pk+4ST# z#ZxnW&!Z1tS0A}`@LT_*wh{sv=f-Dy+2cPoUi{nzYTGjx)eit9s#G5^D0+(|iNBlJ zV$vUX35MrZ8K19VAN|i75_}Z#DO`R~MZQy~2$6gqOvN0Js%d70SzJm|ER&Jy5k>-I z!fh9^fC*zr22w0EG6&Uqo`eqC7_L8gi(#?!A>;y86ak0F7|oHQIhmW!15hHkZ(*|o zF+vd5r!A(imA-b0 }qc4-&FS58}j>!?PW$SEg*;W8H~a^e%b?2`O8 z*`i%!x17FmIo=X;^83K2Y3Hja(b_rMns6%ts^>=(bA-9V<9O1I>564?R3a}v1yYtH z*l6T7AY0T66-95WtZgaP8(}|MBGlfNdh@=~Y1m!IA7($BPUtE`qT@h@;M3Hd z;_dtQw^?1x7-WaPK4XDxuqd5+qVz|PQlALGw|x}&MFa4RtVSK`(e|RtFN=u%s&M?) z7+HD3$diG_iYZuX{0ijc(*2C7cTX)p*3LRRtn3r@wq>%<@A9jY)yX*d v zSq7pIH0)jCA$)wa^7RfPVlWXzzoH}vzHmu4?W&f|zEC#fi<;dYS!Z*G+=!O(wLx7} zkfS~!6{@R-(Uw86L(mJl7`6&&tfK Dx<)c+WIlqL)3pSX=7*`N5ysyr`8ap$bd^E3w89)ZgPiCBi|f{Ji ^U)|AMCk%95n_gVk3|_XmE_Z6(keo8NCgI|@0sfZs3_s1} z$KK|ZCF;AE#cQiOrv*z^HWTBHM`H8Hwdx20FDq8lu^{(Q!@5s%Urrmi_ZX=7)j%7* z2x#|wO+pMI^e#2DpLkU+erWUorFxiNlu1s>XIg^5wIEm| joek2Rd2IsPtNkBRLQTFsnoh4v_<(`f@uV0I_G*I9RD+?L~j{1bx`#0ta zEeZiTNBzhh^|GEN+1vl7{w)Wm!`yhLKAuC&Ve`GhjRo0c|E^`tZXfkQW;&_kBLS|M z7!XYb?!E&&=u`h5Ld{_dyivFMQHW{aI!yVS7oS=ttZ_4U4sb{P=wmO6wCrO3g8Cir zRxN0ht{}^=kNOy`2fdgiLzr_8?$^fWMSdbcHb<)&+4+$`i%$>mB*aF7fv0tiFWhcK zRThLy0Mtx?A6Q34Vn$tJOcHkv?-ldg8_%9Jr8YX#=C;}%u*pWq^?L5VVi61EUkC^@ zTi3LAgna%bC9aB?Qos0?XlUZtnp9cISx)1AbGeO~JGb1<*DpHId@iRrT4e7+!$h07 zWDZ4FAXQ;*hdB%9)8U`#Aq1XW1`G)sm$Ol@ZCv2#2r5~I^BXuYJm%NgOkCQOAufat z)Mo2&C`TDc7EDz1sE;V{`=Bx<#5gYrDb+@@FE3>Yx=pZB79-7UjD-g%Z#qc&td6cl zI`S1u2Q2b!m^1LOg{LEV_eV*@ cFW|i{!+a94itA#8 z2;?I%3?C8LQn5B+Ac|?$1Ejde^`AH_B}3`>#H=np*@ XDR^y^=fZDd~Fz;wS>e@!M7JaPvv zPU?=U|2$6iw_+;&j{0oiARgl1!2p}_PMTg!Yxs?H%{HmJgU62_ghA}_;}{7x*brZc z@>!rSz|M}1YPdKizI;?B3~2O%LY`8A1SF;-m z+Oxu{+PYOU-V9O}bVd$T!;AU2M<2*KtciMEC29!H9V-u9ZUJ$M-4 #Nb$5QVy@LP8HyfiyK->WR(e1g77J;isq@ zxu$>@C(@*mf}RY@L8hJXBrWMOEKDqt3i8iwFSwpR$W>G_j=iMN>(!1>S7GdmXt%UH zpfdn%XxP3S<>d1=1{yBn9c@?(YZkyN N1 zQx^M4-32#mo8SKR;r8t_CV3=RwbSNzS!Jbd%GS0L=qT*0!ERw05x~DzSsUKHYQ||Y zuwKD!+2nux!l3~g>0-F=;qnW{w$F|jqXuhZz#N`4WtzLDj_MYvu(*X@fb3G;s!oPE z?QMW|e7J7#=?C#3QWQRp-~(1;_=?J(Y^}oNmHRoN$^y4Pv2Z8cL)EmwWVNJh@>2ER z)el6y-IQ`!2h2{kx3}jwTf$_!N75)(mi|n=?Ylj_>QzqjfMiO67Wc4{rOcF4JS+{j z&z%duf1`r(U@ZlI{F=sZFnCGJv}cN<(cA|5AP8m+HUK z@vG9%#_zOu)ChxFSxmKsBSSO9XX%g4SU79e4=G!|Cgo(;VeA8dsRxIZ$Eqhj(brh0 z>Jh)P2`<<#u_i^?L>%2jxXAxZX%?<7l073C+~1p!t{Dj_9ZxL$sz|_G{C#{Hv@t=B zP}EsMr62u$;U#=d%MRJHCiNv=5OI3(_o-A=G_9B~AsrRui@pzUDE@tHg#6PmWEuT^ ziPt|@8=kjTNmkqdOlyJS!m{E9I87hqn;%9rT0<0-L99QeURoy K-&OxH^mcao3^t~WeS^K zH`XC|VCLo6*duA78O!ugN@5Elxkhd!CmdSX&*f=utfmDFD9PkBHMk3&aFB&)R8NL4 zD&i)OQL O z(Z_o2Zs~o#^$zu`{XU~$I{T&vAH3;ofJ*ZpJ&JR~s{J0}8cw}`t#a3NvWA?#tMY67 zLG}{Q{#6^CipQ $*V2|W$g2v->Y9+4=(K+K`;I4$BFUb9!Nrk0B*fL+v z_lcdO1uEs@|8I@xoKCB{68@q=)}90JCVF33Lb?M@bC5mog<2~vPXXzk7B$|75Lya& zL)t=%E&Pk`S-PznN<)4iAI;NU!@f0_V&wOND{4!~b@1&pAN$ Goqzvq>;o=lr=43Xx{tUtEaN3B>CWZ)Uac%%Y9--wFCA~Ek7aAC_APm}b zpXAnlNOIF+;t%pPlAxIkvv1neXa8*XxNLX6ZDDR(+U5bi-=^>US$+3TyUFaf{gSPI z&A@*!TUbRQ-p-3$KUDc=Hp9j|c+t%)Z{KNid2DyGia&p6lgtpOkDeM{Qy=)H&22V` zFBRKM=Etf98a&;o2pD`R2ctkyWxz`aTDZXBjY52aOspy*2=?xDIZi>&&))8y?Pe*( zt;DkFm|`@cFI!Kx=wFn7fh&cqy-f1RZb2KRCK7JNBsApYHWk=M5J&|wBQOdb+2_^g z*;b(s3o^wX$sWZHhUhNh^+UU2+hPaWw)eN~kHy66akHOp4#cDm_4zDet K1Mqx+sR1`nMz9wwQP*hL>=&Kei3+FtV>|yg%{T(6f`N5BR!MdXj8xHG^3) zqCJiEswQF>ZLP}3Hs3ciKciD63}0Z^MFL6+`V473sGm^=U1^Mx3`Y|Mrl>H0pEcT6 zg^H5MH*WeRUNMs9VN5fcZQ=>}GHBs};LS}+P-y~P#IlYJ0P8ym@R(0L;jYe*1D4ll zwDy~vES0HtyCCI2411OeiC>SA#1wX;8DRXzVihdy^T9BjrZUmN_=b)~n*!R4%Wps~ zkbFH!%W;I*pJZ#8%)c_#RUtKlOksrV!Y3i%vh>?b076sjL-)-NtH_t7E8;OBZOPa@ zAofQ3jdT&<%k!kzaG)7qW3j4HcvQe1&&jd+f8}J3!f+>UDx7H_B8^6hA&r*!PDQ-B za5jys`+BVIUd>7lmgi)Y&fyh!`yosPQAwyIh?7D-h2#b7);pTpdfDrCm->#&W_JPe zRvi?=>OgitOs_62y`!|JbhXf5STOdjJDPjj*#EK7D|Q>bl1&L=hPkN@2)(QE#vP@l zt9uJeTG&n{WG78N)aYu19%#`y%8i44oVsSwNLRxgR6hF`tsw;8VRy)COB4 `B4i4SsLAa4`Y(WRazi3X`V v!fMiDilJX?r1a{9%U3-*f6J-iKJh{i^La~ z$yJ?ASG(MP>=IKImh$g9bD7xJqR}YghlfIHszUwEmoF2yQ`Xet0HgZCGNmYge2TvH z+d^IF=q3{GD`-m8K+R-7AdPA64e{l|c4AofbmD)4hUvwM1bw^%@mXLok{H%R#q;qz z+gU3h@JZH-G^8$-2?T_&a!E51(fhSa5 Q$w^j>=mA9b7)O1^G1VKyM1v8fOAgDLfFwlSN7aDkBbh=1Vofi; z{_|sQ`!zOY>fWC264~Y0Y;ZbE!j3Cqv4wlfV?E8SiTe3tr;ceTaXo*JV!Oufp0KT} z!>xB&7aARQo9It=F0Wa;$5j)X(=fKBtv5LhYKFC6eJA)BwZ>zny85O7zI6@a-&ln8 zLF2LorHz$i{9dO!8mb#Jp?&t4L$8*9&!)KTkLxQVHBP 8FA!bZwX zC$1xtlq a{pU|8*e#v_V+#E4OT zjwi(7(vGZ$V!mG>tD`=FtRvSqWZ9$*B?GPmVd1ek!0@{$s=gg&_gx>I&W_E$e<7Y+ z5K(_sDS$qH^8rKPSita&*B->#;u88_rMf;Axsguitwh`|=XF8(EVlU^L*PKbu#TN~ zwj8|9X*SENE}$egSAG|3#!^5By}_`$$?RM3+{=QMMid7b`V01GIvvI+&E63R2wQNp zn}sc$*2c&2oUL%!tO4~7wk4n)tpFT)D3<_3R0r=|=}&0KCf!VqIpm|jC(z<~qb-#Q zZxk@2wJZtt%hiN1;J9w_Hzt9B+S-HzVkb8@NIl-+0XLm`=_dDWyDqXB zn&w}0*`hmpYVLH;R9>jKpb gr%Tssmku7 zB4?i;DJ=yE$ 6)n>a-tiWd=_(RksK=Y6Abz5;b5mLI|>)(FA9o zGzACes-Q@1Vend}5C)iY7*G)}1M%Udge?eW(1HnSXri;yq(~2bXQq`x;Yrz#0k&ke zS%JGlk~lDWC_ny*-Pvc@4#dzy&@`+2PkV% % zOIv<3)+u>drFF184*~^AoZL$_J<;#J>d$8hF1HEz)8d7HT$%mI=(a%Fw_CitukY~T zzCPh-wvU#V(e-YoddEiUO$O~Gr_8a91@$Jc+rpZOpW6;!qTct6s-1GiRv51Kzn!ku z>d;8_q{~ie0yF5Z-59^ #vLXATUx*cq!zD=G$XZeu&u5Te*HqWE4IIDJ=3 z;X=s*MnE=AeJ9|E8#P5YEW>Y3>i7+gy{D`72zWgEJ6_;p$$k1u>hqEMJ4WhXT+1`J z2UoHdw1-mEKE?MEYBN#+HGKNk5c-SiJgPNDBrxIO3hq2zQ?Q-Gzn`%I_?VYp&dv2M zvIvf0jiNBnpf1lm=3_A6ApuPS)>4!*8O26GMgpxwaM6T-up7}x$fShgk;qe5v^RIo z>TaB#z4r{2{wUbivuj#sL%^MIIAif88=Zo8VO`(VhtJ#lK)G7`AVbhecjuza-rrB| zo4s>x>$20;IoY}UyhY=kM#Bz+WZSjeUwYHVtw){{#_rt79ybJJr`6`3xa`^N&f)n! zT=yimh90T==dW``)l)vNIle^QUoEWPPd=w1q+I0(zj?aa4;5EaZaQsy5FJ4LeF}5{ z$zg##sP#GwKG2!Ph}IYe2=jqBViZeEZy;=DiXR5O3_2O25 Y~Q9y=cg)D}9l1=&&Xw&3 l?g{8))$`(k@{a1p3a{ens7utuI^2=vshxrlD-kY-br`D+hAM=))3(PZ zpyB3*357l{^D%K-(OTUkjEoJ4X>x<^UfmPAA7hlXG?QgK21ybCZk1lxS0Sifv<291 zEjcA#Q%-#E!a(4PJtQIWk)#atL{s*GU*JZt07Zc#S!1%fwV7fXkwZu$LI=?Jii9b& z9N7&))d3Vh8fPHy4GD@Ijl7yD&?%NGuJ_OccYXkIaDN7{Ux?ntALbeUyb?sbz03s# zLfJD@r)GcJGkZS!PFErpG3low5RJ#jCL63{qLHqyaMc*AVNejQp_b+{ucvHN$a_^~ zK+n|6Qz^l#n5WiWi;#UEURyWC?C}74{5m0i9bm^jS=(82np)-?!p5j&Hj8-6#y5q$ z-cZx{GVhaJT^!E3OK(B$?9)Oq;h*nmgonr@l}$~5ny#*74^BUz-dtT@>WZ;S_3r_} zQNaQi9BKB}jHzND-dA1Yeacj3_qnU%q4vw$L-Baogt=3ig3Ri*h;4T_HQn8u6~D8% zu3dIG R>z7KUO$}07IDA zm>ULZ#zLtQpB=zl`Xly=k@2w#_&57?*Xi!kJ;wQT> Y(diU_s7c9> zJt9NLo6(QTdY?<&%(7s~gGuhxX6Ia@TxNd)1c%NSn z1vg!?!9F%t+BbteRT}T^ikFtgySn40Y{9CQ#s-^l6%*Z|a#r=PT|QRt>uzZ1KDuU2 z_UG&)_39e07-r|Hmy8d@CawADtYBN~ud`dnC6l4WwkC7cwB?%@#G0C73m(O(B@{A= zKYo4MwAZI+m;dFW_8z_0tM6&w{t;apJRSqCB|8-3|G^xy4{cteem4EFg?KyO^H>jM zvPiWhJ7a++c1XQBBKT_Aev;X1adZCx?O6i7i}=MPVM!{DFhM1no>Vgi=FJObSSzE4 z!cz06q4?jt9&?tl`>Ym||8Lbn@fQ|L_G8v#F`IpVs|l!&x&>B}_z$1B(XGyIsHAWY znA8qOJ=@^)4xPoaU-h^g^}_jK@kTQ7$?aFf|5I6D)sIC2%qiC(coF8shYu$ie*)ue ze%G2{U`NRIn<&=&^cNmI;H`MZjd ~?#3I1s@KF{obqiu%g9@l{o^DS=Z{*u!j)-EktzHk%L~ zUeueNeuutfbuxAHnCfe9zB#!P8?xVF){CM-QK}``94 {Bxq4Q=lI*@*(t$ z0*llTSuC3*FY_i0Esz=DU(#!`f?@wi{if=Z>r@~3asMrB8H6RvvkTcW)vbP8ZeWX4 zzxps+&i<@^TXl<*)K}C$u*vFs=c>O<uva_OepgZ3^mp(p%~u)K{5Z{k!@f>W^5N zctHJ;`gb-C%!>u<(kED#4A{XPx$+SHa}?%+(O6P8P)JhxL-2PKS-#1p!TbB=d;5nL zMMOs=yP`{Yvn%^wn}ki9e$C!VtI_NeVz`$Lz%L_RchA@F7J^6AM{gFM+M7MOSKOPu ztXH`F#C^w(VO);r;56Hd1-i|6n#b*T>ceqoYd9adu&Oc+x`?PF5k{oi7$_HEV@K2z zymA4)N+`DI{|3bN<-4D@&N)YxIVoqR5q@8N=Kc5COtz?XZfomYb%y==nU^drYn >b!5Ctr?PZ$sZJGC4(Lx<*GmYK3@9};69v2?xCz*86!x1fq z9-^Oe{|eU+0lSwM-%%oRlZiDYBcsgabpN8BFSM>vThx{{TLd#395z2-=dk J; zUPumj_0A`QOXa%S$dG#HKaV)PHrXJUqTZlMEURp*D&K#c?PX)`>TojQ>yzh(U5ggE z+}3v2ww-mQmrPrgHX82`E)7LZ# 9*S)OrYMVHZ2*%Ix2 z-f6n^R()lg_{@W9puD-%bs!$vZY>)VYBn{#u=iUtgZ1U*4oibOw!C4kr;~&cIo+d? zul5rmlh}%uY=)i|^mJ>IyR&mweFZIu_7x~{W-C@zr5Q1cK^! y+OU~frPEZqXZ04#L0$|tY}D-NPT^J>z!>2 zLk;VdDSg7vTYSmL jc%I1lCVSm>+G7BEY6w@(XH|*G{ zSt~)o`-!M-5J4aV2N@%gOd!0FRF IBn|vW}Drt z-eWVGJOi3H9hf$!nudR8+Nmhg011-@!@NC3DA2QVhVsnWtq@_vVUsn7Lgo{)!})lf zHnxUxXX|Z}q6~&9Cutz=WXN1iJCP;&D8)pBPR#N=xfBTp2pd7-lFF5XXBc!;f}%nR z1Ca6zjC^CAo!5Zpsbiu(lgpE2dZaZQmR3Pl1Nu#$p&}HOO1KhD0hr0cDxiUoC%PDR z z2y;b(?1FUenyXAUfrc`fgeI i%?Q>s#3O>1`S` d7)!ab-ztxcdp z i(oNgfzqrSy+Qa-h~$kCFl>tV#u zT0yo>Sj8|%X=Z5eLYl_j3H$wFA3GlQ`NIC8!J3ZtWgQ*Tf>iySj%6K(I%;b=*zAUs z@a=8sq4nu=XBezD!_2jBtet7FSq Qn zIF@m`p^X#2_+Y@)f(;Nc7NdxOl%T-$NRFKpzZ*Diiyv-9$byI~Y_VA7@fF$ z 4H|Dx5g*3@-my-zW{NS^+s=4LU=S;5ULvFYRU7E$thNp8*A(h3CX5s zqQ~5@=c+ot#VX*Ndavjg1ef4*RI#r4+51F`-Xy>#L9~eMYl6w8mrb%>5bZT?ljVD6 ztEdNv0*uOqR@o*xU>7I~%q&O{-x- #ny*Sp3}O21M?Rd(O98C84<|F{P!iYQi+&Y*nsLu5^Ihu$V)k)=GECZL$l#xZCMb z%xz~?w@;eYGR~3+M _}0ce(?P zl902^TxqD4$DQx-Ouql3YC)>Mv?0+^0b7X9MdejK@03cTh{%+U%}ktHqQF-^C6`xw zO``FD0}P~L0z_&PDjancf@m?ZGR0TUYN{lM-RfudpltLzU;yJ{R+GzQ*P|q&zCuzY zP@pguLKr`*Q*oFilK?v&y$CF+j-b`jSz!_lC6mW>m+2px;ND~mcq=BCmMTz-PuXY< zOa5z2j)rQ{(LTN*&~0=Yh5whf_W+NhI=_eaPTAgjUu|FYx>|LuiX}^yT;wh{;oiU% z_p&Z@Y`}m`F N5C~v?rUXJU2@qOB4H#QH{+~N5*}@@#Jm2%V%+B2D zcW!yhdC$u$WMz8Y@Q7Sm;An!nZCaUSSuojY3}>m>9D|bq{)XtxPsx!lnpMKJ$> l0=VE#0Q${LhbVQ?(avB~M5H(A<6VIs~Hmen|XCr57cj;wDg~y7PjIZR* zau8CZLCaPfRJMsKeNi~1P;*LSAkgMF^Q=afBekooDqXYIppZJ`(kv}2%`0n&8lEg` z4=C( +1ET{^|A%kM #z zXK7m|9Wcfc3=~;>1jcJfX#rU|Ppz!j;7pMyJxd%-z##=(QTY&BIZl!@lVSAb*KE2t zsC)F&?X{LH;g7;@GHG Hi9oIy36f@s3g3 zRt#I$TBG}b-9;4UrV$&5Ij9vP)Y;Np6VLT3k-c!=P<<;z&y-p^C+_T2?PjhnuA3&) zZg_w4 iMx50MTey|GHd-~Qvv|JOonzEpncEx- PZbcYu(#|MF)Yep>~>mY?NK)j*MDlofYp2?IA zdWFjqQYB^@4u{F4kONMK_E=?Xxs$LThk3UpU19S{Nzmr?e_{2qb`9sV2yanqH0d@5 zKGJp8aZ;((RpJ-E(g5Ey-P)#3bab(6W+bgQb9J5E$fs<9fcfNuxIvFo=h1Dgwcy+w zPuTU(HesXi2ZPm;XEiGog3BROSUdQwi5UwQ_J3+1m1G-UYluB@01JOMr|AGf`7CDG z0ig`8Ee4)kL6qbPGy~CNdwL7bt`jNhr{b~f<0Mqx@25+$lS$DH(Vxp|&m0t?&qQTw z7?k*9V*W>p{DU=}4O&dJVTtJY(^>`^lPL~F6O|IFf&j!DWck6E9}tqnN z(gl(B;1+U04#Mx7H@PM!jr;8}`p8X5AFzRgZ z`H&lBbVagpDgs^cAL}3%1zD$XOne$PNmH;OFF;TKQt?TS2u1Xly;A5E%X>i&LS8)c z94WDnS|omqYiN=XeK3B}x+|c@HmfZ(WQ<~YG9AvJ!q|jbd#I*5WUrl&T>ys=H|eYa z=2P;fwY|sZguD`qxdX)M>uI;{{E0Cl55B`!K{}wLHeN|4VH*Yn BfJf$tm5E77<2U`gq >@HG1qNC7Hcyb!M;d687pf$B(PUZ=T|xM7)L(EmRVw z;~E{-q~ZvOOr2pdE3KGuy*wmJ%9P@R0*A2yuAhIFS3E2{e{lXEPa&La>y?- W>-8zjMwKGjQ$BzcAdCp)p^-It?U!LP5Hxpchm^Keq$?$57$5a!Z+()BJRD{ z6WgCQN}23z-^iC&TytVqsnMs6p-*RQ(ixw2F8vzfP=&GB|8F?{vwhrLatNCSGk0hY z#-0-r+MT6XGIxqGf<)4vq(!0^mfU%UhXXyCkz}3fmG;0s&`8l>X!W^JfDuz9HUo@{ zuuFqpp>Uv)!psk76{RqQDF$&!v^n_EC T`}V@{zZoqC)oA7_w~`M~N|5Q|_k zJ;Up>vyh*=Kjn%>HQJW}(v6${w!9Z%lq8ZlF>@K=Ek<&| IT4DB~B~Y_O;v9%9bdID;FI$4}a;O}@l!+Yy zZ67)fU;`NEa8WOT7DH7N_&*q17&?q>qwQXMcFgOOnF<0N*-^sEWbzzvC)kr_vv+i5 zgPm2{O*$B>IAd@{>+WUK><(pc@%$Y%QkK)@5Tn}4^Ln|tOsDsh=f>O`Mru?jc?N+S zjv9?oZ;e0J6*s%IG6n*@)S#6c137i!nnDgDIU_YINmjH(${tUCloc<{sdVK)q-C~s z^SX%F!SQCb+A?8SAq-ab;ILesL&}?2F1w-0Zdb;3_7dq1y_J`mAZv20%2Kk(?Wvhm z?B gJojYahs`X@A7)HA9Qm5P}EkW30FIDr{C1ON{u z1g5dIMr=}b5GjQLE~kiOEsekhAqGW;iWew{c8QDP()f-j!!>b}0<_?aiq6~yI>*3B zi`CdXW~Cg76+JS8SL=N!|F26HjVUaAW#N(;&=GruQ@h?1{-Ra%60++(*a{-;SN={& z3m*yJzP9zU)P6F#y&<2IYIRcSWv>_H=QF%ksji&bymFkwB+s?s!OWBD?KvFpwAYaF z6HB9tl5(fq9jdFlXQI1E?Q^gHx ncuVOg#lH7*|HYd$Tnnm)HD6gV_v+Ekb4 zp_-m+TC}!*?8^M?Y`$XK{JN&qk1Sq6xYYg&+mlym)o2Awb#46$jTWSN#;OI(jOptu zaCbaIeUAorw`cR3Q9bDuE~l}?)pf9WSllS}RTN5{AmKP8TP%l##6 4O+ z<9w~)>KD$L^#-v&PKLdn&JjL-V;0%hPd@a%E}(nDen@49b&%5#O-QsX6;-7Ym_{)3 zVl37&u%3X?ma&!7b)K&CFgV2vc Wds-QvlU}1h5qyxV^(mlpUfHjzhVqKa?A?iY8<~>_=ad! zk8dO`rvOwQj>Y9oP2*Ot9wKK_hBC~WVtf!r`yU%(p%oD8e+cg4QUi%h2a{}O5}EG* zZ-HLS&Y#F kWd<|*0G}o#4taLmE^k0-iGxUlg8Xl6I@jpH*%~?tx@JuRJn#pu1 z@%_I=rNM%Y&`YFTCG|8jY9=GAaO%H4EqhwG9gJlaZKg1oi{db>rau> VdE^b)^5 %>b8}?cL9itw!Y(Bo r%WpI?%Pj4J{j!bwjl?n=A z?##%PqWmuA8zS)5vCxk(#bC(9jFU0xQk5C=7R7TRzMFn&JpLe}gI6mL{C!MbWW0*I zJeV8RWO=t%FK{h(m362pOLR55=AN7W`u2&T{v&qlpQUo)8&gl^+xyG^_=H+E&E8{g zDtj>Tm&AiGOuNYD{?mSBc+fDm!jX{TQ= #IZQaQl l|>^G`1^D^SV zM+ZBRqk?)b(96%pKAv6kG#;Gx_9RUJOrL=Ch#REmXQRXa?RfD@|1DZP OH<>K-+Z~L-ZeSdCe_=8y zv$DF gjbD+f$Xn5p?QtF#T$_pgT|@$@QGPJGo8D>TeAt8fg6onA*w0M> p@iDdM_^a=-IIAa==ijmLcDs$P+!j}iuEj;;q_SK-hF(6t&u*(3 zU!LE)pqCz!$h##W9aWv*rYjeIUm+JxEFjgC8ezyBN-_G-vS}?09R$E(jR6BMU5U^@ z(V0P0B}3^eADjeW+@$S6T2jX+!gXXQh=c{DMBthD%*Mu wk