mirror of
https://github.com/irmen/prog8.git
synced 2025-04-07 16:41:46 +00:00
better handling of loadAddress
This commit is contained in:
parent
9b81955544
commit
92737bb695
@ -51,25 +51,8 @@ abstract class PtNamedNode(val name: String, position: Position): PtNode(positio
|
||||
}
|
||||
|
||||
|
||||
// TODO remove duplicates that are also in CompilationOptions (that is already passed into the AsmGen already)
|
||||
// TODO make sure loadaddress is always set to a sensible value (not 0) see determineProgramLoadAddress()
|
||||
class ProgramOptions(
|
||||
val output: OutputType,
|
||||
val launcher: CbmPrgLauncherType,
|
||||
val zeropage: ZeropageType,
|
||||
val zpReserved: Collection<UIntRange>,
|
||||
val loadAddress: UInt,
|
||||
val floatsEnabled: Boolean,
|
||||
val noSysInit: Boolean,
|
||||
val dontReinitGlobals: Boolean,
|
||||
val optimize: Boolean,
|
||||
val target: String
|
||||
)
|
||||
|
||||
|
||||
class PtProgram(
|
||||
val name: String,
|
||||
val options: ProgramOptions,
|
||||
val memsizer: IMemSizer,
|
||||
val encoding: IStringEncoding
|
||||
) : PtNode(Position.DUMMY) {
|
||||
|
@ -11,7 +11,8 @@ class CompilationOptions(val output: OutputType,
|
||||
val floats: Boolean,
|
||||
val noSysInit: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
// these are set based on command line arguments:
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var slowCodegenWarnings: Boolean = false,
|
||||
var optimize: Boolean = false,
|
||||
var optimizeFloatExpressions: Boolean = false,
|
||||
|
@ -57,7 +57,6 @@ internal class ProgramAndVarsGen(
|
||||
CpuType.CPU65c02 -> "w65c02"
|
||||
else -> "unsupported"
|
||||
}
|
||||
determineProgramLoadAddress(program, errors)
|
||||
|
||||
asmgen.out("; $cpu assembly code for '${program.name}'")
|
||||
asmgen.out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
|
||||
@ -78,16 +77,16 @@ internal class ProgramAndVarsGen(
|
||||
when(options.output) {
|
||||
OutputType.RAW -> {
|
||||
asmgen.out("; ---- raw assembler program ----")
|
||||
asmgen.out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
||||
}
|
||||
OutputType.PRG -> {
|
||||
when(options.launcher) {
|
||||
CbmPrgLauncherType.BASIC -> {
|
||||
if (program.actualLoadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
||||
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
||||
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.toplevelModule.position)
|
||||
}
|
||||
asmgen.out("; ---- basic program with sys call ----")
|
||||
asmgen.out("* = ${program.actualLoadAddress.toHex()}")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
val year = LocalDate.now().year
|
||||
asmgen.out(" .word (+), $year")
|
||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||
@ -99,7 +98,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
CbmPrgLauncherType.NONE -> {
|
||||
asmgen.out("; ---- program without basic sys call ----")
|
||||
asmgen.out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||
@ -108,7 +107,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
asmgen.out("; ---- atari xex program ----")
|
||||
asmgen.out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||
@ -136,35 +135,6 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO do this in the compiler, not in the code generator. Maybe it needs to be part of ICompilationTarget?
|
||||
private fun determineProgramLoadAddress(program: Program, errors: IErrorReporter) {
|
||||
program.actualLoadAddress = program.definedLoadAddress ?: 0u
|
||||
if (program.actualLoadAddress == 0u) {
|
||||
when(options.output) {
|
||||
OutputType.RAW -> {
|
||||
errors.err("load address must be specified with %address when using raw output type", program.toplevelModule.position)
|
||||
return
|
||||
}
|
||||
OutputType.PRG -> {
|
||||
when(options.launcher) {
|
||||
CbmPrgLauncherType.BASIC -> {
|
||||
program.actualLoadAddress = compTarget.machine.PROGRAM_LOAD_ADDRESS
|
||||
}
|
||||
CbmPrgLauncherType.NONE -> {
|
||||
errors.err("load address must be specified with %address when not using basic launcher", program.toplevelModule.position)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
if(options.launcher!=CbmPrgLauncherType.NONE)
|
||||
throw AssemblyError("atari xex output can't contain BASIC launcher")
|
||||
program.actualLoadAddress = compTarget.machine.PROGRAM_LOAD_ADDRESS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun memorySlabs() {
|
||||
asmgen.out("; memory slabs")
|
||||
asmgen.out("prog8_slabs\t.block")
|
||||
|
@ -37,7 +37,7 @@ class AsmGen(internal val program: PtProgram,
|
||||
xml.elt("program")
|
||||
xml.attr("name", program.name)
|
||||
xml.startChildren()
|
||||
write(program.options)
|
||||
writeOptions(options)
|
||||
program.children.forEach { writeNode(it) }
|
||||
writeSymboltable(symbolTable)
|
||||
xml.endElt()
|
||||
@ -130,14 +130,14 @@ class AsmGen(internal val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun write(options: ProgramOptions) {
|
||||
private fun writeOptions(options: CompilationOptions) {
|
||||
xml.elt("options")
|
||||
xml.attr("target", options.target)
|
||||
xml.attr("target", options.compTarget.name)
|
||||
xml.attr("output", options.output.name)
|
||||
xml.attr("launcher", options.launcher.name)
|
||||
xml.attr("zeropage", options.zeropage.name)
|
||||
xml.attr("loadaddress", options.loadAddress.toString())
|
||||
xml.attr("floatsenabled", options.floatsEnabled.toString())
|
||||
xml.attr("floatsenabled", options.floats.toString())
|
||||
xml.attr("nosysinit", options.noSysInit.toString())
|
||||
xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
|
||||
xml.attr("optimize", options.optimize.toString())
|
||||
@ -661,13 +661,15 @@ class AsmGen(internal val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun write(variable: PtVariable) {
|
||||
// TODO get this from the AST only?
|
||||
// the variable declaration nodes are still present in the Ast,
|
||||
// but the Symboltable should be used look up their details.
|
||||
xml.elt("vardecl")
|
||||
xml.attr("name", variable.scopedName.joinToString("."))
|
||||
xml.attr("type", variable.type.name)
|
||||
if(variable.arraySize!=null)
|
||||
xml.attr("arraysize", variable.arraySize.toString())
|
||||
if(variable.value!=null) {
|
||||
// static initialization value
|
||||
xml.startChildren()
|
||||
writeNode(variable.value!!)
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import prog8.parser.ParseError
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
import kotlin.math.exp
|
||||
import kotlin.math.round
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
@ -101,6 +102,9 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
// println("*********** AST BEFORE ASSEMBLYGEN *************")
|
||||
// printProgram(program)
|
||||
|
||||
determineProgramLoadAddress(program, compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if (args.writeAssembly) {
|
||||
if(!createAssemblyAndAssemble(program, args.errors, compilationOptions)) {
|
||||
System.err.println("Error in codegeneration or assembler")
|
||||
@ -108,6 +112,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
val seconds = totalTime/1000.0
|
||||
@ -148,6 +153,45 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
internal fun determineProgramLoadAddress(program: Program, options: CompilationOptions, errors: IErrorReporter) {
|
||||
val specifiedAddress = program.toplevelModule.loadAddress
|
||||
var loadAddress: UInt? = null
|
||||
if(specifiedAddress!=null) {
|
||||
loadAddress = specifiedAddress.first
|
||||
}
|
||||
else {
|
||||
when(options.output) {
|
||||
OutputType.RAW -> { /* no predefined load address */ }
|
||||
OutputType.PRG -> {
|
||||
if(options.launcher==CbmPrgLauncherType.BASIC) {
|
||||
loadAddress = options.compTarget.machine.PROGRAM_LOAD_ADDRESS
|
||||
}
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
if(options.launcher!=CbmPrgLauncherType.NONE)
|
||||
throw AssemblyError("atari xex output can't contain BASIC launcher")
|
||||
loadAddress = options.compTarget.machine.PROGRAM_LOAD_ADDRESS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(options.output==OutputType.PRG && options.launcher==CbmPrgLauncherType.BASIC) {
|
||||
val expected = options.compTarget.machine.PROGRAM_LOAD_ADDRESS
|
||||
if(loadAddress!=expected) {
|
||||
errors.err("BASIC output must have load address ${expected.toHex()}", specifiedAddress?.second ?: program.toplevelModule.position)
|
||||
}
|
||||
}
|
||||
|
||||
if(loadAddress==null) {
|
||||
errors.err("load address must be specified with these output options", program.toplevelModule.position)
|
||||
return
|
||||
}
|
||||
|
||||
options.loadAddress = loadAddress
|
||||
}
|
||||
|
||||
|
||||
private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions {
|
||||
lateinit var program: Program
|
||||
|
||||
@ -276,10 +320,9 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
outputType,
|
||||
launcherType,
|
||||
outputType, launcherType,
|
||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||
compTarget
|
||||
compTarget, 0u
|
||||
)
|
||||
}
|
||||
|
||||
@ -402,7 +445,7 @@ internal fun asmGeneratorFor(program: Program,
|
||||
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
|
||||
|
||||
// TODO for now, only use the new Intermediary Ast for this experimental codegen:
|
||||
val intermediateAst = IntermediateAstMaker(program, options).transform()
|
||||
val intermediateAst = IntermediateAstMaker(program).transform()
|
||||
return prog8.codegen.experimental.AsmGen(intermediateAst, errors, symbolTable, options)
|
||||
}
|
||||
} else {
|
||||
|
@ -5,30 +5,14 @@ import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import kotlin.io.path.Path
|
||||
|
||||
|
||||
class IntermediateAstMaker(val program: Program, val comp: CompilationOptions) {
|
||||
class IntermediateAstMaker(val program: Program) {
|
||||
fun transform(): PtProgram {
|
||||
val loadAddress = program.actualLoadAddress
|
||||
val options = ProgramOptions(
|
||||
comp.output,
|
||||
comp.launcher,
|
||||
comp.zeropage,
|
||||
comp.zpReserved,
|
||||
loadAddress,
|
||||
comp.floats,
|
||||
comp.noSysInit,
|
||||
comp.dontReinitGlobals,
|
||||
comp.optimize,
|
||||
comp.compTarget.name
|
||||
)
|
||||
|
||||
val ptProgram = PtProgram(
|
||||
program.name,
|
||||
options,
|
||||
program.memsizer,
|
||||
program.encoding
|
||||
)
|
||||
|
@ -661,14 +661,14 @@ internal class AstChecker(private val program: Program,
|
||||
"%output" -> {
|
||||
if(directive.parent !is Module)
|
||||
err("this directive may only occur at module level")
|
||||
if(directive.args.size!=1 || directive.args[0].name != "raw" && directive.args[0].name != "prg")
|
||||
err("invalid output directive type, expected raw or prg")
|
||||
if(directive.args.size!=1 || directive.args[0].name !in OutputType.values().map {it.name.lowercase()})
|
||||
err("invalid output directive type")
|
||||
}
|
||||
"%launcher" -> {
|
||||
if(directive.parent !is Module)
|
||||
err("this directive may only occur at module level")
|
||||
if(directive.args.size!=1 || directive.args[0].name != "basic" && directive.args[0].name != "none")
|
||||
err("invalid launcher directive type, expected basic or none")
|
||||
if(directive.args.size!=1 || directive.args[0].name !in CbmPrgLauncherType.values().map{it.name.lowercase()})
|
||||
err("invalid launcher directive type")
|
||||
}
|
||||
"%zeropage" -> {
|
||||
if(directive.parent !is Module)
|
||||
|
@ -112,10 +112,11 @@ internal fun Program.variousCleanups(errors: IErrorReporter, options: Compilatio
|
||||
}
|
||||
|
||||
internal fun Program.moveMainAndStartToFirst() {
|
||||
// the module containing the program entrypoint is moved to the first in the sequence.
|
||||
// The module containing the program entrypoint is moved to the first in the sequence.
|
||||
// the "main" block containing the entrypoint is moved to the top in there,
|
||||
// and finally the entrypoint subroutine "start" itself is moved to the top in that block.
|
||||
|
||||
// sortModules()
|
||||
val directives = modules[0].statements.filterIsInstance<Directive>()
|
||||
val start = this.entrypoint
|
||||
val mod = start.definingModule
|
||||
|
@ -296,7 +296,11 @@ class TestOptimization: FunSpec({
|
||||
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
||||
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
|
||||
val options = CompilationOptions(OutputType.PRG, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), false, true, C64Target(), outputDir= outputDir)
|
||||
val options = CompilationOptions(OutputType.PRG, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
|
||||
floats = false,
|
||||
noSysInit = true,
|
||||
compTarget = C64Target(),
|
||||
loadAddress = 0u, outputDir= outputDir)
|
||||
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
|
||||
|
||||
// assignment is now split into:
|
||||
|
@ -44,9 +44,10 @@ class TestAbstractZeropage: FunSpec({
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.FULL,
|
||||
listOf((0x50u..0x5fu)),
|
||||
false,
|
||||
false,
|
||||
DummyCompilationTarget
|
||||
floats = false,
|
||||
noSysInit = false,
|
||||
compTarget = DummyCompilationTarget,
|
||||
loadAddress = 999u
|
||||
)
|
||||
)
|
||||
zp.free.size shouldBe 256-6-16
|
||||
@ -61,7 +62,11 @@ class TestC64Zeropage: FunSpec({
|
||||
val c64target = C64Target()
|
||||
|
||||
test("testNames") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(),
|
||||
floats = false,
|
||||
noSysInit = false,
|
||||
compTarget = c64target, loadAddress = 999u
|
||||
))
|
||||
|
||||
var result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
|
||||
result.onFailure { fail(it.toString()) }
|
||||
@ -75,33 +80,33 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testZpFloatEnable") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||
var result = zp.allocate(emptyList(), 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(), true, false, c64target))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, c64target, 999u))
|
||||
result = zp2.allocate(emptyList(), 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(), true, false, c64target))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target, 999u))
|
||||
zp3.allocate(emptyList(), DataType.FLOAT, null, null, errors)
|
||||
}
|
||||
|
||||
test("testZpModesWithFloats") {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, c64target, 999u))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target, 999u))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, c64target, 999u))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target, 999u))
|
||||
shouldThrow<InternalCompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, c64target, 999u))
|
||||
}
|
||||
shouldThrow<InternalCompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, c64target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, c64target, 999u))
|
||||
}
|
||||
}
|
||||
|
||||
test("testZpDontuse") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target, 999u))
|
||||
println(zp.free)
|
||||
zp.availableBytes() shouldBe 0
|
||||
val result = zp.allocate(emptyList(), DataType.BYTE, null, null, errors)
|
||||
@ -109,13 +114,13 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testFreeSpacesBytes") {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
||||
zp1.availableBytes() shouldBe 18
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, c64target))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, c64target, 999u))
|
||||
zp2.availableBytes() shouldBe 85
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, c64target))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, c64target, 999u))
|
||||
zp3.availableBytes() shouldBe 125
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||
zp4.availableBytes() shouldBe 239
|
||||
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
||||
zp4.availableBytes() shouldBe 238
|
||||
@ -124,7 +129,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testReservedSpace") {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||
zp1.availableBytes() shouldBe 239
|
||||
50u shouldBeIn zp1.free
|
||||
100u shouldBeIn zp1.free
|
||||
@ -133,7 +138,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), false, false, c64target))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), false, false, c64target, 999u))
|
||||
zp2.availableBytes() shouldBe 139
|
||||
50u shouldNotBeIn zp2.free
|
||||
100u shouldNotBeIn zp2.free
|
||||
@ -145,7 +150,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testBasicsafeAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
||||
zp.availableBytes() shouldBe 18
|
||||
zp.hasByteAvailable() shouldBe true
|
||||
zp.hasWordAvailable() shouldBe true
|
||||
@ -167,7 +172,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testFullAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||
zp.availableBytes() shouldBe 239
|
||||
zp.hasByteAvailable() shouldBe true
|
||||
zp.hasWordAvailable() shouldBe true
|
||||
@ -198,7 +203,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testEfficientAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
||||
zp.availableBytes() shouldBe 18
|
||||
zp.allocate(emptyList(), DataType.WORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x04u
|
||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u
|
||||
@ -216,7 +221,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testReservedLocations") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target, 999u))
|
||||
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
||||
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
||||
}
|
||||
@ -229,18 +234,18 @@ class TestCx16Zeropage: FunSpec({
|
||||
val cx16target = Cx16Target()
|
||||
|
||||
test("testReservedLocations") {
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, cx16target))
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, cx16target, 999u))
|
||||
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(), true, false, cx16target))
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, cx16target, 999u))
|
||||
zp1.availableBytes() shouldBe 88
|
||||
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, cx16target))
|
||||
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, cx16target, 999u))
|
||||
zp2.availableBytes() shouldBe 175
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target))
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target, 999u))
|
||||
zp3.availableBytes() shouldBe 216
|
||||
zp3.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
||||
zp3.availableBytes() shouldBe 215
|
||||
@ -249,7 +254,7 @@ class TestCx16Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testReservedSpace") {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target))
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target, 999u))
|
||||
zp1.availableBytes() shouldBe 216
|
||||
0x22u shouldBeIn zp1.free
|
||||
0x80u shouldBeIn zp1.free
|
||||
@ -259,7 +264,7 @@ class TestCx16Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("preallocated zp vars") {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target))
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target, 999u))
|
||||
zp1.allocatedVariables[listOf("test")] shouldBe null
|
||||
zp1.allocatedVariables[listOf("cx16", "r0")] shouldNotBe null
|
||||
zp1.allocatedVariables[listOf("cx16", "r15")] shouldNotBe null
|
||||
|
@ -4,12 +4,10 @@ import io.kotest.assertions.fail
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import prog8.code.ast.PtAssignment
|
||||
import prog8.code.ast.PtPipe
|
||||
import prog8.code.ast.PtVariable
|
||||
import prog8.code.core.CbmPrgLauncherType
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.OutputType
|
||||
import prog8.code.core.ZeropageType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.compiler.IntermediateAstMaker
|
||||
import prog8tests.helpers.compileText
|
||||
@ -30,19 +28,11 @@ class TestIntermediateAst: FunSpec({
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||
val options = CompilationOptions(
|
||||
OutputType.PRG,
|
||||
CbmPrgLauncherType.BASIC,
|
||||
ZeropageType.BASICSAFE,
|
||||
emptyList(),
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
compTarget = C64Target()
|
||||
)
|
||||
val ast = IntermediateAstMaker(result.program, options).transform()
|
||||
val ast = IntermediateAstMaker(result.program).transform()
|
||||
ast.name shouldBe result.program.name
|
||||
ast.allBlocks().any() shouldBe true
|
||||
val entry = ast.entrypoint() ?: fail("no main.start() found")
|
||||
entry.children.size shouldBe 5
|
||||
entry.name shouldBe "start"
|
||||
entry.scopedName shouldBe listOf("main", "start")
|
||||
val blocks = ast.allBlocks().toList()
|
||||
@ -52,6 +42,13 @@ class TestIntermediateAst: FunSpec({
|
||||
val ccdecl = entry.children[0] as PtVariable
|
||||
ccdecl.name shouldBe "cc"
|
||||
ccdecl.scopedName shouldBe listOf("main", "start", "cc")
|
||||
ccdecl.type shouldBe DataType.UBYTE
|
||||
val arraydecl = entry.children[2] as PtVariable
|
||||
arraydecl.name shouldBe "array"
|
||||
arraydecl.type shouldBe DataType.ARRAY_UB
|
||||
val pipe = (entry.children[4] as PtAssignment).value as PtPipe
|
||||
pipe.void shouldBe false
|
||||
pipe.type shouldBe DataType.UBYTE
|
||||
ast.print()
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ class TestAsmGenSymbols: StringSpec({
|
||||
|
||||
fun createTestAsmGen(program: Program): AsmGen {
|
||||
val errors = ErrorReporterForTests()
|
||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target())
|
||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u)
|
||||
options.compTarget.machine.zeropage = C64Zeropage(options)
|
||||
val st = SymbolTableMaker().makeFrom(program)
|
||||
return AsmGen(program, errors, st, options)
|
||||
|
@ -9,6 +9,7 @@ import prog8.compiler.CompilationResult
|
||||
import prog8.compiler.CompilerArguments
|
||||
import prog8.compiler.astprocessing.SymbolTableMaker
|
||||
import prog8.compiler.compileProgram
|
||||
import prog8.compiler.determineProgramLoadAddress
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.name
|
||||
|
||||
@ -66,9 +67,17 @@ internal fun generateAssembly(
|
||||
program: Program,
|
||||
options: CompilationOptions? = null
|
||||
): IAssemblyProgram? {
|
||||
val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), true, true, C64Target(), outputDir = outputDir)
|
||||
val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
|
||||
floats = true,
|
||||
noSysInit = true,
|
||||
compTarget = C64Target(),
|
||||
loadAddress = 0u, outputDir = outputDir)
|
||||
coptions.compTarget.machine.zeropage = C64Zeropage(coptions)
|
||||
val st = SymbolTableMaker().makeFrom(program)
|
||||
val asmgen = AsmGen(program, ErrorReporterForTests(), st, coptions)
|
||||
val errors = ErrorReporterForTests()
|
||||
determineProgramLoadAddress(program, coptions, errors)
|
||||
errors.report()
|
||||
val asmgen = AsmGen(program, errors, st, coptions)
|
||||
errors.report()
|
||||
return asmgen.compileToAssembly()
|
||||
}
|
||||
|
@ -285,8 +285,12 @@ open class Module(final override var statements: MutableList<Statement>,
|
||||
.substringAfterLast("/")
|
||||
.substringAfterLast("\\")
|
||||
|
||||
val loadAddress: UInt? by lazy {
|
||||
(statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int
|
||||
val loadAddress: Pair<UInt, Position>? by lazy {
|
||||
val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)
|
||||
if(address==null || address.args.single().int==null)
|
||||
null
|
||||
else
|
||||
Pair(address.args.single().int!!, address.position)
|
||||
}
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
|
@ -67,11 +67,6 @@ class Program(val name: String,
|
||||
val toplevelModule: Module
|
||||
get() = modules.first { it.name!= internedStringsModuleName }
|
||||
|
||||
val definedLoadAddress: UInt?
|
||||
get() = toplevelModule.loadAddress
|
||||
|
||||
var actualLoadAddress = 0u
|
||||
|
||||
private val internedStringsReferenceCounts = mutableMapOf<VarDecl, Int>()
|
||||
|
||||
fun internString(string: StringLiteral): List<String> {
|
||||
@ -126,6 +121,8 @@ class Program(val name: String,
|
||||
s.removeStrings(modules)
|
||||
}
|
||||
|
||||
fun sortModules() = _modules.sortBy { it.isLibrary }
|
||||
|
||||
private class StringSearch(val program: Program): IAstVisitor {
|
||||
val removals = mutableListOf<List<String>>()
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
|
@ -3,10 +3,6 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- ProgramOptions: remove duplicates that are also in CompilationOptions (that is already passed into the AsmGen already)
|
||||
- ProgramOptions: make sure loadaddress is always set to a sensible value (not 0) see determineProgramLoadAddress()
|
||||
- unit test for PtProgram AST: should also test new things such as the Datatype in nodes.
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
@ -3,10 +3,9 @@
|
||||
%zeropage basicsafe
|
||||
%zpreserved 50,80
|
||||
%zpreserved 150,155
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
%option align_word
|
||||
|
||||
|
||||
main {
|
||||
%option align_word
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user