streamline handling of launcher type and program load address. %address is now required if not using a basic-launcher.

This commit is contained in:
Irmen de Jong 2022-02-22 22:43:14 +01:00
parent 406658a10f
commit eeb3c968d6
19 changed files with 60 additions and 45 deletions

View File

@ -43,7 +43,7 @@ class AsmGen(internal val program: Program,
override fun compileToAssembly(): IAssemblyProgram? { override fun compileToAssembly(): IAssemblyProgram? {
if(options.compTarget.name=="atari" && options.launcher==LauncherType.BASIC) if(options.compTarget.name=="atari" && options.launcher==LauncherType.CBMBASIC)
throw AssemblyError("atari target cannot use CBM BASIC launcher type") throw AssemblyError("atari target cannot use CBM BASIC launcher type")
assemblyLines.clear() assemblyLines.clear()

View File

@ -70,9 +70,15 @@ internal class ProgramAndVarsGen(
asmgen.out(".cpu '$cpu'\n.enc 'none'\n") asmgen.out(".cpu '$cpu'\n.enc 'none'\n")
program.actualLoadAddress = program.definedLoadAddress program.actualLoadAddress = program.definedLoadAddress
if (program.actualLoadAddress == 0u) // fix load address if (program.actualLoadAddress == 0u) {
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC) if (options.launcher == LauncherType.CBMBASIC) {
compTarget.machine.BASIC_LOAD_ADDRESS else compTarget.machine.RAW_LOAD_ADDRESS program.actualLoadAddress = compTarget.machine.BASIC_LOAD_ADDRESS
}
else {
errors.err("load address must be specified with %address when using launcher type ${options.launcher}", program.toplevelModule.position)
return
}
}
// the global prog8 variables needed // the global prog8 variables needed
val zp = zeropage val zp = zeropage
@ -84,9 +90,10 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}") asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
when { when {
options.launcher == LauncherType.BASIC -> { options.launcher == LauncherType.CBMBASIC -> {
if (program.actualLoadAddress != options.compTarget.machine.BASIC_LOAD_ADDRESS) if (program.actualLoadAddress != options.compTarget.machine.BASIC_LOAD_ADDRESS) {
throw AssemblyError("BASIC output must have correct load address") errors.err("BASIC output must have load address ${options.compTarget.machine.BASIC_LOAD_ADDRESS.toHex()}", program.toplevelModule.position)
}
asmgen.out("; ---- basic program with sys call ----") asmgen.out("; ---- basic program with sys call ----")
asmgen.out("* = ${program.actualLoadAddress.toHex()}") asmgen.out("* = ${program.actualLoadAddress.toHex()}")
val year = LocalDate.now().year val year = LocalDate.now().year

View File

@ -15,7 +15,7 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by C
override val machine = C128MachineDefinition() override val machine = C128MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES) override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val defaultLauncherType = LauncherType.BASIC override val defaultLauncherType = LauncherType.CBMBASIC
companion object { companion object {
const val NAME = "c128" const val NAME = "c128"

View File

@ -15,7 +15,7 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Cb
override val machine = C64MachineDefinition() override val machine = C64MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES) override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val defaultLauncherType = LauncherType.BASIC override val defaultLauncherType = LauncherType.CBMBASIC
companion object { companion object {
const val NAME = "c64" const val NAME = "c64"

View File

@ -15,7 +15,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by C
override val machine = CX16MachineDefinition() override val machine = CX16MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO) override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val defaultLauncherType = LauncherType.BASIC override val defaultLauncherType = LauncherType.CBMBASIC
companion object { companion object {
const val NAME = "cx16" const val NAME = "cx16"

View File

@ -14,7 +14,6 @@ class AtariMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = -9.999999999e97 override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6 override val FLOAT_MEM_SIZE = 6
override val BASIC_LOAD_ADDRESS = 0x2000u override val BASIC_LOAD_ADDRESS = 0x2000u
override val RAW_LOAD_ADDRESS = 0x2000u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
@ -25,7 +24,7 @@ class AtariMachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = TODO("float from number") override fun getFloat(num: Number) = TODO("float from number")
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == LauncherType.CBMBASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")
else else
emptyList() emptyList()

View File

@ -15,7 +15,6 @@ class C128MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val BASIC_LOAD_ADDRESS = 0x1c01u override val BASIC_LOAD_ADDRESS = 0x1c01u
override val RAW_LOAD_ADDRESS = 0x1300u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
@ -26,7 +25,7 @@ class C128MachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == LauncherType.CBMBASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")
else else
emptyList() emptyList()

View File

@ -14,7 +14,6 @@ class C64MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val BASIC_LOAD_ADDRESS = 0x0801u override val BASIC_LOAD_ADDRESS = 0x0801u
override val RAW_LOAD_ADDRESS = 0xc000u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0xce00u // $ce00-$ceff inclusive override val ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
@ -25,7 +24,7 @@ class C64MachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == LauncherType.CBMBASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")
else else
emptyList() emptyList()

View File

@ -14,7 +14,6 @@ class CX16MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val BASIC_LOAD_ADDRESS = 0x0801u override val BASIC_LOAD_ADDRESS = 0x0801u
override val RAW_LOAD_ADDRESS = 0x8000u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x0400u // $0400-$04ff inclusive override val ESTACK_LO = 0x0400u // $0400-$04ff inclusive
@ -24,7 +23,7 @@ class CX16MachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == LauncherType.CBMBASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")
else else
emptyList() emptyList()

View File

@ -9,6 +9,7 @@ import prog8.codegen.target.Cx16Target
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import prog8.compilerinterface.LauncherType
import java.io.File import java.io.File
import java.nio.file.FileSystems import java.nio.file.FileSystems
import java.nio.file.Path import java.nio.file.Path
@ -18,7 +19,7 @@ import kotlin.system.exitProcess
fun main(args: Array<String>) { fun main(args: Array<String>) {
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim() val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim()
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)") println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
@ -167,10 +168,16 @@ private fun compileMain(args: Array<String>): Boolean {
val programNameInPath = outputPath.resolve(compilationResult.programName) val programNameInPath = outputPath.resolve(compilationResult.programName)
if (startEmulator1==true) if(startEmulator1==true || startEmulator2==true) {
compilationResult.compTarget.machine.launchEmulator(1, programNameInPath) if (compilationResult.compilationOptions.launcher != LauncherType.NONE) {
else if (startEmulator2==true) if (startEmulator1 == true)
compilationResult.compTarget.machine.launchEmulator(2, programNameInPath) compilationResult.compilationOptions.compTarget.machine.launchEmulator(1, programNameInPath)
else if (startEmulator2 == true)
compilationResult.compilationOptions.compTarget.machine.launchEmulator(2, programNameInPath)
} else {
println("\nCan't start emulator because program has no launcher type.")
}
}
} }
} }

View File

@ -30,7 +30,7 @@ import kotlin.system.measureTimeMillis
class CompilationResult(val success: Boolean, class CompilationResult(val success: Boolean,
val program: Program, val program: Program,
val programName: String, val programName: String,
val compTarget: ICompilationTarget, val compilationOptions: CompilationOptions,
val importedFiles: List<Path>) val importedFiles: List<Path>)
class CompilerArguments(val filepath: Path, class CompilerArguments(val filepath: Path,
@ -64,10 +64,13 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
else -> throw IllegalArgumentException("invalid compilation target") else -> throw IllegalArgumentException("invalid compilation target")
} }
var compilationOptions: CompilationOptions
try { try {
val totalTime = measureTimeMillis { val totalTime = measureTimeMillis {
// import main module and everything it needs // import main module and everything it needs
val (programresult, compilationOptions, imported) = parseImports(args.filepath, args.errors, compTarget, args.sourceDirs) val (programresult, options, imported) = parseImports(args.filepath, args.errors, compTarget, args.sourceDirs)
compilationOptions = options
print("Parsed ${args.filepath}") print("Parsed ${args.filepath}")
ModuleImporter.ansiEraseRestOfLine(true) ModuleImporter.ansiEraseRestOfLine(true)
@ -106,7 +109,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
is WriteAssemblyResult.Ok -> programName = result.filename is WriteAssemblyResult.Ok -> programName = result.filename
is WriteAssemblyResult.Fail -> { is WriteAssemblyResult.Fail -> {
System.err.println(result.error) System.err.println(result.error)
return CompilationResult(false, program, programName, compTarget, importedFiles) return CompilationResult(false, program, programName, compilationOptions, importedFiles)
} }
} }
} }
@ -115,7 +118,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
System.err.flush() System.err.flush()
val seconds = totalTime/1000.0 val seconds = totalTime/1000.0
println("\nTotal compilation+assemble time: ${round(seconds*100.0)/100.0} sec.") println("\nTotal compilation+assemble time: ${round(seconds*100.0)/100.0} sec.")
return CompilationResult(true, program, programName, compTarget, importedFiles) return CompilationResult(true, program, programName, compilationOptions, importedFiles)
} catch (px: ParseError) { } catch (px: ParseError) {
System.err.print("\n\u001b[91m") // bright red System.err.print("\n\u001b[91m") // bright red
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim()) System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
@ -149,7 +152,13 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
} }
val failedProgram = Program("failed", BuiltinFunctionsFacade(BuiltinFunctions), compTarget, compTarget) val failedProgram = Program("failed", BuiltinFunctionsFacade(BuiltinFunctions), compTarget, compTarget)
return CompilationResult(false, failedProgram, programName, compTarget, emptyList()) val dummyoptions = CompilationOptions(
OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(),
floats = false,
noSysInit = true,
compTarget = compTarget
)
return CompilationResult(false, failedProgram, programName, dummyoptions, emptyList())
} }
private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions { private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions {
@ -206,9 +215,9 @@ fun parseImports(filepath: Path,
importer.importLibraryModule("math") importer.importLibraryModule("math")
importer.importLibraryModule("prog8_lib") importer.importLibraryModule("prog8_lib")
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG) if (compilerOptions.launcher == LauncherType.CBMBASIC && compilerOptions.output != OutputType.PRG)
errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position) errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position)
if(compilerOptions.launcher == LauncherType.BASIC && compTarget.name==AtariTarget.NAME) if(compilerOptions.launcher == LauncherType.CBMBASIC && compTarget.name==AtariTarget.NAME)
errors.err("atari target cannot use CBM BASIC launcher, use NONE", program.toplevelModule.position) errors.err("atari target cannot use CBM BASIC launcher, use NONE", program.toplevelModule.position)
errors.report() errors.report()
@ -265,7 +274,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
LauncherType.valueOf(launcherTypeStr) LauncherType.valueOf(launcherTypeStr)
} catch (x: IllegalArgumentException) { } catch (x: IllegalArgumentException) {
// set default value; actual check and error handling of invalid option is handled in the AstChecker later // set default value; actual check and error handling of invalid option is handled in the AstChecker later
LauncherType.BASIC LauncherType.CBMBASIC
} }
} }

View File

@ -302,7 +302,7 @@ class TestOptimization: FunSpec({
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, true, C64Target(), outputDir= outputDir) val options = CompilationOptions(OutputType.RAW, LauncherType.CBMBASIC, ZeropageType.DONTUSE, emptyList(), false, true, C64Target(), outputDir= outputDir)
result.program.processAstBeforeAsmGeneration(options, DummyVarsAndConsts, ErrorReporterForTests()) result.program.processAstBeforeAsmGeneration(options, DummyVarsAndConsts, ErrorReporterForTests())
// assignment is now split into: // assignment is now split into:

View File

@ -86,7 +86,7 @@ internal fun generateAssembly(
variables: IVariablesAndConsts, variables: IVariablesAndConsts,
options: CompilationOptions? = null options: CompilationOptions? = null
): IAssemblyProgram? { ): IAssemblyProgram? {
val coptions = options ?: CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, true, C64Target(), outputDir = outputDir) val coptions = options ?: CompilationOptions(OutputType.RAW, LauncherType.CBMBASIC, ZeropageType.DONTUSE, emptyList(), true, true, C64Target(), outputDir = outputDir)
coptions.compTarget.machine.zeropage = C64Zeropage(coptions) coptions.compTarget.machine.zeropage = C64Zeropage(coptions)
val asmgen = AsmGen(program, ErrorReporterForTests(), variables, coptions) val asmgen = AsmGen(program, ErrorReporterForTests(), variables, coptions)
return asmgen.compileToAssembly() return asmgen.compileToAssembly()

View File

@ -9,7 +9,7 @@ enum class OutputType {
} }
enum class LauncherType { enum class LauncherType {
BASIC, CBMBASIC,
NONE NONE
} }

View File

@ -20,7 +20,6 @@ interface IMachineDefinition {
val ESTACK_LO: UInt val ESTACK_LO: UInt
val ESTACK_HI: UInt val ESTACK_HI: UInt
val BASIC_LOAD_ADDRESS : UInt val BASIC_LOAD_ADDRESS : UInt
val RAW_LOAD_ADDRESS : UInt
val opcodeNames: Set<String> val opcodeNames: Set<String>
var zeropage: Zeropage var zeropage: Zeropage

View File

@ -32,8 +32,7 @@ RAM, ROM, I/O
============= =============
#. what part(s) of the address space is RAM? What parts of the RAM can be used by user programs? #. what part(s) of the address space is RAM? What parts of the RAM can be used by user programs?
#. what is the load address of Basic programs? #. what is the usual starting memory address of programs?
#. what is a good load address of machine code programs?
#. what is the best place to put 2 pages (512 bytes total) of scratch area data in RAM? #. what is the best place to put 2 pages (512 bytes total) of scratch area data in RAM?
#. what part(s) of the address space is ROM? #. what part(s) of the address space is ROM?
#. what part(s) of the address space is memory mapped I/O registers? #. what part(s) of the address space is memory mapped I/O registers?

View File

@ -90,13 +90,9 @@ Directives
.. data:: %address <address> .. data:: %address <address>
Level: module. Level: module.
Global setting, set the program's start memory address Global setting, set the program's start memory address. It's usually fixed at ``$0801`` because the
default launcher type is a CBM-basic program. But you have to specify this address yourself when
- default for ``raw`` output is ``$c000`` you don't use a CBM-basic launcher.
- default for ``prg`` output is ``$0801``
- cannot be changed if you select ``prg`` with a ``basic`` launcher;
then it is always ``$081e`` (immediately after the BASIC program), and the BASIC program itself is always at ``$0801``.
This is because the C64 expects BASIC programs to start at this address.
.. data:: %import <name> .. data:: %import <name>

View File

@ -3,7 +3,6 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- get rid of RAW_LOAD_ADDRESS and make specifying the load address for RAW launcher mode required
... ...

View File

@ -1,5 +1,8 @@
%import textio %import textio
%zeropage kernalsafe %zeropage kernalsafe
; %launcher none
;%output prg
;%address $2022
main { main {
sub start() { sub start() {