2018-03-02 02:47:16 +00:00
|
|
|
|
INTRODUCTION
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
C02 is a simple C-syntax language designed to generate highly optimized
|
|
|
|
|
code for the 6502 microprocessor. The C02 specification is a highly
|
|
|
|
|
specific subset of the C standard with some modifications and extensions
|
|
|
|
|
|
|
|
|
|
PURPOSE
|
|
|
|
|
|
|
|
|
|
Why create a whole new language, particularly one with severe restrictions,
|
|
|
|
|
when there are already full-featured C compilers available? It can be
|
2018-08-04 22:29:04 +00:00
|
|
|
|
argued that standard C is a poor fit for processors like the 6502. The C
|
|
|
|
|
was language designed to translate directly to machine language instructions
|
|
|
|
|
whenever possible. This works well on 32-bit processors, but requires either
|
2018-02-06 03:40:00 +00:00
|
|
|
|
a byte-code interpreter or the generation of complex code on a typical
|
2018-08-04 22:29:04 +00:00
|
|
|
|
8-bit processor. C02, on the other hand, has been designed to translate
|
2018-02-06 03:40:00 +00:00
|
|
|
|
directly to 6502 machine language instructions.
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
The C02 language and compiler were designed with two goals in mind.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The first goal is the ability to target machines with low memory: a few
|
|
|
|
|
kilobytes of RAM (assuming the generated object code is to be loaded into
|
2018-02-06 03:40:00 +00:00
|
|
|
|
and ran from RAM), or as little as 128 bytes of RAM and 2 kilobytes of ROM
|
2018-08-04 22:29:04 +00:00
|
|
|
|
(assuming the object code is to be run from a ROM or PROM).
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The compiler is agnostic with regard to system calls and library functions.
|
|
|
|
|
Calculations and comparisons are done with 8 bit precision. Intermediate
|
2018-02-06 03:40:00 +00:00
|
|
|
|
results, array indexing, and function calls use the 6502 internal registers.
|
2018-08-04 22:29:04 +00:00
|
|
|
|
While this results in compiled code with virtually no overhead, it severely
|
2018-02-06 03:40:00 +00:00
|
|
|
|
restricts the syntax of the language.
|
|
|
|
|
|
|
|
|
|
The second goal is to port the compiler to C02 code so that it may be
|
|
|
|
|
compiled by itself and run on any 6502 based machine with sufficient memory
|
|
|
|
|
and appropriate peripherals. This slightly restricts the implementation of
|
|
|
|
|
code structures.
|
|
|
|
|
|
|
|
|
|
SOURCE AND OUTPUT FILES
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
C02 source code files are denoted with the .c02 extension. The compiler
|
|
|
|
|
reads the source code file, processes it, and generates an assembly
|
2018-02-06 03:40:00 +00:00
|
|
|
|
language file with the same name as the source code file, but with
|
|
|
|
|
the .asm extension instead of the .c02 extension. This assembly language
|
|
|
|
|
file is then assembled to create the final object code file.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Note: The default implementation of the compiler creates assembly
|
|
|
|
|
language code formatted for the DASM assembler. The generation of the
|
2018-02-06 03:40:00 +00:00
|
|
|
|
assembly language is parameterized, so it may be easily changed to
|
|
|
|
|
work with other assemblers.
|
|
|
|
|
|
|
|
|
|
COMMENTS
|
|
|
|
|
|
|
|
|
|
The parser recognizes both C style and C++ style comments.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
C style comments begin with /* and end at next */. Nested C style comments
|
2018-02-06 03:40:00 +00:00
|
|
|
|
are not supported.
|
|
|
|
|
|
|
|
|
|
C++ style comments begin with // and end at the next newline. C++ style
|
|
|
|
|
comments my be nested inside C style comments.
|
|
|
|
|
|
|
|
|
|
DIRECTIVES
|
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
Directives are special instructions to the compiler. Depending on the
|
2018-08-04 22:29:04 +00:00
|
|
|
|
directive, it may or may not generate compiled code. A directive is
|
|
|
|
|
denoted by a leading # character. Unlike a statements, a directives is
|
2018-03-07 04:35:47 +00:00
|
|
|
|
not followed by a semicolon.
|
2018-03-02 02:47:16 +00:00
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
Note: Unlike standard C and C++, which use a preprocessor to process
|
|
|
|
|
directives, the C02 compiler processes directives directly.
|
|
|
|
|
|
|
|
|
|
DEFINE DIRECTIVE
|
2018-03-02 02:47:16 +00:00
|
|
|
|
|
2018-08-03 15:22:12 +00:00
|
|
|
|
The #define directive is used to define constants (see below).
|
2018-03-02 02:47:16 +00:00
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
INCLUDE DIRECTIVE
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
The #include directive causes the compiler to read and process and external
|
|
|
|
|
file. In most cases, #include directives will be used with libraries of
|
|
|
|
|
function calls, but they can also be used to modularize the code that makes
|
|
|
|
|
up a program.
|
|
|
|
|
|
|
|
|
|
An #include directive is followed by the file name to be included. This
|
|
|
|
|
file name may be surrounded with either a < and > character, or by two "
|
|
|
|
|
characters. In the former case, the compiler looks for the file in an
|
2018-08-04 22:29:04 +00:00
|
|
|
|
implementation specific library directory (the default being ./include),
|
2018-02-06 03:40:00 +00:00
|
|
|
|
while in the latter case, the compiler looks for the file in the current
|
|
|
|
|
working directory. Two file types are currently supported.
|
|
|
|
|
|
|
|
|
|
Header files are denoted by the .h02 extension. A header file is used to
|
|
|
|
|
provide the compiler with the information necessary to use machine
|
|
|
|
|
language system and/or library routines written in assembly language,
|
2018-08-04 22:29:04 +00:00
|
|
|
|
and consists of comments and declarations. The declarations in a header
|
2018-02-06 03:40:00 +00:00
|
|
|
|
file added to the symbol table, but do not directly generate code. After
|
|
|
|
|
a header file has been processed, the compiler reads and process a
|
|
|
|
|
assembly language file with the same name as the header file, but with
|
|
|
|
|
the .a02 extension instead of the .h02 extension.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The compiler does not currently generate any assembler required
|
2018-02-06 03:40:00 +00:00
|
|
|
|
pseudo-operators, such as the specification of the target processor,
|
|
|
|
|
or the starting address of the assembled object code. Therefore, at least
|
|
|
|
|
one header file, with an accompanying assembly language file is needed
|
|
|
|
|
in order to successfully assemble the compiler generated code. Details
|
|
|
|
|
on the structure and implementation of a typical header file can be
|
|
|
|
|
found in the file header.txt.
|
|
|
|
|
|
2018-07-19 03:44:42 +00:00
|
|
|
|
Assembly language files are denoted by the .a02 extension. When the
|
2018-02-06 03:40:00 +00:00
|
|
|
|
compiler processes an assembly language file, it simply inserts the contents
|
|
|
|
|
of the file into the generated code.
|
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
PRAGMA DIRECTIVE
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
The #pragma directive is used to set various compiler options. When using
|
2018-08-04 22:29:04 +00:00
|
|
|
|
a #pragma directive it is followed by the pragma name and possibly an
|
2018-03-07 04:35:47 +00:00
|
|
|
|
option, each separated by whitespace.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Note: The various pragma directives are specific to the cross-compiler and
|
2018-03-07 04:35:47 +00:00
|
|
|
|
may be changed or omitted in future versions of the compiler.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
PRAGMA ASCII
|
2018-03-07 04:35:47 +00:00
|
|
|
|
|
|
|
|
|
The #pragma ascii directive instructs the compiler to convert the characters
|
|
|
|
|
in literal strings to a form expected by the target machine.
|
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
#pragma ascii high //Sets the high bit to 1 (e.g. Apple II)
|
|
|
|
|
#pragma ascii invert //Swaps upper and lower case (e.g. PETSCII)
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
|
|
|
|
PRAGMA ORIGIN
|
2018-03-07 04:35:47 +00:00
|
|
|
|
|
|
|
|
|
The #pragma origin directive sets the target address of compiled code.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
#pragma origin $0400 //Compiled code starts at address 1024
|
|
|
|
|
#pragma origin 8192 //Compiled code starts at address 8192
|
|
|
|
|
|
2018-07-24 04:30:00 +00:00
|
|
|
|
PRAGMA PADDING
|
|
|
|
|
|
|
|
|
|
The #pragma padding directive adds empty bytes to the end of the compiled
|
|
|
|
|
program. It should be used with target systems that require the object
|
|
|
|
|
code to be padded with a specific number of bytes.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
#pragma padding 1 //Add one empty byte to end of code
|
|
|
|
|
#pragma padding $FF //Add 255 empty bytes to end of code
|
|
|
|
|
|
2018-08-16 20:26:44 +00:00
|
|
|
|
PRAGMA RAMBASE
|
|
|
|
|
|
|
|
|
|
The #pragma rambase directive sets the base address for variables in RAM
|
|
|
|
|
(not declared const). This is normally used when the compiled code will be
|
|
|
|
|
stored in ROM (such as in an EPROM or Cartridge), but can be used any time
|
|
|
|
|
variables should be in a specific area of RAM.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
#pragma rambase $0200 //Define Variable RAM base address for NES
|
|
|
|
|
#pragma rambase 828 //Define Variable RAM in C64 Tape Buffer
|
|
|
|
|
#pragma rambase 4096 //Define RAM base for Vic 20 ROM cartridge
|
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
PRAGMA VARTABLE
|
|
|
|
|
|
|
|
|
|
The #pragma vartable directive forces the variable table to be written.
|
|
|
|
|
It should be used before any #include directives that need to generate
|
|
|
|
|
code following the table.
|
|
|
|
|
|
2018-08-16 20:26:44 +00:00
|
|
|
|
PRAGMA WRITEBASE
|
|
|
|
|
|
|
|
|
|
The #pragma writebase directive sets the base address for writing to variables
|
|
|
|
|
in RAM. This is used when target system uses different addresses for reading
|
|
|
|
|
and writing the same memory locations. This directive must be preceded by
|
|
|
|
|
#pragma rambase directive with a non-zero argument.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
#pragma rambase $F080 //Define Superchip RAM Read Base for Atari 2600
|
|
|
|
|
#pragma writebase $F000 //Define Superchip RAM Write Base for Atari 2600
|
|
|
|
|
|
|
|
|
|
Note: Setting a RAM write base causes the compiler to generate a write offset
|
|
|
|
|
which is concatenated to the variable name on all assignments.
|
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
PRAGMA ZEROPAGE
|
|
|
|
|
|
2018-08-16 20:26:44 +00:00
|
|
|
|
The #pragma zeropage directive sets the base address for variables declared
|
2018-03-07 04:35:47 +00:00
|
|
|
|
as zeropage.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
#Pragma zeropage $80 //Start zeropage variables at address 128
|
|
|
|
|
|
|
|
|
|
LITERALS
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
A literal represents a value between 0 and 255. Values may be written as
|
2018-02-06 03:40:00 +00:00
|
|
|
|
a number (binary, decimal, osir hexadecimal) or a character literal.
|
|
|
|
|
|
|
|
|
|
A binary number consists of a % followed by eight binary digits (0 or 1).
|
|
|
|
|
|
|
|
|
|
A decimal number consists of one to three decimal digits (0 through 9).
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A hexadecimal number consists of a $ followed by two hexadecimal digits
|
2018-02-06 03:40:00 +00:00
|
|
|
|
(0 through 9 or A through F).
|
|
|
|
|
|
2018-03-02 02:47:16 +00:00
|
|
|
|
A character literal consists of a single character surrounded by ' symbols.
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A ' character may be specified by escaping it with a \.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
&0101010 Binary Number
|
|
|
|
|
123 Decimal Number
|
|
|
|
|
$FF Hexadecimal Number
|
|
|
|
|
'A' Character Literal
|
|
|
|
|
'\'' Escaped Character Literal
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
STRINGS
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
A string is a consecutive series of characters terminated by an ASCII null
|
|
|
|
|
character (a byte with the value 0).
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A string literal is written as up to 255 printable characters. prefixed and
|
|
|
|
|
suffixed with " characters.
|
2018-07-21 20:08:06 +00:00
|
|
|
|
|
|
|
|
|
The " character and a subset of ASCII control characters can be specified
|
|
|
|
|
in a string literal by using escape sequences prefixed with the \ symbol:
|
|
|
|
|
|
|
|
|
|
\b $08 Backspace
|
2018-08-16 20:26:44 +00:00
|
|
|
|
\e $1B Escape
|
2018-07-21 20:08:06 +00:00
|
|
|
|
\f $0C Form Feed
|
|
|
|
|
\n $0A Line Feed
|
|
|
|
|
\r $0D Carriage Return
|
|
|
|
|
\t $09 Tab
|
|
|
|
|
\v $0B Vertical Tab
|
|
|
|
|
\" $22 Double Quotation Mark
|
|
|
|
|
\\ $5C Backslash
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
SYMBOLS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A symbol consists of an alphabetic character followed by zero to five
|
2018-02-06 03:40:00 +00:00
|
|
|
|
alphanumeric characters. Four types of symbols are supported: labels,
|
|
|
|
|
simple variables, variable arrays, and functions.
|
|
|
|
|
|
|
|
|
|
A label specifies a target point for a goto statement. A label is written
|
|
|
|
|
as a symbol suffixed by a : character.
|
|
|
|
|
|
2018-03-07 16:38:22 +00:00
|
|
|
|
A constant represents a literal value. A constant is written as a symbol
|
|
|
|
|
prefixed by the # character.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A simple variable represents a single byte of memory. A variable is written
|
2018-02-06 03:40:00 +00:00
|
|
|
|
as a symbol without a suffix.
|
|
|
|
|
|
|
|
|
|
A variable array represents a block of up to 256 continuous bytes in
|
|
|
|
|
memory. An Array reference are written as a symbol suffixed a [ character,
|
|
|
|
|
index, and ] character. The lowest index of an array is 0, and the highest
|
|
|
|
|
index is one less than the number of bytes in the array. There is no bounds
|
2018-08-04 22:29:04 +00:00
|
|
|
|
checking on arrays: referencing an element beyond the end of the array will
|
2018-02-06 03:40:00 +00:00
|
|
|
|
access indeterminate memory locations.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A function is a subroutine that receives multiple values as arguments and
|
2018-02-06 03:40:00 +00:00
|
|
|
|
optionally returns a value. A function is written as a symbol suffixed with
|
|
|
|
|
a ( character, up to three arguments separated by commas, and a ) character,
|
|
|
|
|
|
|
|
|
|
The special symbols A, X, and Y represent the 6502 registers with the same
|
|
|
|
|
names. Registers may only be used in specific circumstances (which are
|
2018-08-04 22:29:04 +00:00
|
|
|
|
detailed in the following text). Various C02 statements modify registers
|
2018-02-06 03:40:00 +00:00
|
|
|
|
as they are processed, care should be taken when using them. However, when
|
|
|
|
|
used properly, register references can increase the efficiency of compiled
|
|
|
|
|
code.
|
|
|
|
|
|
|
|
|
|
STATEMENTS
|
|
|
|
|
|
|
|
|
|
Statements include declarations, assignments, stand-alone function calls,
|
|
|
|
|
and control structures. Most statements are suffixed with ; characters,
|
|
|
|
|
but some may be followed with program blocks.
|
|
|
|
|
|
|
|
|
|
BLOCKS
|
|
|
|
|
|
|
|
|
|
A program block is a series of statements surrounded by the { and }
|
|
|
|
|
characters. They may only be used with function definitions and control
|
2018-08-04 22:29:04 +00:00
|
|
|
|
structures.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-03-07 16:38:22 +00:00
|
|
|
|
CONSTANTS
|
|
|
|
|
|
2018-08-03 15:22:12 +00:00
|
|
|
|
A constant is defined by using the #define directive followed the constant
|
|
|
|
|
name (without the # prefix) and the literal value to be assigned to it.
|
2018-03-07 16:38:22 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
2018-08-03 15:22:12 +00:00
|
|
|
|
#define TRUE $FF
|
|
|
|
|
#define FALSE 0
|
|
|
|
|
#define BITS %01010101
|
|
|
|
|
#define ZED 'Z'
|
2018-03-07 16:38:22 +00:00
|
|
|
|
|
2018-03-07 17:00:33 +00:00
|
|
|
|
ENUMERATIONS
|
|
|
|
|
|
|
|
|
|
An enumeration is a sequential list of constants. Enumerations are used to
|
|
|
|
|
generate sets of related but distinct values.
|
|
|
|
|
|
|
|
|
|
An enumeration is defined using an enum statement. When using the enum
|
2018-08-04 22:29:04 +00:00
|
|
|
|
keyword, it is followed by a { character, one or more constant names
|
2018-03-07 17:00:33 +00:00
|
|
|
|
separated by commas, and a } character. The enum statement is terminated
|
|
|
|
|
with a semicolon.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
enum {BLACK, WHITE, RED, CYAN, PURPLE, GREEN, BLUE, YELLOW};
|
|
|
|
|
enum {NONE, FIRST, SECOND, THIRD};
|
|
|
|
|
enum {ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN};
|
|
|
|
|
|
|
|
|
|
Note: Values are automatically assigned to the constants in an enumeration.
|
|
|
|
|
The first constant following an #enum directive is assigned the value 0,
|
|
|
|
|
the second is assigned the value 1, and so on.
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
DECLARATIONS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A declaration statement consists of type keyword (char or void) followed
|
|
|
|
|
by one or more variable names and optional definitions, or a single
|
|
|
|
|
function name and optional function block.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Variables may only be of type char and all variable declaration statements
|
|
|
|
|
are suffixed with a ; character.
|
|
|
|
|
|
|
|
|
|
Examples:
|
2018-08-04 22:29:04 +00:00
|
|
|
|
char c; //Defines variable c
|
2018-02-06 03:40:00 +00:00
|
|
|
|
char i, j; //Defines variables i and j
|
2018-08-04 22:29:04 +00:00
|
|
|
|
char r[7]; //Defines 8 byte array r
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
A function declaration consists of the function name suffixed with a (
|
2018-08-04 22:29:04 +00:00
|
|
|
|
character, followed zero to three comma separated simple variables and
|
2018-02-06 03:40:00 +00:00
|
|
|
|
a ) character. A function declaration terminated with a ; character is
|
2018-08-04 22:29:04 +00:00
|
|
|
|
called a forward declaration and does not generate any code, while one
|
2018-02-06 03:40:00 +00:00
|
|
|
|
followed by a program block creates the specified function. Functions of
|
2018-08-04 22:29:04 +00:00
|
|
|
|
type char explicitly return a value (using a return statement), while
|
2018-02-06 03:40:00 +00:00
|
|
|
|
functions of type void do not.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
void myfunc(); //Forward declaration of function myfunc
|
|
|
|
|
char min(tmp1, tmp2) {if (tmp1 < tmp2) return tmp1; else return tmp2;}
|
|
|
|
|
|
|
|
|
|
Note: Like all variables, function parameters are global. They must be
|
|
|
|
|
declared prior to the function decaration, and retain there values after
|
|
|
|
|
the function call. Although functions may be called recursively, they are
|
2018-08-04 22:29:04 +00:00
|
|
|
|
not re-entrant. Allocation of variables and functions is implementation
|
|
|
|
|
dependent, they could be placed in any part of memory and in any order.
|
|
|
|
|
The default behavior is to place variables directly after the program code,
|
|
|
|
|
including them as part of the generated object file.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The return value of a function is passed through the A register. A return
|
2018-02-06 03:40:00 +00:00
|
|
|
|
statement with an explicit expression will simply process that expression
|
|
|
|
|
(which leaves the result in the A register) before returning. A return
|
|
|
|
|
statement without an expression (including an implicit return) will, by
|
|
|
|
|
default, return the value of the last processed expression.
|
|
|
|
|
|
2018-03-08 19:35:00 +00:00
|
|
|
|
STRUCTS
|
|
|
|
|
|
|
|
|
|
A struct is a special type of variable which is composed of one or more
|
|
|
|
|
unique variables called members. Each member may be either a simple
|
|
|
|
|
variable or an array. However, the total size of the struct may not
|
|
|
|
|
exceed 256 characters.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Member names are local to a struct: each member within a struct must have
|
|
|
|
|
a unique name, but the same member name can be used in different structs
|
2018-03-08 19:35:00 +00:00
|
|
|
|
and may also have the same name as a global variable.
|
|
|
|
|
|
|
|
|
|
The struct keyword is used for both defining the members of a struct type
|
|
|
|
|
as well as declaring struct type variables.
|
|
|
|
|
|
|
|
|
|
When defining a struct type, the struct keyword is followed by the name of
|
2018-08-04 22:29:04 +00:00
|
|
|
|
the struct type, the { character, the member definitions separated by
|
2018-03-08 19:35:00 +00:00
|
|
|
|
commas, and the } character. The struct definition is terminated with a
|
|
|
|
|
semicolon. Each member definition is composed of the optional char keyword,
|
2018-08-04 22:29:04 +00:00
|
|
|
|
and the member name. If the member is an array, the member name is suffixed
|
2018-03-08 19:35:00 +00:00
|
|
|
|
the [ character, the upper bound of the array, and the ] character. Each
|
|
|
|
|
member definition is terminated with a semicolon.
|
|
|
|
|
|
|
|
|
|
When declaring a struct variable, the struct keyword is followed by the struct
|
|
|
|
|
type name, and the name of the struct variable. The struct declaration is
|
|
|
|
|
terminated with a semicolon.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
struct record {char name[8]; char index; data[128];};
|
|
|
|
|
struct record rec;
|
|
|
|
|
|
|
|
|
|
Note: Unlike simple and array variable, the members of a struct variable
|
|
|
|
|
may not be initialized during declaration.
|
|
|
|
|
|
2018-07-19 03:44:42 +00:00
|
|
|
|
MODIFIERS
|
|
|
|
|
|
|
|
|
|
A modifier is used with a declaration to override the default properties of
|
|
|
|
|
an object. Modifiers may currently only be used with simple variable and
|
|
|
|
|
array declarations, although this may be expanded in the future.
|
|
|
|
|
|
2018-09-12 13:54:54 +00:00
|
|
|
|
The alias modifier specifies that a variable is to be located at a specific
|
|
|
|
|
address. The specified address may either be a literal in the range 0 to
|
|
|
|
|
65534 ($0 to $FFFF) or a previously defined variable name. When using the
|
|
|
|
|
alias modifier, the declared variable must be followed by the = character
|
|
|
|
|
and the literal or variable name to be aliased to.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The aligned modifier specifies that the the variable or array will start on
|
|
|
|
|
a page variable. This is used to ensure that accessing an array element will
|
|
|
|
|
not cross a page boundary, which requires extra CPU cycles to execute.
|
|
|
|
|
|
|
|
|
|
The const modifier specifies that a variable or array should not be changed
|
|
|
|
|
by program code. The const modifier may be preceded by the aligned modifier.
|
|
|
|
|
|
|
|
|
|
A const variable declaration may include an initial value definition in
|
|
|
|
|
the form of an = character and literal after the variable name.
|
|
|
|
|
|
|
|
|
|
A const array may be declared in one of two ways: the variable name
|
|
|
|
|
suffixed with a [ character, a literal specifying the upper bound of
|
|
|
|
|
the array, and a ] character; or a variable name followed by an = character
|
|
|
|
|
and string literal or series of atring and numeric literals separated by
|
|
|
|
|
commas and surrounded by the { or } characters.
|
|
|
|
|
|
2018-07-19 03:44:42 +00:00
|
|
|
|
The zeropage modifier specifies that the variable will be defined in page
|
|
|
|
|
zero (addresses 0 through 255). It should be used in conjunction with the
|
|
|
|
|
pragma zeropage directive.
|
|
|
|
|
|
|
|
|
|
Examples:
|
2018-09-12 13:54:54 +00:00
|
|
|
|
alias char putcon = $F001; //Defines variable putcon with address $F001
|
|
|
|
|
alias char alpha = omega; //Defines variable alpha aliased to omega
|
|
|
|
|
aligned char table[240]; //Defines 241 byte array aligned to page boundary
|
|
|
|
|
const char debug = #TRUE; //Defines variable debug initialized to constant
|
|
|
|
|
const char flag = 1; //Defines variable flag initialized to 1
|
|
|
|
|
const char s = "string"; //Defines 7 byte string s initialized to "string"
|
|
|
|
|
const char m = {1,2,3}; //Defines 3 byte array m containing 1, 2, and 3
|
2018-08-04 22:29:04 +00:00
|
|
|
|
aligned const char fbncci[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34};
|
2018-09-12 13:54:54 +00:00
|
|
|
|
zeropage char ptr, tmp; //Defines zero page variables
|
2018-07-19 03:44:42 +00:00
|
|
|
|
|
|
|
|
|
EXPRESSIONS
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
An expression is a series of one or more terms separated by operators.
|
|
|
|
|
|
|
|
|
|
The first term in an expression may be a function call, subscripted array
|
|
|
|
|
element, simple variable, literal, or register (A, X, or Y). An expression
|
|
|
|
|
may be preceded with a - character, in which case the first term is assumed
|
2018-03-07 04:35:47 +00:00
|
|
|
|
to be a literal 0.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-03-07 04:35:47 +00:00
|
|
|
|
Additional terms are limited to subscripted array elements, simple variables,
|
|
|
|
|
literals, and constants.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Operators:
|
|
|
|
|
+ — Add the following value.
|
2018-08-04 22:29:04 +00:00
|
|
|
|
- — Subtract the following value.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
& — Bitwise AND with the following value.
|
|
|
|
|
| — Bitwise OR with the following value.
|
|
|
|
|
^ — Bitwise Exclusive OR with the following value.
|
|
|
|
|
|
|
|
|
|
Arithmetic operators have no precedence. All operations are performed in
|
2018-08-04 22:29:04 +00:00
|
|
|
|
left to right order. Expressions may not contain parenthesis.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Note: the character ! may be substituted for | on systems that do not
|
2018-02-06 03:40:00 +00:00
|
|
|
|
support the latter character. No escaping is necessary because a ! may
|
|
|
|
|
not appear anywere a | would.
|
|
|
|
|
|
|
|
|
|
After an expression has been evaluated, the A register will contain the
|
2018-08-04 22:29:04 +00:00
|
|
|
|
result.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-07-19 18:45:40 +00:00
|
|
|
|
Note: Function calls are allowed in the first term of an expression
|
2018-08-04 22:29:04 +00:00
|
|
|
|
because upon return from the function the return value will be in the
|
|
|
|
|
Accumulator. However, due to the 6502 having only one Accumulatorm which
|
|
|
|
|
is used for all operations between two bytes, there is no simple system
|
2018-07-19 18:45:40 +00:00
|
|
|
|
agnostic method for allowing function calls in subsequent terms.
|
|
|
|
|
|
2018-08-02 04:15:32 +00:00
|
|
|
|
CONTENTIONS
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-02 04:15:32 +00:00
|
|
|
|
An contention is a construct which generates either TRUE or FALSE condition,
|
|
|
|
|
and may be an expressions, comparisons, or test.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A stand-alone expression evaluates to TRUE if the result is non-zero, or
|
2018-02-06 03:40:00 +00:00
|
|
|
|
FALSE if the result is zero.
|
|
|
|
|
|
|
|
|
|
A comparison consists of an expression, a comparator, and a term (subscripted
|
2018-03-07 04:35:47 +00:00
|
|
|
|
array element, simple variable, literal, or constant).
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Comparators:
|
|
|
|
|
= — Evaluates to TRUE if expression is equal to term
|
|
|
|
|
< — Evaluates to TRUE if expression is less than term
|
|
|
|
|
<= — Evaluates to TRUE if expression is less than or equal to term
|
|
|
|
|
> — Evaluates to TRUE if expression is greater than term
|
|
|
|
|
>= — Evaluates to TRUE if expression is greater than or equal to term
|
|
|
|
|
<> — Evaluates to TRUE if expression is not equal to term
|
|
|
|
|
|
|
|
|
|
The parser considers == equivalent to a single =. The operator <>
|
2018-08-04 22:29:04 +00:00
|
|
|
|
was chosen instead of the usual != because it simplified the parser design.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
A test consists of an expression followed by a test-op.
|
|
|
|
|
|
|
|
|
|
Test-Ops:
|
|
|
|
|
:+ — Evaluates to TRUE if the result of the expression is positive
|
|
|
|
|
:- — Evaluates to TRUE if the result of the expression is negative
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
A negative value is one in which the high bit is a 1 (128 — 255), while a
|
|
|
|
|
positive value is one in which the high bit is a 0 (0 — 127). The primary
|
|
|
|
|
purpose of test operators is to check the results of functions that return
|
2018-08-04 22:29:04 +00:00
|
|
|
|
a positive value upon succesful completion and a negative value if an error
|
|
|
|
|
was encounters. They compile into smaller code than would be generated
|
2018-02-06 03:40:00 +00:00
|
|
|
|
using the equivalent comparison operators.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
An contention may be preceded by negation operator (the ! character), which
|
2018-08-02 04:15:32 +00:00
|
|
|
|
reverses the result of the entire contention. For example:
|
2018-02-06 03:40:00 +00:00
|
|
|
|
! expr
|
|
|
|
|
evaluates to TRUE if expr is zero, or FALSE if it is non-zero; while
|
|
|
|
|
! expr = term
|
|
|
|
|
evaluates to TRUE if expr and term are not equal, or FALSE if they are; and
|
|
|
|
|
! expr :+
|
|
|
|
|
evaluates to TRUE if expr is negative, or FALSE if it is positive
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Note: Contentions are compiled directly into 6502 conditional branch
|
|
|
|
|
instructions, which precludes their use inside expressions. Standalone
|
|
|
|
|
expressions and test-ops generate a single branch instruction, and
|
2018-02-06 03:40:00 +00:00
|
|
|
|
therefore result in the most efficient code. Comparisons generate a
|
2018-08-04 22:29:04 +00:00
|
|
|
|
compare instruction and one or two branch instructions (=. <. >=, and <>
|
2018-03-07 04:35:47 +00:00
|
|
|
|
generate one, while <= and > generate two). A preceding negation operator
|
2018-02-06 03:40:00 +00:00
|
|
|
|
will switch the number of branch instructions used in a comparison, but
|
|
|
|
|
otherwise does not change the size of the generated code.
|
|
|
|
|
|
2018-08-02 04:15:32 +00:00
|
|
|
|
CONDITIONALS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A conditional consists of one or more contentions joined with the
|
2018-08-02 04:15:32 +00:00
|
|
|
|
conjunctors "and" and "or".
|
|
|
|
|
|
|
|
|
|
If only one contention is present, the result of the conditional is the
|
2018-08-04 22:29:04 +00:00
|
|
|
|
same as the result of the contention.
|
2018-08-02 04:15:32 +00:00
|
|
|
|
|
|
|
|
|
If two contentions are joined with "and", then the conditional is true only
|
|
|
|
|
if both of the contentions are true. If either or both of the contentions
|
|
|
|
|
are false, then the conditional is false.
|
|
|
|
|
|
|
|
|
|
If two contentions are joined with "or", then the conditional is true if
|
|
|
|
|
either or both of the contentions are true. If both of the contentions are
|
|
|
|
|
false, then the conditional is false.
|
|
|
|
|
|
|
|
|
|
When more three or more contentions are chained together, the conjunctors
|
|
|
|
|
are evaluated in left to right order, using short-circuit evaluation. If
|
|
|
|
|
the contention to the left of an "and" is false, then the entire conditional
|
|
|
|
|
evaluates to false, and if the contention to the left of an "or" is true,
|
|
|
|
|
then the entire conditional evaluates to true. In either case, no further
|
|
|
|
|
contentions in the conditional are evaluated.
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
ARRAY SUBSCRIPTS
|
|
|
|
|
|
|
|
|
|
Individual elements of an array are accessed using subscript notation.
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Subscripted array elements may be used as a terms in an expression, as
|
|
|
|
|
well as the target variable in an assignments. They are written as the
|
|
|
|
|
variable name suffixed with a [ character, followed by an index, and
|
|
|
|
|
the ] character.
|
2018-07-19 18:45:40 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
When assigning to an array element, the index may be a literal, constant,
|
2018-07-19 18:45:40 +00:00
|
|
|
|
or simple variable.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
When using an array element in an expression or pop statement, the index
|
|
|
|
|
may be any expression.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
2018-07-19 18:45:40 +00:00
|
|
|
|
z = r[i]; //Store the value from element i of array r into variable z
|
|
|
|
|
r[0] = z; //Store the value of variable z into the first element of r
|
|
|
|
|
z = d[15-i]; //Store the value element 15-i of array d into variable z
|
2018-08-04 22:29:04 +00:00
|
|
|
|
c = t[getc()]; //Get a character, translate using array t and store in c
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Note: Register references may be used as array indexes within expressions,
|
|
|
|
|
but the contents of each registers may change with each term evaluation.
|
|
|
|
|
Using a constant, literal, or the X or Y registers as an array index will
|
|
|
|
|
generates the same amount of code as a simple variable reference and leave
|
2018-07-22 02:23:10 +00:00
|
|
|
|
both the X and Y registers unchanged. Using the A register as an index will
|
2018-08-04 22:29:04 +00:00
|
|
|
|
generate one extra byte of code, while using a simple variable as index
|
2018-07-22 02:23:10 +00:00
|
|
|
|
will generate 1 to 2 extra bytes of code. In either case, the index value
|
|
|
|
|
will be left in the X register. When an expression is used as an index,
|
2018-08-04 22:29:04 +00:00
|
|
|
|
one extra byte of stack space is used, and an additional three bytes of
|
|
|
|
|
code is generated. The X register will contain the result of the expression
|
2018-07-22 02:23:10 +00:00
|
|
|
|
and the Y register will be left in an unknown state.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-03-08 19:35:00 +00:00
|
|
|
|
STRUCTS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Individual members of a struct variable are specified using the struct
|
2018-03-08 19:35:00 +00:00
|
|
|
|
variable name, a period, and the member name. If a member is an array,
|
|
|
|
|
it's elements are accessed using the same syntax as an array variable.
|
|
|
|
|
|
|
|
|
|
A struct variable can also be treated like an array variable, with each
|
|
|
|
|
byte of the variable accessed as an array index.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
i = rec.index; //Get Struct Member
|
|
|
|
|
rec.data[i] = i; //Set Struct Member Element
|
|
|
|
|
arr[i] = rec[i]; //Copy Struct Byte into Array
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Note: Unlike standard C, structs may not be assigned using an equals
|
2018-03-08 19:35:00 +00:00
|
|
|
|
sign. One struct variable may be copied to another byte by byte or
|
|
|
|
|
through a function call.
|
|
|
|
|
|
2018-03-09 01:07:31 +00:00
|
|
|
|
SIZE-OF OPERATOR
|
|
|
|
|
|
|
|
|
|
The size-of operator @ generates a literal value equal to the size in bytes
|
2018-08-04 22:29:04 +00:00
|
|
|
|
of a specified variable. It is allowed anywhere a literal would be and
|
2018-03-09 01:07:31 +00:00
|
|
|
|
should be used anytime the size of an array, struct, or member is required.
|
|
|
|
|
|
|
|
|
|
When using the size-of operator, it is prefixed to the variable name or
|
|
|
|
|
member specification.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
for (i=0; i<=@z; i++) z[i] = r[i]; //Copy elements from r[] to z[]
|
|
|
|
|
blkput(@rec ,&rec); //Copy struct rec to next block segment
|
|
|
|
|
memcpy(@rec.data, &rec.data); //Copy member data to destination array
|
|
|
|
|
|
|
|
|
|
Note: The size-of operator is evaluated at compile time and generates two
|
|
|
|
|
bytes of machine language code. It is the most efficient method of specifying
|
|
|
|
|
a variable length.
|
|
|
|
|
|
2018-07-30 17:03:54 +00:00
|
|
|
|
INDEX-OF OPERATOR
|
|
|
|
|
|
|
|
|
|
The index-of operator ? generates a literal value equal to the offset in bytes
|
|
|
|
|
of a specified stucture member. It is allowed anywhere a literal would be and
|
|
|
|
|
should be used anytime the offset of the member of a struct is required.
|
|
|
|
|
|
|
|
|
|
When using the size-of operator, it is prefixed to the member specification.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
blkmem(?rec.data, &s); //Search block for segment where data contains s
|
2018-07-30 17:03:54 +00:00
|
|
|
|
memcpy(?rec.data, &t); //Copy bytes of rec up to member data into t
|
|
|
|
|
|
|
|
|
|
Note: The idex-of operator is evaluated at compile time and generates two
|
|
|
|
|
bytes of machine language code. It is the most efficient method of specifying
|
|
|
|
|
a the offset of a struct member.
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
FUNCTION CALLS
|
|
|
|
|
|
|
|
|
|
A function call may be used as a stand-alone statement, or as the first
|
2018-08-04 22:29:04 +00:00
|
|
|
|
term in an expression. A function call consists of the function name
|
|
|
|
|
appended with a ( character, followed by zero to three arguments separated
|
2018-02-06 03:40:00 +00:00
|
|
|
|
with commas, and a closing ) character.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The first argument of a function call may be an expression, address, or
|
2018-02-06 03:40:00 +00:00
|
|
|
|
string (see below).
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The second argument may be a term (subscripted array element, simple
|
2018-02-06 03:40:00 +00:00
|
|
|
|
variable, or constant), address, or string,
|
|
|
|
|
|
|
|
|
|
The third argument may only be a simple variable or constant.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
If the first or second argument is an address or string, then no more
|
2018-02-06 03:40:00 +00:00
|
|
|
|
arguments may be passed.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
When passing the address of a variable, array, struct, or struct member
|
|
|
|
|
into a function, the variable specification is prefixed with the
|
|
|
|
|
address-of operator &. When passing a string, the string is simply
|
2018-03-08 19:35:00 +00:00
|
|
|
|
specified as the argument with.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
c = getc(); //Get character from keyboard
|
|
|
|
|
n = abs(b+c-d); //Return the absolute value of result of expression
|
|
|
|
|
m = min(r[i], r[j]); //Return lesser of to array elements
|
|
|
|
|
l = strlen(&s); //Return the length of string s
|
|
|
|
|
p = strchr(c, &s); //Return position of character c in string s
|
|
|
|
|
putc(getc()); //Echo typed characters to screen
|
|
|
|
|
puts("Hello World"); //Write "Hello World" to screen
|
2018-03-08 19:35:00 +00:00
|
|
|
|
memdst(&dstrec); //Set struct variable as destination
|
|
|
|
|
memcpy(140, &srcrec); //Copy struct variable to destination struct
|
|
|
|
|
puts(&rec.name); //Write struct membet to screen
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Note: This particular argument passing convention has been chosen because
|
|
|
|
|
of the 6502's limited number of registers and stack processing instructions.
|
|
|
|
|
When an address is passed, the high byte is stored in the Y register and
|
|
|
|
|
the low byte in the X register. If a string is passed, it is turned into
|
|
|
|
|
anonymous array, and it's address is passed in the Y and X registers.
|
|
|
|
|
Otherwise, the first argument is passed in the A register, the second in
|
|
|
|
|
the Y register, and the third in the X register.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
EXTENDED PARAMETER PASSING
|
|
|
|
|
|
|
|
|
|
To enable direct calling of machine language routines that that do not match
|
|
|
|
|
the built-in parameter passing convention, C02 supports the non-standard
|
|
|
|
|
statements push, pop, and inline.
|
|
|
|
|
|
|
|
|
|
The push statement is used to push arguments onto the machine stack prior
|
|
|
|
|
to a function call. When using a push statement, it is followed by one or
|
2018-03-07 16:38:22 +00:00
|
|
|
|
more arguments, separated by commas, and terminated with a semicolon. An
|
2018-08-04 22:29:04 +00:00
|
|
|
|
argument may be an expression, in which case the single byte result is
|
2018-02-06 03:40:00 +00:00
|
|
|
|
pushed onto the stack, or it may be an address or string, in which case the
|
|
|
|
|
address is pushed onto the stack, high byte first and low byte second.
|
|
|
|
|
|
|
|
|
|
The pop statement is likewise used to pop arguments off of the machine
|
|
|
|
|
stack after a function call. When using a pop statement, it is followed
|
2018-08-04 22:29:04 +00:00
|
|
|
|
with one or more simple variables or subscripted array elements , separated
|
|
|
|
|
by commas, and terminated with a semicolon. If any of the arguments are to
|
2018-07-19 18:45:40 +00:00
|
|
|
|
be discarded, an asterisk can be specified instead of a variable name.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
The number of arguments pushed and popped may or may not be the same,
|
|
|
|
|
depending on how the machine language routine manipulates the stack pointer.
|
|
|
|
|
|
|
|
|
|
Examples:
|
2018-07-19 18:45:40 +00:00
|
|
|
|
push d,r; mult(); pop p; //multiply d times r and store in p
|
|
|
|
|
push x1,y1,x2,y2; rect(); pop *,*,*,*; //draw rectangle from x1,y1 to x2,y2
|
|
|
|
|
push &s, "tail"; strcat(); //concatenate "tail" onto string s
|
|
|
|
|
push x[i],y[i]; rotate(d); pop x[i],y[i]; //rotate point x[1],y[i] by d
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Note: The push and pop statements could also be used to manipulate the
|
|
|
|
|
stack inside or separate from a function, but this should be done with
|
|
|
|
|
care.
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
The inline statement is used when calling machine language routines that
|
|
|
|
|
expect constant byte or word values immediately following the 6502 JSR
|
|
|
|
|
instruction. A routine of this type will adjust the return address to the
|
|
|
|
|
point directly after the last instruction. When using the inline statement,
|
2018-08-04 22:29:04 +00:00
|
|
|
|
it is followed by one or more arguments, separated by commas, and
|
2018-02-06 03:40:00 +00:00
|
|
|
|
terminated with a semicolon. The arguments may be constants, addresses,
|
|
|
|
|
or strings.
|
|
|
|
|
|
|
|
|
|
Examples;
|
|
|
|
|
iprint(); inline "Hello World"; //Print "Hello World"
|
|
|
|
|
irect(); inline 10,10,100,100; //Draw rectangle from (10,10) to (100,100)
|
|
|
|
|
|
|
|
|
|
Note: If a string is specified in an inline statement, rather than creating
|
|
|
|
|
an anonymous string and compiling the address inline, the entire string will
|
|
|
|
|
be compiled directly inline.
|
|
|
|
|
|
|
|
|
|
ASSIGNMENTS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
An assignment is a statement in which the result of an expression is stored
|
|
|
|
|
in a variable. An assignment usually consists of a simple variable or
|
|
|
|
|
subscripted array element, an = character, and an expression, terminated
|
2018-02-06 03:40:00 +00:00
|
|
|
|
with a ; character.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
i = i + 1; //Add 1 to contents variable i
|
|
|
|
|
c = getchr(); //Call function and store result in variable c
|
|
|
|
|
s[i] = 0; //Terminate string at position i
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
SHORTCUT-IFS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A shortcut-if is a special form of assignment consisting of an contention
|
2018-02-06 03:40:00 +00:00
|
|
|
|
and two expressions, of which one will be assigned based on the result
|
2018-08-04 22:29:04 +00:00
|
|
|
|
of the contention. A shortcut-if is written as a condition surrounded
|
|
|
|
|
by ( and ) characters, followed by a ? character, the expression to be
|
|
|
|
|
evaluated if the condition was true, a : character, and the expression to
|
2018-02-06 03:40:00 +00:00
|
|
|
|
be evaluated if the condition was false.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
result = (value1 < value) ? value1 : value2;
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
Note: Shortcut-ifs may only be used with assignments. This may change in
|
2018-08-04 22:29:04 +00:00
|
|
|
|
the future.
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
POST-OPERATORS
|
|
|
|
|
|
|
|
|
|
A post-operator is a special form of assignment which modifies the value
|
2018-08-04 22:29:04 +00:00
|
|
|
|
of a variable. The post-operator is suffixed to the variable it modifies.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Post-Operators:
|
|
|
|
|
++ Increment variable (increase it's value by 1)
|
|
|
|
|
-- Decrement variable (decrease it's value by 1)
|
|
|
|
|
<< Left shift variable
|
|
|
|
|
>> Right shift variable
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
Post-operators may be used with either simple variables or subscripted
|
2018-08-04 22:29:04 +00:00
|
|
|
|
array elements.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
i++; //Increment the contents variable i
|
2018-03-07 04:35:47 +00:00
|
|
|
|
b[i]<<; //Left shift the contents of element i of array b
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
Note: Post-operators may only be used in stand-alone statements, although
|
|
|
|
|
this may change in the future.
|
|
|
|
|
|
|
|
|
|
ASSIGNMENTS TO REGISTERS
|
|
|
|
|
|
|
|
|
|
Registers A, X, and Y may assigned to using the = character. Register A
|
|
|
|
|
(but not X or Y) may be used with the << and >> post-operators, while
|
|
|
|
|
registers X and Y (but not A) may be used with the ++ and -- post-operators.
|
|
|
|
|
|
|
|
|
|
IMPLICIT ASSIGNMENTS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A statement consisting of only a simple variable is treated as an
|
2018-02-06 03:40:00 +00:00
|
|
|
|
implicit assignment of the A register to the variable in question.
|
|
|
|
|
|
|
|
|
|
This is useful on systems that use memory locations as strobe registers.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
HMOVE; //Move Objects (Atari VCS)
|
|
|
|
|
S80VID; //Enable 80-Column Video (Apple II)
|
|
|
|
|
|
|
|
|
|
Note: An implicit assignment generates an STA opcode with the variable
|
|
|
|
|
as the operand.
|
|
|
|
|
|
2018-03-02 02:47:16 +00:00
|
|
|
|
PLURAL ASSIGNMENTS
|
|
|
|
|
|
|
|
|
|
C02 allows a function to return up to three values by specifying multiple
|
|
|
|
|
variables, separated by commas, to the left of the assignment operator (=).
|
|
|
|
|
|
2018-07-22 02:23:10 +00:00
|
|
|
|
All three variables to be assigned may be either simple variables or
|
|
|
|
|
subscripted array elements. Registers are not allowed in plural assignments.
|
2018-03-02 02:47:16 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
2018-07-19 18:45:40 +00:00
|
|
|
|
row, col = scnpos(); //Get current screen position
|
|
|
|
|
cr, mn, mx = cpmnmx(a, b); //Compare two values, return min and max
|
|
|
|
|
x[i], y[i] = rotate(x[i],y[i],d); //Rotate x[i] and y[i] by d degrees
|
2018-07-22 02:23:10 +00:00
|
|
|
|
x[i], y[i], z[i] = get3d(i); //Generate 3d coordinate for index i
|
2018-03-02 02:47:16 +00:00
|
|
|
|
|
|
|
|
|
Note: When compiled, a plural assignment generates an STX for the third
|
|
|
|
|
assignment (if specified), an STY for the second assignment and an STA for
|
2018-07-22 02:23:10 +00:00
|
|
|
|
the first assignment. Using a subscripted array element for the third
|
2018-08-04 22:29:04 +00:00
|
|
|
|
assignment generates an overhead of three bytes of machine code.
|
2018-03-02 02:47:16 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
GOTO STATEMENT
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A goto statement unconditionally transfers program execution to the
|
2018-02-06 03:40:00 +00:00
|
|
|
|
specified label. When using a goto statement, it is followed by the
|
|
|
|
|
label name and a terminating semicolon.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
goto end;
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
Note: A goto statement may be executed from within a loop structure
|
2018-08-04 22:29:04 +00:00
|
|
|
|
(although a break or continue statement is preferred), but should not
|
|
|
|
|
normally be used to jump from inside a function to outside of it, as
|
2018-02-06 03:40:00 +00:00
|
|
|
|
this would leave the return address on the machine stack.
|
|
|
|
|
|
|
|
|
|
IF AND ELSE STATEMENTS
|
|
|
|
|
|
|
|
|
|
The if then and else statements are used to conditionally execute blocks
|
2018-08-04 22:29:04 +00:00
|
|
|
|
of code.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-02 04:15:32 +00:00
|
|
|
|
When using the if keyword, it is followed by a conditional (surrounded by
|
2018-08-04 22:29:04 +00:00
|
|
|
|
parenthesis) and the block of code to be executed if the conditional was
|
2018-08-02 04:15:32 +00:00
|
|
|
|
true.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
An else statement may directly follow an if statement (with no other
|
2018-02-06 03:40:00 +00:00
|
|
|
|
executable code intervening). The else keyword is followed by the block
|
2018-08-02 04:15:32 +00:00
|
|
|
|
of code to be executed if the conditional was false.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
if (c = 27) goto end;
|
2018-07-19 03:44:42 +00:00
|
|
|
|
if (n) q = div(n,d) else puts("Division by 0!");
|
2018-02-06 03:40:00 +00:00
|
|
|
|
if (r[j]<r[i]) {t=r[i],r[i]=r[j],r[j]=t)}
|
2018-08-04 22:29:04 +00:00
|
|
|
|
|
|
|
|
|
Note: In order to optimize the compiled code, the if and else statements
|
|
|
|
|
are to 6502 relative branch instructions. This limits the amount of
|
2018-02-06 03:40:00 +00:00
|
|
|
|
generated code between the if statement and the end of the if/else block
|
2018-08-04 22:29:04 +00:00
|
|
|
|
to slightly less than 127 bytes. This should be sufficient in most cases,
|
|
|
|
|
but larger code blocks can be accommodated using function calls or goto
|
2018-03-02 02:47:16 +00:00
|
|
|
|
statements.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
SELECT, CASE, AND DEFAULT STATEMENTS
|
|
|
|
|
|
|
|
|
|
The select, case, an default statements are used to execute a specific
|
|
|
|
|
block of code depending on the result of an expression.
|
|
|
|
|
|
|
|
|
|
When using the select keyword, it is followed by an expression (surrounded
|
|
|
|
|
by parenthesis) and an opening curly brace, which begins the select block.
|
|
|
|
|
This must then be followed by a case statement.
|
|
|
|
|
|
2018-02-08 03:50:08 +00:00
|
|
|
|
Each use of the case keyword is followed by one or more comma-separated
|
2018-08-04 22:29:04 +00:00
|
|
|
|
terms and a colon. If the term is equal to the select expression then the
|
|
|
|
|
code immediately following the is executed, otherwise, program execution
|
|
|
|
|
transfers to the next case or default statement.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
The code between two case statements or a case and default statement is
|
|
|
|
|
called a case block. At the end of a case block, program execution
|
|
|
|
|
transfers to the end of the select block (the closing curly brace at
|
|
|
|
|
the end of the default block).
|
|
|
|
|
|
|
|
|
|
The last case block must be followed by a default statement. When using
|
|
|
|
|
the default keyword, it is followed by a colon. The code between the
|
|
|
|
|
default statement and the end of the select block (marked with a closing
|
|
|
|
|
curly-brace) is called the default block and is executed if none of
|
|
|
|
|
the case arguments matched the select expression.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
If the constant 0 is to be used as an argument to any of the case
|
|
|
|
|
statements, using it as the first argument of the first case statement
|
2018-02-08 03:50:08 +00:00
|
|
|
|
will produce slightly more efficient code.
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
Example:
|
|
|
|
|
puts("You pressed ");
|
|
|
|
|
select (getc()) {
|
2018-02-08 03:50:08 +00:00
|
|
|
|
case $00: putln("Nothing");
|
2018-02-06 03:40:00 +00:00
|
|
|
|
case $0D: putln("The Enter key");
|
|
|
|
|
case ' ': putln("The space bar");
|
2018-02-08 03:50:08 +00:00
|
|
|
|
case 'A','a': putln ("The letter A");
|
2018-02-06 03:40:00 +00:00
|
|
|
|
case ltr: putln("The character in variable 'ltr'");
|
2018-08-04 22:29:04 +00:00
|
|
|
|
case s[2]: putln("The third character of string 's'");
|
2018-02-06 03:40:00 +00:00
|
|
|
|
default: putln("some other key");
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Unlike the switch statement in C, the break statement is not needed to
|
|
|
|
|
exit from a case block. It may be used, however, to prematurely exit a
|
2018-02-08 03:50:08 +00:00
|
|
|
|
case block if desired.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
select (arg) {
|
2018-08-04 22:29:04 +00:00
|
|
|
|
case foo:
|
2018-02-08 03:50:08 +00:00
|
|
|
|
puts("fu");
|
|
|
|
|
if (!bar) break;
|
|
|
|
|
puts("bar");
|
|
|
|
|
default: //do nothing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
In addition, fall through of case blocks can be duplicated using the goto
|
|
|
|
|
statement with a label.
|
|
|
|
|
|
|
|
|
|
select (num)
|
|
|
|
|
case 1:
|
|
|
|
|
putc('I');
|
|
|
|
|
goto two;
|
|
|
|
|
case 2:
|
2018-08-04 22:29:04 +00:00
|
|
|
|
two:
|
2018-02-08 03:50:08 +00:00
|
|
|
|
putc('I');
|
|
|
|
|
default: //do nothing
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
Note: It's possible for multiple case statement arguments to evaluate to
|
|
|
|
|
the same value. In this case, only the first case block matching the
|
|
|
|
|
select expression will be executed.
|
|
|
|
|
|
|
|
|
|
WHILE LOOPS
|
|
|
|
|
|
|
|
|
|
The while statement is used to conditionally execute code in a loop. When
|
2018-08-02 04:15:32 +00:00
|
|
|
|
using the while keyword, it is followed by a conditional (surrounded by
|
|
|
|
|
parenthesis) and the the block of code to be executed while the conditional
|
|
|
|
|
is true. If the conditional is false when the while statement is entered,
|
2018-02-06 03:40:00 +00:00
|
|
|
|
the code in the block will never be executed.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Alternatively, the while keyword may be followed by a pair of empty
|
2018-08-02 04:15:32 +00:00
|
|
|
|
parenthesis, in which case a conditional of true is implied.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
2018-03-07 04:35:47 +00:00
|
|
|
|
c = 'A' ; while (c <= 'Z') {putc(c); c++;} //Print letters A-Z
|
|
|
|
|
while() if (rdkey()) break; //Wait for a keypress
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Note: While loops are compiled using the 6502 JMP statements, so the code
|
|
|
|
|
blocks may be arbitrarily large.
|
|
|
|
|
|
|
|
|
|
DO WHILE LOOPS
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
The do statement used with to conditionally execute code in a loop at
|
2018-02-06 03:40:00 +00:00
|
|
|
|
least once. When using the do keyword, it is followed by the block of
|
2018-08-02 04:15:32 +00:00
|
|
|
|
code to be executed, a while statement, a conditional (surrounded
|
2018-02-06 03:40:00 +00:00
|
|
|
|
by parenthesis), and a terminating semicolon.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A while statement that follows a do loop must contain a conditional.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
The while statement is evaluated after each iteration of the loop, and
|
|
|
|
|
if it is true, the code block is repeated.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
do c = rdkey(); while (c=0); //Wait for keypress
|
|
|
|
|
do (c = getchr(); putchr(c); while (c<>13) //Echo line to screen
|
|
|
|
|
|
|
|
|
|
Note: Unlike the other loop structures do/while statements do not use
|
|
|
|
|
6502 JMP instructions. This optimizes the compiled code, but limits
|
2018-03-07 04:35:47 +00:00
|
|
|
|
the code inside the loop to just under 127 bytes.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
FOR LOOPS
|
|
|
|
|
|
|
|
|
|
The for statement allows the initialization, evaluation, and modification
|
2018-08-04 22:29:04 +00:00
|
|
|
|
of a loop condition in one place. For statements are usually used to
|
2018-02-06 03:40:00 +00:00
|
|
|
|
execute a piece of code a specific number of times, or to iterate through
|
|
|
|
|
a set of values.
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
When using the if keyword, it is followed by a pair of parenthesis
|
|
|
|
|
containing an initialization assignment statement (which is executed once),
|
|
|
|
|
a semicolon separator, a conditional (which determines if the code block
|
|
|
|
|
is executed), another semicolon separator, and an increment assignment
|
|
|
|
|
(which is executed after each iteration of the code block). This is then
|
2018-02-06 03:40:00 +00:00
|
|
|
|
followed by the block of code to be conditionally executed.
|
|
|
|
|
|
|
|
|
|
The assignments and conditional of a for loop must be populated. If an
|
|
|
|
|
infinite loop is desired, use a while () statement.
|
|
|
|
|
|
|
|
|
|
Examples:
|
2018-03-07 04:35:47 +00:00
|
|
|
|
for (c='A'; c<='Z'; c++) putc(c); //Print letters A-Z
|
|
|
|
|
for (i=strlen(s)-1;i:+;i--) putc(s[i]); //Print string s backwards
|
2018-08-04 22:29:04 +00:00
|
|
|
|
for (i=0;c>0;i++) {c=getc();s[i]=c} //Read characters into string s
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Note: For loops are compiled using the 6502 JMP statements, so the code
|
2018-03-07 04:35:47 +00:00
|
|
|
|
blocks may be arbitrarily large. A for loop generates less efficient code
|
2018-08-04 22:29:04 +00:00
|
|
|
|
more than a simple while loop, but will always execute the increment
|
2018-02-06 03:40:00 +00:00
|
|
|
|
assignment on a continue.
|
|
|
|
|
|
|
|
|
|
BREAK AND CONTINUE
|
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
A break statement is used to exit out of a do, for, or while loop or a
|
|
|
|
|
case block. The continue statement is used to jump to the beginning of
|
2018-03-07 04:35:47 +00:00
|
|
|
|
a do, for, or while loop. Neither may be used outside it's corresponding
|
|
|
|
|
control structures.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
When a break statement is encountered, program execution is transferred
|
2018-08-04 22:29:04 +00:00
|
|
|
|
to the statement immediately following the end of the block associated
|
|
|
|
|
with the innermost do, for, while, or case statement. When using the
|
2018-03-07 04:35:47 +00:00
|
|
|
|
break keyword, it is followed with a trailing semicolon.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
When a continue statement is encountered, program execution is transferred
|
2018-08-04 22:29:04 +00:00
|
|
|
|
to the beginning of the block associated with the innermost do, for, or
|
|
|
|
|
while statement. In the case of a for statement, the increment assignment
|
|
|
|
|
is executed, followed by the conditional, and in the case of a while
|
|
|
|
|
statement, the conditional is executed. When using the continue keyword, it
|
2018-03-07 04:35:47 +00:00
|
|
|
|
is followed with a trailing semicolon.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
do {c=rdkey(); if (c=0) continue; if (c=27) break;} while (c<>13);`
|
|
|
|
|
for (i=0;i<strlen(s);i++) {if (s[i]=0) break; putchr(s[i]);}
|
|
|
|
|
while() {c=rdkey;if (c=0) continue;putchr(c);if (c=13) break;}
|
|
|
|
|
|
|
|
|
|
UNIMPLEMENTED FEATURES
|
|
|
|
|
|
2018-08-03 15:22:12 +00:00
|
|
|
|
The #define directive allows the definition of constants but not macros.
|
2018-03-07 17:10:23 +00:00
|
|
|
|
|
|
|
|
|
The #if, #else, and #endif directives are not recognized at all by the
|
|
|
|
|
compiler. They may be added in the future.
|
|
|
|
|
|
2018-02-06 03:40:00 +00:00
|
|
|
|
The only type recognized by the compiler is char. Since the 6502 is an
|
2018-08-04 22:29:04 +00:00
|
|
|
|
8-bit processor, multi-byte types would generate over-complicated code.
|
|
|
|
|
In addition, the signed and unsigned keywords are unrecognized, due to the
|
2018-03-07 04:35:47 +00:00
|
|
|
|
6502's limited signed comparison functionality.
|
2018-02-06 03:40:00 +00:00
|
|
|
|
|
2018-08-04 22:29:04 +00:00
|
|
|
|
Because of the 6502's peculiar indirect addressing modes, pointers are not
|
2018-03-07 04:35:47 +00:00
|
|
|
|
currently implemented. Limited pointer operations may be implemented using
|
|
|
|
|
zero page variables in the future.
|