Asar

A multi-architecture SNES assembler originally written by Alcaro, modelled after xkas v0.06 by byuu.

This manual was originally written by RPG Hacker and has been extended by various contributors. If you find any issues, please report them on the GitHub repository.

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.

You can view this entire book on one page by using the "print" button on the top right and canceling the print operation.

Usage

The general command-line syntax is:

asar.exe [options] {asm_file} [rom_file]

For convenience, double-clicking the Asar executable will prompt you to enter paths to an ASM file and a ROM file and thus allow you to directly use Asar without passing any command line arguments to it.

Options

The valid options are:

--version

Displays Asar version information and exits.

asar.exe --version

-v / --verbose

Enables verbose mode.

asar.exe --verbose C:/homebrew/my_game/main.asm

--no-title-check

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.

asar.exe --no-title-check C:/homebrew/my_game/main.asm

--pause-mode={mode}

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.
asar.exe --pause-mode=always C:/homebrew/my_game/main.asm

-I{path} / --include {path}

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

C:/homebrew/my_game.asm

with Asar, adding the include search path

-I"C:/homebrew/binary data"

and the ASM file included the line:

incbin "data/player_gfx.bin"

Asar would now look for a file:

C:/homebrew/data/player_gfx.bin

If this file didn't exist, it would then look for a file:

C:/homebrew/binary data/data/player_gfx.bin

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.

asar.exe -IC:/homebrew/my_game/includes -IC:/homebrew/shared
    C:/homebrew/my_game/main.asm C:/homebrew/my_game/bin/my_game.sfc

asar.exe --include C:/homebrew/my_game/includes
    C:/homebrew/my_game/main.asm C:/homebrew/my_game/bin/my_game.sfc

-D{identifier}[=value] / --define {identifier}[=value]

Adds a define to Asar. When no value is provided, the define is set to an empty string. See section Defines for details.

asar.exe -Ddebug -Dskip_title_screen=0
    C:/homebrew/my_game/main.asm C:/homebrew/my_game/bin/my_game.sfc

asar.exe --define debug=1 --define mytext=" value with whitespace "
    C:/homebrew/my_game/main.asm C:/homebrew/my_game/bin/my_game.sfc

--symbols={format}

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.
asar.exe --symbols=wla C:/homebrew/my_game/main.asm

--symbols-path={path}

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].

asar.exe --symbols=wla
    --symbols-path=C:/homebrew/my_game/symbols/main.symbols
    C:/homebrew/my_game/main.asm

-w{name}

Enables the warning with the specified name. See section Warnings for details.

asar.exe -wWimplicitly_sized_immediate C:/homebrew/my_game/main.asm

-wno{name}

Disables the warning with the specified name. See section Warnings for details.

asar.exe -wnoWfreespace_leaked C:/homebrew/my_game/main.asm

-werror

Causes Asar to treat all warnings as errors.

asar.exe -werror main.asm

--fix-checksum={on/off}

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.

asar.exe --fix-checksum=on C:/homebrew/my_game/main.asm

--full-error-stack

This option makes Asar print a complete call stack whenever an error or warning occurs. This is very useful for patches that use nested macros or mutiple incsrc.

asar.exe --full-error-stack C:/homebrew/my_game/main.asm

--error-limit={n}

Sets the maximum number of errors that Asar will print before stopping. The default is 20.

asar.exe --error-limit=500 C:/homebrew/my_game/main.asm

Positional arguments

{asm_file}

Path to the ASM source file.

[rom_file]

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.

Examples:

asar.exe C:/homebrew/my_game/main.asm C:/homebrew/my_game/bin/my_game.sfc
asar.exe C:/homebrew/my_game/main.asm C:/homebrew/my_game/bin/my_game.smc
asar.exe C:/homebrew/my_game/main.asm

Standard Includes

Aside from passing include search paths to Asar via the command line, it's possible to also do so via a file called 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.

All of the examples below are valid:

C:/asm/stdlib

    ./debug
../../my_game/libraries
        test/

Standard Defines

Aside from passing additional defines to Asar via the command line, it's possible to also do so via a file called 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.

All of the examples below are valid:

!stddefined1=1
 stddefined2=1

stddefined3
stddefined4 = 1 
stddefined5 = " $60,$50,$40 "

Architectures

Asar supports a number of different target architectures for code compilation. They can be activated via the command 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).

Supported architectures

  • 65816: Compiles code for the 65C816, used by the main SNES CPU and by SA-1. This is the default architecture. It supports the syntax recommended by WDC, with the exception of the MVN and MVP instructions. See the instruction list page for details.
  • spc700: Compiles code for the SPC700 CPU, the audio coprocessor in the SNES. 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 also the spcblock section for an alternative way of assembling SPC700 code.
  • superfx: Compiles code for the SuperFX coprocessor.

All of Asar's features should be compatible with all of the supported target architectures, but it's not recommended to mix labels between different architectures as that will lead to undefined behavior. Opcodes in Asar are case-insensitive, which means that LDA and lda will be treated equally.

arch 65816
lda $00

arch spc700
mov a,$00

Number Literals

Asar supports decimal, hexadecimal and binary number literals. Hexadecimal literals use $ 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 Unicode character with '. Asar will automatically convert them to the integer value currently mapped to them (by default their Unicode code point). They can be used in all places where number literals can be used. See section Tables for details on character mapping.

lda #'a'
sta $00

db 'x','x'+1,'x'+2

db '💩'

Opcode Length Specification

By appending .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.

Pseudo Opcodes

Pseudo opcodes are a convenience method of repeatedly using opcodes that don't take an operand. Instead of using the opcode multiple times, the following syntax can be used:

{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

spcblock

SPC blocks are a convenient way of defining command data meant to be sent to the SPC700 in games using well-known SPC engines (though at this time, only the N-SPC engine is supported). The general format looks like this:

spcblock {target_address} [{engine_type}]
    [spc700_instructions...]
endspcblock [execute {execution_address}]

Inside an spcblock, arch spc700 is automatically active (see above 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:

nspc

This engine type implements the format used by the N-SPC engine found in most Nintendo games, as well as by the SPC700's initial program loader. The output format is:

dw <block_length>
dw <target_address>
<instructions...>
[dw $0000, <execution_address>]

Example usage:

                                   ; assembles to:
spcblock $6000 nspc                ; dw $0007 (length of the spcblock contents)
                                   ; dw $6000 (target address)
    db $00,$01,$02,$03             ; db $00,$01,$02,$03,$04
    exec_start:                    ;
    mov $33,#$44                   ; db $8f,$44,$33
endspcblock execute exec_start     ; dw $0000, $6004  (execution_address)

Supported 65816 instructions

Due to legacy from xkas, Asar's MVN and MVP instructions are backwards from most other assemblers: the syntax is MVN dest, src. Also, a 16-bit argument like MVN $ssdd (where $ss is source, $dd is destination) is accepted too (edit: not in asar2 it isn't).

All other instructions use syntax recommended by WDC, with some additional liberties:

  • for accumulator-addressed instructions (inc a etc), the a argument can be omitted.
  • cop, brk and wdm are assembled as 2-byte instructions, with with the second byte being optional and defaulting to zero.
ADC #$00
ADC #$0000
ADC $00
ADC $00,s
ADC $00,x
ADC $0000
ADC $0000,x
ADC $0000,y
ADC $000000
ADC $000000,x
ADC ($00)
ADC ($00),y
ADC ($00,s),y
ADC ($00,x)
ADC [$00]
ADC [$00],y
AND #$00
AND #$0000
AND $00
AND $00,s
AND $00,x
AND $0000
AND $0000,x
AND $0000,y
AND $000000
AND $000000,x
AND ($00)
AND ($00),y
AND ($00,s),y
AND ($00,x)
AND [$00]
AND [$00],y
ASL $00
ASL $00,x
ASL $0000
ASL $0000,x
ASL A
BCC $00
BCS $00
BEQ $00
BIT #$00
BIT #$0000
BIT $00
BIT $00,x
BIT $0000
BIT $0000,x
BMI $00
BNE $00
BPL $00
BRA $00
BRK
BRK #$00
BRL $0000
BVC $00
BVS $00
CLC
CLD
CLI
CLV
CMP #$00
CMP #$0000
CMP $00
CMP $00,s
CMP $00,x
CMP $0000
CMP $0000,x
CMP $0000,y
CMP $000000
CMP $000000,x
CMP ($00)
CMP ($00),y
CMP ($00,s),y
CMP ($00,x)
CMP [$00]
CMP [$00],y
COP
COP #$00
CPX #$00
CPX #$0000
CPX $00
CPX $0000
CPY #$00
CPY #$0000
CPY $00
CPY $0000
DEC $00
DEC $00,x
DEC $0000
DEC $0000,x
DEC A
DEX
DEY
EOR #$00
EOR #$0000
EOR $00
EOR $00,s
EOR $00,x
EOR $0000
EOR $0000,x
EOR $0000,y
EOR $000000
EOR $000000,x
EOR ($00)
EOR ($00),y
EOR ($00,s),y
EOR ($00,x)
EOR [$00]
EOR [$00],y
INC $00
INC $00,x
INC $0000
INC $0000,x
INC A
INX
INY
JML $000000
JML [$0000]
JMP $0000
JMP ($0000)
JMP ($0000,x)
JSL $000000
JSR $0000
JSR ($0000,x)
LDA #$00
LDA #$0000
LDA $00
LDA $00,s
LDA $00,x
LDA $0000
LDA $0000,x
LDA $0000,y
LDA $000000
LDA $000000,x
LDA ($00)
LDA ($00),y
LDA ($00,s),y
LDA ($00,x)
LDA [$00]
LDA [$00],y
LDX #$00
LDX #$0000
LDX $00
LDX $00,y
LDX $0000
LDX $0000,y
LDY #$00
LDY #$0000
LDY $00
LDY $00,x
LDY $0000
LDY $0000,x
LSR $00
LSR $00,x
LSR $0000
LSR $0000,x
LSR A
MVN $00,$00
MVN $0000
MVP $00,$00
MVP $0000
NOP
ORA #$00
ORA #$0000
ORA $00
ORA $00,s
ORA $00,x
ORA $0000
ORA $0000,x
ORA $0000,y
ORA $000000
ORA $000000,x
ORA ($00)
ORA ($00),y
ORA ($00,s),y
ORA ($00,x)
ORA [$00]
ORA [$00],y
PEA $0000
PEI ($00)
PER $0000
PHA
PHB
PHD
PHK
PHP
PHX
PHY
PLA
PLB
PLD
PLP
PLX
PLY
REP #$00
ROL $00
ROL $00,x
ROL $0000
ROL $0000,x
ROL A
ROR $00
ROR $00,x
ROR $0000
ROR $0000,x
ROR A
RTI
RTL
RTS
SBC #$00
SBC #$0000
SBC $00
SBC $00,s
SBC $00,x
SBC $0000
SBC $0000,x
SBC $0000,y
SBC $000000
SBC $000000,x
SBC ($00)
SBC ($00),y
SBC ($00,s),y
SBC ($00,x)
SBC [$00]
SBC [$00],y
SEC
SED
SEI
SEP #$00
STA $00
STA $00,s
STA $00,x
STA $0000
STA $0000,x
STA $0000,y
STA $000000
STA $000000,x
STA ($00)
STA ($00),y
STA ($00,s),y
STA ($00,x)
STA [$00]
STA [$00],y
STP
STX $00
STX $00,y
STX $0000
STY $00
STY $00,x
STY $0000
STZ $00
STZ $00,x
STZ $0000
STZ $0000,x
TAX
TAY
TCD
TCS
TDC
TRB $00
TRB $0000
TSB $00
TSB $0000
TSC
TSX
TXA
TXS
TXY
TYA
TYX
WAI
WDM
WDM #$00
XBA
XCE

Supported SPC700 instructions

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+).

ADC $00,#$00
ADC $00,$00
ADC (X),(Y)
ADC A,#$00
ADC A,$00
ADC A,$00+X
ADC A,$0000
ADC A,$0000+X
ADC A,$0000+Y
ADC A,($00)+Y
ADC A,($00+X)
ADC A,(X)
ADDW YA,$00
AND $00,#$00
AND $00,$00
AND (X),(Y)
AND A,#$00
AND A,$00
AND A,$00+X
AND A,$0000
AND A,$0000+X
AND A,$0000+Y
AND A,($00)+Y
AND A,($00+X)
AND A,(X)
AND1 C,!$0000
AND1 C,$0000
ASL $00
ASL $00+X
ASL $0000
ASL A
BBC0 $00,$00
BBC1 $00,$00
BBC2 $00,$00
BBC3 $00,$00
BBC4 $00,$00
BBC5 $00,$00
BBC6 $00,$00
BBC7 $00,$00
BBS0 $00,$00
BBS1 $00,$00
BBS2 $00,$00
BBS3 $00,$00
BBS4 $00,$00
BBS5 $00,$00
BBS6 $00,$00
BBS7 $00,$00
BCC $00
BCS $00
BEQ $00
BMI $00
BNE $00
BPL $00
BRA $00
BRK
BVC $00
BVS $00
CALL $0000
CBNE $00+x,$00
CBNE $00,$00
CLR0 $00
CLR1 $00
CLR2 $00
CLR3 $00
CLR4 $00
CLR5 $00
CLR6 $00
CLR7 $00
CLRC
CLRP
CLRV
CMP $00,#$00
CMP $00,$00
CMP (X),(Y)
CMP A,#$00
CMP A,$00
CMP A,$00+X
CMP A,$0000
CMP A,$0000+X
CMP A,$0000+Y
CMP A,($00)+Y
CMP A,($00+X)
CMP A,(X)
CMP X,#$00
CMP X,$00
CMP X,$0000
CMP Y,#$00
CMP Y,$00
CMP Y,$0000
CMPW YA,$00
DAA A
DAS A
DBNZ $00,$00
DBNZ Y,$00
DEC $00
DEC $00+X
DEC $0000
DEC A
DEC X
DEC Y
DECW $00
DI
DIV YA,X
EI
EOR $00,#$00
EOR $00,$00
EOR (X),(Y)
EOR A,#$00
EOR A,$00
EOR A,$00+X
EOR A,$0000
EOR A,$0000+X
EOR A,$0000+Y
EOR A,($00)+Y
EOR A,($00+X)
EOR A,(X)
EOR1 C,$0000
INC $00
INC $00+X
INC $0000
INC A
INC X
INC Y
INCW $00
JMP $0000
JMP ($0000+X)
LSR $00
LSR $00+X
LSR $0000
LSR A
MOV $00+X,A
MOV $00+X,Y
MOV $00+Y,X
MOV $00,#$00
MOV $00,$00
MOV $00,A
MOV $00,X
MOV $00,Y
MOV $0000+X,A
MOV $0000+Y,A
MOV $0000,A
MOV $0000,X
MOV $0000,Y
MOV ($00)+Y,A
MOV ($00+X),A
MOV (X),A
MOV (X+),A
MOV A,#$00
MOV A,$00
MOV A,$00+X
MOV A,$0000
MOV A,$0000+X
MOV A,$0000+Y
MOV A,($00)+Y
MOV A,($00+X)
MOV A,(X)
MOV A,(X+)
MOV A,X
MOV A,Y
MOV SP,X
MOV X,#$00
MOV X,$00
MOV X,$00+Y
MOV X,$0000
MOV X,A
MOV X,SP
MOV Y,#$00
MOV Y,$00
MOV Y,$00+X
MOV Y,$0000
MOV Y,A
MOV1 $0000,C
MOV1 C,$0000
MOVW $00,YA
MOVW YA,$00
MUL YA
NOP
NOT1 $0000
NOTC
OR $00,#$00
OR $00,$00
OR (X),(Y)
OR A,#$00
OR A,$00
OR A,$00+X
OR A,$0000
OR A,$0000+X
OR A,$0000+Y
OR A,($00)+Y
OR A,($00+X)
OR A,(X)
OR1 C,!$0000
OR1 C,$0000
PCALL $00
POP A
POP P
POP X
POP Y
PUSH A
PUSH P
PUSH X
PUSH Y
RET
RETI
ROL $00
ROL $00+X
ROL $0000
ROL A
ROR $00
ROR $00+X
ROR $0000
ROR A
SBC $00,#$00
SBC $00,$00
SBC (X),(Y)
SBC A,#$00
SBC A,$00
SBC A,$00+X
SBC A,$0000
SBC A,$0000+X
SBC A,$0000+Y
SBC A,($00)+Y
SBC A,($00+X)
SBC A,(X)
SET0 $00
SET1 $00
SET2 $00
SET3 $00
SET4 $00
SET5 $00
SET6 $00
SET7 $00
SETC
SETP
SLEEP
STOP
SUBW YA,$00
TCALL 0
TCALL 1
TCALL 2
TCALL 3
TCALL 4
TCALL 5
TCALL 6
TCALL 7
TCALL 8
TCALL 9
TCALL 10
TCALL 11
TCALL 12
TCALL 13
TCALL 14
TCALL 15
TCLR $0000,a
TSET $0000,a
XCN A

Supported SuperFX instructions

i don't really know who originally specified this syntax or where asar's version comes from tbh

ADC #0
ADC #0
ADC #1
ADC #2
ADC #3
ADC #4
ADC #5
ADC #6
ADC #7
ADC #8
ADC #9
ADC #10
ADC #11
ADC #12
ADC #13
ADC #14
ADC #15
ADC R0
ADC R1
ADC R2
ADC R3
ADC R4
ADC R5
ADC R6
ADC R7
ADC R8
ADC R9
ADC R10
ADC R11
ADC R12
ADC R13
ADC R14
ADC R15
ADD #0
ADD #1
ADD #2
ADD #3
ADD #4
ADD #5
ADD #6
ADD #7
ADD #8
ADD #9
ADD #10
ADD #11
ADD #12
ADD #13
ADD #14
ADD #15
ADD R0
ADD R1
ADD R2
ADD R3
ADD R4
ADD R5
ADD R6
ADD R7
ADD R8
ADD R9
ADD R10
ADD R11
ADD R12
ADD R13
ADD R14
ADD R15
ALT1
ALT2
ALT3
AND #1
AND #2
AND #3
AND #4
AND #5
AND #6
AND #7
AND #8
AND #9
AND #10
AND #11
AND #12
AND #13
AND #14
AND #15
AND R1
AND R2
AND R3
AND R4
AND R5
AND R6
AND R7
AND R8
AND R9
AND R10
AND R11
AND R12
AND R13
AND R14
AND R15
ASR
BCC $00
BCS $00
BEQ $00
BGE $00
BIC #1
BIC #2
BIC #3
BIC #4
BIC #5
BIC #6
BIC #7
BIC #8
BIC #9
BIC #10
BIC #11
BIC #12
BIC #13
BIC #14
BIC #15
BIC R1
BIC R2
BIC R3
BIC R4
BIC R5
BIC R6
BIC R7
BIC R8
BIC R9
BIC R10
BIC R11
BIC R12
BIC R13
BIC R14
BIC R15
BLT $00
BMI $00
BNE $00
BPL $00
BRA $00
BVC $00
BVS $00
CACHE
CMODE
CMP R0
CMP R1
CMP R2
CMP R3
CMP R4
CMP R5
CMP R6
CMP R7
CMP R8
CMP R9
CMP R10
CMP R11
CMP R12
CMP R13
CMP R14
CMP R15
COLOR
DEC R0
DEC R1
DEC R2
DEC R3
DEC R4
DEC R5
DEC R6
DEC R7
DEC R8
DEC R9
DEC R10
DEC R11
DEC R12
DEC R13
DEC R14
DIV2
FMULT
FROM R0
FROM R1
FROM R2
FROM R3
FROM R4
FROM R5
FROM R6
FROM R7
FROM R8
FROM R9
FROM R10
FROM R11
FROM R12
FROM R13
FROM R14
FROM R15
GETB
GETBH
GETBL
GETBS
GETC
HIB
IBT R0,#$00
IBT R1,#$00
IBT R2,#$00
IBT R3,#$00
IBT R4,#$00
IBT R5,#$00
IBT R6,#$00
IBT R7,#$00
IBT R8,#$00
IBT R9,#$00
IBT R10,#$00
IBT R11,#$00
IBT R12,#$00
IBT R13,#$00
IBT R14,#$00
IBT R15,#$00
INC R0
INC R1
INC R2
INC R3
INC R4
INC R5
INC R6
INC R7
INC R8
INC R9
INC R10
INC R11
INC R12
INC R13
INC R14
IWT R0,#0000
IWT R1,#0000
IWT R2,#0000
IWT R3,#0000
IWT R4,#0000
IWT R5,#0000
IWT R6,#0000
IWT R7,#0000
IWT R8,#0000
IWT R9,#0000
IWT R10,#0000
IWT R11,#0000
IWT R12,#0000
IWT R13,#0000
IWT R14,#0000
IWT R15,#0000
JMP R8
JMP R9
JMP R10
JMP R11
JMP R12
JMP R13
LDB (R0)
LDB (R1)
LDB (R2)
LDB (R3)
LDB (R4)
LDB (R5)
LDB (R6)
LDB (R7)
LDB (R8)
LDB (R9)
LDB (R10)
LDB (R11)
LDW (R0)
LDW (R1)
LDW (R2)
LDW (R3)
LDW (R4)
LDW (R5)
LDW (R6)
LDW (R7)
LDW (R8)
LDW (R9)
LDW (R10)
LDW (R11)
LEA R0,$0000
LINK #1
LINK #2
LINK #3
LINK #4
LJMP R8
LJMP R9
LJMP R10
LJMP R11
LJMP R12
LJMP R13
LM R0,($0000)
LM R1,($0000)
LM R2,($0000)
LM R3,($0000)
LM R4,($0000)
LM R5,($0000)
LM R6,($0000)
LM R7,($0000)
LM R8,($0000)
LM R9,($0000)
LM R10,($0000)
LM R11,($0000)
LM R12,($0000)
LM R13,($0000)
LM R14,($0000)
LM R15,($0000)
LMS R0,($00)
LMS R1,($00)
LMS R2,($00)
LMS R3,($00)
LMS R4,($00)
LMS R5,($00)
LMS R6,($00)
LMS R7,($00)
LMS R8,($00)
LMS R9,($00)
LMS R10,($00)
LMS R11,($00)
LMS R12,($00)
LMS R13,($00)
LMS R14,($00)
LMS R15,($00)
LMULT
LOB
LOOP
LSR
MERGE
MOVE ($00),R0
MOVE R0,#$00
MOVE R0,($00)
MOVE R0,R0
MOVEB (R0),R0
MOVEB R0,(R0)
MOVES R0,R0
MOVEW (R0),R0
MOVEW R0,(R0)
MULT #0
MULT #1
MULT #2
MULT #3
MULT #4
MULT #5
MULT #6
MULT #7
MULT #8
MULT #9
MULT #10
MULT #11
MULT #12
MULT #13
MULT #14
MULT #15
MULT R0
MULT R1
MULT R2
MULT R3
MULT R4
MULT R5
MULT R6
MULT R7
MULT R8
MULT R9
MULT R10
MULT R11
MULT R12
MULT R13
MULT R14
MULT R15
NOP
NOT
OR #1
OR #2
OR #3
OR #4
OR #5
OR #6
OR #7
OR #8
OR #9
OR #10
OR #11
OR #12
OR #13
OR #14
OR #15
OR R1
OR R2
OR R3
OR R4
OR R5
OR R6
OR R7
OR R8
OR R9
OR R10
OR R11
OR R12
OR R13
OR R14
OR R15
PLOT
RAMB
ROL
ROMB
ROR
RPIX
SBC R0
SBC R1
SBC R2
SBC R3
SBC R4
SBC R5
SBC R6
SBC R7
SBC R8
SBC R9
SBC R10
SBC R11
SBC R12
SBC R13
SBC R14
SBC R15
SBK
SEX
SM ($0000),R0
SM ($0000),R1
SM ($0000),R2
SM ($0000),R3
SM ($0000),R4
SM ($0000),R5
SM ($0000),R6
SM ($0000),R7
SM ($0000),R8
SM ($0000),R9
SM ($0000),R10
SM ($0000),R11
SM ($0000),R12
SM ($0000),R13
SM ($0000),R14
SM ($0000),R15
SMS ($00),R0
SMS ($00),R1
SMS ($00),R2
SMS ($00),R3
SMS ($00),R4
SMS ($00),R5
SMS ($00),R6
SMS ($00),R7
SMS ($00),R8
SMS ($00),R9
SMS ($00),R10
SMS ($00),R11
SMS ($00),R12
SMS ($00),R13
SMS ($00),R14
SMS ($00),R15
STB (R0)
STB (R1)
STB (R2)
STB (R3)
STB (R4)
STB (R5)
STB (R6)
STB (R7)
STB (R8)
STB (R9)
STB (R10)
STB (R11)
STOP
STW (R0)
STW (R1)
STW (R2)
STW (R3)
STW (R4)
STW (R5)
STW (R6)
STW (R7)
STW (R8)
STW (R9)
STW (R10)
STW (R11)
SUB #0
SUB #1
SUB #2
SUB #3
SUB #4
SUB #5
SUB #6
SUB #7
SUB #8
SUB #9
SUB #10
SUB #11
SUB #12
SUB #13
SUB #14
SUB #15
SUB R0
SUB R1
SUB R2
SUB R3
SUB R4
SUB R5
SUB R6
SUB R7
SUB R8
SUB R9
SUB R10
SUB R11
SUB R12
SUB R13
SUB R14
SUB R15
SWAP
TO R0
TO R1
TO R2
TO R3
TO R4
TO R5
TO R6
TO R7
TO R8
TO R9
TO R10
TO R11
TO R12
TO R13
TO R14
TO R15
UMULT #0
UMULT #1
UMULT #2
UMULT #3
UMULT #4
UMULT #5
UMULT #6
UMULT #7
UMULT #8
UMULT #9
UMULT #10
UMULT #11
UMULT #12
UMULT #13
UMULT #14
UMULT #15
UMULT R0
UMULT R1
UMULT R2
UMULT R3
UMULT R4
UMULT R5
UMULT R6
UMULT R7
UMULT R8
UMULT R9
UMULT R10
UMULT R11
UMULT R12
UMULT R13
UMULT R14
UMULT R15
WITH R0
WITH R1
WITH R2
WITH R3
WITH R4
WITH R5
WITH R6
WITH R7
WITH R8
WITH R9
WITH R10
WITH R11
WITH R12
WITH R13
WITH R14
WITH R15
XOR #1
XOR #2
XOR #3
XOR #4
XOR #5
XOR #6
XOR #7
XOR #8
XOR #9
XOR #10
XOR #11
XOR #12
XOR #13
XOR #14
XOR #15
XOR R1
XOR R2
XOR R3
XOR R4
XOR R5
XOR R6
XOR R7
XOR R8
XOR R9
XOR R10
XOR R11
XOR R12
XOR R13
XOR R14
XOR R15

Mapping Modes

Asar supports a number of different mapping modes. They control the address translation used by Asar during compilation (aka where in the output file Asar writes to). Historically, SNES cartridges used a number of different mappers to address data in ROM. Those mappers can be supported by using the respective mapping mode in Asar. It's possible, but not recommended, to use different mapping modes on the same ROM. Detailed explanations on each mapping mode are beyond the scope of this manual, so please check the SNES Dev Manual or other specialized resources for that.

NOTE: Changing the mapper after having previously set it will generate warning Wmapper_already_set.

  • 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 [num, num, num, num]: Switch to hybrid SA-1 mapping mode. To tell which banks are mapped in (maximum is 7) use the optional parameter, like so: sa1rom 0,1,4,6. 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.

When no mapping mode is specified, Asar tries to determine the mapping mode from the output ROM. If that isn't possible, Asar defaults to lorom.

lorom
org $008000
db $FF      ; Will write to PC address 0x000000

hirom
org $008000
db $FF      ; Will write to PC address 0x008000

Compatibility Settings

Compatibility settings determine how Asar operates in certain situations. They can be changed via a number of commands.

asar

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

namespace nested

namespace nested {on/off}

The namespace nested command enables (on) or disables (off) nested namespaces. The default is off. See section Namespaces for details.

Code Formatting and Syntax

Encoding

Asar expects all source files to be UTF-8-encoded and will throw an error when detecting any other encoding.

Comments

You can use ; to add comments to your code, making it easier to read and understand for other people. Everything from the ; to the end of the line is silently ignored by Asar.

    lda $00        ; Asar only sees the lda $00 and ignores everything else

There are also multiline comments, which are started with ;[[ and ended with ]]:

lda $00   ;[[ this is a comment
still a comment
more comment ]] : lda $01

Brackets

Brackets, { and }, may be used to help organize your code structurally. They're treated as commands by the assembler, which means they follow the same rules as other commands, but they otherwise have no effect on code assembly and are silently ignored. Since brackets have no effect on code assembly, they don't even have to match, either. It's entirely up to the coder whether, how and in what quantity brackets are used.

    lda $00
    beq .IsZero
    
.GreaterThanZero
    {
        dec $00
    }

.IsZero
    rts

Multi-Line Operators

The , 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"

Single-Line Operator

Contrary to the multi-line operators, the single-line operator : 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

Program Counter

The program counter (short: pc) refers to the position in the ROM at which Asar currently writes assembled code and/or data. It advances automatically whenever Asar writes to the ROM and is affected by the current mapping mode, as well as a number of special commands. Note that all commands affecting the pc that take an address expect an SNES address and thus are also affected by the current mapping mode.

org

org {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

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

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

bank

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

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

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

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.

pushpc / pullpc

The pushpc command pushes the current pc to the stack, the pullpc command restores the pc by pulling its value from the stack. This can be useful for inserting code in another location and then continuing at the original location.

org $008000
        
Main:
    jsl CodeInAnotherBank

pushpc
org $018000

CodeInAnotherBank:
    ; ...
    rtl
    
pullpc

bra Main

pushbase / pullbase

The pushbase command pushes the current base to the stack, the pullbase command restores the base by pulling its value from the stack.

base $7E2000
        
InsideRam:
    jsl OutsideOfRam
    ; ...

pushbase
pushpc
base off

freecode

OutsideOfRam:
    ; ...
    jsl InRamAgain
    rtl

pullpc
pullbase

InRamAgain:
    ; ...
    rtl

base off

Math

Math is supported in all opcodes, functions and labels. Asar applies the conventional operator prioritization rules (PEMDAS) in math expressions and supports parentheses for explicit control over the order of operations.

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:

OpAction
+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)

Note that whitespace is not supported inside math statements (ed: in asar 2 it is. kinda.), but the multi-line operator \ can be used to split them into multiple lines. Using math in labels can be useful when you want to apply an offset to the label:

lda .Data+3    ; Will load $03 into A
        
.Data
    db $00,$01,$02
    db $03,$02,$03

Labels

Labels are used to represent a position in the ROM and allow you to code without having to constantly update branches and jumps/calls. They can be used with any opcode, but were specifically designed to be used with branches, jumps, calls, pointer tables etc. When used with branches, they're automatically converted to offsets.

Main Labels

[#]{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 characters: a-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.

Sub Labels

[#].{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

+[+...][:]

-[-...][:]

+/- 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 for + labels and :neg_x_y for - labels, where x = number of chained +/- and 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

Macro Labels

[#]?{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_ where x = total macro call count. 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.

Structs

Structs are an advanced form of labels with the purpose of making access into structured data blocks easier. The general syntax is as follows

struct {identifier} {snes_address}
    [label...]
endstruct [align {num}]

where identifier can contain any of the following characters:
a-z A-Z 0-9 _
The 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
So in this case, our final address is $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.

Extending structs

Another feature that is unique to structs is the possibility of extending previously defined structs with new data. The general syntax for this is as follows:

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 as
parent_struct_start_address + parent_struct_size + extension_struct_label_offset,
in other words, our final address is $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 as
parent_struct_start_address + (combined_struct_size * array_index) + parent_struct_size + extension_struct_label_offset,
whereas in the second example, it's calculated as
parent_struct_start_address + parent_struct_size + (extension_struct_size * array_index) + extension_struct_label_offset,
so we end up with final addresses of $7E0100 + (12 * 2) + 7 + (1 + 2) = $7E0122 and $7E0100 + 7 + (5 * 2) + (1 + 2) = $7E0114.

A few further things to note when using structs in Asar:

  • It's possible to extend a single struct with multiple extension structs. However, this can be counter-intuitive. The size of the extended struct becomes the size of the parent struct plus the size of its largest extension struct, rather than the size of the parent struct plus the sizes of each of its extension structs. This also means that when accessing those extension structs, they all start at the same offset relative to the parent struct. This can be confusing and is often not what's actually intended, so for code clarity, it's recommended to only extend structs with at most a single other struct.
  • It's possible to enforce alignments when using extension structs. However, this will only determine the alignment of the parent struct and/or the extension struct(s), depending on where it's specified. It won't determine the alignment of the combined struct. This can be confusing and is usually not what is intended. There currently is no universal workaround for this, so when a certain alignment is required for a struct, it's recommended to not use extension structs with it.
  • It's not possible to access both, a parent struct and its extension struct, as arrays simultanously.
  • An extension struct can't be extended itself.

Namespaces

namespace {identifier}
namespace off

Namespaces are a feature which makes it easier to avoid name conflicts between different labels without having to give them long or cryptic names. They work similarly to C++ namespaces and accomplish this by automatically adding a prefix to all labels declared or accessed within them. This prefix consists of an identifier, followed by an underscore _ . 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).

Use namespace {identifier} to enter a namespace, where identifier can contain any of the following characters: a-z A-Z 0-9 _

Use 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 / pullns

pushns saves the current namespace. pullns restores the last-pushed value of the namespace.

Global labels

While in a namespace, you can use the keyword 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. Note that you cannot use the global command with sublabels or macro labels. Outside of a namespace, global acts just like a regular label definition.

Defines

Asar supports a define system that functions similarly to defines in other programming languages, such as C++. Defines are identifiers that you can assign any kind of text to and use in other places as substitues for that text. During compilation, Asar replaces each define it encounters with the respective text assigned to it. Defines are prefixed with a ! 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 _

The space on both sides of the = is required, which means that !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 = 34
!x = $12!y
        
lda !x        ; Treated as "lda $1234"
        
!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.

OperatorFunctionality
=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.
?=Equal to the standard =, but only assigns text to a define that doesn't exist yet, otherwise does nothing.
!define = 10
!anotherdefine = !define+1
; !define now contains "10" and !anotherdefine now contains "!define+1"
!define = 10
!define += 1
; !define now contains "101"
!define = 10
!define := !define+1
; !define now contains "10+1"
!define = 10
!anotherdefine #= !define+1
; !anotherdefine now contains "11"
!lastdefine ?= 10
!lastdefine ?= 1
; !lastdefine now contains "10"

Similarly to C's ifdef and undef, Asar allows you to check for a define's existence using the 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.

Nested Defines

By default, the define parser in Asar considers every supported character in a connected string to be a part of the define's name. This may not always be desired as it can lead to a certain define becoming inaccessible in a certain situation. In cases like that, the {} 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"

Built-in Defines

Aside from user defines, Asar also supports a number of built-in defines. These defines are read-only and any attempt to modify them will throw an error.

DefineDetails
!assemblerContains 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_verContains the version number of Asar in the format (major_version * 10000) + (minor_version * 100) + revision. For Asar version 1.60, this contains 10600.
!assembler_timeContains the current Unix timestamp as an integer (number of seconds since 1970-01-01 00:00:00 UTC).
if not(stringsequal("!assembler", "asar"))
    warn "This patch was written for Asar and may not be compatible with your current assembler."
endif
if !assembler_ver < 10600
    warn "This patch might not behave correctly due to a bug in Asar versions prior to 1.60."
endif

Macros

Macros are a mechanism for recording a sequence of commands that can be used in other places. The main purpose of this is to include commonly used code in multiple places without actually having to rewrite or copy that code every time. Instead you can move it into a macro and write it only once. Macros, in concept, work similarly to defines in that they are a text-replacement mechanism, but they have a few key differences:

  • Macros only record a sequence of commands rather than any kind of text.
  • A macro call is itself considered a command and thus needs to go on its own line (or separated via the single-line operator :). This means that unlike a define, a macro can't just be used whereever.
  • Macros can include parameters, which are identifiers that are replaced by a text value whenever the respective macro is called. For simplicity, you could consider parameters a macro-specific version of defines.

Use the following syntax to define a macro:

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 _

Use the syntax <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")

Variadic Macros

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 traditional macros do not parse defines inside parameters, 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
    endwhile
endmacro

macro example2(named_parameter, ...)
    !a #= 0
    while !a < sizeof(...)
        db <...[!a]>        ;02 03 04 05 06 07
        !a #= !a+1
    endwhile
    db <named_parameter>    ;01
endmacro

macro macro_with_optional_arguments(required, ...)
    db <required>
    if sizeof(...) > 0
        db <...[0]>
    endif
endmacro

%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)

Nested Macro Definitions

Macro definitions can be nested. Doing this, a few special rules come into play. Normally, macro parameters and defines inside macros are only resolved whenever the respective macro is being called, and macros can only resolve their own parameters. This might be impractical once working with nested macro definitions, so Asar provides a special syntax to control the resolution timing of macro parameters and defines. By prepending a number of ^ to the respective name, earlier resolution can be forced, called "backwards-resolution". For example, <^param> will be resolved while the parent macro is being called; !^^define will be resolved while the grand-parent macro is being called, and so forth.

!define_01 = $01
!define_02 = $02

macro threefold_one(shadowed)
    db <shadowed> ; Will be resolved when %threefold_one() is called.
    macro threefold_two(not_shadowed)
        !define_01 = $FF ; Will be resolved when %threefold_two() is called.
        !define_02 = $FF ; Will be resolved when %threefold_two() is called.
        db !define_01 ; Will be resolved when %threefold_two() is called.
        db !^define_02 ; Will be resolved when %threefold_one() is called.
        db <^shadowed> ; Will be resolved when %threefold_one() is called.
        db <not_shadowed>
        macro threefold_three(shadowed)
            db <^^shadowed> ; Will be resolved when %threefold_one() is called.
            db <^not_shadowed> ; Will be resolved when %threefold_two() is called.
            db <shadowed> ; Will be resolved when %threefold_three() is called.
        endmacro
    endmacro
endmacro

%threefold_one($03)
%threefold_two($04)
%threefold_three($05)

; Writes: 03 FF 02 03 04 03 04 05

Functions

Functions in Asar can be considered the math equivalent of macros. They are a convenient way of wrapping commonly used math statements, can include parameters and can be called in all places where math is supported. Use the following syntax to define a function:

function {identifier}([param1_name[, param2_name...]]) = {math}

where all the names can contain any of the following characters: a-z A-Z 0-9 _ and where {math} 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.

Note that user-defined functions can't use string parameters themselves. However, they can take strings as arguments and pass them on to built-in functions.

function readfilenormalized(filename, pos) = readfile4(filename, pos)/2147483648.0
db readfilenormalizd("datafile.bin", 0)

Built-in Functions

Aside from user-defined functions mentioned above, Asar also supports a number of built-in functions. Some built-in functions take string parameters, which must be wrapped in double quotes.

  • 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.

    if read1($00FFD5) == $23
        !is_sa1_rom = 1
    else
        !is_sa1_rom = 0
    endif
    
  • 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.

    !readresult = readfile4("datafile.bin", 0, $FFFFFFFF)
    if !readresult != $FFFFFFFF
        print "Read $",hex(!readresult)," from datafile.bin."
    endif
    
  • 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.

    if canread1($00FFD5) == 1
        print "Detected ROM type: $",hex(read1($00FFD5))
    else
        error "Failed to detect ROM type!"
    endif
    
  • 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).

    if canreadfile4("datafile.bin", 512) == 1
        print "Read $",hex(readfile4("datafile.bin", 512))," from datafile.bin at position 512."
    else
        error "datafile.bin either doesn't exist or is too small."
    endif
    
  • filesize(filename)

    Returns the size of file filename. Throws an error if the file doesn't exist.

    !fsize #= filesize("datafile.bin")
    !fpos = 0
    
    assert !fsize >= 0, "datafile.bin doesn't exist or can't be opened".
    while !fpos < !fsize
        ; Do something with datafile.bin here, like calling readfile1("datafile.bin", !fpos)
        ;...
        
        !fpos #= !fpos+1
    endwhile
    
  • 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.).

    assert getfilestatus("datafile.bin") != 1, "datafile.bin doesn't seem to exist"
    
  • 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.

    print "SNES address $018000 in the current mapping mode is equivalent to PC address 0x",dec(snestopc($018000))
    
  • min(a, b), max(a, b)

    Return the minimum/maximum of two numbers.

    !start_index #= max(!current_index-1, 0)
    
  • clamp(value, minimum, maximum)

    Makes sure that value stays within the bounds set by minimum and maximum. Equal to min(max(value, minimum), maximum).

    !used_amount #= clamp(!used_percentage, 0.0, 1.0)*!total_amount
    
  • 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.

    !single_sprite_memory = safediv(!total_sprite_memory, !max_num_sprites, 0)
    
  • 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.

    function sprite_size() = select(!extra_bytes_enabled, 16+4, 16)
    
  • not(value)

    Returns 1 if value is 0 and 0 in any other case. Useful for negating statements in the select() function.

    function required_sprite_memory(num_sprites) = not(!sprites_disabled)*sprite_size()*num_sprites
    
  • bank(value)

    Returns value>>16

    lda #bank(some_label)
    
  • 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.

    function abs(num) = select(less(num, 0), num*-1, num)
    
  • 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.

    function total_sprite_extra_bytes(num_sprites) = select(and(not(!sprites_disabled), !extra_bytes_enabled), 4, 0)*num_sprites
    
  • round(number, precision)

    Rounds number to precision decimal places. Pass 0 as precision to round to the nearest integer.

    if round(!distance, 2) == 0.0
        error "Distance is zero or almost zero. Please choose a bigger value for distance as small values will cause problems."
    endif
    
  • floor(number), ceil(number)

    Rounds a number up (in the case of ceil) or down (in the case of floor) to the nearest integer.

    !banks_used #= ceil(!data_size/65536)
    
  • 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.

    if defined("include_guard") == 0
        !include_guard = 1
        ; ...
    endif
    
  • sizeof(identifier)

    Takes the identifier of a struct as a parameter and returns the base size of that struct (without any extension structs).

    struct parent $0000
        .data1: skip 2
    endstruct
    
    struct child extends parent
        .data2: skip 3
    endstruct
    
    db sizeof(parent)             ; db 2
    db sizeof(parent.child)       ; db 3
    
  • 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.

    struct parent $0000
        .data1: skip 2
    endstruct
    
    struct child extends parent
        .data2: skip 3
    endstruct
    
    db objectsize(parent)         ; db 5
    db objectsize(parent.child)   ; db 3
    
  • 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.

    org $008000
    main:
    
    lda #datasize(my_table)       ;3
    lda #datasize(other_label)    ;0x7FF3 (last label, throws a warning. calculated as $FFFFFF-$00800C)
    lda #datasize(main)           ;9
    
    
    my_table:
        db $00, $00, $02
    other_label:
    
  • stringsequal(string1, string2)

    Returns 1 if the given string parameters are equal and 0 otherwise.

    if not(stringsequal("!assembler", "asar"))
        warn "This patch was only tested in Asar and might not work correctly in your assembler."
    endif
    
  • stringsequalnocase(string1, string2)

    Returns 1 if the given string parameters are equal and 0 otherwise. The comparison is case-insensitive.

    if not(stringsequalnocase("!assembler", "ASAR"))
        warn "This patch was only tested in Asar and might not work correctly in your assembler."
    endif
    
  • char(string, index)

    Returns the index-th character in string (starting at zero). Note that this function ignores the current table mapping, and also doesn't respect Unicode properly: it returns bytes of the string's UTF-8 encoding. For example, db char("ä", 0) writes C3, which is the first byte of 'ä' in UTF-8.

  • stringlength(string)

    Returns the length of string in bytes (encoded as UTF-8). I.e. char(str, stringlength(str)-1) is the last byte of the string.

  • 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.

Conditionals and Loops

Conditional compilation allows you to only compile specific sections of code when certain conditions are met. This can be used in a number of ways, but is most commonly used in conjunction with defines to make code easily customizable and/or provide some simple configuration options to end users.

if / elseif / else / endif

The most basic form of conditionals are if conditionals. They are given a math statement and only compile their enclosed code if that statement evaluates to a value greater than 0.

if {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.

OperatorDetails
a == bReturns 1 if a is equal to b
a != bReturns 1 if a is not equal to b
a > bReturns 1 if a is greater than b
a < bReturns 1 if a is less than b
a >= bReturns 1 if a is greater than or equal to b
a <= bReturns 1 if a is less than or equal to b

You can use logical operators to chain multiple conditions.

OperatorDetails
a || bReturns 1 if at least one of a and b evaluates to 1
a && bReturns 1 if both of a and b evaluate to 1

Evaluation is lazy (TODO it's not anymore, is it?) which means that the assembler will stop evaluating a condition as soon as the result can be determined (for example, in the condition 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 assembler 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 statements can also be constructed on a single line via the following syntax:

if {condition} : {codeblock}[ : codeblock...] : endif
PressedY:
    if !fireballs_enabled : %PlaySoundEffect(!fireball_sfx) : jsr ShootFireball : endif
    rtl

If you plan to use labels in if conditions, 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 Elabel_in_conditional. The label could move between passes.
if SecondLabel == 0
endif

while

while {condition}
    {code}
endwhile

A special variation of if conditionals are while loops. Instead of compiling their enclosed code only once, they compile it repeatedly until their condition evaluates to <= 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

Be warned as improper use of while loops can lead to infinite loops and thus a dead-lock of the assembler, as Asar won't attempt to detect those.

for

for {variable} = {start}..{end}
    {code}
endfor

For loops repeat the contents a specified number of times. In the for loop body, you have access to a loop counter as a define. The range is specified as start-inclusive and end-exclusive. For example:

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. For example:

for i = 0..10 : nop : endfor

Binary Data

Asar supports a number of commands which allow you to insert binary data directly into the ROM.

Tables

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 a Unicode 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 a Unicode string used in a table maps onto the respective Unicode code points it's composed of. This mapping can be customized via character literal assignments:

'{character}' = {value}

Where character is a Unicode code point and value is any math expression, specifying what value that code point will be remapped to. Only single code points can be remapped at this time - e.g., a precomposed "Ä" will work, while a split "¨" and an "A" will throw an error.
To reset all mappings to a direct Unicode code point mapping, use the command cleartable. Additionally, the pushtable command lets you push all current mappings onto a stack, whereas the pulltable command lets you restore the mappings from that stack.

; Contents of table1.asm:
;'A' = $1A
;'B' = $1B
;'C' = $1C
;'日' = $2A
;'本' = $2B
;'語' = $2C

; Contents of table2.asm:
;'A' = $1D
;'B' = $1E
;'C' = $1F
;'日' = $2D
;'本' = $2E
;'語' = $2F

; This writes $41 $42 $43 $E5 (from U+65E5) $2C (from U+672C) $9E (from U+8A9E)
db "ABC日本語"

incsrc "table1.asm"

; This writes $1A $1B $1C $2A $2B $2C
db "ABC日本語"

pushtable
incsrc "table2.asm"

; This writes $1D $1E $1F $2D $2E $2F
db "ABC日本語"

pulltable

; This writes $1A $1B $1C $2A $2B $2C
db "ABC日本語"

cleartable

; This writes $41 $42 $43 $E5 $2C $9E
db "ABC日本語"

'A' = $20
'B' = $20+1
'C' = $20+2
'日' = $20+3
'本' = $20+4
'語' = $20+5

; Those both write $20 $21 $22 $23 $24 $25
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 / fill

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 / pad

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

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).

; 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

; This writes $01 $02 $03 $04
incbin "datafile.bin":$F-$E..2+3

Includes

Includes make it possible for your code to reference other files. This can be done for a number of reasons. The most common scenarios are to split code into multiple source files (see incsrc) or to separate code from data (see incbin). Whenever using a command or function referencing another file, Asar tries to locate that file by applying a set of rules to the file path in a specific order:

  1. If the path is an absolute path:
    1. Asar tries to locate the file directly via the specified path.
    2. If this fails, an error is thrown.
  2. If the path is a relative path:
    1. Asar tries to locate the file relatively to the file currently being assembled. (Caution: when used inside macros, paths are relative to the macro definition rather than to the macro call).
    2. If this fails, Asar tries to locate the file relatively to any of the include search paths that were specified, in the order they were specified in, until the file is found. (See section Usage for details on include search paths).
    3. If all of the previous fail, an error is thrown.

Asar supports Unicode file names where available, including on Windows.

incsrc

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

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 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 != 0
    ; ...
endif

includeonce

includeonce

The includeonce command places an include guard on the file that is currently being assembled. This prevents it from being assembled again when included again. 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

Freespace

Freespace is a concept that comes into play when extending an existing ROM. To insert new code or data into a ROM, the ROM must contain enough continuous unused space for everything to fit into. Space like that is referred to as freespace. Many tools attempt to find freespace in a ROM by looking for continuous blocks of a certain value (most commonly $00). This method on its own isn't reliable as freespace finders could erroneously detect binary data or code with a certain pattern as freespace. For this reason, the RATS format was invented to protect data inserted into a ROM (see SMW Wiki for details on the RATS format). When placing RATS tags at the beginning of occupied memory blocks inside a ROM, freespace finders can search for them to know which parts of the ROM not to overwrite. Asar supports a number of commands for working with freespace directly, including freespace finders with automatic RATS tag generation.

freespace / freecode / freedata / segment

freespace {ram/noram}[,norats][,align][,cleaned][,static][,bankcross][,bank={num}][,start={num}][,pin={label}]
freecode [options...]
freedata [options...]
segment [ram/noram][,align][,bankcross][,bank={num}][,start={num}][,pin={label}]

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.

The freecode command is an alias of freespace ram, whereas freedata is an alias of freespace noram. The segment command is an alias for freespace norats. (Additionally, with segment specifying ram or noram is optional and defaults to noram.)

ParameterDetails
ramThe freespace finder searches for an area where RAM mirrors are available (banks $10 to $3F). Recommended when inserting code.
noramThe 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.
noratsDo not write a RATS tag. This is mostly useful for homebrews, or other setups where the ROM is rebuilt from scratch. Using this option makes the ram/noram options optional, defaulting to noram. It also cannot be used with cleaned or static, which are specfic to the RATS system.
alignThe freespace finder searches for an area at the beginning of a bank.
cleanedSuppresses the warning about freespace leaking. Useful when Asar's leak detection misbehaves on an autoclean with a complicated math statement or similar.
staticPrevents 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).
bank=Only search for freespace in the given bank. Mostly useful when using norats.
start=Search for freespace starting at the specified position in ROM. Useful for hacking non-SMW games, where the original ROM might be bigger or smaller. Note that technically, this limits the freespace finder to positions that are after the location of start in the ROM file: For example, when using $C00000 as the start position in hirom, the entire ROM will be searched, since $C00000 is at the start of the ROM file.
pin=Pin this freespace to another one, forcing them to be placed in the same bank.
bankcrossAllow this freespace to cross bank borders. You must also use check bankcross off inside the freespace to allow the data inside it to cross borders. Note that all of the concerns regarding check bankcross off also apply here.

Every boolean option also has a negative version prefixed with no that allows disabling it to restore the default behavior. (In the case of norats, using rats will restore the default behavior.)

One thing to note about freespaces is that 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.

; 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

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.

freespace_settings

freespace_settings {options...}

This command allows giving default values for the freespace/freecode/... commands. The syntax is the same as the freespace command. Asar will act like any options provided here are prepended to all freespace/etc commands.

freespace_settings start=$088000
freecode static ; this will act like `freespace start=$088000,ram,static`
; (the `ram` comes from using freecode instead of freespace:
;  note how it's added after the default settings.)

autoclean

autoclean jml/jsl/lda/cmp/.../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:

  • When used with an assembly instruction (most commonly jml or jsl):
    The label parameter must be a label pointing to inside a freespace area. The instruction can be any instruction that supports long (24-bit) addressing. When the patch is applied and the autoclean is encountered, Asar checks whether the output ROM contains the same instruction at the current pc. If it does, and the instruction points into a freespace area with a valid RATS tag, the RATS tag is removed along with its contents.
  • When used with a dl:
    The 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.
  • When used with just an address:
    The 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.

When using autoclean with an instruction or dl, Asar will also assemble the respective line of code at the current pc. For simplicity, you can treat the autoclean command like a modifier in those cases. A few more things to note when using the autoclean command:

  • The autoclean command itself may not be used inside a freespace area. To automatically clean up freespace that is only referenced within another freespace area, you can use the prot command.
  • It is safe to have multiple autoclean commands pointing to the same freespace area.
  • You can not use autoclean with a label pointing to the very end of a freespace area.
; 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

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

Text Output

Text output functions allow you to communicate certain information, states, warnings, errors etc. to end users of your code.

print

The print command lets you output general-purpose text to the user. Most commonly this is used to inform the user about certain states or to output debug information. Usage:

print {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:

FunctionDetails
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.
pcPrints the current PC.
freespaceusePrints the total number of bytes used by commands that acquire freespace (such as freespace, freecode, freedata etc.). You can use the command reset freespaceuse to reset this value.
bytesPrints the total number of bytes written to the output ROM. You can use the command reset bytes to reset this value.

warn

The warn command lets you output a warning message to the user. Usage:

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.

if read1($00FFD5) == $23
    warn "SA-1 compatibility of this patch is untested, use with caution!"
endif

error

The error command lets you output an error message to the user. Usage:

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.

if read1($00FFD5) == $23
    error "This patch is not SA-1 compatible!"
endif

assert

An assert can be considered a short version of the code

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!"

Checks

Checks allow Asar to monitor certain states and throw warnings or errors when certain criteria are met. This can be helpful for catching or preventing certain problems.

check title

check 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

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 full

print pc    ; Will print 818001 when using LoROM mapper

Warnings

Warnings are messages that Asar outputs to inform the user about potentially unintended or risky code that isn't critical and thus doesn't cause assembly to fail on its own. These messages can be useful for detecting potential problems in the code, but in some situations may be undesirable. For this reason, Asar supports a few methods of explicitly enabling or disabling certain warnings (see section Usage for details on how to configure warnings via the command line). Additionally, there are warnings which may be useful in some situations, but would be intrusive in most other situations. They are disabled by default and have to be enabled explicitly to be used. Commands that enable or disable warnings refer to them via their names. The easiest way of finding the name of a specific warning is to look at the console output of a patch producing it. Asar will always output the warning name along with the respective warning. A list of all warnings can also be found here.

Disabled Warnings

This is a list of all warnings that are disabled by default and have to be enabled explicitly.

Warning nameDetails
Wimplicitly_sized_immediateThrown 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_fileOnly 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}

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}

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!"

List of commands

This is a list of all commands Asar supports, linking to the manual chapter explaining them. In addition to these, Asar accepts assembly instructions, the list of which depends on the currently active architecture (65816, spc700, superfx).

arch {name}: Architectures
spcblock {target_address} [{engine_type}]: Architectures
endspcblock [execute {execution_address}]: Architectures
lorom: Mapping Modes
hirom: Mapping Modes
exlorom: Mapping Modes
exhirom: Mapping Modes
sa1rom [num, num, num, num]: Mapping Modes
fullsa1rom: Mapping Modes
sfxrom: Mapping Modes
norom: Mapping Modes
asar {ver}: Compatibility Settings
namespace nested {on/off}: Compatibility Settings
{: Code Formatting and Syntax
}: Code Formatting and Syntax
org {snes_address}: Program Counter
base {snes_address/off}: Program Counter
skip {num_bytes}: Program Counter
skip align {alignment} [offset {offset}]: Program Counter
bank {data_bank/noassume/auto}: Program Counter
dpbase {snes_address}: Program Counter
optimize dp {none/ram/always}: Program Counter
optimize address {default/ram/mirrors}: Program Counter
pushpc: Program Counter
pullpc: Program Counter
pushbase: Program Counter
pullbase: Program Counter
struct {identifier} {snes_address}: Structs
endstruct [align {num}]: Structs
struct {identifier} extends {identifier}: Structs
namespace {identifier}: Namespaces
namespace off: Namespaces
pushns: Namespaces
pullns: Namespaces
global [#]{identifier}:: Namespaces
!{identifier} = {value}: Defines
!{identifier} = "{value}": Defines
undef "{identifier}": Defines
macro {identifier}(...): Macros
endmacro: Macros
function {identifier}(...) = {math}: Functions
if {condition}: Conditionals and Loops
elseif {condition}: Conditionals and Loops
else: Conditionals and Loops
endif: Conditionals and Loops
while {condition}: Conditionals and Loops
endwhile: Conditionals and Loops
for {variable} = {start}..{end}: Conditionals and Loops
endfor: Conditionals and Loops
db {value}[,value...]: Binary Data
dw {value}[,value...]: Binary Data
dl {value}[,value...]: Binary Data
dd {value}[,value...]: Binary Data
'{character}' = {value}: Binary Data
cleartable: Binary Data
pushtable: Binary Data
pulltable: Binary Data
fillbyte {byte}: Binary Data
fill {num}: Binary Data
fill align {alignment} [offset {offset}]: Binary Data
fillword: Binary Data
filllong: Binary Data
filldword: Binary Data
padbyte {byte}: Binary Data
pad {snes_address}: Binary Data
padword: Binary Data
padlong: Binary Data
paddword: Binary Data
incbin {filename}[:range_start..range_end]: Binary Data
incsrc {filename}: Includes
include: Includes
includefrom {filename}: Includes
includeonce: Includes
freespace {ram/noram}[,norats][,align][,cleaned][,static][,bankcross][,bank={num}][,start={num}][,pin={label}]: Freespace
freecode [options...]: Freespace
freedata [options...]: Freespace
segment [ram/noram][,align][,bankcross][,bank={num}][,start={num}][,pin={label}]: Freespace
freespacebyte {value}: Freespace
freespace_settings {options...}: Freespace
autoclean jml/jsl/lda/cmp/.../dl {label}: Freespace
autoclean {snes_address}: Freespace
prot {label}[,label...]: Freespace
print {text_or_function}[,text_or_function...]: Text Output
reset freespaceuse: Text Output
reset bytes: Text Output
warn [text_or_function...]: Text Output
error [text_or_function...]: Text Output
assert {condition}[,text_or_function...]: Text Output
check title "{title}": Checks
check bankcross {off/half/full}: Checks
warnings {push/pull}: Warnings
warnings {enable/disable} {name}: Warnings

List of all warnings

This list is extracted directly from the source code. Some warnings might be unused or have incomplete messages (due to the messages being partially computed at runtime).

Warning nameMessageEnabled by default
Wrelative_path_usedRelative %s path passed to asar_patch_ex() - please use absolute paths only to prevent undefined behavior!yes
Wrom_too_shortROM is too short to have a title. (Expected '%s')yes
Wrom_title_incorrectROM title is incorrect. Expected '%s', got '%s'.yes
Wspc700_assuming_8_bitThis opcode does not exist with 16-bit parameters, assuming 8-bit.yes
Wassuming_address_modeThe addressing mode %s is not valid for this instruction, assuming %s.%syes
Wset_middle_byteIt would be wise to set the 008000 bit of this address.yes
Wfreespace_leakedThis freespace appears to be leaked.yes
Wwarn_commandwarn command%syes
Wimplicitly_sized_immediateImplicitly sized immediate.no
Wcheck_memory_fileAccessing file '%s' which is not in memory while W%d is enabled.no
Wdatasize_last_labelDatasize used on last detected label '%s'.yes
Wdatasize_exceeds_sizeDatasize exceeds 0xFFFF for label '%s'.yes
Wmapper_already_setA mapper has already been selected.yes
Wfeature_deprecatedDEPRECATION 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.yes
Winvalid_warning_idWarning '%s' (passed to %s) doesn't exist.yes
Wbyte_order_mark_utf8UTF-8 byte order mark detected and skipped.yes

List of all errors

This list is extracted directly from the source code. Some errors might be unused or have incomplete messages (due to the messages being partially computed at runtime).

Error nameMessage
Elimit_reachedOver %d errors detected. Aborting.
EwerrorOne or more warnings was detected with werror on.
Ebuffer_too_smallThe given buffer is too small to contain the resulting ROM.
Eparams_nullparams passed to asar_patch_ex() is null.
Eparams_invalid_sizeSize of params passed to asar_patch_ex() is invalid.
Ecmdl_define_invalidInvalid define name in %s: '%s'.
Ecmdl_define_override%s '%s' overrides a previous define. Did you specify the same define twice?
Ecreate_rom_failedCouldn't create ROM.
Eopen_rom_failedCouldn't open ROM.
Eopen_rom_not_smw_extensionDoesn't look like an SMW ROM. (Maybe its extension is wrong?)
Eopen_rom_not_smw_headerDoesn't look like an SMW ROM. (Maybe it's headered?)
Estddefines_no_identifierstddefines.txt contains a line with a value, but no identifier.
Estddefine_after_closing_quoteBroken std defines. (Something after closing quote)
Efailed_to_open_fileFailed to open file '%s'.
Efile_not_foundFile '%s' wasn't found.
Efile_offset_out_of_boundsFile offset %s out of bounds for file '%s'.
Emismatched_parenthesesMismatched parentheses.
Einvalid_hex_valueInvalid hex value.
Einvalid_binary_valueInvalid binary value.
Einvalid_characterInvalid character.
Estring_literal_not_terminatedString literal not terminated.
Emalformed_function_callMalformed function call.
Einvalid_numberInvalid number.
Egarbage_near_quoted_stringGarbage near quoted string.
Emismatched_quotesMismatched quotes.
Erom_too_shortROM is too short to have a title. (Expected '%s')
Erom_title_incorrectROM title is incorrect. Expected '%s', got '%s'.
Ebank_border_crossedA bank border was crossed, SNES address $%06X.
Estart_of_fileThis command may only be used at the start of a file.
Einvalid_version_numberInvalid version number.
Easar_too_oldThis version of Asar is too old for this patch.
Erelative_branch_out_of_boundsRelative branch out of bounds. (Distance is %s).
Esnes_address_doesnt_map_to_romSNES address %s doesn't map to ROM.
Esnes_address_out_of_boundsSNES address %s out of bounds.
Einvalid_tcallInvalid tcall.
Euse_xplusUse (x+) instead.
Eopcode_length_too_longOpcode length is too long.
Esuperfx_invalid_short_addressInvalid short address parameter: $%s. (Must be even number and $0000-$01FE)
Esuperfx_invalid_registerInvalid register for opcode; valid range is %d-%d.
Einvalid_opcode_lengthInvalid opcode length specification.
Einvalid_mapperInvalid mapper.
EnanNaN encountered.
Edivision_by_zeroDivision by zero.
Emodulo_by_zeroModulo by zero.
Eunknown_operatorUnknown operator.
Einvalid_inputInvalid input.
Einvalid_function_nameInvalid function name.
Efunction_not_foundFunction '%s' wasn't found.
Efunction_redefinedFunction '%s' redefined.
Ebroken_function_declarationBroken function declaration.
Ewrong_num_parametersWrong number of parameters to function.
Einvalid_param_nameInvalid parameter name.
Einvalid_label_nameInvalid label name.
Elabel_not_foundLabel '%s' wasn't found.
Elabel_redefinedLabel '%s' redefined.
Ebroken_label_definitionBroken label definition.
Emacro_label_outside_of_macroMacro label outside of a macro.
Einvalid_namespace_nameInvalid namespace name.
Einvalid_namespace_useInvalid use of namespace command.
Einvalid_struct_nameInvalid struct name.
Estruct_not_foundStruct '%s' wasn't found.
Estruct_redefinedStruct '%s' redefined.
Estruct_invalid_parent_nameInvalid parent name.
Einvalid_label_missing_closerInvalid label name, missing array closer.
Einvalid_subscriptInvalid array subscript after first scope resolution.
Elabel_missing_parentThis label has no parent.
Estruct_without_endstructstruct without matching endstruct.
Enested_structCan not nest structs.
Emissing_struct_paramsMissing struct parameters.
Etoo_many_struct_paramsToo many struct parameters.
Emissing_extendsMissing extends keyword.
Einvalid_endstruct_countInvalid endstruct parameter count.
Eexpected_alignExpected align parameter.
Eendstruct_without_structendstruct can only be used in combination with struct.
Ealignment_too_smallAlignment must be >= 1.
Einvalid_define_nameInvalid define name.
Edefine_not_foundDefine '%s' wasn't found.
Ebroken_define_declarationBroken define declaration.
Eoverriding_builtin_defineTrying 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_bracesMismatched braces.
Einvalid_macro_nameInvalid macro name.
Emacro_not_foundMacro '%s' wasn't found.
Emacro_redefinedMacro '%s' redefined. First defined at: %s:%d
Ebroken_macro_declarationBroken macro declaration.
Einvalid_macro_param_nameInvalid macro parameter name.
Emacro_param_not_foundMacro parameter '%s' wasn't found.%s
Emacro_param_redefinedMacro parameter '%s' redefined
Ebroken_macro_usageBroken macro usage.
Emacro_wrong_num_paramsWrong number of parameters to macro.
Emisplaced_endmacroMisplaced endmacro.
Eunclosed_macroUnclosed macro: '%s'.
Elabel_in_conditionalNon-static label in %s command.
Emisplaced_elseifMisplaced elseif.
Eelseif_in_whileCan't use elseif in a while loop.
Emisplaced_endifMisplaced endif.
Emisplaced_elseMisplaced else.
Eelse_in_while_loopCan't use else in a while loop.
Eunclosed_ifUnclosed if statement.
Eunknown_commandUnknown command.
Ebroken_incbinBroken incbin command.
Erecursion_limitRecursion limit reached.
Ecant_be_main_fileThis file may not be used as the main file.%s
Eno_labels_hereCan't use non-static labels here.
Einvalid_freespace_requestInvalid freespace request.
Estatic_freespace_autocleanA static freespace must be targeted by at least one autoclean.
Estatic_freespace_growingA static freespace may not grow.
Eno_freespace_in_mapped_banksNo freespace found in the mapped banks. (Requested size: %s)
Eno_freespaceNo freespace found. (Requested size: %s)
Eprot_not_at_freespace_startPROT must be used at the start of a freespace block.
Eprot_too_many_entriesToo many entries to PROT.
Eautoclean_in_freespaceautoclean used in freespace.
Eautoclean_label_at_freespace_endDon't autoclean a label at the end of a freespace block, you'll remove some stuff you're not supposed to remove.
Ebroken_autocleanBroken autoclean command.
Epulltable_without_tableUsing pulltable when there is no table on the stack yet.
Epad_in_freespacepad does not make sense in a freespaced code.
Ebase_label_invalidbase Label is not valid.
Epushpc_without_pullpcpushpc without matching pullpc.
Epullpc_without_pushpcpullpc without matching pushpc.
Epullpc_different_archpullpc in another architecture than the pushpc.
Epullbase_without_pushbasepullbase without matching pushbase.
Einvalid_checkInvalid check command.
Eassertion_failedAssertion failed%s
Eerror_commanderror command%s
Einvalid_print_function_syntaxInvalid printable string syntax.
Eunknown_variableUnknown variable.
Epushwarnings_without_pullwarningswarnings push without matching warnings pull.
Epullwarnings_without_pushwarningswarnings pull without matching warnings push.
Efailed_to_open_file_access_deniedFailed to open file '%s'. Access denied.
Efailed_to_open_not_regular_fileFailed to open file '%s'. Not a regular file (did you try to use a directory?)
Ebad_dp_baseThe dp base should be page aligned (i.e. a multiple of 256), got %s
Ebad_dp_optimizeBad dp optimize value %s, expected: [none, ram, always]
Ebad_address_optimizeBad dp optimize value %s, expected: [default, ram, mirrors]
Ebad_optimizeBad optimize value %s, expected: [dp, address]
Erequire_parameterMissing required function parameter
Eexpected_parameterNot enough parameters in calling of function %s
Eunexpected_parameterToo many parameters in calling of function %s
Eduplicate_param_nameDuplicated parameter name: %s in creation of function %s
Einvalid_alignmentInvalid alignment. Expected a power of 2.
Ealignment_too_bigRequested alignment too large.
Enegative_shiftBitshift amount is negative.
Emacro_not_varadicInvalid use of %s, active macro is not variadic.
Evararg_sizeof_nomacroInvalid use of sizeof(...), no active macro.
Emacro_wrong_min_paramsVariadic macro call with too few parameters
Evararg_out_of_boundsVariadic macro parameter %s is out of bounds.%s
Evararg_must_be_lastVariadic macro parameter must be the last parameter.
Einvalid_global_labelGlobal label definition contains an invalid label [%s].
Espc700_addr_out_of_rangeAddress %s out of range for instruction, valid range is 0000-1FFF
Elabel_ambiguousLabel (%s) location is ambiguous due to straddling optimization border.
Efeature_unavaliable_in_spcblockThis feature may not be used while an spcblock is active
Eendspcblock_without_spcblockUse of endspcblock without matching spcblock
Emissing_endspcblockUse of endspcblock without matching spcblock
Espcblock_inside_structCan not start an spcblock while a struct is still open
Espcblock_too_few_argsToo few args passed to spcblock
Espcblock_too_many_argsToo many args passed to spcblock
Eunknown_spcblock_typeUnknown spcblock format
Ecustom_spcblock_missing_macroCustom spcblock types must refer to a valid macro
Espcblock_macro_doesnt_existMacro specified to custom spcblock was not found
Eextra_spcblock_arg_for_typeOnly custom spcblock type takes a fourth argument
Espcblock_macro_must_be_varadicCustom spcblock macros must be variadic
Espcblock_macro_invalid_static_argsCustom spcblock macros must have three static arguments
Espcblock_custom_types_incompleteCustom spcblock types are not yet supported. One day.
Einvalid_endspcblock_argInvalid argument to endspcblock: "%s"
Eunknown_endspcblock_formatUnsupported endspcblock format. Currently supported formats are "endspcblock" and "endspcblock execute [label]"
Einternal_errorAn internal error occured (%s). This is a bug in Asar, please report it at https://github.com/RPGHacker/asar/issues, along with a patch that reproduces it if possible.
Epushns_without_pullnspushns without matching pullns.
Epullns_without_pushnspullns without matching pushns.
Elabel_forwardThe use of forward labels is not allowed in this context.
Eundefined_char'%s' is not defined in the character table
Einvalid_utf8Invalid text encoding detected. Asar expects UTF-8-encoded text. Please re-save this file in a text editor of choice using UTF-8 encoding.
Ecmdl_utf16_to_utf8_failedUTF-16 to UTF-8 string conversion failed: %s.
Ebroken_commandBroken %s command. %s
EoobOperation out of bounds: Requested index %d for object of size %d
Eunclosed_varargVariadic macro parameter wasn't closed properly.
Einvalid_varargTrying to use variadic macro parameter syntax to resolve a non variadic argument <%s>.
Einvalid_depth_resolveInvalid %s resolution depth: Trying to backwards-resolve a %s using %i '^', but current scope only supports up to %i '^'.
Eplatform_pathsPlatform-specific paths aren'supported. Please use platform-independent paths (use / instead of \\).
Ebad_single_line_forSingle-line for loop not allowed here.
Ebroken_for_loopBroken for loop command: %s
Emissing_orgMissing org or freespace command.
Eunclosed_block_commentUnclosed block comment.
Ebad_addr_modeThis addressing mode is not valid for this instruction.
Ebad_access_widthThis addressing mode can accept %s arguments, but the provided argument is %d-bit.
Elabel_before_ifLabels are not allowed before "%s" commands. Suggestion: move the label to a separate line.

Asar Changelog

v2.0.0

Release: ???

Contributors:

  • trillian
  • RPG Hacker
  • p4plus2

New features:

  • UTF-8 support: Asar now expects all source files to be UTF-8-encoded. Unicode code points (encoded as UTF-8) are now supported in table files, math commands, tables etc. Unicode file names are now also supported on Windows. (trillian, RPG Hacker)
  • Added support for nested macro definitions. (RPG Hacker)
  • Improved readability of some error messages. (RPG Hacker)
  • Generally improved error formatting of Asar and added the --full-error-stack option to display complete call stacks. (RPG Hacker)
  • Added multiline comments using ;[[ comment ]] (trillian)
  • Freespaces have a bunch of new features which make them more useful outside of SMW hacking - new options for disabling RATS tags, looking for code in a specific bank or a specific part of ROM. Along with this, freespace allocation should now be a bit more efficient, and the 125 freespace limit has been lifted. (trillian)
  • Freespaces can now have some options applied by default using the freespace_settings command. (trillian)
  • Allowed spaces in math. (p4plus2)
  • A struct can now be used directly like a regular label. (p4plus2)
  • Greatly improved "invalid instruction" errors, which now tell you when an addressing mode doesn't exist for the current instruction, instead of saying "unknown command" for everything. (trillian)
  • autoclean can now be used with more instructions, allowing such contraptions as autoclean cmp label,x if desired. (trillian)

Bug fixes:

  • Asar no longer strips just any white space from defines, allowing them to more closely reflect user intent. (p4plus2)
  • Using org $xxxxxx : db $00 to expand the ROM to a specific size will now allow the freespace finder to use the newly created space. (trillian)
  • In norom, using base will now no longer give bank cross errors for the "real" position, since there are no real banks in norom. (trillian)
  • Better branch instruction handling: some legal but previously rejected branches (especially wrapping around bank borders) are now accepted, and some illegal branches are now properly rejected. (binary1230)
  • A bunch more bugfixes and crash fixes that are too minor to list here

Removed features:

  • All features that were deprecated in Asar v1.90 or earlier.

v1.90

Release: February 23, 2024

Contributors:

  • RPG Hacker
  • trillian
  • p4plus2
  • Atari2
  • Alcaro
  • spooonsss

Notes:

  • The primary purpose of this release is to be a stepping stone towards Asar 2.0. For this purpose, a lot of features have been deprecated and will now throw warnings. Please fix any of those warnings you come across in your patches to assure they will still assemble in Asar 2.0.

New features:

  • The error, warn and assert commands now support the same functions as the print command. (RPG Hacker)
  • Static labels (i.e. labels that don't move between passes) can now be used in more places, including if statements. (RPG Hacker)
  • Asar can be built as a static library. (Atari2)
  • Asar now uses named warnings and errors instead of magic numbers as identifiers. (p4plus2, RPG Hacker)
  • Variadic macro parameters now use the syntax <...[math]>, which makes them less ambiguous and helps prevent syntax parsing bugs. (RPG Hacker)
  • HiROM, ExHiROM, and ExLoROM mappers can now use freecode. (p4plus2)
  • check bankcross now allows disabling checking for half-bank crossings (going from $7FFF to $8000). (p4plus2)
  • New pc() and realbase() functions: return the current SNES address that is being written to. (p4plus2, trillian)
  • Namespaces can now be saved using pushns and pullns. (p4plus2)
  • while loops can now be ended with endwhile. (p4plus2)
  • It's now possible to use macro arguments and macro labels in files that are incsrc'd from macros. (trillian)
  • Added 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)
  • Added 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)
  • Added the --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)

Bug fixes:

  • Variadic arguments in macros can now also take zero arguments, which can be used to implement optional arguments. (RPG Hacker)
  • Escaping quotes in macro calls now works more reliably. (RPG Hacker)
  • Macro calls & definitions no longer break as easily from including whitespace. (RPG Hacker)
  • For invalid table files, Asar now prints the line number of the first invalid entry. (RPG Hacker)
  • Addr-to-line mappings now include 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)
  • Fixed some edge case bugs in Asar's virtual filesystem (usable via the DLL) on Windows. (Atari2)
  • The --version commandline flag now causes asar to exit afterwards, which is the expected behavior for command-line flags. (Alcaro)
  • Fixed some bugs with the label optimizer, making it optimize better. (p4plus2)
  • Fixed freespaces sometimes being a few bytes too long. (trillian)
  • Assigning labels with = now puts them in the right namespace. (p4plus2)
  • Fixed some memory leaks and possible crashes in the DLL. (trillian)
  • Fixed some phantom errors when using forward labels. (trillian)
  • Asar should now allow as much recursion as system resources allow, and then throw a nice error instead of crashing. (Atari2.0, trillian, p4plus2)
  • optimize address now works a bit more consistently (new behavior also properly documented in manual). (trillian)
  • Fixed bug where pointing multiple autocleans at the same freespace would sometimes eat some of the data in it. (spooonsss)

Deprecated features:

  • Warning and error IDs: Use new name strings instead.
  • xkas compatibility mode: port your patch to asar instead. Everything xkas compatibility mode does should already throw warnings.
  • JMP.l: Use JML instead.
  • Quoted symbolic arguments to functions (e.g. sizeof("my_struct")): Remove the quotes (sizeof(my_struct)).
  • Redefining previously defined functions.
  • 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.
  • While blocks ending with 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.
  • Windows-specific paths (e.g. 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.
  • Non-UTF-8 source files: Re-save your source files as UTF-8 in a text editor of choice.
  • ;@command and @command notation: Use command instead.
  • Single-line/inline if statements: Use full if blocks with an 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 with a range like 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.
  • Comments starting with ;[[ : 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 ]].
  • Using 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.
  • The startpos command of the spc700-inline architecture: use the optional execute argument to endspcblock instead.
  • Specifying the byte to use for freespace like freecode $ff. Use the new separate freespacebyte command for it.
  • The warnpc command: use assert pc() <= $xxxxxx instead.

v1.81

Release: January 20, 2021

Contributors:

  • p4plus2

Bug fixes:

  • Prevent defines from being resolved in false branches of if statements when inside variadic macros
  • Fix a phantom error when referencing a struct before defining it

v1.80

Release: January 18, 2021

Contributors:

  • RPG Hacker
  • randomdude999
  • p4plus2
  • CypherSignal
  • LDAsuku
  • Katrina
  • Atari2.0
  • Vitor Vilela

New features:

  • The C DLL binding for Windows should now print a few more details if loading the DLL failed. (RPG Hacker)
  • The 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)
  • Labels can now be used in the condition of an assert command. (randomdude999)
  • The WLA symbol file now includes an address-to-line mapping, which can be used by some debuggers to provide source-level debugging. (CypherSignal)
  • The sizeof() and objectsize() functions don't need quotes around the struct name anymore. For backwards compatibilty, quotes are still allowed. (p4plus2)
  • There is a new label optimizer, which can convert RAM labels to direct page or word accesses where possible. For this there are 3 new commands: dpbase, optimize dp and optimize address. (p4plus2)
  • The floor() and ceil() functions have been added. (randomdude999)
  • The bank() and <: function and math operator have been added (p4plus2)
  • The datasize() function has been added. (p4plus2)
  • Warning if the mapper mode is changed after being set (p4plus2)
  • Major improvements to internal string handling in asar (makes asar significantly faster) (p4plus2)
  • Variadic macro support (p4plus2)
  • Support for global labels (p4plus2)
  • Errors about crossing banks have been improved, now they print the exact line where the bank border was crossed instead of saying it was "somewhere before this point". (randomdude999)
  • Add fill align and skip align (randomdude999)

Bug fixes:

  • The Asar DLL can now be loaded in Windows applications that define UNICODE. (RPG Hacker)
  • Labels can now start with underscores. (LDAsuku)
  • An undef command inside an unexecuted if statement would still get executed. This is now fixed. (randomdude999)
  • The assert command would give strange errors when the condition to test contained a comma. This is now fixed. (randomdude999)
  • Trailing commas can now be used to join lines that contain more than one comma, and the joined lines can now have comments. (randomdude999)
  • An invalid warning ID in a warnings enable command no longer prints the error message three times. (randomdude999)
  • When using the Asar DLL, built-in defines (e.g. !assembler_ver) are now always present. Previously they were missing when the DLL was called without specifying a standard defines file. (randomdude999)
  • Repeatable instructions (like LSR #4 don't accept labels in their argument anymore. (randomdude999)
  • Asar no longer writes an incorrect checksum for ROMs whose size isn't a power of 2, e.g. 2.5MB, 3MB, 6MB. (randomdude999)
  • The "It would be wise to set the 008000 bit" warning has been relaxed and isn't thrown for labels in banks 70-7F. (randomdude999)
  • Overwriting built-in functions now throws a warning. Overwriting user-defined functions now also throws a warning for consistency. (randomdude999)
  • Opcode length specifications were sometimes being ignored in arch spc700. Now adc.w a, $0 properly assembles to 85 00 00 instead of 84 00. (randomdude999)
  • Fixed case sensitivity problem in function calls. (p4plus2)
  • Fixed struct definition with label base corrupting label name. (p4plus2)
  • Fixed missing error on certain types of label. (p4plus2)
  • Fix some phantom errors in math processor (randomdude999)
  • Fixed a memory leak in the cli version of asar (p4plus2)
  • Fixed a memory corruption in spc700 processing (p4plus2)
  • Fixed various memory bugs (found via valgrind/fsanitize) (p4plus2)
  • Fixed a case where if statements wouldn't always throw an error (randomdude999)
  • Fixed a case where you could get invisible errors when prefixing commands with @ (randomdude999)
  • Fixed arch superfx ignoring all commands with more than 2 words (randomdude999)
  • Fixed non-ASCII characters in strings being written as garbage (Katrina)
  • Fixed crashes when getting into an infinite recursion loop (randomdude999)
  • Improvements to syntax highlighting in the Asar manual (Atari2.0)
  • The C# wrapper has been updated to work with Asar versions 1.60 and newer. (Vitor Vilela)
  • Improved error message for SPC instructions with out of range arguments (randomdude999)

v1.71

Release: January 6, 2019

Contributors:

  • randomdude999

Bug fixes:

  • The Super FX ROM mapping should now properly support freespace.
  • The canread() functions returned true for the first byte after the end of the ROM. That has been fixed.
  • Using check bankcross off now should mess with the PC less (i.e. no forced fastrom addressing).
  • Using the 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.)

v1.70

Release: January 4, 2019

Contributors:

  • randomdude999
  • boldowa

New features:

  • The 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)

Bug fixes:

  • Fixed quite a few crashes in Asar:
    • Fixed a crash when including a directory (randomdude999, found by Selicre)
    • Fixed a crash when using a macro sublabel outside of a macro (randomdude999)
    • Fixed a crash when having unmatched structs in a weird way (randomdude999)
    • Fixed a crash when appending to a non-existent define (randomdude999, found by Selicre)
    • Fixed a crash when Asar encountered mismatched quotes after define evaluation (randomdude999, found by Alcaro)
  • Made Asar about 2x faster than before (randomdude999)
  • Values larger than $80000000 in things like dd should work better (randomdude999)
  • Fixed a freespace leak when using prot (randomdude999, found by WhiteYoshiEgg)
  • Fixed Windows header includes, to make cross-compilation of Asar on Linux work better (boldowa)

v1.61

Release: August 22, 2018

Contributors:

  • randomdude999

Bug fixes:

  • Fixed asar quietly skipping assembling a line if it had a label followed by 3 or more spaces and ended with ".
  • In 1.60, 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.

v1.60

Release: July 6, 2018

Contributors:

  • RPG Hacker
  • boldowa/6646
  • zetaPRIME
  • Horrowind
  • randomdude999
  • TheBiob
  • BenJuan26
  • Vitor Vilela
  • KevinCathcart
  • KungFuFurby

New features:

  • Asar is now officially hosted on GitHub: https://github.com/RPGHacker/asar

  • Asar now uses CMake, which should make it easier to build on different platforms and with different compilers. (RPG Hacker, randomdude999, boldowa)

  • Added a proper manual in HTML format to Asar, rather than just having the xkas manual and a TXT file with all changes. (RPG Hacker)

  • Added Python binding for the Asar DLL. (randomdude999)

  • Added defined() function (randomdude999) and undef command. (zetaPRIME)

  • Added simple syntax highlighting for Sublime Text. (randomdude999)

  • +/- label declarations can also end with an optional : now for consistency. (RPG Hacker)

  • Added macro-local variations for +/- labels and sub labels, which are also prefixed with a ?. (RPG Hacker)

  • Added macro main label assignment. (RPG Hacker)

    macro my_macro()
        ?MacroLabel = $008000
        
        dl ?MacroLabel  ; Valid
    endmacro
    
    %my_macro()
    
    dl ?MacroLabel      ; Error!
    
  • Added support for a new type of label declaration: prefixing a label or sub label declaration (including macro labels) with # 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.

  • Added support for include search paths. (RPG Hacker)

  • Added asar_patch_ex() function to DLL API, which works like asar_patch(), but takes additional parameters. (RPG Hacker)

  • You can now escape ! by doing \! (useful for preventing Asar to replace defines in certain places). (randomdude999)

  • You can now escape \ by doing \\. (RPG Hacker)

  • Added support for pushbase/pullbase. (randomdude999, RPG Hacker)

  • Added check title command which makes Asar verify the title of the output ROM (to assure a patch is applied to the correct ROM). (randomdude999)

  • Added support for nested namespaces. (randomdude999, RPG Hacker)
    Enable them via the command:

    namespace nested on
    
  • Added support for 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)

  • Added a new, simple README. (randomdude999, RPG Hacker)

  • Added a filesize() function and a getfilestatus() function. (randomdude999)

  • Added support for stdincludes.txt and stddefines.txt - see manual for details. (RPG Hacker, randomdude999)

  • Added support for setting defines via the command line or DLL API. See manual (section: usage) for details. (randomdude999)

  • Added includeonce command for shared files which may be included multiple times, but should only be assembled once. (RPG Hacker)

  • Added support for generating a symbols file. Currently, WLA and no$sns format are supported. (KevinCathcart)

  • Opcode length specifiers (.b, .w and .l) are now also supported for the SPC700 architecture. (KungFuFurby)

  • Added stringsequal() and stringsequalnocase() functions. (RPG Hacker)

  • Added support for some built-in defines. Currently, !assembler and !assembler_ver are supported. Trying to modify those defines will throw an error. (RPG Hacker)

  • Added IDs to all warnings and errors, together with functionality to enable or disable specific warnings. (RPG Hacker)

  • Added a new optional warning that has to be enabled manually to throw warnings when opcodes are implicitly sized. (KevinCathcart)

  • Added functionality to allow overriding Asar's default behavior of enabling/disabling checksum generation based on context. (randomdude999)

Bug fixes:

  • Fixed various bugs in the DLL interface. It should now hopefully be possible to apply multiple patches in succession via the DLL interface without resorting to hacks. (RPG Hacker, randomdude999, boldowa)

  • Rewrote big parts of test applications once again to make it a lot easier to test a bunch of ASM files and actually get some meaningful information out of it. (RPG Hacker)

  • Fixed the way ;@ 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)
    As a result of this change, any of the following are now valid Asar code:

    ;@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.

  • Changed format of existing command line arguments for consistency. (RPG Hacker)

  • LoROM banks $70 to $7D can be used correctly. (randomdude999)

  • Struct definitions are cleared correctly in Asar lib. (randomdude999)

  • Fixed a freeze that could occur when using freedata align. (TheBiob)

  • Fixed a bug in 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)

  • Fixed a rare crash when using the command-line interface in ways you're not supposed to use it. (randomdude999)

  • Fixed readfile()/canreadfile() crashing when reading from more than 16 different files in the same patch. (randomdude999)

  • Fixed the ROM title being reported incorrectly. (randomdude999)

  • Fixed using really large values in math with math round on making the values negative sometimes. (randomdude999)

  • Fixed an SA-1 mapping issue. (Vitor Vilela, randomdude999)

  • Fixed print double() not working when any of their arguments contain commas or parentheses. (randomdude999)

  • Made it possible to pass string parameters to functions like readfile1() via user-defined functions. Previously, this failed as Asar expected all parameters in user-defined functions to be numbers. (KevinCathcart)

  • Fixed a bug related to evaluation of parameters in user-defined functions. In some situations, Asar returned the incorrect parameter. For example: given a user-defined function with a parameter ab and a paramater abc, Asar occasionally returned the parameter abc when referencing the paramter ab. (KevinCathcart)

  • Added more undocumented features to the manual. (RPG Hacker)

  • 0x hex literals were supported by Asar on some platforms, which wasn't supposed to be the case. Made sure 0x hex literals throw errors on all platforms. (randomdude999)

Deprecated features:

  • Started the process of deprecating xkas compatibility mode. From now on, using xkas compatibility features will throw warnings. In a future version of Asar, xkas compatibility will be removed entirely.
  • Started deprecating autoclear, which is an alias of autoclean. Please use only autoclean from now on.
  • Started deprecating freespace fixed, which is an alias of freespace static. Please use only freespace static from now on.
  • Deprecated and removed support for 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).

v1.50

Release: February 28, 2017

Contributors:

  • RPG Hacker
  • p4plus2

New features:

  • Added support for structs.
  • Added API to Asar lib for getting information on all blocks of data written by Asar.
  • Added API to Asar lib for getting the mapper currently used by Asar.
  • Added support for ExLoROM and ExHiROM mappers.
    NOTE: Based entirely on conversion tables I got from Ladida; don't know if these conversions are actually correct. Some features may not work as intended when using those mappers (such as freedata, for example), but I can't verify this.
  • Added pushtable and pulltable commands, which let you back-up or restore the current character table to the/from the stack.
  • Added 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++.

Bug fixes:

  • Lines starting with @ or ;@ that don't map to a recognized special command now only throw warnings at best and no errors.
  • Rewrote code of tests a little to make them easier to execute and make them clean up their own mess.
  • C# wrapper for Asar DLL was non-functional since it didn't specify a calling convention, making it always lead to an exception in some scenarios.

Notes:

  • Just like the last version, this version of Asar was built in MSVC rather than g++, but this time I also updated the Asar DLL (which I had overlooked last time). I'm not sure if Windows applications are compatible with DLLs that were built by a different compiler, so if you're planning to use the DLL, this is something to watch out for. If you're planning to use a compiler other than MSVC, I recommend just rebuilding the DLL from source in whatever compiler you're using (or directly including the Asar library code in your project).

v1.40

Release: October 23, 2016

Contributors:

  • RPG Hacker

New features:

  • 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.

  • comparison functions: 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.

  • logical functions: 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.

  • Multiline operator: You can now put \ 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.

Bug fixes:

  • Asar 1.37 officially suppported overloaded versions of read1() to read4(), but always threw "Wrong number of parameters to function" errors when actually using those overloaded versions.

  • Asar 1.37 threw "Wrong number of parameters to function" error for function canread() when passing 2 arguments to it, because it actually treated it as canread1() due to an internal error in string comparison.

  • Using better double -> int conversions in some places - where

    dd $FFFFFFFF
    

    would assemble to 00 00 00 80 ($80000000) in Asar 1.37, it now assembles to FF FF FF FF

  • Defines in elseif conditionals now get properly resolved.

  • The #= define operator now doesn't truncate its value when using math round off, making it possible to do double-precision math with it.

  • Asar 1.37 detected misplaced elses and endifs, but not misplaced elseifs.

  • Putting @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.

  • Special commands like @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.

  • Asar 1.37 fixed a bug in SuperFX compilation, but src/test/arch-superfx.asm was never edited to acknowledge this fix, so the test always failed

  • Added different define operators (=, +=, :=, #=, ?=) to manual.txt. Those have been in Asar for quite a while, but were never documented yet, although they can be quite useful.

Notes:

  • This version of Asar was built in MSVC rather than g++, mainly because I already had that installed and use Visual Studo as an IDE, anyways. Functionally, this shouldn't make any difference, unless using Asar in unintended ways, where anything goes. I did build the Linux version in g++ to confirm compatibility, though.

v1.37

Release: March 26, 2016

Contributors:

  • Raidenthequick

New features:

  • New 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.
  • In line with this, autoclean was hardcoded to clean using $00. This was fixed also to clean with the byte supplied by freecode, or $00 if not supplied.

Bug fixes:

  • Super FX short addressing fixed, and added error checking for valid short address. For example, 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.)
  • Super FX and SPC700 labels were broken if used within freespace. This has been fixed by doing what 65816 does: mask the address with 0xFFFFFF because freespace addresses use a high byte to indicate that they're freespace.
  • Fixed SA-1 mapping using wrong Super MMC banks range.

Historical changelog

This part of the changelog was written retroactively and may be incorrect and/or incomplete.

v1.36

Release: November 28, 2013

Contributors:

  • Alcaro

New features:

  • Added the fullsa1rom mapper.
  • Asar now halts if more than 20 errors are detected.

Bug fixes:

  • Fixed a warning about $xx,y not existing if $xxxx,y doesn't exist either
  • Fixed sublabels requiring a parent label even in xkas compatibility mode
  • Fixed a memory leak in the DLL

v1.35

Release: December 13, 2012

Contributors:

  • Alcaro

New features:

  • Made freecode print the size of the requested freespace if allocation fails.

Bug fixes:

  • Fixed a crash caused by incbin'ing a file and specifying an endpoint beyond the end of the file.
  • Blocked readN on exactly one byte after the end of the input ROM.

Deprecated features:

  • fastrom has been removed. It gives far too many problems relative to its advantages (are there any?). In exchange, freespace searching in lorom is now always fastrom enabled. (The command itself still exists, but it does nothing.

v?.??

Release: November 11, 2012

Contributors:

  • Alcaro

New features:

  • Added -DTIMELIMIT support on Windows. Note that it's more limited than on Linux; it seems to only check the time limit every seven seconds, it can't print error messages, and it bugs up on anything above 429 seconds. (But you shouldn't need to set it above one or two anyways.)

Bug fixes:

  • Missing an org/freespace command now no longer places the first byte at SNES $FFFFFF, but jumps to $008000 directly.
  • Allowed JMP.l in xkas mode...
  • Fixed PC->SNES conversion for 0x380000 and higher.
  • Fixed BEQ a.
  • fastrom : org $8000 : db $01 : warnpc $8001 no longer throws errors.
  • Made bit shifting unsigned, to match how xkas behaves.
  • Made db $0-10 return -10, not -16.
  • Including a file that enables xkas mode, while xkas mode is already enabled, no longer throws errors.
  • Fixed dd $FFFFFFFF acting like dd $7FFFFFFF.
  • Fixed compile errors on GCC 4.7.
  • Made blockreleasedebug and blockdebugrelease not show up anymore.
  • Files saying @asar can no longer be included from files saying ;@xkas.
  • Locked org $F00000.

v1.33b

Release: October 26, 2012

Contributors:

  • Alcaro

Bug fixes:

  • Rearranged the manual a little to have a more obvious filename.
  • Nuked a debug code that kept printing nonsense.

Notes

  • Asar now considers OS X a non-Linux Unix-line and recommends -Dlinux.

v1.33

Release: October 22, 2012

Contributors:

  • Alcaro

New features:

  • Made incbin file.bin -> $123456 possible. This one can go as far as you want; it doesn't mind crossing banks nor exceeding the size of a RATS tag. It does, however, demand to know the SNES position in advance; you can't use labels here. (It also refuses to go outside the ROM)
  • Added a sandbox mode that disables everything that loads external files. Only accessible by recompiling (-DSANDBOX) since it should only be useful for people who have compilers already.
  • Added a way to make the process abort if it runs too long. Linux+recompile (-DTIMELIMIT=60) only.

Bug fixes:

  • Nuked some spurious warnings in incsrc/incbin if the base path contains backslashes.
  • Made Asar a little more eager to detect bank crossings. Now those errors appear in the correct file.
  • Made REP #$FFFF throw errors instead of repeating 65535 times.
  • Fixed a bunch of weird code in the freespace finders.
  • Made sure . does not count as a label.
  • Fixed LDA Label,y in emulation mode. Asar tried using 24-bit addressing for that, but there is no such addressing mode.
  • Another accuracy improvement in emulation mode.
  • Detonated some more SPC700 inaccuracies and bugs.
  • Made JMP ($1234)+($3412) assemble to JMP abs, not JMP (abs).

v1.32

Release: October 8, 2012

Contributors:

  • Alcaro

New features:

  • Added @xkas, which will revert to maximum xkas compatibility mode and change the behaviour of a couple of commands (as well as throwing warnings on various Asar-only commands).
  • Any line starting with ;@ will now be assembled. This is intended to be combined with the above one to create ;@xkas, which will send Asar into xkas mode while letting xkas ignore it, but you can also use ;@if 0 : org !Freespace : ;@endif : ;@freecode (except with the colons replaced with linebreaks).
  • Replaced bank off with bank auto, and added bank noassume, which always uses long addressing if possible.
  • Removed the @ trick for suppressing warnings. Its only legit use was telling Asar that a freespace is cleaned in a way it can't understand. freecode cleaned is the new way to say that.
  • Whitespace, blank lines and comments are now allowed to exist before @asar, ;@xkas and @include/@includefrom.
  • Added !define ?= value, which sets !define to value if !define is previously undefined.

Bug fixes:

  • Blocked \ in incsrc/bin because linux.
  • Reworked prot a bit. You can now use multiple prot statements in the same freespace; Asar has always allowed cleaning them out anyways. The old implementation was rather hacky.
  • Fixed build scripts a little. Some files didn't get included.
  • Fixed the internal ROM size when expanding SA-1 ROMs.
  • Killed some dead code.
  • Made db 0x80 assemble properly with math round on (the default).
  • Killed a phantom error on ALS A and a couple of similar constructions.
  • Squished a possible moving labels error.
  • Fixed a bug where the major version of Asar got compared backwards in @asar.

v1.31

Release: September 13, 2012

Contributors:

  • Alcaro

New features:

  • Added incbin file.bin -> Labelname support.
  • Added align detail to freespace/freecode/freedata.
  • Made reset freespaceuse a valid operation.
  • Added arch spc700.

Bug fixes:

  • Killed some more Linux compile errors.

Deprecated features:

  • Deprecated arch spc700-raw.

v1.30

Release: August 24, 2012

Contributors:

  • Alcaro

New features:

  • Added SuperFX mapper and arch superfx.
  • Unlocked $[F0-FF]:[8000-FFFF] in lorom again. Turns out bsnes only maps SRAM to the lower halves of those banks.
  • Added an elseif command.
  • Added && and || support to if/etc conditionals.
  • Reworked the Relative branch out of bounds errors a little, so it tells how far outside the bounds they are.
  • Added print dec($1234) and print hex(1234).
  • Attached test suite in the source zip, in case some other programmers are interested.
  • Added @include and @includefrom, which throws errors if Asar is invoked directly on them.
  • Added a -pause= flag on the command line. Valid actions are "yes", "warn", "err", and "no" (default).
  • Added a couple of synonyms to the single-bit SPC700 opcodes.
  • Added underscore-prefixed versions of all builtin functions.
  • Added error-safe readN and canreadN.

Bug fixes:

  • Reworded the 125 freespaces limit to make sure nobody can think there's a limit of 250 labels.
  • Made readN()ing and writing to the same byte in ROM well defined (it gives the same value as it had in the input ROM).
  • Made the size guesser a little smarter. It now assumes .b for LDA #$123456/65536.
  • Killed a phantom error on the very rare code pattern db $80/(Label2-Label1) : Label1: : db 0 : Label2: and similar.
  • Adjusted the error messages read1/etc gives if the address is invalid.
  • Suppressed a couple of useless errors if labels are redefined.
  • Killed a couple of Invalid endif errors if an if statement is invalid.
  • Repaired nested pushpc/freedata.

v1.29

Release: July 19, 2012

Contributors:

  • Alcaro

New features:

  • Added -v and -version switches.
  • Removed the lock on $700000-$7DFFFF in HiROM as it's not mapped to SRAM there.
  • Edited the library API a bit.

Bug fixes:

  • Added an error message for mov (x)+,a in the SPC700 modes.
  • Nuked a bug making tabs not treated as spaces in all contexts.
  • Fixed a bug where Label = 123 checked that there is an org in front.
  • Fixed a bug where the line number was removed if the last code block on a line crossed a bank border.

v1.28

Release: ?

Contributors:

  • Alcaro

New features:

  • Flipped the switch to autocorrect the checksum to on by default.

Bug fixes:

  • Nuked some double spaces in outputs.
  • Fixed sa1rom mapping breaking on everything except the first megabyte.
  • Fixed a couple of bugs with valid table files being rejected.

v1.27

Release: ?

Contributors:

  • Alcaro

New features:

  • Made freespace fixed a synonym of freespace static.
  • Made Asar try to autodetect whether the ROM is LoROM or HiROM. Homebrews default to LoROM.

Bug fixes:

  • Zapped a phantom error if the code arch spc700-raw : org $010000 : db $00 is used.
  • Nuked some pointless errors with static freespaces.
  • Zapped a bunch of bugs with static/fixed freespace.
  • Killed a bug that crashed Asar if a line contained both commas and unmatched quotes.
  • Replaced the Press Enter to continue with the standard Press any key to continue prompt on Windows.
  • Refactored some parts of the math parser, to get rid of some possible escaping NaNs.
  • Added some #errors if no frontend is chosen, and edited some others a bit.
  • Adjusted Linux support a bit. Looks like it bitrotted.
  • Fixed some off-by-one errors in spc700-inline.

v1.26

Release: April 28, 2012 or earlier

Contributors:

  • Alcaro

New features:

  • Added support for db +1. There's no reason not to allow it.

Bug fixes:

  • Removed some more files made useless by dropping asar-clr.dll.
  • Fixed a bug where db $12,$34 broke in the spc700 modes.
  • Fixed a bug where an invisible bank border was hit if a base command was used without an org command.

v1.25

Release: ?

Contributors:

  • Alcaro

New features:

  • Rewrote the .NET library as a C# wrapper around asar.dll using P/Invokes, for better Linux compatibility (Mono really seems to hate C++/CLI). This also means that asar-clr.dll no longer exists.
  • Made errors not shred the ROM in the library frontend.

Bug fixes:

  • Killed a bunch of moving label errors if a PROT is misplaced. No point throwing 300 errors for one misplaced line.
  • Fixed error messages erroneously being printed to stdout instead of stderr.
  • Fixed errors with prot being incompatible with fastrom.
  • Fixed an uninitialized variable.
  • Fixed a buffer underflow Valgrind whined about.
  • Zapped a null pointer bug in the library if a file was not found.
  • Nuked a bug where the library did not clear out previous errors when applying a new patch.

v1.24

Release: ?

Contributors:

  • Alcaro

New features:

  • Added SA-1 support. Syntax is sa1rom 0,1,2,3, where the four numbers are the bank numbers, 0 through 7. It's allowed to not give them; that will result in 0,1,2,3 being picked as default.
  • Added freespace finding support for hirom and sa1rom.
  • Made Asar remove extranous quotes around filenames if a file is pulled to the Asar window.
  • Minor adjustment to the ROM title verifier.

Bug fixes:

  • Fixed a phantom error if prot isn't used directly after a free* statement.
  • Fixed a bogus error if prot is placed at any of the first eight bytes of a bank.
  • Repaired autoclean, which appears to have broken a few versions ago.
  • Fixed a crash bug if the ROM does not exist and couldn't be created.

v1.23

Release: ?

Contributors:

  • Alcaro

Bug fixes:

  • Fixed around ten broken SPC700 opcodes.

v1.22

Release: ?

Contributors:

  • Alcaro

New features:

  • Added a few special cases for some functions, and adjusted some compiler flags. The result is dramatically improved performance.
  • Adjusted the string table a bit.

Bug fixes:

  • Fixed an uninitialized value in the macro handling code that may (but usually doesn't) throw errors if a macro has no arguments.
  • Repaired partial incbin if the filename is quoted.

v1.21

Release: ?

Contributors:

  • Alcaro

Bug fixes:

  • Fixed: read1() etc. went missing.

v1.20b (?)

Release: March 4, 2012

Contributors:

  • Alcaro

Bug fixes:

  • Added freecode static and norom to changes.txt.

v1.20 (?)

Release: March 4, 2012

Contributors:

  • Alcaro

New features:

  • freecode/freedata have been expanded: They now take an optional parameter containing comma separated parameters on how to hand out the freespace. Valid parameters are ram, noram, and static. ram tells Asar that this code needs the RAM mirrors and puts the code in banks 10-3F; noram tells Asar that the code (or table) does not need the RAM mirrors, and therefore uses banks 40-6F if possible; static demands that the freespace does not move; if it grows, Asar throws an error. freespace has also been added; freecode is a shortcut to freespace ram.
  • Added hirom support. It works just like in xkas: hirom : org $C00000 : JML Main. Also added norom, where the PC address is the same as the SNES address (not counting SMC headers); use this (and base) if you want to implement your own mapper (preferably in a macro).
  • Simplified table code, and made it accept assigning stuff to the = character.
  • LDY #Label>>16 (and LDA/LDX/etc) is now considered 8bit. This should reduce the need for explicit .b hints.
  • Asar can now use the last byte of the ROM as freespace.
  • Made the default value for untouched table bytes be more random to make errors in usage more obvious.
  • Lowered the recursion limit a bit.
  • You can now drop the extensions on both filenames, if you want to.
  • Asar now tries to no longer parse defines inside if 0 blocks. Note that this doesn't help on single-line if statements; you'll need a multiline if.
  • Added !def := val, which expands everything inside the value before setting it (making !a := a!a a perfectly safe operation).
  • Added !def #= val, which expands the value into an integer and stores it into the define. !a #= 1+1 : L!a: is a valid operation.

Bug fixes:

  • Fixed fastrom again again again.
  • Labels before the first org/freecode/etc are now considered to be at $008000, not $FFFFFFFF. (They're still considered invalid.)
  • Some minor fixes in autoclean dl Label.
  • Repaired the ** operator.
  • Calling a function no longer hits a user-defined function starting with the name of called function.
  • Fixed skip command, which previously only edited the base address.

v1.14 (?)

Release: February 8, 2012

Contributors:

  • Alcaro

Bug fixes:

  • Killed a crash bug.

v1.13 (?)

Release: February 7, 2012 (?)

Contributors:

  • Alcaro

New features:

  • Added a random icon, to fit the Nordic theme of Asar's name.
  • Made asar_patch destroy the ROM if any errors are encountered, to ensure that nobody attempts to use a ROM that may contain subtle breakage.
  • A label can now be on the same line as a macro call.

Bug fixes:

  • Fixed the bug that makes macros look like they're called from themselves if the macro call is broken.

v1.12 (?)

Release: February 6, 2012 or earlier

Contributors:

  • Alcaro

Bug fixes:

  • Fixed fastrom mode again...
  • Fixed crashes when closing Asar while waiting for input.
  • Macro definitions are now ignored inside if 0 blocks.
  • Macros and incsrc may now be called inside if 1 blocks.
  • Locked banks $F0-$FF from usage. They are not usable.

Deprecated features:

  • Removed most of the emulation mode. The only remaining change is that it makes Asar print the errors to stdout instead of stderr, since that quirk isn't fixable by editing the ASM files.

v1.11 (?)

Release: January 25, 2012 or earlier

Contributors:

  • Alcaro

New features:

  • Prefixing a command with @ will now make Asar not print warnings for that line.

Bug fixes:

  • JMP and JSR within the current bank with bank active has been fixed.
  • xkas appears to allow sublabels starting with numbers. Asar has been updated to do the same.
  • fastrom no longer messes up branches.
  • autoclean read3($123456) can now be used before the first org.
  • db ($04<<2)|($80>>1) no longer breaks due to unmatched parentheses.
  • Fixed problem with autoclean $123456, where it read a pointer from $123456 instead of removing $123456.
  • Fixed a crash if a freespace block protects itself.

v1.10 (?)

Release: January 13, 2012

Contributors:

  • Alcaro

New features:

  • Made asardll.h and asardll.cpp work if compiled as both C and C++, and renamed asardll.cpp to asardll.c, for greater compatibility with other languages (hi, Objective-C). As an effect of this, the library name argument has disappeared from asar_init.
  • base off is now a valid operation. It tosses the base address back to the current code insertion location.
  • New command: bank. It makes the label optimizer act as if it's in another bank. This is not the same as base; LDA Label,x : Label: db $01,$02,$03,$04 will use 24-bit addressing for the LDA if bank is active. It's intended for long codes that assume the data bank register is not the same as the code bank register.
  • Added a copy of GPLv3 to asar.zip, since LGPL is apparently meaningless on its own. These licenses are starting to get a little tricky.

Bug fixes:

  • Reworded the documentation for prot.
  • Asar now throws a warning if a freespace block appears to be leaked.
  • Adjusted "autoclean at the end of a RATS tag" error a bit.
  • Made Asar not read uninitialized/garbage memory if there's crappy content at the end of a macro.
  • Fixed infinite loop if a freespace of size 32768 is requested.

v1.09 (?)

Release: January 6, 2012

Contributors:

  • Alcaro

New features:

  • New command: warn.
  • The math parser now accepts strings like 0.5 if you turn off rounding. It'll break if rounding is on, since that wouldn't make any sense. It'll round down as soon as it's returned from the math parser; db 0.9 is the same as db 0, but db 0.4*0.4 is the same as db 1.
  • assert can now take another parameter. If this is given, it is printed in the error message. (All error-generating blocks are printed, which makes these messages appear twice)
  • Asar now attempts to check if the ROM title looks sane before applying a patch.
  • Added Linux/OSX support to asardll.cpp.
  • Added asar_math() to the library frontend.
  • Now prints a message if patching is successful. However, calling it from the command line makes it remain silent, unless you add -verbose.

Bug fixes:

  • Restored documentation of the functions to changes.txt that were forgotten when removing stdlib.asm.
  • Fixed crashes if a macro is called with wrong number of arguments.
  • Fixed silent errors when using LDA Label,y if Label is in another bank.

v1.08 (?)

Release: December 27, 2011

Contributors:

  • Alcaro

New features:

  • Added special meaning to the define !assembler: Its value is always "asar", even if you assign something else to it. Intended usage: !assembler = xkas : %freespace_!assembler(), where %freespace_xkas() requires the user to set freespace and %freespace_asar() contains a freecode.

Bug fixes:

  • Restored db 1+'a' support to the math parser.
  • Merged the dupe descriptions for the fastrom command in the documentation.
  • Fixed sublabel support, which the new math engine broke as well.

v1.07 (?)

Release: December 25, 2011

Contributors:

  • Alcaro

New features:

  • Replaced the math library with a more powerful one.
  • New command: math.
  • Homemade functions can now safely replace builtin ones, to compensate for the possibility that new builtin functions may collide with ones in your codes.
  • Added a few new functions: log, log10, and log2.

Bug fixes:

  • Removed #ifdef ALCAROBOT (sandbox mode) from a few source files.
  • Fixed a few more memory leaks.

v1.06 (?)

Release: December 25, 2011

Contributors:

  • Alcaro

Bug fixes:

  • Sprite Tool compatibility was still broken. Looks like tmpasm.bin is unheadered after all. Repaired again.

v1.05 (?)

Release: ?

Contributors:

  • Alcaro

New features:

  • Updated libsmw and libstr to the latest versions from AlcaRobot.

Bug fixes:

  • Asar no longer crashes if it tries to open a file of less than 512 bytes if it thinks it's headered. This fixes Sprite Tool compatibility.

v1.04 (?)

Release: ?

Contributors:

  • Alcaro

Bug fixes:

  • $xxFFFF can now safely be overwritten.
  • Removed garbage from some errors.

v1.03 (?)

Release: ?

Contributors:

  • Alcaro

New features:

  • Renamed the DLL frontend to LIB, since DLLs only exist on Windows and dynamic libraries exist elsewhere too. No point using more Windows terminology than needed.
  • Various edits to the library frontends. asar_i_* has been renamed to asar_*, and errors and warnings now tell where they're called from, if found inside a macro.
  • The command line frontend tells where problematic macros are called from.
  • Made license choice explicit.

v1.02 (?)

Release: ?

Contributors:

  • Alcaro

New features:

  • Made it possible to call asar_i_patch() multiple times without calling asar_reset() between them. The errors will remain until you asar_reset() is used, meaning they'll accumulate until they are cleared.

Bug fixes:

  • Fixed a crash bug in asar_i_reset() if the function does not create any custom functions.

v1.01 (?)

Release: ?

Contributors:

  • Alcaro

Bug fixes:

  • Put a few lines back where they should be, to get rid of a crash.

v1.00 (?)

Release: December 2, 2011

Contributors:

  • Alcaro

New features:

  • If an error occurs, it now prints the buggy block, if relevant.
  • Various changes to the DLL API.
  • Crappily cobbled together a hack to make Asar compatible with Sprite Tool (a file called tmpasm.bin is now considered to be headered).
  • Included the script used to compile the .NET DLL.

Bug fixes:

  • Fixed a memory allocation mismatch.

Deprecated features:

  • Dropped autocolon, since it's useless and never used.
  • The set command has been removed.
  • stdlib.asm has lost its special meaning.

v?.??

Release: November 29, 2011

Contributors:

  • Alcaro

New features:

  • asar-cli.dll has been renamed to asar-clr.dll, and its contents has been moved to namespace AsarCLR instead of asarcli. This will require your tools to be edited, but it shouldn't take longer than a minute or two.
  • Added a function to the DLLs to view the table data.
  • New setting: werror. It makes warnings emit errors if encountered.

Bug fixes:

  • pushpc no longer throws idiotic errors everywhere.
  • asar-clr.dll can now return errors/warnings/etc without crashes.
  • Killed off a bunch of memory leaks in asar-clr.dll.
  • STA $12,y warnings now print only once.
  • asar_resolvedefines no longer throws exceptions outside the DLL.
  • Squashed a memory leak bug where the math module allocates some memory without freeing it when the DLL is unloaded.

Deprecated features:

  • Removed AsarCLR::Asar::unmanageerrors() from asar-clr.dll. It's an internally used function that shouldn't be exported.

v?.??

Release: November 21, 2011

Contributors:

  • Alcaro

Bug fixes:

  • Fixed a bug that made + and - labels not be treated as labels under some circumstances.
  • Made "relative branch out of bounds" errors disappear again unless they're supposed to exist.

v?.??

Release: November 20, 2011

New features:

  • if 0 now blocks label creation.
  • asar-cli.dll now exports a set of functions using .NET types instead of C types.
  • The DLL APIs have been slightly changed with regards to initialization and stdlib.asm.
  • Asar now prints warnings and errors to stderr instead of stdout. (Emulation mode is unaffected.)

Bug fixes:

  • Repaired "branch out of bounds" errors.

v?.??

Release: November 17, 2011

Contributors:

  • Alcaro

Bug fixes:

  • Removed more debug code
  • Fixed org Label if the label is in freespace

v?.??

Release: November 17, 2011

Contributors:

  • Alcaro

New features:

  • You can now use if !condition to negate the statement. Note that you may need an extra set of parentheses due to conflicts with define syntax.
  • Parentheses can now be used inside macro arguments. (Just make sure to close them.)
  • Included a .NET version of asar.dll. Note that it still uses unmanaged types.
  • Asar now accepts org Label if the label is defined prior to use. (It's still not allowed to jump to a label that's defined later in the patch.)

Bug fixes:

  • If a relative branch is out of bounds, Asar no longer throws "labels keep moving around" errors.
  • Removed the strange size hex value warnings.

v?.??

Release: November 14, 2011

Contributors:

  • Alcaro

Bug fixes:

  • The autoclean warning for the end of a freespace area has been fixed. Again.
  • The freespace finder no longer skips a RATS tag if it's preceeded by an unprotected 00.

v?.??

Release: ?

Contributors:

  • Alcaro

Bug fixes:

  • autoclean no longer complains if it attempts to overwrite something that isn't a valid SNES pointer.

v?.??

Release: November 13, 2011

Contributors:

  • Alcaro

New features:

  • if statements have been made much more powerful. The old action if the statement is false was skipping the rest of the line if the statement is false; the new action is skipping the rest of the line, or if the if statement is at the end of the line, it skips everything until an endif. Nested if statements are allowed, as are else statements. (Note that you can't use else on one-line if statements.)
  • New command: prot. If used directly after a freecode or freedata command (that is, at the start of the RATS tag), it may contain a list of labels (up to 80 or something). The freespace blocks pointed to by these labels are removed if the freespace area is cleared by an autoclean. Example: org $008000 : autoclean JSL Main : freecode : prot Mydata : Main: LDA Mydata : RTL : freedata : Mydata: db $12,$34,$56,$78
  • Labels may be defined as Main(), not just Main:. This has been noted in the documentation.
  • Asar can now be built as a DLL file, but it's not tested very throughly.
  • 'x' = $1234 is now a valid operation. It's equivalent to table file, but it accepts math and it's one file less to keep track of.
  • db 'x'+4 is now a valid operation.
  • freecode and freedata can now expand the ROM if needed.
  • !a = abc : !{a}def is now valid operation; it's equivalent to abcdef. You can nest defines inside the !{}s; a!{b!{c!def}g}h is accepted (assuming all defines exist).

Bug fixes:

  • autoclean now works if called from $008000.
  • autoclean now works if aiming for the last eight bytes of a RATS tag.
  • autoclean now refuses to protect anything that isn't a label, for example math or constants, unless using the two-parameter method.
  • autoclean now refuses to protect a label at the end of a freespace block, since it'll think the RATS tag after that is the one that should be removed.
  • db 1/Label : Label: is now accepted, instead of being treated as a division by zero.
  • ($12),x is an error in xkas, since that addressing mode doesn't exist. However, Asar accepts parentheses and treats it as LDA $12,x, which isn't what the user meant. Therefore, it now emits a warning if this happens.

Deprecated features:

  • !a equ $1234 is no longer valid.

v?.??

Release: November 7, 2011

Contributors:

  • Alcaro

New features:

  • set fastrom is now documented in stdlib.asm.
  • Minor edits to the documentation.
  • An xkas emulation mode was added, which makes Asar act more like xkas 0.06. It should hopefully be 100% compatible with all previously submitted patches/sprites/etc.
  • Asar can now fix the checksum.
  • The command line options have been edited a bit, including a few bugfixes.

Bug fixes:

  • Opcodes that may or may not take an argument (INC, LSR, etc) now work better together with autocolon.
  • "Unknown command" errors have been edited.

Deprecated features:

  • set resizable has been removed.

v?.??

Release: October 25, 2011

Contributors:

  • Alcaro

New features:

  • labelopt is no longer off
  • A lone { or } is now treated as a null command (for use with code folding). Note that they're not allowed elsewhere. They don't need to be matched.

Bug fixes:

  • autoclean no longer kills inappropriate data if pointed to an unused area. Operator precendence screwed up stuff.

v?.??

Release: ?

Contributors:

  • Alcaro

Bug fixes:

  • Asar will no longer place rats tags at any part of the unexpanded area, not even if it's a long list of 00s
  • Fixed a bunch of odd bugs with the automatic size finder and label optimizer, including one that treated LDA.l Label,x as LDA.w Label,x

v?.??

Release: October 23, 2011

Contributors:

  • Alcaro

New features:

  • Blank lines are now allowed in macros.

Bug fixes:

  • Broken or unknown macro arguments no longer abort assembling the rest of the macro.

Deprecated features:

  • Removed the command DIEDIEDIE, a useless command that intentionally froze Asar if it's called from $088000 or later.

v?.??

Release: October 23, 2011

Contributors:

  • Alcaro

Bug fixes:

  • Fixed relative forward branches in freespace too.

v?.??

Release: October 23, 2011

Contributors:

  • Alcaro

New features:

  • How quickly Asar aborts if it finds errors can be set
  • autocolon now accepts db $20, $30
  • autoclean JSL.l is now valid

Bug fixes:

  • Fixed some uninitialized value errors that created garbage error messages if the patch doesn't exist
  • Putting a label in front of an opcode messed up earlier; this has been fixed
  • freecode forgot setting a few variables, which creates a bunch of errors if it's used

v?.??

Release: October 18, 2011

Contributors:

  • Alcaro

Bug fixes:

  • Added a missing else statement on the label optimizer disabling flag that made it turn it on, or get values that change in crappy ways, for everything.

v?.??

Release: October 18, 2011

Contributors:

  • Alcaro

New features:

  • Asar can now assemble SPC700 code, though very little testing was done on this and a lot of bugs may still be included. Also note that mov a,(x)+ has been moved to mov a,(x+), since that opcode increases X and not the value X points to. Two lines in arch-spc700.cpp can be uncommented to reenable the first syntax.
  • The fastrom command has been moved to set fastrom on.
  • Readded the base command.
  • Other small changes.

Bug fixes:

  • The first line of stdlib is no longer ignored (a variable wasn't initialized).
  • Assembling is aborted if errors are detected, instead of continuing. As a side effect from this, errors are no longer printed twice or thrice.

Deprecated features:

  • rep Label has been blocked. It was never supposed to be allowed.

v"0.30 or 0.40 or something"

Release: October 11, 2011

Contributors:

  • Alcaro

New features:

  • LDA $9E,y is now treated as LDA $009E,y, but it prints a warning.
  • New command: set. It can set various options, including expected ROM title (the romtitle command has been removed), if the .l->.w optimizer should be active, and if warnings should be shown.
  • Asar can now automatically (try to) add colons where it thinks they should be. Note that this is disabled by default and not recommended for anything serious.
  • As with the colon adder, it should be avoided unless there is a good reason to use it (for example running it from an IRC bot). (Note that a time limit should be enabled when running it from an IRC bot so that noone can freeze the bot with slow patches).

Bug fixes:

  • Unknown command errors have been made saner. The old method was some sort of debug code.
  • Carriage returns are now ignored on Linux.
  • Assembling blank patches on nonexistent ROMs or ROMs with length zero has been fixed.

v?.??

Release: October 9, 2011

Contributors:

  • Alcaro

New features:

  • !a += +1 has been implemented.

Bug fixes:

  • If you type "freespace", the tool gives a more detailed error message that "Unknown command."
  • A bug related to using pushpc inside a freespace has been fixed.

Notes

  • The tool has been renamed to Asar. Pronounciation: The first A is short, and the second is long (as in "Bazaar", except without the B). It doesn't matter if the S is pronounced as S or Z.

v?.??

Release: October 8, 2011

Contributors:

  • Alcaro

New features:

  • It now looks for and assembles stdlib.asm. However, it prints an error if it doesn't find it.
  • Expected ROM title can now be set, see stdlib.asm for details. It defaults to SUPER MARIOWORLD if omitted, and it may not be set outside of stdlib.asm.
  • A 64-bit version has been included.
  • Both autoclean and autoclear works now. They act identically.

v?.?? (Initial Release)

Release: October 6, 2011

Contributors:

  • Alcaro

New features:

  • Parentheses can be used, which allows some previously impossible statements.
  • Defines can be made longer without risk for crashes. This makes it much easier to implement Hijack Everywhere.
  • An if statement has been included, to get rid of the need for including those rep -1 tricks.
  • Freespace can be set automatically (it even includes a simple way to reclaim freespace used by older versions of the patch)

Notes

  • In this original release, the tool was named "a.as"