diff --git a/codeCore/src/prog8/code/core/CompilationOptions.kt b/codeCore/src/prog8/code/core/CompilationOptions.kt index a971a8055..9a24b7d3b 100644 --- a/codeCore/src/prog8/code/core/CompilationOptions.kt +++ b/codeCore/src/prog8/code/core/CompilationOptions.kt @@ -19,6 +19,7 @@ class CompilationOptions(val output: OutputType, var asmQuiet: Boolean = false, var asmListfile: Boolean = false, var experimentalCodegen: Boolean = false, + var varsHigh: Boolean = false, var evalStackBaseAddress: UInt? = null, var outputDir: Path = Path(""), var symbolDefs: Map = emptyMap() diff --git a/codeCore/src/prog8/code/core/IMachineDefinition.kt b/codeCore/src/prog8/code/core/IMachineDefinition.kt index 89a384e6f..ed973afb1 100644 --- a/codeCore/src/prog8/code/core/IMachineDefinition.kt +++ b/codeCore/src/prog8/code/core/IMachineDefinition.kt @@ -16,6 +16,8 @@ interface IMachineDefinition { var ESTACK_LO: UInt var ESTACK_HI: UInt val PROGRAM_LOAD_ADDRESS : UInt + val BSSHIGHRAM_START: UInt + val BSSHIGHRAM_END: UInt val cpu: CpuType var zeropage: Zeropage diff --git a/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt b/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt index 3b350f812..0e9cddab8 100644 --- a/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt +++ b/codeCore/src/prog8/code/target/atari/AtariMachineDefinition.kt @@ -17,6 +17,9 @@ class AtariMachineDefinition: IMachineDefinition { override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive // TODO override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive // TODO + override val BSSHIGHRAM_START = 0u // TODO + override val BSSHIGHRAM_END = 0u // TODO + override lateinit var zeropage: Zeropage override lateinit var golden: GoldenRam diff --git a/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt b/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt index 04c47596b..e15d31b55 100644 --- a/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt +++ b/codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt @@ -17,6 +17,10 @@ class C128MachineDefinition: IMachineDefinition { // the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations) override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive + + override val BSSHIGHRAM_START = 0u // TODO + override val BSSHIGHRAM_END = 0u // TODO + override lateinit var zeropage: Zeropage override lateinit var golden: GoldenRam diff --git a/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt b/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt index c9bfb2312..94d2f3a03 100644 --- a/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt +++ b/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt @@ -18,6 +18,10 @@ class C64MachineDefinition: IMachineDefinition { // the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations) override var ESTACK_LO = 0xcf00u // $cf00-$cf7f inclusive override var ESTACK_HI = 0xcf80u // $cf80-$cfff inclusive + + override val BSSHIGHRAM_START = 0xc000u + override val BSSHIGHRAM_END = ESTACK_LO + override lateinit var zeropage: Zeropage override lateinit var golden: GoldenRam diff --git a/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt b/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt index 1639673a6..276212eee 100644 --- a/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt +++ b/codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt @@ -17,6 +17,10 @@ class CX16MachineDefinition: IMachineDefinition { // the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations) override var ESTACK_LO = 0x0700u // $0700-$077f inclusive override var ESTACK_HI = 0x0780u // $0780-$07ff inclusive + + override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active + override val BSSHIGHRAM_END = 0xc000u // rom starts here. + override lateinit var zeropage: Zeropage override lateinit var golden: GoldenRam diff --git a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt index e63426423..2c555f76d 100644 --- a/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt +++ b/codeCore/src/prog8/code/target/virtual/VirtualMachineDefinition.kt @@ -17,6 +17,8 @@ class VirtualMachineDefinition: IMachineDefinition { override var ESTACK_LO = 0u // not actually used override var ESTACK_HI = 0u // not actually used + override val BSSHIGHRAM_START = 0u // not actually used + override val BSSHIGHRAM_END = 0u // not actually used override lateinit var zeropage: Zeropage // not actually used override lateinit var golden: GoldenRam // not actually used diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 3fa14c443..335741a81 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -162,23 +162,44 @@ internal class ProgramAndVarsGen( } private fun memorySlabs() { - asmgen.out("; memory slabs\n .section slabs_BSS") - asmgen.out("prog8_slabs\t.block") - for(slab in symboltable.allMemorySlabs) { - if(slab.align>1u) - asmgen.out("\t.align ${slab.align.toHex()}") - asmgen.out("${slab.name}\t.fill ${slab.size}") + if(symboltable.allMemorySlabs.isNotEmpty()) { + asmgen.out("; memory slabs\n .section slabs_BSS") + asmgen.out("prog8_slabs\t.block") + for (slab in symboltable.allMemorySlabs) { + if (slab.align > 1u) + asmgen.out("\t.align ${slab.align.toHex()}") + asmgen.out("${slab.name}\t.fill ${slab.size}") + } + asmgen.out("\t.bend\n .send slabs_BSS") } - asmgen.out("\t.bend\n .send slabs_BSS") } private fun footer() { asmgen.out("; bss sections") - asmgen.out("prog8_bss_section_start") - asmgen.out(" .dsection BSS") - asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") - asmgen.out(" .dsection slabs_BSS") - asmgen.out("prog8_program_end\t; end of program label for progend()") + if(options.varsHigh) { + if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) { + throw AssemblyError("current compilation target hasn't got the high ram area properly defined") + } + // BSS vars in high ram area, memory() slabs just concatenated at the end of the program. + if(symboltable.allMemorySlabs.isNotEmpty()) { + asmgen.out(" .dsection slabs_BSS") + } + asmgen.out("prog8_program_end\t; end of program label for progend()") + asmgen.out(" * = ${options.compTarget.machine.BSSHIGHRAM_START.toHex()}") + asmgen.out("prog8_bss_section_start") + asmgen.out(" .dsection BSS") + asmgen.out(" .cerror * >= ${options.compTarget.machine.BSSHIGHRAM_END.toHex()}, \"too many variables for BSS section\"") + asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") + } else { + // BSS vars followed by memory() slabs, concatenated at the end of the program. + asmgen.out("prog8_bss_section_start") + asmgen.out(" .dsection BSS") + asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") + if(symboltable.allMemorySlabs.isNotEmpty()) { + asmgen.out(" .dsection slabs_BSS") + } + asmgen.out("prog8_program_end\t; end of program label for progend()") + } } private fun block2asm(block: PtBlock) { diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index cc15de348..d0fcca6dc 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -51,6 +51,7 @@ private fun compileMain(args: Array): Boolean { val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME) val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM") val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)") + val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program") val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999) try { @@ -124,6 +125,7 @@ private fun compileMain(args: Array): Boolean { quietAssembler == true, asmListfile == true, experimentalCodegen == true, + varsHigh == true, compilationTarget, evalStackAddr, processedSymbols, @@ -187,6 +189,7 @@ private fun compileMain(args: Array): Boolean { quietAssembler == true, asmListfile == true, experimentalCodegen == true, + varsHigh == true, compilationTarget, evalStackAddr, processedSymbols, diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 7c04a46dd..330d798c3 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -35,6 +35,7 @@ class CompilerArguments(val filepath: Path, val quietAssembler: Boolean, val asmListfile: Boolean, val experimentalCodegen: Boolean, + val varsHigh: Boolean, val compilationTarget: String, val evalStackBaseAddress: UInt?, val symbolDefs: Map, @@ -77,6 +78,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { asmQuiet = args.quietAssembler asmListfile = args.asmListfile experimentalCodegen = args.experimentalCodegen + varsHigh = args.varsHigh evalStackBaseAddress = args.evalStackBaseAddress outputDir = args.outputDir.normalize() symbolDefs = args.symbolDefs diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 5f5f61bfc..6097a85d6 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -32,6 +32,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat quietAssembler = true, asmListfile = false, experimentalCodegen = false, + varsHigh = false, compilationTarget = target.name, evalStackBaseAddress = null, symbolDefs = emptyMap(), diff --git a/compiler/test/TestCompilerOptionLibdirs.kt b/compiler/test/TestCompilerOptionLibdirs.kt index 040f6864c..6b35d3f7d 100644 --- a/compiler/test/TestCompilerOptionLibdirs.kt +++ b/compiler/test/TestCompilerOptionLibdirs.kt @@ -49,6 +49,7 @@ class TestCompilerOptionSourcedirs: FunSpec({ quietAssembler = true, asmListfile = false, experimentalCodegen = false, + varsHigh = false, compilationTarget = Cx16Target.NAME, evalStackBaseAddress = null, symbolDefs = emptyMap(), diff --git a/compiler/test/helpers/compileXyz.kt b/compiler/test/helpers/compileXyz.kt index fff933890..23b779d47 100644 --- a/compiler/test/helpers/compileXyz.kt +++ b/compiler/test/helpers/compileXyz.kt @@ -30,6 +30,7 @@ internal fun compileFile( quietAssembler = true, asmListfile = false, experimentalCodegen = false, + varsHigh = false, platform.name, evalStackBaseAddress = null, symbolDefs = emptyMap(), diff --git a/docs/source/building.rst b/docs/source/building.rst index 418ac8cdf..4f210889c 100644 --- a/docs/source/building.rst +++ b/docs/source/building.rst @@ -175,6 +175,23 @@ One or more .p8 module files When not compiling for the Commander X16 target, the location of the 16 virtual registers cx16.r0..r15 is changed accordingly (to keep them in the same memory space as the evaluation stack). +``-varshigh`` + Places the uninitialized (and zero-initialized) variables in a separate high memory area, instead of + inside the program itself. This results in an increase of the amount of system ram available for the program + itself. The amount of ram saved depends on the amount and types of variables in the program, but can be + several hundreds of bytes or more. + The new memory location of the variables depends on the compilation target machine + + c64: $C000 - $CEFF + + cx16: $A000 - $BFFF (note: assumes that the correct HiRam bank #1 is mapped in at all times!) + + If you use this option, you can no longer use the part of the above memory area that is + now alotted to the variables for your own purposes. The output of the 64tass assembler step at the + end of compilation shows precise details of where and how much memory is used by the variables + (it's called 'BSS' section or Gap). Compilation (or rather, assembling) will fail if there are too + many variables to fit in the high ram block. + Module source code files ------------------------ diff --git a/docs/source/technical.rst b/docs/source/technical.rst index d3a1c2835..f94a9c0eb 100644 --- a/docs/source/technical.rst +++ b/docs/source/technical.rst @@ -12,6 +12,13 @@ but care should be taken of course to avoid unexpected side effects. Especially when you're dealing with interrupts or re-entrant routines: don't modify variables that you not own or else you will break stuff. +Uninitialized and zero-initialized variables that are not put into zeropage, will be put into +a special 'BSS' section for the assembler. This section is usually placed at the end of the resulting program +but because it only contains empty space it won't actually increase the size of the resulting program binary. +Prog8 takes care of properly filling this memory area with zeros at program startup. +It is possible to relocate the BSS section using a compiler option +so that more system ram is available for the program code itself. + .. _three-letter-prefixing: diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ff459755a..6896dbe3e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,21 +3,18 @@ TODO For next minor release ^^^^^^^^^^^^^^^^^^^^^^ -- option to put BSS in specific upper memory block ($a000-$bfff on x16, $c000-$cdff on C64) add a .cerror check for overflow! -- document bss stuff in the manual - ... + For 9.0 major changes ^^^^^^^^^^^^^^^^^^^^^ - duplicate diskio for cx16 (get rid of cx16diskio, just copy diskio and tweak everything) + documentation - get f_seek_w working like in the BASIC program - this needs the changes to diskio.f_open to use suffixes ,p,m -- SEGMENTS. +- Some support for (64tass) SEGMENTS ? - Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class) - - block "golden" treated specially: every var in here will be allocated in the Golden ram area - - that block can only contain variables. + - maybe treat block "golden" in a special way: can only contain vars, every var will be allocated in the Golden ram area? - the variables can NOT have initialization values, they will all be set to zero on startup (simple memset) - - just initialize them yourself in start() if you need a non-zero value + just initialize them yourself in start() if you need a non-zero value - OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram - OR.... make all this more generic and use some %segment option to create real segments for 64tass? - (need separate step in codegen and IR to write the "golden" variables) diff --git a/httpCompilerService/src/prog8/http/TestHttp.kt b/httpCompilerService/src/prog8/http/TestHttp.kt index a1c58495d..c4481966c 100644 --- a/httpCompilerService/src/prog8/http/TestHttp.kt +++ b/httpCompilerService/src/prog8/http/TestHttp.kt @@ -41,7 +41,8 @@ class RequestParser : Take { symbolDefs = emptyMap(), quietAssembler = false, asmListfile = false, - experimentalCodegen = false + experimentalCodegen = false, + varsHigh = false ) val compilationResult = compileProgram(args) return RsJson(Jsonding())