Merge branch 'master' into next_compositetypes

This commit is contained in:
Irmen de Jong 2024-10-27 01:02:30 +02:00
commit df1a038932
8 changed files with 168 additions and 132 deletions

View File

@ -915,6 +915,17 @@ internal class AstChecker(private val program: Program,
errors.err(msg, directive.position)
}
when(directive.directive) {
"%align" -> {
if(directive.parent !is INameScope || directive.parent is Module)
err("this directive can't be used here")
if(directive.args.size!=1 || directive.args[0].int == null)
err("missing correct alignment size")
if(directive.args[0].int!! >= 64u)
errors.info("large alignment might waste a lot of memory (check Gaps in assembler output)", directive.position)
val prev = directive.previousSibling()
if (prev !=null && prev !is Block && prev !is Break && prev !is Continue && prev !is Jump && prev !is Return && prev !is Subroutine && prev !is VarDecl)
errors.warn("dangerous location for %align, after a regular statement it will likely corrupt the program", directive.position)
}
"%output" -> {
if(directive.parent !is Module)
err("this directive may only occur at module level")
@ -1009,14 +1020,6 @@ internal class AstChecker(private val program: Program,
if(directive.args.size!=1 || directive.args[0].name !in allowedEncodings)
err("invalid encoding directive, expected one of $allowedEncodings")
}
"%align" -> {
if(directive.parent !is INameScope || directive.parent is Module)
err("this directive can't be used here")
if(directive.args.size!=1 || directive.args[0].int == null)
err("missing correct alignment size")
if(directive.args[0].int!! >= 64u)
errors.info("large alignment might waste a lot of memory (check Gaps in assembler output)", directive.position)
}
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
}
super.visit(directive)

View File

@ -28,7 +28,7 @@ Comments
Directive
These are special instructions for the compiler, to change how it processes the code
and what kind of program it creates. A directive is on its own line in the file, and
starts with ``%``, optionally followed by some arguments.
starts with ``%``, optionally followed by some arguments. See the syntax reference for all directives.
Code block
A block of actual program code. It has a starting address in memory,
@ -202,7 +202,7 @@ Values will usually be part of an expression or assignment statement::
byte counter = 42 ; variable of size 8 bits, with initial value 42
*putting a variable in zeropage:*
**putting a variable in zeropage:**
If you add the ``@zp`` tag to the variable declaration, the compiler will prioritize this variable
when selecting variables to put into zeropage (but no guarantees). If there are enough free locations in the zeropage,
it will try to fill it with as much other variables as possible (before they will be put in regular memory pages).
@ -217,7 +217,7 @@ Example::
uword @requirezp zppointer = $4000
*shared tag:*
**shared variables:**
If you add the ``@shared`` tag to the variable declaration, the compiler will know that this variable
is a prog8 variable shared with some assembly code elsewhere. This means that the assembly code can
refer to the variable even if it's otherwise not used in prog8 code itself.
@ -227,6 +227,18 @@ when assembling the rest of the code). Example::
byte @shared assemblyVariable = 42
**memory alignment:**
A string or array variable can be aligned to a couple of possible interval sizes in memory.
The use for this is very situational, but two examples are: sprite data for the C64 that needs
to be on a 64 byte aligned memory address, or an array aligned on a full page boundary to avoid
any possible extra page boundary clock cycles on certain instructions when accessing the array.
You can align on word, 64 bytes, and page boundaries::
ubyte[] @alignword array = [1, 2, 3, 4, ...]
ubyte[] @align64 spritedata = [ %00000000, %11111111, ...]
ubyte[] @alignpage lookup = [11, 22, 33, 44, ...]
Integers
^^^^^^^^

View File

@ -40,73 +40,6 @@ Examples::
Directives
-----------
.. data:: %output <type>
Level: module.
Global setting, selects program output type. Default is ``prg``.
- type ``raw`` : no header at all, just the raw machine code data
- type ``prg`` : C64 program (with load address header)
.. data:: %launcher <type>
Level: module.
Global setting, selects the program launcher stub to use.
Only relevant when using the ``prg`` output type. Defaults to ``basic``.
- type ``basic`` : add a tiny C64 BASIC program, with a SYS statement calling into the machine code
- type ``none`` : no launcher logic is added at all
.. data:: %zeropage <style>
Level: module.
Global setting, select zeropage handling style. Defaults to ``kernalsafe``.
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
and don't change anything else. This allows full use of Kernal ROM routines (but not BASIC routines),
including default IRQs during normal system operation.
It's not possible to return cleanly to BASIC when the program exits. The only choice is
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
are required to perform floating point operations (from the BASIC Kernal). No clean exit is possible.
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
touch change anything else. This allows full use of BASIC and Kernal ROM routines including default IRQs
during normal system operation.
When the program exits, it simply returns to the BASIC ready prompt.
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
except for a few addresses that are used by the system's IRQ handler.
Even though that default IRQ handler is still active, it is impossible to use most BASIC and Kernal ROM routines.
This includes many floating point operations and several utility routines that do I/O, such as ``print``.
This option makes programs smaller and faster because even more variables can
be stored in the ZP (which allows for more efficient assembly code).
It's not possible to return cleanly to BASIC when the program exits. The only choice is
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
- style ``dontuse`` -- don't use *any* location in the zeropage.
.. note::
``kernalsafe`` and ``full`` on the C64 leave enough room in the zeropage to reallocate the
16 virtual registers cx16.r0...cx16.r15 from the Commander X16 into the zeropage as well
(but not on the same locations). They are relocated automatically by the compiler.
The other options need those locations for other things so those virtual registers have
to be put into memory elsewhere (outside of the zeropage). Trying to use them as zeropage
variables or pointers etc. will be a lot slower in those cases!
On the Commander X16 the registers are always in zeropage. On other targets, for now, they
are always outside of the zeropage.
.. data:: %zpreserved <fromaddress>,<toaddress>
Level: module.
Global setting, can occur multiple times. It allows you to reserve or 'block' a part of the zeropage so
that it will not be used by the compiler.
.. data:: %zpallowed <fromaddress>,<toaddress>
Level: module.
Global setting, can occur multiple times. It allows you to designate a part of the zeropage that
the compiler is allowed to use (if other options don't prevent usage).
.. data:: %address <address>
Level: module.
@ -115,44 +48,39 @@ Directives
you don't use a CBM-BASIC launcher.
.. data:: %import <name>
.. data:: %align <interval>
Level: module.
This reads and compiles the named module source file as part of your current program.
Symbols from the imported module become available in your code,
without a module or filename prefix.
You can import modules one at a time, and importing a module more than once has no effect.
Level: not at module scope.
Tells the assembler to continue assembling on the given alignment interval. For example, ``%align $100``
will insert an assembler command to align on the next page boundary.
Note that this has no impact on variables following this directive! Prog8 reallocates all variables
using different rules. If you want to align a specific variable (array or string), you should use
one of the alignment tags for variable declarations instead.
Valid intervals are from 2 to 65536.
**Warning:** if you use this directive in between normal statements, it will disrupt the output
of the machine code instructions by making gaps between them, this will probably crash the program!
.. data:: %option <option> [, <option> ...]
.. data:: %asm {{ ... }}
Level: module, block.
Sets special compiler options.
Level: not at module scope.
Declares that a piece of *assembly code* is inside the curly braces.
This code will be copied as-is into the generated output assembly source file.
Note that the start and end markers are both *double curly braces* to minimize the chance
that the assembly code itself contains either of those. If it does contain a ``}}``,
it will confuse the parser.
- ``enable_floats`` (module level) tells the compiler
to deal with floating point numbers (by using various subroutines from the Kernal).
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
importing the ``floats`` library is required anyway and that will enable it for you automatically.
- ``no_sysinit`` (module level) which cause the resulting program to *not* include
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
take care of that yourself. The program will just start running from whatever state the machine is in when the
program was launched.
- ``force_output`` (in a block) will force the block to be outputted in the final program.
Can be useful to make sure some data is generated that would otherwise be discarded because the compiler thinks it's not referenced (such as sprite data)
- ``merge`` (in a block) will merge this block's contents into an already existing block with the same name. Useful in library scenarios. Can result in a bunch of unused symbol warnings, this depends on the import order.
- ``splitarrays`` (block or module) makes all word-arrays in this scope lsb/msb split arrays (as if they all have the @split tag). See Arrays.
- ``no_symbol_prefixing`` (block or module) makes the compiler *not* use symbol-prefixing when translating prog8 code into assembly.
Only use this if you know what you're doing because it could result in invalid assembly code being generated.
This option can be useful when writing library modules that you don't want to be exposing prefixed assembly symbols.
- ``ignore_unused`` (block or module) suppress warnings about unused variables and subroutines. Instead, these will be silently stripped.
This option is useful in library modules that contain many more routines beside the ones that you actually use.
- ``verafxmuls`` (block, cx16 target only) uses Vera FX hardware word multiplication on the CommanderX16 for all word multiplications in this block. Warning: this may interfere with IRQs and other Vera operations, so use this only when you know what you're doing. It's safer to explicitly use ``verafx.muls()``.
If you use the correct scoping rules you can access symbols from the prog8 program from inside
the assembly code. Sometimes you'll have to declare a variable in prog8 with `@shared` if it
is only used in such assembly code.
.. data:: %encoding <encodingname>
.. note::
64tass syntax is required for the assembly code. As such, mnemonics need to be written in lowercase.
.. caution::
Avoid using single-letter symbols in included assembly code, as they could be confused with CPU registers.
Also, note that all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`.
Overrides, in the module file it occurs in,
the default text encoding to use for strings and characters that have no explicit encoding prefix.
You can use one of the recognised encoding names, see :ref:`encodings`.
.. data:: %asmbinary "<filename>" [, <offset>[, <length>]]
@ -229,25 +157,116 @@ Directives
Level: not at module scope.
Defines a debugging breakpoint at this location. See :ref:`debugging`
.. data:: %asm {{ ... }}
Level: not at module scope.
Declares that a piece of *assembly code* is inside the curly braces.
This code will be copied as-is into the generated output assembly source file.
Note that the start and end markers are both *double curly braces* to minimize the chance
that the assembly code itself contains either of those. If it does contain a ``}}``,
it will confuse the parser.
.. data:: %encoding <encodingname>
If you use the correct scoping rules you can access symbols from the prog8 program from inside
the assembly code. Sometimes you'll have to declare a variable in prog8 with `@shared` if it
is only used in such assembly code.
Overrides, in the module file it occurs in,
the default text encoding to use for strings and characters that have no explicit encoding prefix.
You can use one of the recognised encoding names, see :ref:`encodings`.
.. note::
64tass syntax is required for the assembly code. As such, mnemonics need to be written in lowercase.
.. caution::
Avoid using single-letter symbols in included assembly code, as they could be confused with CPU registers.
Also, note that all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`.
.. data:: %import <name>
Level: module.
This reads and compiles the named module source file as part of your current program.
Symbols from the imported module become available in your code,
without a module or filename prefix.
You can import modules one at a time, and importing a module more than once has no effect.
.. data:: %launcher <type>
Level: module.
Global setting, selects the program launcher stub to use.
Only relevant when using the ``prg`` output type. Defaults to ``basic``.
- type ``basic`` : add a tiny C64 BASIC program, with a SYS statement calling into the machine code
- type ``none`` : no launcher logic is added at all
.. data:: %option <option> [, <option> ...]
Level: module, block.
Sets special compiler options.
- ``enable_floats`` (module level) tells the compiler
to deal with floating point numbers (by using various subroutines from the Kernal).
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
importing the ``floats`` library is required anyway and that will enable it for you automatically.
- ``no_sysinit`` (module level) which cause the resulting program to *not* include
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
take care of that yourself. The program will just start running from whatever state the machine is in when the
program was launched.
- ``force_output`` (in a block) will force the block to be outputted in the final program.
Can be useful to make sure some data is generated that would otherwise be discarded because the compiler thinks it's not referenced (such as sprite data)
- ``merge`` (in a block) will merge this block's contents into an already existing block with the same name. Useful in library scenarios. Can result in a bunch of unused symbol warnings, this depends on the import order.
- ``splitarrays`` (block or module) makes all word-arrays in this scope lsb/msb split arrays (as if they all have the @split tag). See Arrays.
- ``no_symbol_prefixing`` (block or module) makes the compiler *not* use symbol-prefixing when translating prog8 code into assembly.
Only use this if you know what you're doing because it could result in invalid assembly code being generated.
This option can be useful when writing library modules that you don't want to be exposing prefixed assembly symbols.
- ``ignore_unused`` (block or module) suppress warnings about unused variables and subroutines. Instead, these will be silently stripped.
This option is useful in library modules that contain many more routines beside the ones that you actually use.
- ``verafxmuls`` (block, cx16 target only) uses Vera FX hardware word multiplication on the CommanderX16 for all word multiplications in this block. Warning: this may interfere with IRQs and other Vera operations, so use this only when you know what you're doing. It's safer to explicitly use ``verafx.muls()``.
.. data:: %output <type>
Level: module.
Global setting, selects program output type. Default is ``prg``.
- type ``raw`` : no header at all, just the raw machine code data
- type ``prg`` : C64 program (with load address header)
.. data:: %zeropage <style>
Level: module.
Global setting, select zeropage handling style. Defaults to ``kernalsafe``.
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
and don't change anything else. This allows full use of Kernal ROM routines (but not BASIC routines),
including default IRQs during normal system operation.
It's not possible to return cleanly to BASIC when the program exits. The only choice is
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
are required to perform floating point operations (from the BASIC Kernal). No clean exit is possible.
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
touch change anything else. This allows full use of BASIC and Kernal ROM routines including default IRQs
during normal system operation.
When the program exits, it simply returns to the BASIC ready prompt.
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
except for a few addresses that are used by the system's IRQ handler.
Even though that default IRQ handler is still active, it is impossible to use most BASIC and Kernal ROM routines.
This includes many floating point operations and several utility routines that do I/O, such as ``print``.
This option makes programs smaller and faster because even more variables can
be stored in the ZP (which allows for more efficient assembly code).
It's not possible to return cleanly to BASIC when the program exits. The only choice is
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
- style ``dontuse`` -- don't use *any* location in the zeropage.
.. note::
``kernalsafe`` and ``full`` on the C64 leave enough room in the zeropage to reallocate the
16 virtual registers cx16.r0...cx16.r15 from the Commander X16 into the zeropage as well
(but not on the same locations). They are relocated automatically by the compiler.
The other options need those locations for other things so those virtual registers have
to be put into memory elsewhere (outside of the zeropage). Trying to use them as zeropage
variables or pointers etc. will be a lot slower in those cases!
On the Commander X16 the registers are always in zeropage. On other targets, for now, they
are always outside of the zeropage.
.. data:: %zpallowed <fromaddress>,<toaddress>
Level: module.
Global setting, can occur multiple times. It allows you to designate a part of the zeropage that
the compiler is allowed to use (if other options don't prevent usage).
.. data:: %zpreserved <fromaddress>,<toaddress>
Level: module.
Global setting, can occur multiple times. It allows you to reserve or 'block' a part of the zeropage so
that it will not be used by the compiler.
Identifiers
@ -341,6 +360,9 @@ Tag Effect
@nozp force the variable to normal system ram, never place it into zeropage.
@shared means the variable is shared with some assembly code and that it cannot be optimized away if not used elsewhere.
@split (only valid on (u)word arrays) Makes the array to be placed in memory as 2 separate byte arrays; one with the LSBs one with the MSBs of the word values. Usually improves performance and code size.
@alignword aligns string or array variable on an even memory address
@align64 aligns string or array variable on a 64 byte address interval (example: for C64 sprite data)
@alignpage aligns string or array variable on a 256 byte address interval (example: to avoid page boundaries)
========== ======

View File

@ -1,11 +1,6 @@
TODO
====
- add docs for @alignxxx. Note: uninitialized (bss) variables are also correctly aligned (%option align docs say they're not, but that is fixed)
- add docs for %align. Note: the directive doesn't modify variable declarations that may follow it!
Improve register load order in subroutine call args assignments:
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)

View File

@ -41,5 +41,9 @@ main {
name2[2]++
name3[2]++
name4[2]++
%align 2
%align 3
%align 1000
}
}

View File

@ -12,7 +12,7 @@
<option name="HAS_STRING_ESCAPES" value="true" />
</options>
<keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;defer;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
</highlighting>

View File

@ -25,7 +25,7 @@
<Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close"></Keywords>
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split requirezp nozp</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">abs call callfar callfar2 clamp cmp defer divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>

View File

@ -35,7 +35,7 @@ syn keyword prog8Operator and or to downto as void
syn match prog8Directive "\(^\|\s\)%\(output\|launcher\|zeropage\)\>"
syn match prog8Directive "\(^\|\s\)%\(zpreserved\|zpallowed\|address\|encoding\|import\|option\)\>"
syn match prog8Directive "\(^\|\s\)%\(asmbinary\|asminclude\|breakpoint\)\>"
syn match prog8Directive "\(^\|\s\)%\(align\|asmbinary\|asminclude\|breakpoint\)\>"
syn match prog8Directive "\(^\|\s\)%\(asm\|ir\)\>"
syn match prog8Type "\<\%(u\?byte\|u\?word\|float\|str\|bool\)\>"