From 3b798097b95acd6ee41edee82bc0838bc69bc086 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 2 Nov 2024 00:13:23 +0100 Subject: [PATCH] added memtop to machine definition and asm source code check added %memtop directive --- .../src/prog8/code/core/CompilationOptions.kt | 1 + .../src/prog8/code/core/IMachineDefinition.kt | 1 + .../target/atari/AtariMachineDefinition.kt | 1 + .../code/target/c128/C128MachineDefinition.kt | 1 + .../code/target/c64/C64MachineDefinition.kt | 1 + .../code/target/cx16/CX16MachineDefinition.kt | 1 + .../code/target/pet/PETMachineDefinition.kt | 1 + .../virtual/VirtualMachineDefinition.kt | 1 + .../codegen/cpu6502/ProgramAndVarsGen.kt | 2 + codeGenCpu6502/test/TestCodegen.kt | 3 +- codeGenIntermediate/test/TestIRPeepholeOpt.kt | 3 +- codeGenIntermediate/test/TestVmCodeGen.kt | 3 +- compiler/src/prog8/compiler/Compiler.kt | 3 +- compiler/src/prog8/compiler/ModuleImporter.kt | 2 +- .../compiler/astprocessing/AstChecker.kt | 8 ++- .../astprocessing/StatementReorderer.kt | 2 +- compiler/test/TestGoldenRam.kt | 3 +- compiler/test/TestZeropage.kt | 63 ++++++++++--------- .../test/codegeneration/TestAsmGenSymbols.kt | 2 +- compilerAst/src/prog8/ast/AstToplevel.kt | 8 +++ .../src/prog8/ast/statements/AstStatements.kt | 1 + docs/source/syntaxreference.rst | 11 ++++ docs/source/todo.rst | 2 +- examples/cx16/zsmkit/demo2.p8 | 1 + examples/test.p8 | 1 + .../src/prog8/intermediate/IRFileReader.kt | 1 + intermediate/test/TestIRFileInOut.kt | 1 + parser/antlr/Prog8ANTLR.g4 | 2 +- syntax-files/IDEA/Prog8.xml | 2 +- syntax-files/NotepadPlusPlus/Prog8.xml | 2 +- virtualmachine/test/TestVm.kt | 3 +- 31 files changed, 92 insertions(+), 45 deletions(-) diff --git a/codeCore/src/prog8/code/core/CompilationOptions.kt b/codeCore/src/prog8/code/core/CompilationOptions.kt index 36d01f946..8e7f8a8b1 100644 --- a/codeCore/src/prog8/code/core/CompilationOptions.kt +++ b/codeCore/src/prog8/code/core/CompilationOptions.kt @@ -14,6 +14,7 @@ class CompilationOptions(val output: OutputType, val compTarget: ICompilationTarget, // these are set later, based on command line arguments or options in the source code: var loadAddress: UInt, + var memtopAddress: UInt, var warnSymbolShadowing: Boolean = false, var optimize: Boolean = false, var asmQuiet: Boolean = false, diff --git a/codeCore/src/prog8/code/core/IMachineDefinition.kt b/codeCore/src/prog8/code/core/IMachineDefinition.kt index be18c1f18..178ce45c3 100644 --- a/codeCore/src/prog8/code/core/IMachineDefinition.kt +++ b/codeCore/src/prog8/code/core/IMachineDefinition.kt @@ -14,6 +14,7 @@ interface IMachineDefinition { val FLOAT_MAX_POSITIVE: Double val FLOAT_MEM_SIZE: Int val PROGRAM_LOAD_ADDRESS : UInt + val PROGRAM_TOP_ADDRESS: UInt val BSSHIGHRAM_START: UInt val BSSHIGHRAM_END: UInt val BSSGOLDENRAM_START: UInt diff --git a/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt b/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt index 6ca5f1194..3c4fd9288 100644 --- a/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt +++ b/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt @@ -12,6 +12,7 @@ class AtariMachineDefinition: IMachineDefinition { override val FLOAT_MAX_NEGATIVE = -9.999999999e97 override val FLOAT_MEM_SIZE = 6 override val PROGRAM_LOAD_ADDRESS = 0x2000u + override val PROGRAM_TOP_ADDRESS = 0xffffu // TODO what's memtop override val BSSHIGHRAM_START = 0u // TODO override val BSSHIGHRAM_END = 0u // TODO diff --git a/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt b/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt index a3d58d3ca..8c4d3923b 100644 --- a/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt +++ b/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt @@ -14,6 +14,7 @@ class C128MachineDefinition: IMachineDefinition { override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val PROGRAM_LOAD_ADDRESS = 0x1c01u + override val PROGRAM_TOP_ADDRESS = 0xfeffu override val BSSHIGHRAM_START = 0u // TODO override val BSSHIGHRAM_END = 0u // TODO diff --git a/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt b/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt index 616a5760e..1bcec08d8 100644 --- a/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt +++ b/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt @@ -15,6 +15,7 @@ class C64MachineDefinition: IMachineDefinition { override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val PROGRAM_LOAD_ADDRESS = 0x0801u + override val PROGRAM_TOP_ADDRESS = 0xbfffu override val BSSHIGHRAM_START = 0xc000u override val BSSHIGHRAM_END = 0xcfffu diff --git a/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt b/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt index 8530e3c97..fa72e9099 100644 --- a/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt +++ b/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt @@ -14,6 +14,7 @@ class CX16MachineDefinition: IMachineDefinition { override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val PROGRAM_LOAD_ADDRESS = 0x0801u + override val PROGRAM_TOP_ADDRESS = 0x9effu override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000 diff --git a/codeCore/src/prog8/code/target/pet/PETMachineDefinition.kt b/codeCore/src/prog8/code/target/pet/PETMachineDefinition.kt index c9c7fd7bb..b71cda9ef 100644 --- a/codeCore/src/prog8/code/target/pet/PETMachineDefinition.kt +++ b/codeCore/src/prog8/code/target/pet/PETMachineDefinition.kt @@ -14,6 +14,7 @@ class PETMachineDefinition: IMachineDefinition { override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val PROGRAM_LOAD_ADDRESS = 0x0401u + override val PROGRAM_TOP_ADDRESS = 0x7fffu override val BSSHIGHRAM_START = 0u override val BSSHIGHRAM_END = 0u diff --git a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt index 54308d7c5..15cd7bc74 100644 --- a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt +++ b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt @@ -14,6 +14,7 @@ class VirtualMachineDefinition: IMachineDefinition { override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble() override val FLOAT_MEM_SIZE = 8 // 64-bits double override val PROGRAM_LOAD_ADDRESS = 0u // not actually used + override val PROGRAM_TOP_ADDRESS = 0xffffu // not actually used override val BSSHIGHRAM_START = 0u // not actually used override val BSSHIGHRAM_END = 0u // not actually used diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 70459089b..f704118a6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -284,6 +284,8 @@ internal class ProgramAndVarsGen( asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"") } } + asmgen.out(" ; memtop check") + asmgen.out(" .cerror * > ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${options.memtopAddress.toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"") } private fun block2asm(block: PtBlock) { diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt index 144ae4681..001496128 100644 --- a/codeGenCpu6502/test/TestCodegen.kt +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -26,7 +26,8 @@ class TestCodegen: FunSpec({ floats = true, noSysInit = false, compTarget = target, - loadAddress = target.machine.PROGRAM_LOAD_ADDRESS + loadAddress = target.machine.PROGRAM_LOAD_ADDRESS, + memtopAddress = 0xffffu ) } diff --git a/codeGenIntermediate/test/TestIRPeepholeOpt.kt b/codeGenIntermediate/test/TestIRPeepholeOpt.kt index 8546705aa..77debf444 100644 --- a/codeGenIntermediate/test/TestIRPeepholeOpt.kt +++ b/codeGenIntermediate/test/TestIRPeepholeOpt.kt @@ -22,7 +22,8 @@ class TestIRPeepholeOpt: FunSpec({ floats = false, noSysInit = true, compTarget = target, - loadAddress = target.machine.PROGRAM_LOAD_ADDRESS + loadAddress = target.machine.PROGRAM_LOAD_ADDRESS, + memtopAddress = 0xffffu ) val prog = IRProgram("test", IRSymbolTable(), options, target) prog.addBlock(block) diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index c66a8f4cb..cea861c72 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -23,7 +23,8 @@ class TestVmCodeGen: FunSpec({ floats = true, noSysInit = false, compTarget = target, - loadAddress = target.machine.PROGRAM_LOAD_ADDRESS + loadAddress = target.machine.PROGRAM_LOAD_ADDRESS, + memtopAddress = 0xffffu ) } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 20e2f66ab..a11e17cb5 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -263,6 +263,7 @@ internal fun determineProgramLoadAddress(program: Program, options: CompilationO } options.loadAddress = loadAddress + options.memtopAddress = program.toplevelModule.memtopAddress?.first ?: options.compTarget.machine.PROGRAM_TOP_ADDRESS } @@ -398,7 +399,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget return CompilationOptions( outputType, launcherType, zpType, zpReserved, zpAllowed, floatsEnabled, noSysInit, - compTarget, 0u + compTarget, 0u, 0xffffu ) } diff --git a/compiler/src/prog8/compiler/ModuleImporter.kt b/compiler/src/prog8/compiler/ModuleImporter.kt index b3401bd6d..b088ce613 100644 --- a/compiler/src/prog8/compiler/ModuleImporter.kt +++ b/compiler/src/prog8/compiler/ModuleImporter.kt @@ -110,7 +110,7 @@ class ModuleImporter(private val program: Program, private fun removeDirectivesFromImportedModule(importedModule: Module) { // Most global directives don't apply for imported modules, so remove them - val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%zpallowed", "%address") + val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%zpallowed", "%address", "%memtop") var directives = importedModule.statements.filterIsInstance() importedModule.statements.removeAll(directives.toSet()) directives = directives.filter{ it.directive !in moduleLevelDirectives } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index d56e9c7c1..58cf42fc0 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -57,7 +57,7 @@ internal class AstChecker(private val program: Program, val directives = module.statements.filterIsInstance().groupBy { it.directive } directives.filter { it.value.size > 1 }.forEach{ entry -> when(entry.key) { - "%output", "%launcher", "%zeropage", "%address", "%encoding" -> + "%output", "%launcher", "%zeropage", "%address", "%memtop", "%encoding" -> entry.value.forEach { errors.err("directive can just occur once", it.position) } } } @@ -962,6 +962,12 @@ internal class AstChecker(private val program: Program, if(directive.args.size!=1 || directive.args[0].int == null) err("invalid address directive, expected numeric address argument") } + "%memtop" -> { + if(directive.parent !is Module) + err("this directive may only occur at module level") + if(directive.args.size!=1 || directive.args[0].int == null) + err("invalid memtop directive, expected numeric address argument") + } "%import" -> { if(directive.parent !is Module) err("this directive may only occur at module level") diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index c5c3fe032..41311481a 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -22,7 +22,7 @@ internal class StatementReorderer( // - sorts the choices in when statement. // - insert AddressOf (&) expression where required (string params to a UWORD function param etc.). - private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%zpallowed", "%address", "%option", "%encoding") + private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%zpallowed", "%address", "%memtop", "%option", "%encoding") override fun after(module: Module, parent: Node): Iterable { val (blocks, other) = module.statements.partition { it is Block } diff --git a/compiler/test/TestGoldenRam.kt b/compiler/test/TestGoldenRam.kt index aa8be1832..3918ac642 100644 --- a/compiler/test/TestGoldenRam.kt +++ b/compiler/test/TestGoldenRam.kt @@ -20,7 +20,8 @@ class TestGoldenRam: FunSpec({ floats = true, noSysInit = false, compTarget = VMTarget(), - loadAddress = 999u + loadAddress = 999u, + memtopAddress = 0xffffu ) test("empty golden ram allocations") { diff --git a/compiler/test/TestZeropage.kt b/compiler/test/TestZeropage.kt index cd7c2a2b3..63136d405 100644 --- a/compiler/test/TestZeropage.kt +++ b/compiler/test/TestZeropage.kt @@ -52,7 +52,8 @@ class TestAbstractZeropage: FunSpec({ floats = false, noSysInit = false, compTarget = DummyCompilationTarget, - loadAddress = 999u + loadAddress = 999u, + memtopAddress = 0xffffu ) ) zp.free.size shouldBe 256-6-16 @@ -70,7 +71,7 @@ class TestC64Zeropage: FunSpec({ val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, floats = false, noSysInit = false, - compTarget = c64target, loadAddress = 999u + compTarget = c64target, loadAddress = 999u, memtopAddress = 0xffffu )) var result = zp.allocate("", DataType.UBYTE, null, null, errors) @@ -85,33 +86,33 @@ class TestC64Zeropage: FunSpec({ } test("testZpFloatEnable") { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) var result = zp.allocate("", DataType.FLOAT, null, null, errors) result.expectError { "should be allocation error due to disabled floats" } - val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) result = zp2.allocate("", DataType.FLOAT, null, null, errors) result.expectError { "should be allocation error due to disabled ZP use" } - val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) zp3.allocate("", DataType.FLOAT, null, null, errors) } test("testZpModesWithFloats") { - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) shouldThrow { - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) } shouldThrow { - C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) } } test("testZpDontuse") { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) println(zp.free) zp.availableBytes() shouldBe 0 val result = zp.allocate("", DataType.BYTE, null, null, errors) @@ -119,13 +120,13 @@ class TestC64Zeropage: FunSpec({ } test("testFreeSpacesBytes") { - val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) zp1.availableBytes() shouldBe 17 - val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) zp2.availableBytes() shouldBe 87 - val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) zp3.availableBytes() shouldBe 96 - val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) zp4.availableBytes() shouldBe 207 zp4.allocate("test", DataType.UBYTE, null, null, errors) zp4.availableBytes() shouldBe 206 @@ -134,7 +135,7 @@ class TestC64Zeropage: FunSpec({ } test("testReservedSpace") { - val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) zp1.availableBytes() shouldBe 207 4u shouldNotBeIn zp1.free 35u shouldNotBeIn zp1.free @@ -145,7 +146,7 @@ class TestC64Zeropage: FunSpec({ 200u shouldBeIn zp1.free 255u shouldBeIn zp1.free 199u shouldBeIn zp1.free - val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) zp2.availableBytes() shouldBe 107 4u shouldNotBeIn zp2.free 35u shouldNotBeIn zp2.free @@ -156,14 +157,14 @@ class TestC64Zeropage: FunSpec({ 200u shouldNotBeIn zp2.free 255u shouldNotBeIn zp2.free 199u shouldBeIn zp2.free - val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) zp2.availableBytes() shouldBe 107 4u shouldBeIn zp3.free 35u shouldNotBeIn zp3.free } test("testBasicsafeAllocation") { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) zp.availableBytes() shouldBe 17 zp.hasByteAvailable() shouldBe true zp.hasWordAvailable() shouldBe true @@ -185,7 +186,7 @@ class TestC64Zeropage: FunSpec({ } test("testFullAllocation") { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) zp.availableBytes() shouldBe 207 zp.hasByteAvailable() shouldBe true zp.hasWordAvailable() shouldBe true @@ -216,7 +217,7 @@ class TestC64Zeropage: FunSpec({ } test("testEfficientAllocation") { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, c64target, 999u, 0xffffu)) zp.availableBytes() shouldBe 17 zp.allocate("", DataType.WORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x04u zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x06u @@ -234,7 +235,7 @@ class TestC64Zeropage: FunSpec({ } test("testReservedLocations") { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, c64target, 999u, 0xffffu)) withClue("zp _B1 and _REG must be next to each other to create a word") { zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG } @@ -247,18 +248,18 @@ class TestCx16Zeropage: FunSpec({ val cx16target = Cx16Target() test("testReservedLocations") { - val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u)) + val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u, 0xffffu)) withClue("zp _B1 and _REG must be next to each other to create a word") { zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG } } test("testFreeSpacesBytes") { - val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, cx16target, 999u)) + val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, cx16target, 999u, 0xffffu)) zp1.availableBytes() shouldBe 88 - val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u)) + val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u, 0xffffu)) zp2.availableBytes() shouldBe 175 - val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u)) + val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u, 0xffffu)) zp3.availableBytes() shouldBe 216 zp3.allocate("test", DataType.UBYTE, null, null, errors) zp3.availableBytes() shouldBe 215 @@ -267,7 +268,7 @@ class TestCx16Zeropage: FunSpec({ } test("testReservedSpace") { - val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u)) + val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u, 0xffffu)) zp1.availableBytes() shouldBe 216 0x22u shouldBeIn zp1.free 0x80u shouldBeIn zp1.free @@ -277,7 +278,7 @@ class TestCx16Zeropage: FunSpec({ } test("preallocated zp vars") { - val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u)) + val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, cx16target, 999u, 0xffffu)) zp1.allocatedVariables["test"] shouldBe null zp1.allocatedVariables["cx16.r0"] shouldNotBe null zp1.allocatedVariables["cx16.r15"] shouldNotBe null diff --git a/compiler/test/codegeneration/TestAsmGenSymbols.kt b/compiler/test/codegeneration/TestAsmGenSymbols.kt index df940492b..89add59a7 100644 --- a/compiler/test/codegeneration/TestAsmGenSymbols.kt +++ b/compiler/test/codegeneration/TestAsmGenSymbols.kt @@ -74,7 +74,7 @@ class TestAsmGenSymbols: StringSpec({ fun createTestAsmGen6502(program: Program): AsmGen6502Internal { val errors = ErrorReporterForTests() - val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, C64Target(), 999u) + val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, C64Target(), 999u, 0xffffu) val ptProgram = IntermediateAstMaker(program, errors).transform() val st = SymbolTableMaker(ptProgram, options).make() return AsmGen6502Internal(ptProgram, st, options, errors) diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 07f6f7644..13d251ea5 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -303,6 +303,14 @@ open class Module(final override val statements: MutableList, Pair(address.args.single().int!!, address.position) } + val memtopAddress: Pair? by lazy { + val address = (statements.singleOrNull { it is Directive && it.directive == "%memtop" } as? Directive) + if(address==null || address.args.single().int==null) + null + else + Pair(address.args.single().int!!, address.position) + } + override fun linkParents(parent: Node) { require(parent is GlobalNamespace) this.parent = parent diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 292493237..fb743dd3c 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -106,6 +106,7 @@ data class Directive(val directive: String, val args: List, overri // init { // require(directive in arrayOf( // "%address", +// "%memtop", // "%asmbinary", // "%asminclude", // "%breakpoint", diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index b8c6650e5..bc47c1d5f 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -184,6 +184,17 @@ Directives - type ``none`` : no launcher logic is added at all +.. data:: %memtop
+ + Level: module. + Global setting, changes the program's top memory address. This is usually specified internally by the compiler target, + but with this you can change it to another value. This can be useful for example to 'reserve' a piece + of memory at the end of program space where other data such as external library files can be loaded into. + This memtop value is used for a check instruction for the assembler to see if the resulting program size + exceeds the given memtop address. This value is inclusive, so $9eff means that the program can use up to + and including the address $9eff and that $9f00 is the first address out of bounds. + + .. data:: %option