Kick Assembler
Kick Assembler
Kick Assembler
Reference Manual
By Mads Nielsen
Table of Contents
1. Introduction .............................................................................................................................. 1
2. Getting Started .......................................................................................................................... 2
2.1. Running the Assembler .................................................................................................... 2
2.2. An Example Interrupt ....................................................................................................... 2
2.3. Configuring the Assembler ................................................................................................ 3
3. Basic Assembler Functionality ..................................................................................................... 4
3.1. Mnemonics ..................................................................................................................... 4
3.2. Argument Types .............................................................................................................. 4
3.3. Number formats .............................................................................................................. 5
3.4. Labels, Arguments Labels and Multi Labels ......................................................................... 6
3.5. Memory Directives .......................................................................................................... 7
3.6. Data Directives ............................................................................................................... 8
3.7. Encoding ........................................................................................................................ 9
3.8. Importing source code .................................................................................................... 10
3.9. Importing data ............................................................................................................... 10
3.10. Comments ................................................................................................................... 11
3.11. Console Output ............................................................................................................ 11
3.12. Breakpoints and watches ............................................................................................... 12
4. Introducing the Script Language ................................................................................................. 14
4.1. Expressions ................................................................................................................... 14
4.2. Variables, Constants and User Defined Labels .................................................................... 14
4.3. Scoping ........................................................................................................................ 15
4.4. Numeric Values ............................................................................................................. 16
4.5. Parentheses ................................................................................................................... 17
4.6. String Values ................................................................................................................ 17
4.7. Char Values .................................................................................................................. 19
4.8. The Math Library .......................................................................................................... 20
5. Branching and Looping ............................................................................................................. 22
5.1. Boolean Values ............................................................................................................. 22
5.2. The .if directive ............................................................................................................. 23
5.3. Question mark if's .......................................................................................................... 23
5.4. The .for directive ........................................................................................................... 24
5.5. The .while directive ........................................................................................................ 24
5.6. Optimization Considerations when using Loops .................................................................. 25
6. Data Structures ........................................................................................................................ 26
6.1. User Defined Structures .................................................................................................. 26
6.2. List Values ................................................................................................................... 27
6.3. Working with Mutable Values ......................................................................................... 28
6.4. Hashtable Values ........................................................................................................... 28
7. Functions and Macros ............................................................................................................... 30
7.1. Functions ...................................................................................................................... 30
7.2. Macros ......................................................................................................................... 30
7.3. Pseudo Commands ......................................................................................................... 31
8. Preprocessor ............................................................................................................................ 34
8.1. Defining preprocessor symbols ......................................................................................... 34
8.2. Deciding what gets included ............................................................................................ 34
8.3. Importing files ............................................................................................................... 35
8.4. List of preprocessor directives .......................................................................................... 35
8.5. Boolean operators .......................................................................................................... 36
9. Scopes and Namespaces ............................................................................................................ 37
9.1. Scopes ......................................................................................................................... 37
9.2. Namespaces .................................................................................................................. 37
9.3. Scoping hierarchy .......................................................................................................... 38
9.4. The Namespace Directives .............................................................................................. 38
9.5. Escaping the current scope or namespace ........................................................................... 39
ii
Kick Assembler Manual
iii
Kick Assembler Manual
iv
Chapter 1
Introduction
Welcome to Kick Assembler, an advanced MOS 65xx assembler combined with a Java Script like script lan-
guage.
The assembler has all the features you would expect of a modern assembler like macros, illegal and DTV op-
codes and commands for unrolling loops. It also has features like pseudo commands, import of SID files, import
of standard graphic formats and support for 3rd party Java plugins. The script language makes it easy to gener-
ate data for your programs. This could be data such as sine waves, coordinates for a vector object, or graphic
converters. Writing small data generating programs directly in you assembler source code is much handier than
writing them in external languages like Java or C++.The script language and the assembler is integrated. Unlike
other solutions, where scripts are prepassed, the script code and the assembler directives works together giving
a more complete solution.
As seen by the size of this manual, Kick Assembler has a lot of functionality. You don't need to know it all to
use the assembler, and getting to know all the features may take some time. If you are new to Kick Assembler, a
good way to start is to read Chapter 2, Getting Started, Chapter 3, Basic Assembler Functionality and Chapter 4,
Introducing the Script Language and then supplement with the features you need. Also notice the quick reference
appendix which contains lists of directives, options and values.
This is the fifth version of Kick Assembler. The first version (1.x) was a normal 6510 cross assembler developed
around 2003 and was never made public. The second version (2.x) was developed in 2006 and combined the
assembler with a script language, giving you the opportunity to write programs that generate data for the assembler
code. Finally in august 2006 the project went public. The third version (3.x) improved the underlying assembling
mechanism using a flexible pass algorithm, recording of side effects and handling of invalid values. This gave
better performance, and made it possible make more advanced feature. The fourth version (4.x) replaced the
parsing mechanism, which where made using a parser generator, with a handwritten one which is faster, more
flexible and included a preprocessor. This made it possible to do new language constructs and have better error
handling. It also replaced the scoping system so it includes all entities, not just symbols. The fifth version (5.x)
added segments which give the opportunity to manage the output of directives and channel it to files, disk images
and other segments.
Through the years the project have grown quite big, with a professional setup including a its own code reposi-
tory, a large automated test suite and automatic building and deploying.
A lot of people have contributed with valuable comments and suggestions by mail and on CSDB. Thanks guys.
Your feedback is greatly appreciated. Also thanks to Gerwin Klein for doing JFlex (the lexical analyser used for
this assembler); Scott Hudson, Frank Flannery and C. Scott Ananian for doing CUP (The parser generator). And
finally, Thanks to XMLMind for sponsoring the project with a pro version of their XML editor in which this
manual is written.
I would like to hear from people that use Kick Assembler so do not hesitate to write your comments to
kickassembler@no.spam.theweb.dk (<- Remove no.spam. for real address).
I wish you happy coding..
1
Chapter 2
Getting Started
This chapter is written to quickly get you started using Kick Assembler. The details of the assembler's func-
tionalities will be presented later.
java –version
Java will now display the Java version if it's correctly installed.
BasicUpstart2(start)
//----------------------------------------------------------
//----------------------------------------------------------
// Simple IRQ
//----------------------------------------------------------
//----------------------------------------------------------
* = $4000 “Main Program”
start: lda #$00
sta $d020
sta $d021
lda #$00
jsr $1000 // init music
sei
lda #<irq1
sta $0314
lda #>irq1
sta $0315
lda #$7f
sta $dc0d
sta $dd0d
lda #$81
sta $d01a
lda #$1b
sta $d011
lda #$80
sta $d012
lda $dc0d
lda $dd0d
2
Getting Started
asl $d019
cli
jmp *
//----------------------------------------------------------
irq1: asl $d019
SetBorderColor(2)
jsr $1003 // play music
SetBorderColor(0)
jmp $ea81
//----------------------------------------------------------
*=$1000 “Music”
.import binary “ode to 64.bin”
//----------------------------------------------------------
// A little macro
.macro SetBorderColor(color) {
lda #color
sta $d020
}
By placing a file called KickAss.cfg in the same folder as the KickAss.jar, you can set command line options
that are used at every assembling. Lets say you always wants to have shown a memorymap after assembling and
then have the result executed in the C64 emulator VICE. Then you write the following in the KickAss.cfg file:
-showmem
-execute “c:/c64/winvice/x64.exe –confirmexit”
# This is a comment
(Replace c:/c64/winvice/ with a path that points to the vicefolder on your machine)
All lines starting with # are treated as comments.
3
Chapter 3
Basic Assembler Functionality
This chapter describes the mnemonics and the basic directives that are not related to the script language.
3.1. Mnemonics
In Kick Assembler you can write assembler mnemonics the traditional way:
lda #0
sta $d020
sta $d021
If you want to write several commands on one line then separate them with ; like this:
Kick Assembler supports different sets of opcodes. The default set includes the standard 6502 mnemonics plus
the illegal opcodes. To switch between instruction sets of different cpu's use the .cpu directive: The following will
switch to the 65c02 instruction set:
.cpu _65c02
A complete listing of the CPU instructions and their opcodes can be found in the Quick Reference Appendix.
4
Basic Assembler Functionality
Mode Example
Zeropage,y ldx $30,y
Indirect zeropage,x lda ($30,x)
Indirect zeropage,y lda ($30),y
Abolute lda $1000
Absolute,x lda $1000,x
Absolute,y lda $1000,y
Indirect jmp ($1000)
Relative to program counter bne loop
Indirect zeropage (65c02 only) adc ($12)
Zeropage, Relative (65c02 only) bbr1 $12,label
indirect,x (65c02 only) jmp ($1234,x)
An argument is converted to its zeropage mode if possible. This means that lda $0030 will generate an lda
command in its zeropage mode1
You can force the assembler to use the absolute form of the mnemonic by appending .a or .abs. The same way
you can tell the assembler to use zeropage mode when it would otherwise use an absolute mode.
With the following extensions you can force specific modes. The are deprecated and only kept for backward
compatibility:
5
Basic Assembler Functionality
You can put labels in front of mnemonic arguments. This can be useful when creating self modifying code:
stx tmpX
...
ldx tmpX:#$00
Kick Assembler also supports multi labels, which are labels that can be declared more than once. These are
useful to prevent name conflicts between labels. A multi label starts with a ‘!’ and when your reference it you have
to end with a ‘+’ to refer to the next multi label or ‘-‘ to refer to the previous multi label:
ldx #100
!loop: inc $d020
dex
bne !loop- // Jumps to the previous instance of !loop
ldx #100
!loop: inc $d021
dex
bne !loop- // Jumps to the previous instance of !loop
or
ldx #10
!loop:
jmp !+ // Jumps over the two next nops to the ! label
nop
nop
!: jmp !+ // Jumps over the two next nops to the ! label
nop
nop
!:
dex
bne !loop- // Jumps to the previous !loop label
Applying more than one '+' or '-' will skip labels. E.g. '+++' will jump to the third label:
Another way to avoid conflicting variables is to use user defined scopes, which are explained in the scoping
section of Chapter 4, Introducing the Script Language.
6
Basic Assembler Functionality
A ‘*’ returns the value of the current memory location so instead of using labels you can write your jumps
like this:
inc $d020
inc $d021
jmp *-6
When referencing a label that is not yet resolved, the assembler will assume a two byte address, even though it
later is found to be in the zeropage. You can mark labels as being in the zeropage with the .zp directive:
// Uses zeropage form of lda and sta eventhough the labels is first
// resolved later
lda zpReg1
sta zpReg2
*=$10 virtual
.zp {
zpReg1: .byte 0
zpReg2: .byte 0
}
Note: Currently the .zp directive doesn't handle macros and pseudocommands called within the {}. Labels
inside these will be in the form defined in the macro.
*=$1000 "Program"
ldx #10
!loop: dex
bne !loop-
rts
*=$4000 "Data"
.byte 1,0,2,0,3,0,4,0
Note: The old notation ('.pc=$1000') from Kick Assembler 2.x and 3.x is still supported.
The last argument is optional and is used to name the memory block created by the directive. When using the
‘-showmem’ option when running the assembler a memory map will be generated that displays the memory usage
and block names. The map of the above program looks like this:
Memory Map
----------
$1000-$1005 Program
$4000-$4007 Data
$5000-$5004 More data
7
Basic Assembler Functionality
By using the virtual option on the .pc directive you can declare a memory block that is not saved in the resulting
file.
*=$1000 "Program"
ldx #0
lda table1,x
…
Note that virtual memory blocks can overlap other memory blocks. They are marked with an asterisk in the
memory map.
Memory Map
----------
*$0400-$05ff Data Tables 1
*$0400-$064f Data Tables 2
$1000-$1005 Program
Since virtual memory blocks aren’t saved, the above example will only save the memory from $1000 to $1005.
With the .align directive, you can align the program counter to a given interval. This is useful for optimizing
your code as crossing a memory page boundary yields a penalty of one cycle for memory referring commands.
To avoid this, use the .align command to align your tables:
*=$1000 "Program"
ldx #1
lda data,x
rts
In case you want your code placed at position $1000 in the memory but want it assembled like it was placed
at $2000, you can use the .pseudopc directive:
You can use .by, .wo and .dw as aliases for .byte, .word and .dword, so '.by $10' is the same as '.byte $10'.
With the .fill directive you can fill a section of the memory with bytes. It works like a loop and automatically
sets the variable i to the iteration number.
8
Basic Assembler Functionality
// Nomal filling
.fill 5, 0 // Generates byte 0,0,0,0,0
.fill 5, i // Generates byte 0,1,2,3,4
.fill 256, 127.5 + 127.5*sin(toRadians(i*360/256)) // Generates a sine curve
In most cases it is more desirable to have two lists, one with low byte and one with high byte, than a word list.
To generate this you can use the .lohifill directive. It generates the two list right after each each other and lets your
access them using a hi/lo field on a connected label like this:
Generating bytes using the fill directive will compile faster than generating byte using the .for and .byte direc-
tives. (The .for directive will be explained later.)
3.7. Encoding
The .text directive outputs bytes to the memory that represents the given textstring. The default encoding is
'screencode_mixed', which maps to the screencode representations of the charset with both uppercase and lower-
case letters. To change the encoding, use the .encoding directive:
.encoding "screencode_mixed"
.text "In this ENCODING we have both UPPER and lower case chars."
.text "Remember to swith to a charset that fits the encoding."
The encoding affects every operation that converts characters in the sourcecode to byte values, for instance the
'.import text' directive is also affected.
The supported encodings are:
9
Basic Assembler Functionality
Note that preprocessor commands starts with #. Refer to the chapter on the preprocessor for a detailed descrip-
tion.
When Kick Assembler searches for a file, it first look in the current directory. Afterwards it looks in the direc-
tories supplied by the ‘-libdir’ parameter when running the assembler. This enables you to create standard libraries
for files you use in several different sources. A command line could look like this:
If you build source code libraries you might want to ensure that the library is only included once in your code.
This can be done by placing a #importonce directive in the top of the library file:
File1.asm:
#importonce
.print "This will only be printed once!"
File2.asm:
#import "File1.asm" // This will import File1
#import "File1.asm" // This will not import anything
NOTE! The v3.x directives for importing source files using the import directive (.import source "myfile.asm"
and .importonce), not the preprocessor, is still supported. But its recommended to use the preprocessor directives,
since they will give a more natural order of evaluation. Using the preprocessor will import the source at once while
using the old import directive will first parse the entire file, and then import external files during evaluation.
10
Basic Assembler Functionality
The binary, c64 and text import takes an offset and a length as optional parameters:
// import the bytes from the file 'music.bin', but skip the first 100 bytes
.import binary "Music.bin", 100
// Imports $200 bytes starting from position $402 (the two extra bytes is because
its a c64 file)
.import c64 "charset.c64", $400, $200
As when importing sources files, the import directive also searches the folders given by the –libdir option when
looking for a file.
3.10. Comments
Comments are pieces of the program that are ignored by the assembler. Kick Assembler supports line and block
comments known from languages such as C++ and Java. When the assembler sees ‘//’ it ignores the rest of that
line. C block comments ignores everything between /* and */.
/*----------------------------------------------------------
This little program is made to demonstrate comments
------------------------------------------------------------*/
lda #10
sta $d020 // This is also a comment
sta /* Comments can be placed anywhere */ $d021
rts
Traditional 65xx assembler line comments (;) are not supported since the semicolon is used in for-loops in the
script language.
parsing
flex pass 1
Output pass
Hello world
x=2.0
Notice that the output is given during the output pass. You can also print the output immediately with the .print-
now command. This is useful for debugging script where errors prevent the execution of the output pass. The .print-
now command will print the output in each pass, and in some passes the output might be incomplete due to lack
of information. In the following example we print a label that isn't resolved in the first pass:
*=$1000
loop: jmp loop
11
Basic Assembler Functionality
parsing
flex pass 1
loop=$<<Invalid String>>
flex pass 2
loop=$1000
Output pass
If you detect an error while assembling, you can use the .error directive to terminate the assembling and display
an error message:
.var width = 45
.if (width>40) .error "width can’t be higher than 40"
Another way of writing this is to use the .errorif directive that takes a boolean expression and a message text.
An error is raised if the boolean expression is evaluated to true:
.var width = 45
.errorif with>40, "width can’t be higher than 40"
This is more flexible since it standard .if's has to be decided in the first pass which will give an (unwanted)
error if you for example compare not yet resolved labels. If you for instance want to check for a page boundary
crossing you can do like this:
beq label1
.errorif (>*) != (>label1), "Page crossed!"
nop
nop
label1:
// Example 1
ldy #10
loop:
.break // This will put a breakpoint on 'inc $d020'
inc $d020
dey
.break "if y<5" // This will add a string as argument for the breakpoint
bne loop
// Example 2
lda #10
.break // Will place a breakpoint at the first nop in the macro
MyMacro()
.macro MyMacro() {
nop
nop
nop
}
The .break directive puts a breakpoint on the current memory position. As seen in the second breakpoint, you
can add an argument to a breakpoint. The syntax of this is dependant on the consumer. The above case (.break "if
y<5") is written for VICE's conditional expressions. VICE will then break if the y register is below 5.
12
Basic Assembler Functionality
First argument is the address. If second argument is given its the range between the two. Third argument is
an optional text string with additional information. Consult your emulater/debugger manual for possible content
of third argument.
13
Chapter 4
Introducing the Script Language
In this chapter the basics of the script language is introduced. We will focus on how Kick Assembler evaluates
expressions, the standard values and libraries. Later chapters will deal with more advanced areas.
4.1. Expressions
Kick assembler has a built in mechanism for evaluating expressions. An example of an expression is 25+2*3/
x. Expressions can be used in many different contexts, for example to calculate the value of a variable or to define
a byte:
lda #25+2*3/x
.byte 25+2*3/x
Standard assemblers can only calculate expressions based on numbers, while Kick Assembler can evaluate
expressions based on many different types like: Numbers, Booleans, Strings, Lists, Vectors, and Matrixes. So, if
you want to calculate an argument based on the second value in a list you write:
Or perhaps you want to generate your argument based on the x-coordinate of a vector:
lda #35+myVector.getX()
lda #35+myVectorList.get(2).getX()
I think you get the idea by now. Kick Assembler's evaluation mechanism is much like those in modern pro-
gramming languages. It has a kind of object oriented approach, so calling a function on a value(/object) executes
a function specially connected to the value. Operators like +, -,*, /, ==, !=, etc., are seen as functions and are also
specially defined for each type of value.
In the following chapters, a detailed description of how to use the value types and functions in Kick Assembler
will be presented.
.var x=25
lda #x // Gives lda #25
.eval x=x+10
lda #x // Gives lda #35
This will increase x by 10. The .eval directive is used to make Kick Assembler evaluate expressions. In fact,
the .var directive above is just a convenient shorthand of ‘.eval var x =25’, where ‘var’ is subexpression that
declares a variable (this will come in handy later when we want to define variables in for-loops).
Other shorthands exist. The operators ++, --, +=, -=, *= and /= will automatically call a referenced variable
with +1,-1, +y, -y, *y and /y. For example:
14
Introducing the Script Language
.var x = 0
.eval x++ // Gives x=x+1
.eval x-- // Gives x=x-1
.eval x+=3 // Gives x=x+3
.eval x-=7 // Gives x=x-7
.eval x*=3 // Gives x=x*3
.eval x/=2 // Gives x=x/2
Experienced users of modern programming languages will know that assignments return a value, e.g. x = y =
z = 25 first assigns 25 to z, which returns 25 that is assigned to y, which returns 25 that is assigned to x. Kick
Assembler supports this as well. Notice that the ++ and -- works as real ++ and –- postfix operators, which means
that they return the original value and not the new (Ex: .eval x=0 .eval y=x++, will set x to 1 and y to 0)
You can also declare constants:
A constant can't be assigned a new value, so .eval pi=22 will generate an error. Note that not all values are
immutable. If you define a constant that points to a list, the content of the list can still change. If you want to make
a mutable value immutable, you can use its lock() function, which will lock it's content:
After this you will get an error if you try to add an element or modify existing elements.
With the .enum statement you can define enumerations, which are series of constants:
Variables and constants can only be seen after they are declared while labels can be seen in the entire scope.
You can define a label with the .label directive like you define variables and constants:
// This fails
inc myLabel1
.const myLabel1 = $d020
// This is ok
inc myLabel2
.label myLabel2 = $d020
4.3. Scoping
You can limit the scope of your variables and labels by defining a user defined scope. This is done by {..}.
Everything between the brackets is defined in a local scope and can't be seen from the outside.
Function1: {
.var length = 10
ldx #0
lda #0
loop: sta table1,x
inx
cpx #length
bne loop
}
Function2: {
.var length = 20 // doesn’t collide with the previous ‘length’
15
Introducing the Script Language
ldx #0
lda #0
loop: sta table2,x // the label doesn’t collide with the previous ‘loop’
inx
cpx #length
bne loop
}
Scopes can be nested as many times as you wish as demonstrated by the following program:
.var x = 10
{
.var x=20
{
.print "X in 2nd level scope read from 3rd level scope is " + x
.var x=30
.print "X in 3rd level scope is " + x
}
.print "X in 2nd level scope is " + x
}
.print "X in first level scope is " + x
25+3
5+2.5*3-10/2
charmem + y * $100
You can also use bitwise operators to perform and, or, exclusive or, and bit shifting operations.
.var x=$12345678
.word x & $00ff, [x>>16] & $00ff // gives .word $0078, $0034
Special for 65xx assemblers are the high and low-byte operators (>,<) that are typically used like this:
16
Introducing the Script Language
You can get the number representation of an arbitrary value by using the general .number() function. Eg.
.print ‘x’.number()
4.5. Parentheses
You can use both soft parentheses () and har parentheses [] to tell the order of evaluation.
Note that 65xx assemblers use soft parenthesis to signal an indirect addressing mode:
You can nest as many parentheses as you want, so (([((2+4))])*3)+25.5 is a legal expression.
17
Introducing the Script Language
// Plain strings
.var message = "Hello World"
.text message // Gives .text "Hello world"
.const file="c:\newstuff"
.text @"This text will loop now\$ff" // placing hex values ($ff) in the
text
@ in front of a string means you can use escape characters. Notice how '\n' in "c:\newstuff" is not a newline
while '\n' in @"First line.\nSecond line." is. (Note: This is the opposite of C# and is this way to avoid breaking
file references in old sources).
The supported escape codes are:
Every object has a string representation and you can concatenate strings with the + operator. For example:
.var x=25
.var myString= “X is “ + x // Gives myString = "X is 25"
You can use the .print directive to print a string to the console while assembling. This is useful when debugging.
Printing x and y can be done like this:
.print "x="+x
.print "y="+y
You can also print labels to see which location they refer to. If you do this, it's best to convert the label value
to hexadecimal notation first:
.print “int1=$”+toHexString(int1)
18
Introducing the Script Language
Here are the functions that take a number value and convert it to a string:
You can get the string representation of an arbitrary value by using the general .string() function. Eg.
19
Introducing the Script Language
lda #'H'
sta $0400
lda #'i'
sta $0401
lda #"?!#".charAt(1)
sta $0402
In the above example, chars are used in two ways. In the first examples their numeric representation are used
as arguments to the lda commands and in the final example, '!'s string representation is appended to the "World"
string.
Char values is a subclass of number values, which means that it has all the functions that are placed on the
number values, so you can do stuff like.
20
Introducing the Script Language
Function Description
floor(x) Rounds down to the nearest integer.
hypot(a,b) Returns sqrt(x2+y2).
IEEEremainder(x,y) Returns the remainder of the two numbers as described
in the IEEE 754 standard.
log(x) Returns the natural logarithm of x.
log10(x) Returns the base 10 logarithm of x.
log1p(x) Returns log(x+1).
max(x,y) Returns the highest number of x and y.
min(x,y) Returns the smallest number of x and y.
mod(a,b) Converts a and b to integers and returns the remainder
of a/b.
pow(x,y) Returns x raised to the power of y.
random() Returns a random number x where 0 ≤ x < 1.
round(x) Rounds x to the nearest integer.
signum(x) Returns 1 if x>0, -1 if x<0 and 0 if x=0.
sin(r) Returns the sine of r.
sinh(x) Returns the hyperbolic sine of x.
sqrt(x) Returns the square root of x.
tan(r) Returns the tangent of r.
tanh(x) Returns the hyperbolic tangent of x.
toDegrees(r) Converts a radian angle to degrees.
toRadians(d) Converts a degree angle to radians.
21
Chapter 5
Branching and Looping
Kick Assembler has control directives that let you put conditions on when a directive is executed and how
many time it is executed. These are explained in this chapter.
All the operators are defined for numeric values, other values have defined a subset of the above. E.g. you can't
say that one boolean is greater than another, but you can see if they have the same values:
Boolean values have a set of operators assigned. These are the following:
22
Branching and Looping
Like in languages like C++ or Java, the && and || operators are short circuited. This means that if the first
argument of an && operator is false, then the second argument won't be evaluated since the result can only be
false. The same happens if the first argument of an || operator is true.
You can group several statements together in a block with {…} and have them executed together if the boolean
expression is true:
By adding an else statement you can have an expression executed if the boolean expression is false:
23
Branching and Looping
.var debug=true
inc debug ? $d020:$d013 // Increases $d020 since debug=true
Since argument 1 and 3 are lists, you can leave them out, or you can write several expressions separated by
comma:
// Sum the numbers from 0 to 9 and print the sum at each step
.for(var i=0, var sum=0;i<10;sum=sum+i,i++)
.print “The sum at step “ + I “ is “ + sum
With the for loop you can quickly generate tables and unroll loops. You can, for example, do a classic ‘blitter
fill’ routine like this:
.var blitterBuffer=$3000
.var charset=$3800
.for (x=0;x<16;x++) {
.for(var y=0;y<128;y++) {
if (var y=0) lda blitterBuffer+x*128+y
else eor blitterBuffer+x*128+y
sta charset+x*128+y
}
}
24
Branching and Looping
25
Chapter 6
Data Structures
In the chapter, we will examine user defined data and predefined structures.
// Create a point with the default contructor and modify its arguments
.var p2 = Point()
.eval p2.x =3
.eval p2.y =4
You define a structure with the .struct directive. The above structure has the name ‘Point’ and consists of the
variables x and y. To create an instance of the structure, you use its name as a function. You can either supply
no arguments or give the init values of all the variables. You use the values generated by structures as any other
variables, ex:
lda #0
ldy #p1.y
sta charset+(p1.x>>3)*height,y
You can get access to informations about the struct and access the fields in a more generic way by using the
struct’s functions:
.struct Person{firstName,lastName}
.var p1 = Person(“Peter”,”Schmeichel”)
26
Data Structures
You can determine the number of elements in a list with the size-function and the add-function adds additional
elements.
27
Data Structures
// The define directive locks the defined variables outside its scope
.define list2, list3 {
.var list2 = List().add(1,2)
The .define directive defines the symbols that are listed after the .define keyword (list2 and list3). The directives
inside {…} are executed in a new scope so any local defined variables can't be seen from the outside. After
executing the inner directives, the defined values are locked and inserted as constants in the outside scope.
The inner directives are executed in 'function mode', which is a bit faster and requires less memory than ordinary
execution. So if you are using for loops to do some heavy calculations, you can optimize performance by placing
your loop inside a define directive. As the name 'function mode' suggests, directives placed inside functions are also
executed in ‘function mode’. In ‘function mode’ you can only use script directives (like .var, .const, .eval, .enum,
etc) while byte output generating directives (like lda #10, byte $22, .word $33, .fill 10, 0) are not allowed.
.define ht {
// Define the table
.var ht = Hashtable()
28
Data Structures
When a value is used as a key then it is the values string representation that is used. This means that ht.get(“1.0”)
and ht.get(1) returns the same element. If you try to get an element that isn't present in the table, null is returned.
29
Chapter 7
Functions and Macros
This chapter shows how to group directives together in units for later execution. In other words, how to define
and use functions, macros and finally pseudo commands which are a special kind of macros.
7.1. Functions
You can define you own functions which you can use like any of the build in library functions. Here is an
example of a function:
.function area(width,height) {
.return width*height
}
.var x = area(3,2)
lda #10+area(4,8)
Functions consist of non-byte generating directives like .eval, .for, .var, and .if. When the assembler evaluates
the .return directive it returns the value given by the proceeding expression. If no expression is given, or if no .return
directive is reached, a null value is returned. Here are some more examples of functions:
You can have several functions of the same name, as long as they have different number of arguments. So
this is valid code:
7.2. Macros
Macros are collections of assembler directives. When called, they generate code as if the directives where
placed at the macro call. The following code defines and executes the macro ‘SetColor’:
// Define macro
.macro SetColor(color) {
lda #color
sta $d020
}
30
Functions and Macros
// Execute macro
:SetColor(1)
SetColor(2) // The colon in front of macro calls is optional from version 4.0
A macro can have any number of arguments. Macro calls are encapsulated in a scope, hence any variable
defined inside a macro can't be seen from directly the outside. This means that a series of macro calls to the same
macro doesn't interfere:
// Execute macro
ClearScreen($0400,$20) // Since they are encapsulated in a scope
ClearScreen($4400,$20) // the two resulting loop labels don’t
// interfere
// Define macro
.macro ClearScreen(screen,clearByte) {
lda #clearByte
ldx #0
Loop: // The loop label can’t be seen from the outside
sta screen,x
sta screen+$100,x
sta screen+$200,x
sta screen+$300,x
inx
bne Loop
}
If you need to access the labels of a macro execution, you can do that by adding a label in front of the execution
(See the chapter on scopes and namespaces)
Notice that it is ok to use the macro before it is declared.
Macros in Kick Assembler are a little more flexible than ordinary macros. They can call other macros or even
call themselves - Just make sure there is a condition to stop the recursion so you won't get an endless loop.
The arguments to a pseudo command are separated by colon and you can use any argument you would give
to a mnemonic.
Note: In version 3.x, arguments where separated by semicolon. To make old code compile use the -pseudoc3x
commandline option or convert the code with the 3.x to 4.x converter.
You can add an optional colon in front of the pseudocommand calls. This enables you to call a command with
the same name as a mnemonic.
31
Functions and Macros
The command arguments are passed to the pseudo command as CmdValues. These are values that contain an
argument type and a number value. You access these by their getter functions. Here is a table of the functions:
Some addressing modes, like absolute zeropage and relative, are missing from the above table. This is because
the assembler automatically detect when these should be used from the corresponding absolute mode.
You can construct new command arguments with the CmdArgument function. If you want to construct a new
immediate argument with the value 100, you do it like this:
Now let’s use the above functionalities to define a 16 bit instruction set. We start by defining a function that
given the first argument will return the next in a 16 bit instruction.
.function _16bitnextArgument(arg) {
.if (arg.getType()==AT_IMMEDIATE)
.return CmdArgument(arg.getType(),>arg.getValue())
.return CmdArgument(arg.getType(),arg.getValue()+1)
}
We always return an argument of the same type as the original. If it's an immediate argument we set the value to
be the high byte of the original value, otherwise we just increment it by 1. This will supply the correct argument for
the ABSOLUTE, ABSOLUTEX, ABSOLUTEY and IMMEDIATE addressing modes. With this we can easily
define some 16 bits commands:
32
Functions and Macros
inc16 counter
mov16 #irq1 : $0314
mov16 #startAddress : $30
add16 $30 : #128
add16 $30 : #$1000: $32
Note how the target argument of the add16 command can be left out. When this is the case an argument with
type AT_NONE is passed to the pseudo command and the first argument is then used as target.
With the pseudo command directive you can define your own extended instruction libraries, which can speed
up some of the more trivial tasks of programming.
33
Chapter 8
Preprocessor
Before the contents of the source file is handed to the main parser, it goes through the preprocessor. The pre-
processor knows nothing of mnemonics or the script language. It's a simple mechanism that enables you to select
pieces of the source to be discarded or included in what the main parser sees. This chapter explains how. (NOTE:
The preprocessor is made like the one used in C# with the addition of #import, #importif and #importonce so you
might find this familiar)
#define TEST
You can recognize a preprocessor directive on the '#'. If the first non-whitespace character on a line is a '#' then
the line is a call to the preprocessor. If you want to remove the definition of a symbol you use the #undef directive.
#undef TEST
// Simple if block
#if TEST
inc $d020
#endif // <- Use an endif to close this if block
Since the source isn't passed on to the main parser, you can write anything inside an untaken if, and it will
still compile.
#undef UNDEFINED_SYMBOL
#if UNDEFINED_SYMBOL
Here we can write anything since it will never be seen by the main parser...
#endif
#elif is the combination of an #else and an #if. It can be used like this:
#if X
.print "X"
#elif Y
.print "Y"
34
Preprocessor
#elif Z
.print "Z"
#else
.print "Not X, Y and Z"
#endif
#if A
#if B
.print "A and B"
#endif
#else
#if X
.print "not A and X"
#elif Y
.print "not A and Y"
#endif
#endif
The indentations doesn't change anything, its just to make the code easier to read.
#import "MyLibrary.asm"
To ensure that a file (e.g. a library) is only imported once, place an #importonce inside the imported file
File1.asm:
#importonce
.print "This will only be printed once!"
File2.asm:
#import "File1.asm" // This will import File1
#import "File1.asm" // This will not import anything
35
Preprocessor
Directive Description
#endif Ends an #if or #else block.
#else Creates an else block.
#elif EXPR The combination of an #else and an #if directiveB
36
Chapter 9
Scopes and Namespaces
Scopes and namespaces are use to avoid entities like symbols and functions in different parts of the program
to collide with each other. This section will cover how they works.
9.1. Scopes
Scopes are containers of symbols (variables, constants and labels). There can only be one symbol of each name
in a scope. Scopes are automatically in many situations. For example, a scope is set up when you execute a macro.
This prevent the internal labels to collide if you execute the macro twice.
The easiest way to define a scope yourself is using brackets.
.var x = 1
{
.var x = 2 // <- this x won't collide with the previous
}
9.2. Namespaces
Namespaces are containers of functions, macros and pseudocommands. There can only be one of each of these
entities in namespace. Every namespace also have an its own associated scope so each time you define a namespace
a scopes is automatically defined.
A simple way to declare a namespace is shown in the following example. The namespace directives is covered
in more detail later (and often the .filenamespace directive is more handy):
Namespace can be declared more than once. The second time you declare it, it will simply continue with the
already existing namespace.
.namespace repeatedSpace {
endless: jmp *
.function myFunc() { return 1}
}
If you are in doubt of which namespace you are in, you can get its name by the 'getNamespace()' function.
37
Scopes and Namespaces
Namespace = <RootNS>
Namespace = MySpace
Namespace = MySpace.MySubSpace
1. System namespace & scope - Contains system mnemonics, constants, functions, macros and pseudocom-
mands.
4. User defined scopes - Created by macros, functions, for-loops, brackets {}, etc.
Lets look at an simple example. It contains some scopes and some nonsense code :
*=$1000
start:
loop: //<-- 'loop' defined in the root scope
The above code will form the scope hierarchy: System scope <- Root Scope <- BracketScope1 <- BracketS-
cope2.
When Kick Assembler resolves a symbol, it checks if it is present in the the current scope. If it can't be found it
looks in the parent scope. If it still can't be found it looks in the parent scope of the parent and so forth. In the above
example, the 'jmp loop' is placed in BracketScope2, so 'loop' is resolved to the loop symbol in BracketScope2. But
'start' is not defined in BracketScope2 or BracketScope1 so it will be resolved to the label in the root scope.
Since no namespaces are defined in the above, the namespace hierarchy is: System namespace <- Root Name-
space. The entities of namespaces is resolved similar to the scope resolving mechanism explained above.
.namespace vic {
.label borderColor = $d020
.label backgroundColor0 = $d021
.label backgroundColor1 = $d022
38
Scopes and Namespaces
lda #0
sta vic.backgroundColor0
sta vic.borderColor
Namespaces are normally used to make sure that code in a source file (Like a library) is not colliding with
other parts of the code. For this, Place the filenamespace directive at the top of the file and everything after that
is placed in the desired namespace:
/* FILE 0 */
jsr part1.init
jsr part1.exec
jsr part2.init
jsr part2.exec
rts
/* FILE 1 */
.filenamespace part1
init:
...
rts
exec:
...
rts
/* FILE 2 */
.filenamespace part2
init:
...
rts
exec:
...
rts
.label myLabel = 1
{
.label myLabel = 2
The same can be done for functions, macros and pseudo commands. So the following example will print 'root'
not 'mySpace':
39
Scopes and Namespaces
You can also put new entities in the root scope when defining them from within another scope:
jsr outside_label
rts
{
@outside_label:
lda #0
sta $d020
sta $d020
rts
}
or:
{
.label @x = 1234
.var @y= "Hello world"
.const @z= true
}
.print "x="+x
.print "y="+y
.print "z="+z
#import "mylib.lib"
.print myFunction()
MyMacro()
MyPseudoCommand
/* File mylib.lib */
#importonce
.filenamespace MyLibrary
.function @myFunction() {
.return 1
}
.macro @MyMacro() {
.print "Macro Called"
}
.macro @MyPseudoCommand {
.print "PseudoCommand Called"
}
lda #’ ‘
sta clearScreen.fillbyte+1
jsr clearScreen
rts
clearScreen: {
fillbyte: lda #0
40
Scopes and Namespaces
ldx #0
loop:
sta $0400,x
sta $0500,x
sta $0600,x
sta $0700,x
inx
bne loop
rts
}
The above code fills the screen with black spaces. The code that calls the clearScreen subroutine use
clearScreen.fillbyte to access the fillbyte label. If you use the label directive to define the fillbyte label, the code
can be done a little nicer:
lda #’a’
sta clearScreen2.fillbyte
jsr clearScreen2
rts
ClearScreen2: {
.label fillbyte = *+1
lda #0
ldx #0
loop:
sta $0400,x
sta $0500,x
sta $0600,x
sta $0700,x
inx
bne loop
rts
}
Now you don't have to remember to add one to the address before storing the fill byte.
Label scopes also works with the label directive, so its also possible to write programs like this:
*=$1000
start: inc c1.color
dec c2.color
c1: :setColor()
c2: :setColor()
jmp start
.macro setColor() {
.label color = *+1
lda #0
sta $d020
}
41
Scopes and Namespaces
jmp myIf.label
42
Chapter 10
Segments
10.1. Introduction
Segments are lists of memory blocks which are used to organize your code. You can use them to define the
order which things are placed in memory (data after code etc). Your can combine segments to form new segments
and you can use modifiers to process the output of a segment. Finally, you can direct the output of a segment to
a files or disks or simply throw it away.
This is implemented in Kick Assembler in a backward compatible way, so if you don't use segments, everything
is placed on a default segment and directed to the standard output file as you are used to.
If you want to patch a file you can load the file into a Base segment, put a Patch segment on top of it with the
modifications and write the result to a file. Since the Patch is on top it will overwrite the base:
Segments can also be used for outputting code in alternative formats. Here is an example writing code for a
cartridge with 4 banks:
.segment BANK1
..code for segment 1 goes here...
.segment BANK2
..code for segment 2 goes here...
.segment BANK3
..code for segment 3 goes here...
43
Segments
.segment BANK4
..code for segment 4 goes here...
A segment is set up for each bank and they are output in the right order to a binary file. The code in the 4
segments is restricted to the address space $1000-$1fff. Notice how the same address space can be used multiple
times, since the code resides in different segments.
10.3. Segments
In Kick Assembler, a segment is a list of memory blocks, so let's look at these first.
A memory block is generated each time you use the *= directive. It has a start, an optional name and might
be marked as virtual. If you add code without defining a memory block first, a default block is created for you.
Here are examples of memory blocks.
A segment is a list of memory blocks. Since you haven't selected any segment in the above code, they are all
placed on the 'Default' segment.
A segment is defined by the .segmentdef directive and you use the .segment directive to decide which segment
to add code to:
In the above code MySegment1 is defined used the default parameters for a segment. While MySegment2 is
defined setting the start address for the default memory block to $1000. A complete list of parameters is given
in the end of this chapter.
Notice that you can switch back to a segment at any time and continue adding code to its current memory block.
44
Segments
Sometimes, it's convenient to define a memory block and switch to it with the same command. This is done by
adding a parameters block ([...]) to the segment directive.
// This:
.segment MySegment [start=$1000]
A segment can only by be defined once so the above will give produce an error saying that 'MySegment' is
double defined.
The simplest way of getting the code to a program file is to specify a 'outPrg' parameter:
*=$1000
inc $d020
jmp *-3
If you use the 'outBin' parameter instead a binary file will be output. In the output chapter you can see more
options for outputting segments to files or disks images.
.segment Default
45
Segments
// This
.segment Code "My Code"
To demonstrate this style is here given a larger example. Some of the features are first covered later. :
//--------------------------------------------------------
// Main
//--------------------------------------------------------
.segment Code "Main"
jsr colorSetup
jsr textSetup
rts
//--------------------------------------------------------
// Color
//--------------------------------------------------------
.segment Code "Color Setup"
colorSetup:
lda colors
sta $d020
lda colors+1
sta $d021
rts
//--------------------------------------------------------
// Text
//--------------------------------------------------------
.segment Code "Text Setup"
textSetup: {
ldx #0
loop: lda text,x
cmp #$ff
beq out
sta $0400,x
inx
jmp loop
out:
rts
You will now get a memory map like this, when you use the -showmem’ option:
Code-segment:
$0900-$0906 Main
$0907-$0913 Color Setup
46
Segments
Data-segment:
$8000-$8001 Colors
$8002-$800e Static Text
The code and data are now separated in memory, but close together in the source code.
Note that scoping and segments don't affect each other so you can switch segments within a scope. In the above
its used so the 'text' label is local. It can be seen from textSetup code but not from other routines. If you want to
have a scroll text routine it could have its own 'text' label and they wouldn't collide.
The default memory block is special since it can be controlled by parameters given when the segment is defined.
Notice the 'start=$1000' parameter that sets the start of the default memory block.
In some cases you want one segment to start after each other. This is done with the 'startAfter' parameter.
The ability to control code in this way can be useful, for instance when you want to save memory. If you have
some initialization code, that is only used once in the upstart phase, then you could place it after the rest of the
code, and use the same memory for a buffer that is used after the init phase:
.segment Buffer
table1: .fill $100, 0
table2: .fill $100, 0
Notice that overlapping code only gives an error if it's inside the same segment. So you can place code in both
'InitCode' and 'Buffer' without getting errors. The Code and InitCode segments are saved in the file while the
Buffer is thrown away.
By using the 'align' parameter together with 'startAfter' you align the default memory block.
47
Segments
By the memory map printed while assembling, you see the start of the Virtual100 segment is aligned to a $100
boundary to avoid spending an extra cycle when accessing the table:
Code-segment:
$8000-$8004 Some code
Virtual100-segment:
*$8100-$81ff Table
In the above example was also used 'virtual' (When no '=' is present its shorthand for 'virtual=true') to declare
all the memory blocks in the virtual100 segment virtual. In most cases this won't be necessary since you just don't
direct the segment anywhere so the generated bytes are thrown away, but in some cases it can come in handy.
'segmentAfter' works by taking the last defined memory block (Either the default or user defined by *=) and
starts where this ends. Block included in other ways (imported from other segments, included from files etc.) are
not considered.
A segment can be included in multiple other segment as seen by the 'Code' and 'Data' segment in the above
example.
This can be combined freely with adding code from other sources or directly using commands (lda, sta) inside
the segment.
Again, this can freely be combined with other ways of adding blocks to the segment.
48
Segments
BasicUpstart2(start)
start: sei
lda #00
tax
tay
jsr $1000
10.11. Boundaries
It is possible to set a minimum and maximum address of the segment using the 'min' and 'max' parameters. If
a block gets outside the given boundaries, it will give an error:
In some cases it is useful to ensure a segment have a specific site. By setting the 'fill' parameter to true all non
used values in the min-max range is set to the fill byte:
In the above example the fill byte is zero, but it can be specified with the 'fillByte' parameter.
Restricting size can be used to avoid using the ROM area or simply enforcing the rules of a maximum size
of 256 or 128 bytes.
The following entry was submitted to the 128 byte font competition on CSDb by Jesper Balman Gravgaard
(Rex). It rotates the ROM font 90 degrees. The max size of 128 bytes includes the two address bytes of the prg file.
*=$0801 "Basic"
BasicUpstart(ch2)
*=$080d "Program"
ch4: dey // Wait for 8 char lines
bne ch
lda pix+1 // Next char
clc
adc #8
sta pix+1
ch2: sei // Start char
lda #$32
sta $1
ldy #8
49
Segments
// Setup
.file [name="patched.prg", segments="Base,Patch", allowOverlap]
.segmentdef Base [prgFiles="data/base.prg"]
.segmentdef Patch []
// Patch Code
.segment Patch
*=$3802 "Insert jmp"
jmp $3fe0
Base-segment:
$3800-$39ff base.prg
Patch-segment:
$3802-$3804 Insert jmp
$38c2-$38c3 Insert lda #$ff
In the above example we have a base segment with the original file and a patch segment with the modifica-
tions. They are combined in the intermediate segment generated by the file directive which has the allowOverlap
parameter set.
Overlapping blocks are cut so the byte from the block with the highest priority are returned. The latest added
blocks wins so since the 'Patch' segment lies after 'Base' in the segments list the patch code is chosen.
50
Segments
The 'modify' parameter assigns the 'BasicUpstart' modifier. As a convention, arguments to the modifier has a
_ appended in front, so '_start' is an argument for the BasicUpstart modifier.
Users can write their own modifiers as plug-ins (Crunchers etc.) as shown in the plug-in chapter.
Here is a list of build in segment modifiers:
The only parameter that is special for the file directive is 'name'. The rest is standard parameters for directives
using intermediate segments. For a complete list of intermediate parameters see the 'List of segment parameters'
section placed last in this chapter.
// Main code
BasicUpstart2(start)
start: sei
ldx #0
loop: lda zpCode,x
sta zpStart,x
inx
cpx #zpCodeSize
bne loop
jmp zpStart
// Zeropage code
.segment ZeroPage_Code [start=$10]
zpStart:
inc $d020
jmp *-3
In the memory map, you can now see the zeropage code:
51
Segments
Memory Map
----------
Default-segment:
$0801-$080c Basic
$080e-$0824 Basic End
ZeroPage_Code-segment:
$0010-$0015 ZeroPage_Code
Since the bytes are supplied through an intermediate segment all intermediate parameters can be used. In the
following example, a sid file is placed at an alternative address:
.segmentdef [dest="DISKDRIVE"]
52
Segments
53
Chapter 11
PRG files and D64 Disks
11.1. Introduction
This chapter explains how to create prg-files and d64 disk images using the .file and .disk directive.
The .file directive is quite straight forward, but adds a few extra options over the outPrg parameter for segments.
With the .disk directive you can use Kick Assembler as a standalone disk creation tool, by selecting files from
the hard disk to add to a disk image, or you can assemble directly to the disk using segments, or you can mix the
two methods.The directive collects parameters and sends them to a disk writer which can either be the build in
disk writer or one given by a plug in. The build in default writer is based on the 'CC1541' disk tool by Andreas
Larsson, and should cover all needs when creating standard disks. With specialized writers from plugins you can
write disks for specific loaders etc.
A big thanks to Andreas for rewriting CC1541 to Java for use in Kick Assembler!
You can assign any type of value (strings, numbers, booleans, etc) to a parameter. Notice the last parameter has
no assignment. This is a short notation for assigning the boolean value 'true' to the parameter ('wearsTshirt=true').
// %o in the filename will be replaced with the name of the root source
file without the extension
// If you called the assembler with a file called Source27.asm this will
output to Source27.prg
.file [name="%o.prg", segments="Code"]
.segment Data []
*=$0f00 "Mul3"
.fill $40, i*3
*=$2000 "Sinus"
.fill $100, 127.5 + 127.5*sin(toRadians(i*360/256))
54
PRG files and D64 Disks
The content of the file is given using an intermediate segment which makes it quite flexible. See the segment
chapter for all options or the disk directive sections for more examples.
The name parameter is mandatory, the rest is optional. Here are the list of specific .file directive parameters:
The writer name is optional, if left empty the default disk writer is called. Otherwise the writer name is used
to look up a 3rd party disk writer imported from a plug in. In the following sections described how the default
writer works.
.disk [filename="MyDisk.d64"]
{
}
Like in the .file directive you can use %o notation to get the name of the sourcefile. You fill in extra parameters
as a comma separated list. Here we add a disk name and an id, which is displayed in the top of the directory:
55
PRG files and D64 Disks
.segment BORDER_COLORS []
BasicUpstart2(start1)
start1: inc $d020
jmp *-3
.segment BACK_COLORS []
BasicUpstart2(start2)
start2: dec $d021
jmp *-3
.segment HIDDEN []
.text "THIS IS THE HIDDEN MESSAGE!"
The content of a file is done using an intermediate segment, which gives a wide range possibilities of specifying
input. In the example, the content of the first three prg files comes from the segments specified below. The third
uses a prg file from the hard drive and the fourth the content of a sid file. For all the possibilities of working with
intermediate segments, see the segments chapter.
The 'name' and 'type' parameters specifies the name and type of the file. Notice the '<' at the end of the second
prg type which means the file is locked.
The third prg file is not shown in the directory due to the 'hide' option. You can get its start track and sector
by using the 'showInfo' disk parameter.
A complete list of parameters is given here.
56
PRG files and D64 Disks
.plugin "myplugins.Mydiskwriter"
57
Chapter 12
Import and Export
In this chapter we will look at other ways to get data in and out of Kick Assembler.
The three variables x, sound and beta2 and their string values will now be placed in a hashtable that can be
accessed by the global variable cmdLineVars:
The get function extracts signed bytes as defined by java, which means the byte value $ff gives the number -1.
This is not a problem when dumping bytes to memory, however if you want to process the data you might want
an unsigned byte. To get an unsigned byte use the uget function instead. The byte value $ff will then return 255.
When you know the format of the file, you can supply a template string that describes the memory blocks.
Each block is given a name and a start address relative to the start of the file. When you supply a template to the
LoadBinary function, the returned value will contain a get and a size function for each memory block:
58
Import and Export
*=$0810 "Program"
lda #$38
sta $d018
lda #$d8
sta $d016
lda #$3b
sta $d011
lda #0
sta $d020
lda #picture.getBackgroundColor()
sta $d021
ldx #0
!loop:
.for (var i=0; i<4; i++) {
lda colorRam+i*$100,x
sta $d800+i*$100,x
}
inx
bne !loop-
jmp *
Notice how easy it is to reallocate the screen and color ram by combining the *= and .fill directives. To avoid
typing in format types too often, Kick Assembler has some build in constants you can use:
The formats were chosen so they cover the outputs of Timanthes (NB. Timanthes doesn’t save the background
color in koala format, so if you use that you will get an overflow error).
TIP: If you want to know how data is placed in the above formats, just print the constant to the console while
assembling. Example:
59
Import and Export
From this you can extract data such as the init address, the play address, info about the music and the song data.
//---------------------------------------------------------
//---------------------------------------------------------
// SID Player
//---------------------------------------------------------
//---------------------------------------------------------
.var music = LoadSid("Nightshift.sid")
BasicUpstart2(start)
start:
lda #$00
sta $d020
sta $d021
ldx #0
ldy #0
lda #music.startSong-1
jsr music.init
sei
lda #<irq1
sta $0314
lda #>irq1
sta $0315
asl $d019
lda #$7b
sta $dc0d
lda #$81
sta $d01a
lda #$1b
60
Import and Export
sta $d011
lda #$80
sta $d012
cli
jmp *
//---------------------------------------------------------
irq1:
asl $d019
inc $d020
jsr music.play
dec $d020
pla
tay
pla
tax
pla
rti
//---------------------------------------------------------
*=music.location "Music"
.fill music.size, music.getData(i)
//----------------------------------------------------------
// Print the music info while assembling
.print ""
.print "SID Data"
.print "--------"
.print "location=$"+toHexString(music.location)
.print "init=$"+toHexString(music.init)
.print "play=$"+toHexString(music.play)
.print "songs="+music.songs
.print "startSong="+music.startSong
.print "size=$"+toHexString(music.size)
.print "name="+music.name
.print "author="+music.author
.print "copyright="+music.copyright
.print ""
.print "Additional tech data"
.print "--------------------"
.print "header="+music.header
.print "header version="+music.version
.print "flags="+toBinaryString(music.flags)
.print "speed="+toBinaryString(music.speed)
.print "startpage="+music.startpage
.print "pagelength="+music.pagelength
Assembling the above code will create a musicplayer for the given sidfile and print the information in the music
file while assembling:
SID Data
--------
location=$1000
init=$1d70
play=$1003
songs=1.0
startSong=1.0
size=$d78
name=Nightshift
author=Ari Yliaho (Agemixer)
copyright=2001 Scallop
61
Import and Export
header version=2.0
flags=100100
speed=0
startpage=0.0
TIP: If you use the –libdir option to point to your HVSC main directory, you don’t have to write long filenames.
For example:
will be
*=$2000
.var logo = LoadPicture("CML_32x8.gif")
.fill $800, logo.getSinglecolorByte((i>>3)&$1f, (i&7) | (i>>8)<<3)
If you don't like the compact form of the .fill command you can use a for loop instead. The following will
produce the same data:
*=$2000
.var logo = LoadPicture("CML_32x8.gif")
.for (var y=0; y<8; y++)
.for (var x=0;x<32; x++)
.for(var charPosY=0; charPosY<8; charPosY++)
.byte logo.getSinglecolorByte(x,charPosY+y*8)
The LoadPicture can take a color table as the second argument. This is used to decide which bit pattern is
produced by a pixel. In single color mode there are two bit patters (%0 and %1) and multi color mode has four
(%00, %01, %10 and %11). If you don’t specify a color table, a default table is created based on the colors in the
picture. However, normally you wish to control which color is mapped to a bit pattern. The following shows how
to convert a picture to a 16x16 multi color char matrix charset:
*=$2800 “Logo”
.var picture = LoadPicture("Picture_16x16.gif",
List().add($444444, $6c6c6c,$959595,$000000))
.fill $800, picture.getMulticolorByte(i>>7,i&$7f)
The four colors added to the list are the RGB values for the colors that are mapped to each bit pattern.
Finally the picture value contains a getPixel function from which you can get the RGB color of a pixel. This
comes in handy when you want to make your own format for some special purpose.
Attributes and functions available on picture values:
62
Import and Export
Attribute/Function Description
getPixel(x,y) Returns the RGB value of the pixel at position x,y. Both
x and y are given in pixels.
getSinglecolorByte(x,y) Converts 8 pixels to a single color byte using the color
table. X is given as a byte number (= pixel position/8)
and y is given in pixels.
getMulticolorByte(x,y) Converts 4 pixels to a multi color byte using the color ta-
ble. X is given as a byte number (= pixel position/8) and
y is given in pixels. (NB. This function ignores every
second pixel since the C64 multi color format is half the
resolution of the single color.)
IMPORTANT! For security reasons, you will have to use the –afo switch on the command line otherwise file
generation will be blocked. Eg “java –jar KickAss.jar source.asm -afo” will do the trick.
File creation is useful for generating extra data for emulators. The following example shows how to generate
a file with breakpoint for VICE:
.macro break() {
.eval brkFile.writeln(“break “ + toHexString(*))
}
*=$0801 “Basic”
BasicUpstart(start)
*=$1000 "Code"
start:
inc $d020
break()
jmp start
When running VICE with the breakpoint file (use the –moncommands switch), VICE will run until the break
and then exit to the monitor.
Here is a list of the functions on a file value:
63
Import and Export
will generate the file source1.sym while assembling. Lets say the content of source1 is:
.filenamespace source1
*=$2000
clearColor:
lda #0
sta $d020
sta $d021
rts
.namespace source1 {
.label clearColor = $2000
}
It's now possible to refer to the labels of source1.asm from another file just by importing the .sym file:
64
Chapter 13
Modifiers
With modifiers, you can modify assembled bytes before they are stored to the target file. It could be you want
to encrypt, pack or crunch the bytes. Currently, the only way to create a modifier is to implement a java plugin
(See the plugin chapter).
.filemodify MyModifier(25)
.modify MyModifier() {
*=$8080
main:
inc $d020
dec $d021
jmp main
*=$1000
.fill $100, i
}
65
Chapter 14
Special Features
Misc features
*= $0810 "Program"
start: inc $d020
inc $d021
jmp start
TIP: Insert at basic upstart program in the start of your programs and use the –execute option to start Vice. This
will automatically load and execute your program in Vice after successful assembling.
There is a second variation of the basic upstart macro that also takes care of setting up memory blocks:
BasicUpstart2(start) // 10 sys$0810
start: inc $d020
inc $d021
jmp start
If you want to see the script code for the macros, you can look in the autoinclude.asm file in the KickAss.jar file.
lda #RTS
sta target
66
Special Features
Example of use:
lda #BLACK
sta $d020
lda #WHITE
sta $d021
67
Special Features
.var v1 = Vector(1,2,3)
.var v2 = Vector(0,0,2)
You can access the coordinates of the vector by its get functions and do the most common vector operations
by the assigned functions. Here are some examples:
The matrix value represents a 4x4 matrix. You create it by using the Matrix function, or one of the other
constructor functions described later. You access the entries of the matrix by using its get and set functions:
In 3d graphics matrixes are usually used to describe a transformation of a vector space. That can be to move
the coordinates, to scale them, to rotate then, etc. The Matrix() operator creates an identity matrix, which is one
that leaves the coordinates unchanged. By using the set function you can construct any matrix you like. However,
Kick Assembler has constructor functions that create the most common transformation matrixes:
68
Special Features
Function Description
RotationMatrix(aX,aY,aZ) Creates a rotation matrix where aX, aY and aZ are the
angles rotated around the x, y and z axis. The angles are
given in radians.
ScaleMatrix(sX,sY,sZ) Creates a scale matrix where the x coordinate is scaled
by sX, the y-coordinate by sY and the z-coordinate by
sZ.
MoveMatrix(mX,mY,mZ) Creates a move matrix that moves mX along the x-axis,
mY along the y-axis and mZ along the z-axis.
PerspectiveMatrix(zProj) Creates a perspective projection where the eye-point is
placed in (0,0,0) and coordinates are projected on the
XY-plane where z=zProj.
You can multiply the matrixes and thereby combine their transformations. The transformation is read from
right to left, so if you want to move the space 10 along the x axis and then rotate it 45 degrees around the z-
axis, you write:
.var m = RotationMatrix(0,0,toRadians(45))*MoveMatrix(10,0,0)
.var v = m*Vector(10,0,0)
.print "Transformed v=" + v
Here is a little program to illustrate how matrixes can be used. It pre calculates an animation of a cube that rotates
around the x, y and z-axis and is projected on the plane where z=2.5. The data is placed at the label ‘cubeCoords’:
//--------------------------------------------------------------------------------
// Objects
//--------------------------------------------------------------------------------
.var Cube = List().add(
Vector(1,1,1), Vector(1,1,-1), Vector(1,-1,1), Vector(1,-1,-1),
Vector(-1,1,1), Vector(-1,1,-1), Vector(-1,-1,1), Vector(-1,-1,-1))
//--------------------------------------------------------------------------------
// Macro for doing the precalculation
//--------------------------------------------------------------------------------
.macro PrecalcObject(object, animLength, nrOfXrot, nrOfYrot, nrOfZrot) {
// Rotate the coordinate and place the coordinates of each frams in a list
.var frames = List()
.for(var frameNr=0; frameNr<animLength;frameNr++) {
// Set up the transform matrix
.var aX = toRadians(frameNr*360*nrOfXrot/animLength)
.var aY = toRadians(frameNr*360*nrOfYrot/animLength)
69
Special Features
.var aZ = toRadians(frameNr*360*nrOfZrot/animLength)
.var zp = 2.5 // z-coordinate for the projection plane
.var m = ScaleMatrix(120,120,0)*
PerspectiveMatrix(zp)*
MoveMatrix(0,0,zp+5)*
RotationMatrix(aX,aY,aZ)
70
Chapter 15
Assemble Information
Kick Assembler 4, and later versions, exposes information of build in features and of the assembled source
files. This is intended for authors of editors who want to provide extra support for Kick Assembler such as realtime
error and syntax feedback and help text for build in directives and libraries. These features are under development
and the interface might change. If you plan to use this get in touch with the author so we can coordinate our efforts.
When executing the above statement, output is written to the file "asminfo.txt", but you can specify the file
by the -asminfofile option:
The content of the file will have different sections dependent on what info you have requested. The second
parameter describes which info is returned, so in the above example all possible info is returned. The output divided
into sections, with different types of information, here is an example:
[libraries]
Math;constant;PI
Math;constant;E
Math;function;abs;1
Math;function;acos;1
...
[directives]
.text;.text "hello";Dumps text bytes to memory.
.by;.by $01,$02,$03;An alias for '.byte'.
...
[files]
0;KickAss.jar:/include/autoinclude.asm
1;mySource.asm
[syntax]
symbolReference;38,8,38,17,0
symbolReference;41,20,41,26,0
functionCall;41,8,41,18,0
symbolReference;56,8,56,17,0
...
[errors]
...
And this will export all predefined assemble info sections and any errors:
71
Assemble Information
Notice the '|' is used to give several selections - you can add as many as you want. This is the available options:
When the category says 'meta' the option is used to select several of the sections. When the category is not
'meta' the option refers to a specific section. The details of the sections is given in later chapters.
This replaces the content of the first file with the second. It doesn't matter if the file is the main file or included
by another filer, and your can have as many replaceFile options as you want.
Secondly, you don't want Kick Assembler to do a complete assembling each time you call it. It might take
too much time to assemble and you don't want the assembler to overwrite output. To take care of this, use the -
noeval option.
This make Kick Assembler parse the source file and do an initial pass, no evaluation will be done. This will
detect syntax errors and return syntax information.
[section1]
field1;field2;field3
field1;field2;field3
field1;field2;field3
72
Assemble Information
[section2]
field1;field2
field1;field2
field1;field2
As special type of field, which is used in several sections is a 'source range' which describes a range of chars
in a source file. It consist of 5 integers:
The positions is the positions in a given line. The file index tell which file it is and is an index pointing to an
entry in the files section. An example of a source range is:
38,8,38,17,1
[version]
5.12
libraryname;entrytype;typedata
There are two entry types: 'function' and 'constant'. The type data depends on the entry type, and is either:
libraryname;constant;constantname
libraryname;function;functionname;numberOfArguments
Examples:
[libraries]
Math;constant;PI
Math;constant;E
Math;function;abs;1
Math;function;acos;1
directive;example;description
Example:
[directives]
.text;.text "hello";Dumps text bytes to memory.
73
Assemble Information
directive;example;description
Example:
[ppdirectives]
#define;#define DEBUG;Defines a preprocessor symbol.
index;filepath
[files]
0;KickAss.jar:/include/autoinclude.asm
1;test1.asm
Notice the first entry starts with KickAss.jar. This means that its a file included from inside the KickAss.jar file.
type;sourcerange
Example:
[syntax]
operator;21,20,21,20,0
Note: Its the plan to add more fields here, like where a the label is defined if its a label reference, etc.
level;sourcerange;message
Example:
[errors]
Error;41,2,41,7,1;Unknown preprocessor directive #defin
74
Chapter 16
Testing
Kick Assembler has .assert directives that are useful for testing. They were made to make it easy to test the
assembler itself, but you can use them for testing your own pseudo-commands, macros, functions. When assertions
are used, the assembler will automatically count the number of assertions and the number of failed assertions and
display these when the assembling has finished.
When assembling this code the assembler prints the description, the result of the expression and the expected
result. If these don’t match an error message is appended:
2+5*10/2=27.0 (27.0)
2+2=4.0 (5.0) – ERROR IN ASSERTION!!!
Vector(1,2,3)+Vector(1,1,1)=(2.0,3.0,4.0) ((2.0,3.0,4.0))
In the above example test1 will fail since its perfectly legal to divide 20 by 10. Test2 will produce the expected
error so this assertion is ok. The above will give the following output:
.assert "Test", {
.for (var i=0; i<4; i++)
sta $0400+i
}, {
sta $0400
sta $0401
sta $0402
sta $0403
}
The assert directive will give an ok or failed message and the assembled result as output. The output of the
above example is as follows:
75
Testing
Output:
Test – OK. | The value of a Command Argument Value must be an integer. Can’t get an
integer from a value of type ‘string’
76
Chapter 17
3rd Party Java plugins
It's possible to write you own plugins for Kick Assembler. Currently the following types of plugins are sup-
ported:
2. ’Import->Existing Projects into workspace->Select archive file’ and select the downloaded project file.
3. Replace the KickAss.jar file in the jars folder with the newest version, if necessary.
You are now ready to start. In the src folder you can see examples of how the plugins are made. The files in
PluginTest shows how to use them and in the launch folder is launch files for running the examples (Rightclick-
>Run As).
.plugin "test.plugins.macros.MyMacro"
If the plugin should be available every time you use Kick Assembler, you place the class name in a line in the
file ‘KickAss.plugin’ which should be placed in the same locations as the KickAss.jar. Using // in the start of the
line makes it a comment. Example of a KickAss.plugin file:
// My macro plugins
test.plugins.macros.MyMacro1
test.plugins.macros.MyMacro2
test.plugins.macros.MyMacro3
77
3rd Party Java plugins
The interface has two methods, one that gets parameters that defines the macro, and one executes it. This is
the basic structure of nearly all the plugins. The MacroDefinition class is really simple. It consist of a getter and
setters for the defining properties. Since the only defining property of a macro is its name, it looks like this:
A simple example of a macro implementation that prints ‘Hello World from MyMacro!’ and returns zero bytes
looks like this:
package test.plugins.macros;
import kickass.plugins.interf.general.IEngine;
import kickass.plugins.interf.general.IValue;
import kickass.plugins.interf.macro.IMacro;
import kickass.plugins.interf.macro.MacroDefinition;
public MyMacro() {
definition = new MacroDefinition();
definition.setName("MyMacro");
}
@Override
public MacroDefinition getDefinition() {
return definition;
}
@Override
public byte[] execute(IValue[] parameters, IEngine engine) {
engine.print("Hello world from mymacro!");
return new byte[0];
}
}
.plugin "test.plugins.macros.MyMacro"
MyMacro()
The ‘arguments’ parameter is the arguments parsed to the macro. You can read about these in the 'general
communication classes' section. The same goes for the ‘engine’ parameter which is used to do additional commu-
nication with the Kick Assembler engine.
78
3rd Party Java plugins
79
3rd Party Java plugins
Method Description
Boolean getBoolean(); Gets a Boolean from the value if possible, otherwise
it will give an error message.
List<IValue> getList(); Gets at list of values if possible, otherwise it will
give an error message. The list implements size(), get(i),
isEmpty() and iterator().
Boolean hasIntRepresentation(); Tells if you can get an integer from the value. Every
number value can produce an integer. 3.2 will produce
3).
boolean hasDoubleRepresentation(); Tells if you can get a double from the value.
boolean hasStringRepresentation(); Tells if you can get a string from the value.
boolean hasBooleanRepresentation(); Tells if you can get a boolean from the value.
boolean hasListRepresentation(); Tells if you can get a list from the value.
It can either be passed as argument to the plugin or created by the plugin and returned as a result. Use the
'createMemoryBlock' in the IEngine interface to create new memory blocks.
80
3rd Party Java plugins
The main methods defined on parameter maps are exists(), getValue(), getSourceRange() and getParameter-
Names(). In addition there are some convenience methods for easy retrieval of values of specific types:
The XYZDefinition class simply contains getters and setters for the definition of the plugin, so your get-
Definition() method should simply return an XYZDefinition where you have set the fields using the setters
(setName("MyPlugin") etc). Many of the definitions only contains a name, but having a definition class makes it
easier to extend without breaking backwards compatibility.
You will find that all plugin interfaces extends IPlugin. IPlugin is empty and simply a way of ensuring type
safety if you want an object you are sure is a plugin.
81
3rd Party Java plugins
Macro plugins are described previously in the 'Quick Example' section, so look there for a complete example.
.modify MyModifier(27) {
*=$8080
main:
inc $d020
jmp main
}
You implement a modifier by implementing the following interface. The 'name' in the definition is the modifier
name ('MyModifier' in the above example.):
A segment modifier is created by implementing a class thats realises the ISegmentModifier plugin:
The allParameters set defines the possible parameters for the modifier. As a convention you should prefix them
with _ like '_start' in the above example. This way the names won't collide with future segment parameter names
and you can easily tell which parameters belong to the modifier.
See the 'segments' chapter for more about Segment Modifiers and the example project of how to implement.
82
3rd Party Java plugins
Recall the format of the .disk directive to understand the definition properties:
When WRITERNAME matches the name given in the definition the writer is called. Then we have two kinds of
parameters: disk and file parameters. For each of these is a set of all possible parameters and a set of non-optional
parameters. If a parameter is give that is not included in the allParameters set Kick Assembler will generate an
error. The same will happen if a non optional parameter is missing.
The execute method has parameters of two new interfaces:
These represent the given parameters and provides the values and the bytes which should be stored in each file.
When creating the output file, use the IEngine object to open an output stream for storing the bytes. For details,
refer to the example project.
83
3rd Party Java plugins
The following plugin directive will then register both MyMacro and MyModifyer.
.plugin "test.plugins.archives.MyArchive"
//FILE: MyAutoInclude.asm
.macro SetColor(color) {
lda #color
sta $d020
}
Then you put MyAutoInclude.asm in your jar-file in the package 'include' and add an object of the class Au-
toIncludeFile to your archive. You archive could look like this:
@Override
public List<IPlugin> getPluginObjects() {
ArrayList<IPlugin> plugins = new ArrayList<>();
plugins.add(new SomePlugin1());
plugins.add(new SomePlugin2());
plugins.add(new AutoIncludeFile("MyArcive.jar",getClass(),"/include/
MyAutoInclude.asm"));
return plugins;
}
}
2. A random 'class'-object from the jar - this is used to open the resource.
The file will now be compiled with the rest of the source if the archive is imported.
For completeness, here is the IAutoIncludeFile-interface, but as mentioned, you probably wont need it.
84
Appendix A. Quick Reference
A.1. Command Line Options
Table A.1. Command Line Options
Option Example Description
-afo -afo Allows file output outside the output
dir.
-aom -aom Allow overlapping memory blocks.
With this option, overlapping memo-
ry blocks will produce a warning in-
stead of an error.
-asminfo -asminfo all Turn on exporting of assemble info
-asminfofile -asminfofile myAsmInfo.txt Tells where to output the asminfo
file.
-asminfotostdout -asminfoToStdOut Directs asminfo to stdout instead of
a file (Note that parameternames are
case insensitive which can be used to
do it more readable)
-binfile -binfile Sets the output to be a bin file instead
of a prg file. The difference between
a bin and a prg file is that the bin file
doesn’t contain the two start address
bytes.
-bytedump -bytebump Dumps the assembled bytes in the
file ByteDump.txt together with the
code that generated them.
-bytedumpfile -bytebumpfile myfile.txt Same as -bytedump but with an argu-
ment specifying the name of the file
-cfgfile -cfgfile "../../MyConfig.Cfg" Use additional configuration file
(like KickAss.cfg). Supply the file as
an absolute path, or a path relative
to the source file. You can have as
many additional config files as you
want.
-debug -debug For development use. Adds addition-
al debug info, like stacktraces, to the
output.
-debugdump -debugdump Dumps an infofile for c64 debugger
that maps assembled bytes to loca-
tions in the sourcecode.
-define -define DEBUG Defines a preprocessor symbol.
-dtv -dtv Enables DTV opcodes.
-excludeillegal -excludeillegal Exclude the illegal opcodes from the
instruction set
-execute -execute "x64 +sound" Execute a given program with the as-
sembled file as argument. You can
use this to start a C64 emulator with
the assembled program if the assem-
bling is successful.
85
Quick Reference
86
Quick Reference
A.3. Mnemonics
87
Quick Reference
cmd noarg imm zp zpx zpy izx izy abs Abx aby ind rel
clv $b8
cmp $c9 $c5 $d5 $c1 $d1 $cd $dd $d9
cpx $e0 $e4 $ec
cpy $c0 $c4 $cc
dec $c6 $d6 $ce $de
dex $ca
dey $88
eor $49 $45 $55 $41 $51 $4d $5d $59
inc $e6 $f6 $ee $fe
inx $e8
iny $c8
jmp $4c $6c
jsr $20
lda $a9 $a5 $b5 $a1 $b1 $ad $bd $b9
ldx $a2 $a6 $b6 $ae $be
ldy $a0 $a4 $b4 $ac $bc
lsr $4a $46 $56 $4e $5e
nop $ea
ora $09 $05 $15 $01 $11 $0d $1d $19
pha $48
php $08
pla $68
plp $28
rol $2a $26 $36 $2e $3e
ror $6a $66 $76 $6e $7e
rti $40
rts $60
sbc $e9 $e5 $f5 $e1 $f1 $ed $fd $f9
sec $38
sed $f8
sei $78
sta $85 $95 $81 $91 $8d $9d $99
stx $86 $96 $8e
sty $84 $94 $8c
tax $aa
tay $a8
tsx $ba
txa $8a
txs $9a
tya $98
88
Quick Reference
cmd noarg imm zp zpx zpy izx izy abs abx aby ind rel
ahx,sha $93 $9f
alr,asr $4b
anc $0b
anc2 $2b
arr $6b
axs,sbx $cb
dcp,dcm $c7 $d7 $c3 $d3 $cf $df $db
isc,ins,isb $e7 $f7 $e3 $f3 $ef $ff $fb
las,lae,lds $bb
lax,lxa $ab $a7 $b7 $a3 $b3 $af $bf
nop $ea $80 $04 $14 $0c $1c
rla $27 $37 $23 $33 $2f $3f $3b
rra $67 $77 $63 $73 $6f $7f $7b
sax $87 $97 $83 $8f
sbc2 $eb
shx $9e
shy $9c
slo $07 $17 $03 $13 $0f $1f $1b
sre $47 $57 $43 $53 $4f $5f $5b
tas,shs $9b
xaa,ane $8b
A.3.3. DTV
The DTV instruction set contains the standard+illegal 6502 mnemonics plus the below modifications. You get
it by writing '.cpu dtv'
cmd noarg imm zp zpx zpy izx izy abs abx aby ind rel
bra $12
sac $32
sir $42
89
Quick Reference
90
Quick Reference
cmd noarg imm zp zpx zpy izx izy abs abx aby ind rel izp zprel indx
smb0 $87
smb1 $97
smb2 $a7
smb3 $b7
smb4 $c7
smb5 $d7
smb6 $e7
smb7 $f7
sta $85 $95 $81 $91 $8d $9d $99 $92
stp $db
stz $64 $74 $9c $9e
trb $14 $1c
tsb $04 $0c
wai $cb
91
Quick Reference
92
Quick Reference
93
Quick Reference
94
Appendix B. Technical Details
In Kick Assembler 3 some rather advanced techniques have been implemented to make the assembling more
flexible and correct. I'll describe some of the main points here. YOU DON'T NEED TO KNOW THIS, but if you
are curious about technical details then read on.
95