mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
added lsb, msb functions.
fixed missing errormessages. changed some zp options.
This commit is contained in:
parent
0b4135698e
commit
3933fdab13
@ -177,6 +177,10 @@ for instance.
|
|||||||
.. todo::
|
.. todo::
|
||||||
matrix datatype
|
matrix datatype
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
There must be a way to tell the compiler which variables you require to be in Zeropage:
|
||||||
|
``zeropage`` modifier keyword on vardecl perhaps?
|
||||||
|
|
||||||
|
|
||||||
Variables that represent CPU hardware registers
|
Variables that represent CPU hardware registers
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -203,6 +207,13 @@ address you specified, and setting the varible will directly modify that memory
|
|||||||
memory word SCREENCOLORS = $d020 ; a 16-bit word at the addres $d020-$d021
|
memory word SCREENCOLORS = $d020 ; a 16-bit word at the addres $d020-$d021
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Directly accessing random memory locations is not yet supported without the
|
||||||
|
intermediate step of declaring a memory-mapped variable for the memory location.
|
||||||
|
The advantages of this however, is that it's clearer what the memory location
|
||||||
|
stands for, and the compiler also knows the data type.
|
||||||
|
|
||||||
|
|
||||||
Integers
|
Integers
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
@ -460,6 +471,12 @@ len(x)
|
|||||||
Number of values in the non-scalar (array or matrix) value x.
|
Number of values in the non-scalar (array or matrix) value x.
|
||||||
(This is different from the number of *bytes* in memory if the datatype isn't byte)
|
(This is different from the number of *bytes* in memory if the datatype isn't byte)
|
||||||
|
|
||||||
|
lsb(x)
|
||||||
|
Get the least significant byte of the word x.
|
||||||
|
|
||||||
|
msb(x)
|
||||||
|
Get the most significant byte of the word x.
|
||||||
|
|
||||||
any(x)
|
any(x)
|
||||||
1 ('true') if any of the values in the non-scalar (array or matrix) value x is 'true' (not zero), else 0 ('false')
|
1 ('true') if any of the values in the non-scalar (array or matrix) value x is 'true' (not zero), else 0 ('false')
|
||||||
|
|
||||||
|
@ -55,10 +55,14 @@ Directives
|
|||||||
.. data:: %zeropage <style>
|
.. data:: %zeropage <style>
|
||||||
|
|
||||||
Level: module.
|
Level: module.
|
||||||
Global setting, select ZeroPage handling style. Defaults to ``compatible``.
|
Global setting, select ZeroPage handling style. Defaults to ``kernalsafe``.
|
||||||
|
|
||||||
- style ``compatible`` -- only use the few 'free' addresses in the ZP, and don't change anything else.
|
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
|
||||||
This allows full use of BASIC and KERNAL ROM routines including default IRQs during normal system operation.
|
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.
|
||||||
|
- 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.
|
||||||
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
||||||
except the few addresses mentioned above that are used by the system's IRQ routine.
|
except the few addresses mentioned above that are used by the system's IRQ routine.
|
||||||
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
|
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
|
||||||
@ -66,9 +70,6 @@ Directives
|
|||||||
It is also not possible to cleanly exit the program, other than resetting the machine.
|
It is also not possible to cleanly exit the program, other than resetting the machine.
|
||||||
This option makes programs smaller and faster because many more variables can
|
This option makes programs smaller and faster because many more variables can
|
||||||
be stored in the ZP, which is more efficient.
|
be stored in the ZP, which is more efficient.
|
||||||
- style ``full-restore`` -- like ``full``, but makes a backup copy of the original values at program start.
|
|
||||||
These are restored (except for the software jiffy clock in ``$a0``--``$a2``)
|
|
||||||
when the program exits, and allows it to exit back to the BASIC prompt.
|
|
||||||
|
|
||||||
Also read :ref:`zeropage`.
|
Also read :ref:`zeropage`.
|
||||||
|
|
||||||
@ -192,20 +193,6 @@ Values in the source code are written using *value literals*. In the table of th
|
|||||||
data types below you can see how they should be written.
|
data types below you can see how they should be written.
|
||||||
|
|
||||||
|
|
||||||
Range expression
|
|
||||||
----------------
|
|
||||||
A special value is the *range expression* ( ``<startvalue> to <endvalue>`` )
|
|
||||||
which represents a range of numbers or characters,
|
|
||||||
from the starting value to (and including) the ending value.
|
|
||||||
If used in the place of a literal value, it expands into the actual array of values::
|
|
||||||
|
|
||||||
byte[100] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
|
||||||
|
|
||||||
|
|
||||||
.. todo::
|
|
||||||
this may be used later in the for-loop as well. Add 'step' to range expression as well?
|
|
||||||
|
|
||||||
|
|
||||||
Variable declarations
|
Variable declarations
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -267,7 +254,7 @@ Memory mapped variables
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The ``memory`` keyword is used in front of a data type keyword, to say that no storage
|
The ``memory`` keyword is used in front of a data type keyword, to say that no storage
|
||||||
should be allocated by the compiler. Instead, the value assigned to the variable (mandatory now!)
|
should be allocated by the compiler. Instead, the (mandatory) value assigned to the variable
|
||||||
should be the *memory address* where the value is located::
|
should be the *memory address* where the value is located::
|
||||||
|
|
||||||
memory byte BORDER = $d020
|
memory byte BORDER = $d020
|
||||||
@ -292,6 +279,21 @@ The following names are reserved, they have a special meaning::
|
|||||||
AX AY XY ; 16-bit pseudo register pairs
|
AX AY XY ; 16-bit pseudo register pairs
|
||||||
|
|
||||||
|
|
||||||
|
Range expression
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A special value is the *range expression* ( ``<startvalue> to <endvalue>`` )
|
||||||
|
which represents a range of numbers or characters,
|
||||||
|
from the starting value to (and including) the ending value.
|
||||||
|
If used in the place of a literal value, it expands into the actual array of values::
|
||||||
|
|
||||||
|
byte[100] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
||||||
|
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
this may be used later in the for-loop as well. Add 'step' to range expression as well?
|
||||||
|
|
||||||
|
|
||||||
Operators
|
Operators
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -77,19 +77,22 @@ Theoretically they can all be used in a program, with the follwoing limitations:
|
|||||||
- it's more convenient and safe to let IL65 allocate these addresses for you and just
|
- it's more convenient and safe to let IL65 allocate these addresses for you and just
|
||||||
use symbolic names in the program code.
|
use symbolic names in the program code.
|
||||||
|
|
||||||
Here is the list of the remaining free-to-use ZP addresses with BASIC and KERNAL active in the Commodore-64:
|
.. todo::
|
||||||
|
There must be a way to tell the compiler which variables you require to be in Zeropage:
|
||||||
|
``zeropage`` modifier keyword on vardecl perhaps?
|
||||||
|
|
||||||
``$02``, ``$03``, ``$04``, ``$05``, ``$06``, ``$2a``, ``$52``,
|
|
||||||
``$f7 - $f8``, ``$f9 - $fa``, ``$fb - $fc``, ``$fd - $fe``
|
|
||||||
|
|
||||||
*The six reserved addresses mentioned earliser are subtracted from this set,* leaving you with
|
IL65 knows what addresses are safe to use in the various ZP handling configurations.
|
||||||
just *five* 1-byte and *two* 2-byte usable ZP 'registers' for use by the program.
|
It will use the free ZP addresses to place its ZP variables in,
|
||||||
|
|
||||||
**IL65 knows about all of this.** It will use the free ZP addresses to place its ZP variables in,
|
|
||||||
until they're all used up. If instructed to output a program that takes over the entire
|
until they're all used up. If instructed to output a program that takes over the entire
|
||||||
machine, (almost) all of the ZP addresses are suddenly available and will be used.
|
machine, (almost) all of the ZP addresses are suddenly available and will be used.
|
||||||
IL65 can also generate a special routine that saves and restores the ZP to let the program run
|
|
||||||
and return safely back to the system afterwards - you don't have to take care of that yourself.
|
**ZeroPage handling is configurable:**
|
||||||
|
There's a global program directive to specify the way the compiler
|
||||||
|
treats the ZP for the program. The default is to be reasonably restrictive to use the
|
||||||
|
part of the ZP that is not used by the C64's kernal routines.
|
||||||
|
It's possible to claim the whole ZP as well (by disabling the operating system or kernal).
|
||||||
|
If you want, it's also possible to be more restricive and stay clear of the addresses used by BASIC routines too.
|
||||||
|
|
||||||
|
|
||||||
IRQs and the ZeroPage
|
IRQs and the ZeroPage
|
||||||
@ -100,21 +103,12 @@ The normal IRQ routine in the C-64's kernal will read and write several addresse
|
|||||||
|
|
||||||
``$a0 - $a2``; ``$91``; ``$c0``; ``$c5``; ``$cb``; ``$f5 - $f6``
|
``$a0 - $a2``; ``$91``; ``$c0``; ``$c5``; ``$cb``; ``$f5 - $f6``
|
||||||
|
|
||||||
These addresses will never be used by the compiler for ZP variables, so variables will
|
These addresses will *never* be used by the compiler for ZP variables, so variables will
|
||||||
not interfere with the IRQ routine and vice versa. This is true for the normal ZP mode but also
|
not interfere with the IRQ routine and vice versa. This is true for the normal ZP mode but also
|
||||||
for the mode where the whole system and ZP have been taken over.
|
for the mode where the whole system and ZP have been taken over.
|
||||||
So the normal IRQ vector can still run and will be when the program is started!
|
So the normal IRQ vector can still run and will be when the program is started!
|
||||||
|
|
||||||
|
|
||||||
ZeroPage handling is configurable
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
There's a global program directive to specify the way the compiler
|
|
||||||
treats the ZP for the program. The default is to be restrictive to just
|
|
||||||
the few free locations mentioned above, where most of the ZP is considered a no-go zone by the compiler.
|
|
||||||
It's possible to claim the whole ZP as well (by disabling the operating system or kernal),
|
|
||||||
and even ask for a save/restore of the original values to be able to cleanly exit back to a BASIC prompt.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CPU
|
CPU
|
||||||
|
@ -3,14 +3,13 @@
|
|||||||
%import mathlib
|
%import mathlib
|
||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
sub start() -> () {
|
||||||
str name = "?" * 80
|
str name = "?" * 80
|
||||||
str guess = "?" * 80
|
str guess = "?" * 80
|
||||||
byte secretnumber = 0
|
byte secretnumber = 0
|
||||||
byte attempts_left = 10
|
byte attempts_left = 10
|
||||||
memory word freadstr_arg = $22 ; argument for FREADSTR
|
memory word freadstr_arg = $22 ; argument for FREADSTR
|
||||||
|
|
||||||
|
|
||||||
start:
|
|
||||||
c64.init_system()
|
c64.init_system()
|
||||||
c64.VMCSB |= 2 ; lowercase charset
|
c64.VMCSB |= 2 ; lowercase charset
|
||||||
|
|
||||||
@ -73,3 +72,4 @@ goodbye:
|
|||||||
c64scr.print_string("\nThanks for playing. Bye!\n")
|
c64scr.print_string("\nThanks for playing. Bye!\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -11,6 +11,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
~ main $c003 {
|
~ main $c003 {
|
||||||
|
word lsb1 = lsb($ea31)
|
||||||
|
word msb1 = msb($ea31)
|
||||||
|
byte lsb2 = lsb($ea31)
|
||||||
|
byte msb2 = msb($ea31)
|
||||||
|
word lsb3 = lsb($ea)
|
||||||
|
word msb3 = msb($ea)
|
||||||
|
|
||||||
const word len1 = len([1,2,3,wa1, wa2, ws1, all1])
|
const word len1 = len([1,2,3,wa1, wa2, ws1, all1])
|
||||||
const word wa1 = ceil(abs(-999.22))
|
const word wa1 = ceil(abs(-999.22))
|
||||||
const byte wa2 = abs(-99)
|
const byte wa2 = abs(-99)
|
||||||
@ -22,7 +29,7 @@
|
|||||||
const word ws2 = ceil(avg([1,2,3,4.9]))
|
const word ws2 = ceil(avg([1,2,3,4.9]))
|
||||||
const word ws3 = round(sum([1,2,3,4.9]))
|
const word ws3 = round(sum([1,2,3,4.9]))
|
||||||
const word any1 = any([0,0,0,0,0,22,0,0])
|
const word any1 = any([0,0,0,0,0,22,0,0])
|
||||||
const word any2 = any([2+sin(2), 2])
|
const word any2 = any([2+sin(2.0), 2])
|
||||||
const word all1 = all([0,0,0,0,0,22,0,0])
|
const word all1 = all([0,0,0,0,0,22,0,0])
|
||||||
const word all2 = all([0.0])
|
const word all2 = all([0.0])
|
||||||
const word all3 = all([wa1, wa2, ws1, all1])
|
const word all3 = all([wa1, wa2, ws1, all1])
|
||||||
@ -48,7 +55,7 @@
|
|||||||
memory byte cderp = min([$ffdd])+ (1/1)
|
memory byte cderp = min([$ffdd])+ (1/1)
|
||||||
memory byte cderpA = min([$ffdd, 10, 20, 30])
|
memory byte cderpA = min([$ffdd, 10, 20, 30])
|
||||||
memory byte cderpB = min([1, 2.2, 4.4, 100])
|
memory byte cderpB = min([1, 2.2, 4.4, 100])
|
||||||
memory byte derp2 = 2+$ffdd+round(10*sin(3))
|
memory byte derp2 = 2+$ffdd+round(10*sin(3.1))
|
||||||
const byte hopla=55-33
|
const byte hopla=55-33
|
||||||
const byte hopla3=100+(-hopla)
|
const byte hopla3=100+(-hopla)
|
||||||
const byte hopla4 = 100-hopla
|
const byte hopla4 = 100-hopla
|
||||||
@ -99,10 +106,9 @@
|
|||||||
|
|
||||||
if(6==6) {
|
if(6==6) {
|
||||||
A=sin(X)
|
A=sin(X)
|
||||||
A=sin([X])
|
|
||||||
X=max([1,2,Y])
|
X=max([1,2,Y])
|
||||||
X=min([1,2,Y])
|
X=min([1,2,Y])
|
||||||
X=lsl(1.2)
|
X=lsl(12)
|
||||||
X=lsl(Y)
|
X=lsl(Y)
|
||||||
P_carry(0)
|
P_carry(0)
|
||||||
P_carry(1)
|
P_carry(1)
|
||||||
|
@ -40,7 +40,7 @@ fun main(args: Array<String>) {
|
|||||||
val compilerOptions = CompilationOptions(
|
val compilerOptions = CompilationOptions(
|
||||||
if(outputType==null) OutputType.PRG else OutputType.valueOf(outputType),
|
if(outputType==null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||||
if(launcherType==null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
if(launcherType==null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||||
if(zpType==null) ZeropageType.COMPATIBLE else ZeropageType.valueOf(zpType),
|
if(zpType==null) ZeropageType.KERNALSAFE else ZeropageType.valueOf(zpType),
|
||||||
options.contains(DirectiveArg(null, "enable_floats", null))
|
options.contains(DirectiveArg(null, "enable_floats", null))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -779,6 +779,8 @@ class FunctionCall(override var target: IdentifierReference, override var arglis
|
|||||||
"sum" -> builtinSum(arglist, position, namespace)
|
"sum" -> builtinSum(arglist, position, namespace)
|
||||||
"avg" -> builtinAvg(arglist, position, namespace)
|
"avg" -> builtinAvg(arglist, position, namespace)
|
||||||
"len" -> builtinLen(arglist, position, namespace)
|
"len" -> builtinLen(arglist, position, namespace)
|
||||||
|
"lsb" -> builtinLsb(arglist, position, namespace)
|
||||||
|
"msb" -> builtinMsb(arglist, position, namespace)
|
||||||
"any" -> builtinAny(arglist, position, namespace)
|
"any" -> builtinAny(arglist, position, namespace)
|
||||||
"all" -> builtinAll(arglist, position, namespace)
|
"all" -> builtinAll(arglist, position, namespace)
|
||||||
"floor" -> builtinFloor(arglist, position, namespace)
|
"floor" -> builtinFloor(arglist, position, namespace)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package il65.ast
|
package il65.ast
|
||||||
|
|
||||||
import il65.compiler.CompilationOptions
|
import il65.compiler.CompilationOptions
|
||||||
import il65.functions.BuiltIns
|
import il65.functions.BuiltinFunctionNames
|
||||||
import il65.parser.ParsingFailedError
|
import il65.parser.ParsingFailedError
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,8 +81,8 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
|
|||||||
// if(subroutine.parent !is Block)
|
// if(subroutine.parent !is Block)
|
||||||
// err("subroutines can only be defined in a block (not in other scopes)")
|
// err("subroutines can only be defined in a block (not in other scopes)")
|
||||||
|
|
||||||
if(BuiltIns.contains(subroutine.name))
|
if(BuiltinFunctionNames.contains(subroutine.name))
|
||||||
err("cannot override a built-in function")
|
err("cannot redefine a built-in function")
|
||||||
|
|
||||||
val uniqueNames = subroutine.parameters.map { it.name }.toSet()
|
val uniqueNames = subroutine.parameters.map { it.name }.toSet()
|
||||||
if(uniqueNames.size!=subroutine.parameters.size)
|
if(uniqueNames.size!=subroutine.parameters.size)
|
||||||
@ -224,10 +224,10 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
|
|||||||
"%zeropage" -> {
|
"%zeropage" -> {
|
||||||
if(directive.parent !is Module) err("this directive may only occur at module level")
|
if(directive.parent !is Module) err("this directive may only occur at module level")
|
||||||
if(directive.args.size!=1 ||
|
if(directive.args.size!=1 ||
|
||||||
directive.args[0].name != "compatible" &&
|
directive.args[0].name != "basicsafe" &&
|
||||||
directive.args[0].name != "full" &&
|
directive.args[0].name != "kernalsafe" &&
|
||||||
directive.args[0].name != "full-restore")
|
directive.args[0].name != "full")
|
||||||
err("invalid zp directive style, expected compatible, full or full-restore")
|
err("invalid zp directive style, expected basicsafe, kernalsafe, or full")
|
||||||
}
|
}
|
||||||
"%address" -> {
|
"%address" -> {
|
||||||
if(directive.parent !is Module) err("this directive may only occur at module level")
|
if(directive.parent !is Module) err("this directive may only occur at module level")
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package il65.ast
|
package il65.ast
|
||||||
|
|
||||||
|
import il65.functions.BuiltinFunctionNames
|
||||||
import il65.parser.ParsingFailedError
|
import il65.parser.ParsingFailedError
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,13 +21,6 @@ fun Module.checkIdentifiers(): MutableMap<String, IStatement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val BuiltinFunctionNames = setOf(
|
|
||||||
"P_carry", "P_irqd", "rol", "ror", "rol2", "ror2", "lsl", "lsr",
|
|
||||||
"sin", "cos", "abs", "acos", "asin", "tan", "atan",
|
|
||||||
"log", "log10", "sqrt", "rad", "deg", "round", "floor", "ceil",
|
|
||||||
"max", "min", "avg", "sum", "len", "any", "all")
|
|
||||||
|
|
||||||
|
|
||||||
class AstIdentifiersChecker : IAstProcessor {
|
class AstIdentifiersChecker : IAstProcessor {
|
||||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||||
|
|
||||||
|
@ -130,9 +130,9 @@ enum class LauncherType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class ZeropageType {
|
enum class ZeropageType {
|
||||||
COMPATIBLE,
|
BASICSAFE,
|
||||||
FULL,
|
KERNALSAFE,
|
||||||
FULL_RESTORE
|
FULL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,14 +19,25 @@ class Zeropage(private val options: CompilationOptions) {
|
|||||||
val free = mutableListOf<Int>()
|
val free = mutableListOf<Int>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.FULL_RESTORE) {
|
if(options.zeropage==ZeropageType.FULL) {
|
||||||
free.addAll(0x04 .. 0xfa)
|
free.addAll(0x04 .. 0xfa)
|
||||||
free.add(0xff)
|
free.add(0xff)
|
||||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||||
} else {
|
} else {
|
||||||
|
if(options.zeropage==ZeropageType.KERNALSAFE) {
|
||||||
|
// add the Zp addresses that are just used by BASIC routines to the free list
|
||||||
|
free.addAll(listOf(0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||||
|
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||||
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||||
|
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x6f, 0x70))
|
||||||
|
}
|
||||||
|
// add the Zp addresses not even used by BASIC
|
||||||
// these are valid for the C-64 (when no RS232 I/O is performed):
|
// these are valid for the C-64 (when no RS232 I/O is performed):
|
||||||
// ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines)
|
// ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines)
|
||||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa))
|
// KNOWN WORKING FREE: 0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa))
|
||||||
|
free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e,
|
||||||
|
0x12, 0x2a, 0x52, 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
|
||||||
|
0xb5, 0xb6, 0xf7, 0xf8, 0xf9, 0xfa))
|
||||||
}
|
}
|
||||||
assert(!free.contains(Zeropage.SCRATCH_B1))
|
assert(!free.contains(Zeropage.SCRATCH_B1))
|
||||||
assert(!free.contains(Zeropage.SCRATCH_B2))
|
assert(!free.contains(Zeropage.SCRATCH_B2))
|
||||||
@ -42,7 +53,9 @@ class Zeropage(private val options: CompilationOptions) {
|
|||||||
|
|
||||||
val size =
|
val size =
|
||||||
if(vardecl.arrayspec!=null) {
|
if(vardecl.arrayspec!=null) {
|
||||||
println("${vardecl.position} warning: allocating a large value in zeropage")
|
if(vardecl.position!=null)
|
||||||
|
print(vardecl.position)
|
||||||
|
println(" warning: allocating a large value (array) in zeropage")
|
||||||
val y = (vardecl.arrayspec.y as? LiteralValue)?.intvalue
|
val y = (vardecl.arrayspec.y as? LiteralValue)?.intvalue
|
||||||
if(y==null) {
|
if(y==null) {
|
||||||
// 1 dimensional array
|
// 1 dimensional array
|
||||||
@ -65,7 +78,9 @@ class Zeropage(private val options: CompilationOptions) {
|
|||||||
DataType.WORD -> 2
|
DataType.WORD -> 2
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
println("${vardecl.position} warning: allocating a large value in zeropage")
|
if(vardecl.position!=null)
|
||||||
|
print(vardecl.position)
|
||||||
|
println(" warning: allocating a large value (float) in zeropage")
|
||||||
5
|
5
|
||||||
} else throw CompilerException("floating point option not enabled")
|
} else throw CompilerException("floating point option not enabled")
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
val BuiltIns = listOf(
|
val BuiltinFunctionNames = setOf(
|
||||||
"sin", "cos", "abs", "acos", "asin", "tan", "atan", "log", "log10",
|
"P_carry", "P_irqd", "rol", "ror", "rol2", "ror2", "lsl", "lsr",
|
||||||
"sqrt", "max", "min", "round", "rad", "deg", "avg", "sum"
|
"sin", "cos", "abs", "acos", "asin", "tan", "atan",
|
||||||
)
|
"log", "log10", "sqrt", "rad", "deg", "round", "floor", "ceil",
|
||||||
|
"max", "min", "avg", "sum", "len", "any", "all", "lsb", "msb")
|
||||||
|
|
||||||
|
|
||||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||||
@ -17,44 +18,41 @@ class NotConstArgumentException: AstException("not a const argument to a built-i
|
|||||||
private fun oneDoubleArg(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
private fun oneDoubleArg(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
|
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
||||||
|
if(!constval.isFloat)
|
||||||
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
|
|
||||||
val float = args[0].constValue(namespace)?.asNumericValue?.toDouble()
|
val float = constval.asNumericValue?.toDouble()!!
|
||||||
if(float!=null) {
|
|
||||||
val result = numericLiteral(function(float), args[0].position)
|
val result = numericLiteral(function(float), args[0].position)
|
||||||
result.position = args[0].position
|
result.position = args[0].position
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else
|
|
||||||
throw NotConstArgumentException()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
|
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
||||||
|
if(!constval.isFloat)
|
||||||
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
|
|
||||||
val float = args[0].constValue(namespace)?.asNumericValue?.toDouble()
|
val float = constval.asNumericValue?.toDouble()!!
|
||||||
if(float!=null) {
|
|
||||||
val result = LiteralValue(function(float).toInt())
|
val result = LiteralValue(function(float).toInt())
|
||||||
result.position = args[0].position
|
result.position = args[0].position
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else
|
|
||||||
throw NotConstArgumentException()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Int)->Number): LiteralValue {
|
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Int)->Number): LiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one integer argument", position)
|
throw SyntaxError("built-in function requires one integer argument", position)
|
||||||
|
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
||||||
|
if(!constval.isInteger)
|
||||||
|
throw SyntaxError("built-in function requires one integer argument", position)
|
||||||
|
|
||||||
val integer = args[0].constValue(namespace)?.asNumericValue?.toInt()
|
val integer = constval.asNumericValue?.toInt()!!
|
||||||
if(integer!=null) {
|
|
||||||
val result = LiteralValue(function(integer).toInt())
|
val result = LiteralValue(function(integer).toInt())
|
||||||
result.position = args[0].position
|
result.position = args[0].position
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else
|
|
||||||
throw NotConstArgumentException()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position?, namespace:INameScope,
|
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position?, namespace:INameScope,
|
||||||
function: (arg: Collection<Double>)->Number): LiteralValue {
|
function: (arg: Collection<Double>)->Number): LiteralValue {
|
||||||
@ -153,6 +151,13 @@ fun builtinAbs(args: List<IExpression>, position: Position?, namespace:INameScop
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun builtinLsb(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x and 255 }
|
||||||
|
|
||||||
|
fun builtinMsb(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 8 and 255}
|
||||||
|
|
||||||
fun builtinLsl(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
fun builtinLsl(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 }
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 }
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
|
|
||||||
fun addError(x: AstException) {
|
fun addError(x: AstException) {
|
||||||
// check that we don't add the same error more than once
|
// check that we don't add the same error more than once
|
||||||
if(!reportedErrorMessages.contains(x.message)) {
|
if(!reportedErrorMessages.contains(x.toString())) {
|
||||||
reportedErrorMessages.add(x.message)
|
reportedErrorMessages.add(x.toString())
|
||||||
errors.add(x)
|
errors.add(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,8 @@ enum class Opcode {
|
|||||||
OR,
|
OR,
|
||||||
XOR,
|
XOR,
|
||||||
INV,
|
INV,
|
||||||
|
LSB,
|
||||||
|
MSB,
|
||||||
|
|
||||||
// logical operations (?)
|
// logical operations (?)
|
||||||
|
|
||||||
@ -375,6 +377,22 @@ data class Value(val type: DataType, private val numericvalue: Number?, val stri
|
|||||||
else -> throw VmExecutionException("dec can only work on byte/word/float")
|
else -> throw VmExecutionException("dec can only work on byte/word/float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun lsb(): Value {
|
||||||
|
return when(type) {
|
||||||
|
DataType.BYTE -> Value(DataType.BYTE, byteval!!.toInt() and 255)
|
||||||
|
DataType.WORD -> Value(DataType.WORD, wordval!! and 255)
|
||||||
|
else -> throw VmExecutionException("not can only work on byte/word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun msb(): Value {
|
||||||
|
return when(type) {
|
||||||
|
DataType.BYTE -> Value(DataType.BYTE, byteval!!.toInt() ushr 8 and 255)
|
||||||
|
DataType.WORD -> Value(DataType.WORD, wordval!! ushr 8 and 255)
|
||||||
|
else -> throw VmExecutionException("not can only work on byte/word")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -468,9 +486,9 @@ class Program (prog: MutableList<Instruction>,
|
|||||||
Instruction(opcode, callLabel = args)
|
Instruction(opcode, callLabel = args)
|
||||||
}
|
}
|
||||||
Opcode.SYSCALL -> {
|
Opcode.SYSCALL -> {
|
||||||
val parts = args!!.split(' ')
|
val syscallparts = args!!.split(' ')
|
||||||
val call = Syscall.valueOf(parts[0])
|
val call = Syscall.valueOf(syscallparts[0])
|
||||||
val callValue = if(parts.size==2) getArgValue(parts[1]) else null
|
val callValue = if(parts.size==2) getArgValue(syscallparts[1]) else null
|
||||||
val callValues = if(callValue==null) emptyList() else listOf(callValue)
|
val callValues = if(callValue==null) emptyList() else listOf(callValue)
|
||||||
Instruction(opcode, Value(DataType.BYTE, call.callNr), callValues)
|
Instruction(opcode, Value(DataType.BYTE, call.callNr), callValues)
|
||||||
}
|
}
|
||||||
@ -966,6 +984,14 @@ class StackVm(val traceOutputFile: String?) {
|
|||||||
val variable = variables[varname] ?: throw VmExecutionException("unknown variable: $varname")
|
val variable = variables[varname] ?: throw VmExecutionException("unknown variable: $varname")
|
||||||
variables[varname] = variable.dec()
|
variables[varname] = variable.dec()
|
||||||
}
|
}
|
||||||
|
Opcode.LSB -> {
|
||||||
|
val v = evalstack.pop()
|
||||||
|
evalstack.push(v.lsb())
|
||||||
|
}
|
||||||
|
Opcode.MSB -> {
|
||||||
|
val v = evalstack.pop()
|
||||||
|
evalstack.push(v.msb())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(traceOutput!=null) {
|
if(traceOutput!=null) {
|
||||||
|
@ -11,6 +11,8 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@ -108,7 +110,7 @@ class TestCompiler {
|
|||||||
class TestZeropage {
|
class TestZeropage {
|
||||||
@Test
|
@Test
|
||||||
fun testNames() {
|
fun testNames() {
|
||||||
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.COMPATIBLE, false))
|
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, false))
|
||||||
|
|
||||||
assertFailsWith<AssertionError> {
|
assertFailsWith<AssertionError> {
|
||||||
zp.allocate(VarDecl(VarDeclType.MEMORY, DataType.BYTE, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.MEMORY, DataType.BYTE, null, "", null))
|
||||||
@ -134,18 +136,31 @@ class TestZeropage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCompatibleAllocation() {
|
fun testFreeSpaces() {
|
||||||
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.COMPATIBLE, true))
|
val zp1 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true))
|
||||||
assert(zp.available() == 9)
|
assertEquals(23, zp1.available())
|
||||||
|
val zp2 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, true))
|
||||||
|
assertEquals(71, zp2.available())
|
||||||
|
val zp3 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
|
||||||
|
assertEquals(239, zp3.available())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBasicsafeAllocation() {
|
||||||
|
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true))
|
||||||
|
assertEquals(23, zp.available())
|
||||||
|
|
||||||
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
// in regular zp there aren't 5 sequential bytes free
|
// in regular zp there aren't 5 sequential bytes free after we take the first sequence
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until zp.available()) {
|
for (i in 0 until zp.available()) {
|
||||||
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
||||||
assert(loc > 0)
|
assertTrue(loc > 0)
|
||||||
}
|
}
|
||||||
assert(zp.available() == 0)
|
assertEquals(0, zp.available())
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
||||||
}
|
}
|
||||||
@ -157,17 +172,17 @@ class TestZeropage {
|
|||||||
@Test
|
@Test
|
||||||
fun testFullAllocation() {
|
fun testFullAllocation() {
|
||||||
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
|
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
|
||||||
assert(zp.available() == 239)
|
assertEquals(239, zp.available())
|
||||||
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
||||||
assert(loc > 3)
|
assertTrue(loc > 3)
|
||||||
assert(!zp.free.contains(loc))
|
assertFalse(zp.free.contains(loc))
|
||||||
val num = zp.available() / 5
|
val num = zp.available() / 5
|
||||||
val rest = zp.available() % 5
|
val rest = zp.available() % 5
|
||||||
|
|
||||||
for(i in 0..num-4) {
|
for(i in 0..num-4) {
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
||||||
}
|
}
|
||||||
assert(zp.available() == 19)
|
assertEquals(19,zp.available())
|
||||||
|
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
// can't allocate because no more sequential bytes, only fragmented
|
// can't allocate because no more sequential bytes, only fragmented
|
||||||
@ -180,7 +195,7 @@ class TestZeropage {
|
|||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
|
||||||
|
|
||||||
assert(zp.available() == 1)
|
assertEquals(1, zp.available())
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
// no more space
|
// no more space
|
||||||
@ -190,16 +205,24 @@ class TestZeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEfficientAllocation() {
|
fun testEfficientAllocation() {
|
||||||
// free = [0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa]
|
// free = (0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e,
|
||||||
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.COMPATIBLE, true))
|
// 0x12, 0x2a, 0x52, 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
|
||||||
assert(zp.available()==9)
|
// 0xb5, 0xb6, 0xf7, 0xf8, 0xf9, 0xfa))
|
||||||
assert(0x2a == zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
|
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true))
|
||||||
assert(0x52 == zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
|
assertEquals(23, zp.available())
|
||||||
assert(0x04 == zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
assertEquals(0x04, zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null)))
|
||||||
assert(0xf7 == zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
assertEquals(0x09, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
|
||||||
assert(0x06 == zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
|
assertEquals(0x12, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
|
||||||
assert(0xf9 == zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
assertEquals(0x0d, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
||||||
assert(zp.available()==0)
|
assertEquals(0x94, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
||||||
|
assertEquals(0x2a, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
|
||||||
|
assertEquals(0xa7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
||||||
|
assertEquals(0xa9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
||||||
|
assertEquals(0xb5, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
||||||
|
assertEquals(0xf7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
||||||
|
assertEquals(0xf9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
|
||||||
|
assertEquals(0x52, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
|
||||||
|
assertEquals(0, zp.available())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
; backup/restore the ZeroPage
|
|
||||||
; this is in a separate file so it can be omitted completely if it's not needed.
|
|
||||||
|
|
||||||
_il65_save_zeropage
|
|
||||||
lda #%00101111
|
|
||||||
sta _il65_zp_backup ; default value for $00
|
|
||||||
lda #%00100111
|
|
||||||
sta _il65_zp_backup+1 ; default value for $01
|
|
||||||
ldx #2
|
|
||||||
sei
|
|
||||||
- lda $00,x
|
|
||||||
sta _il65_zp_backup,x
|
|
||||||
inx
|
|
||||||
bne -
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
|
|
||||||
_il65_restore_zeropage
|
|
||||||
php
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
pha
|
|
||||||
sei
|
|
||||||
|
|
||||||
lda $a0 ; save the current jiffy clock
|
|
||||||
sta _il65_zp_backup+$a0
|
|
||||||
lda $a1
|
|
||||||
sta _il65_zp_backup+$a1
|
|
||||||
lda $a2
|
|
||||||
sta _il65_zp_backup+$a2
|
|
||||||
|
|
||||||
ldx #0
|
|
||||||
- lda _il65_zp_backup,x
|
|
||||||
sta $00,x
|
|
||||||
inx
|
|
||||||
bne -
|
|
||||||
cli
|
|
||||||
pla
|
|
||||||
tax
|
|
||||||
pla
|
|
||||||
plp
|
|
||||||
rts
|
|
||||||
|
|
||||||
_il65_zp_backup
|
|
||||||
.fill 256
|
|
Loading…
x
Reference in New Issue
Block a user