better handling of loadAddress

This commit is contained in:
Irmen de Jong 2022-03-13 12:52:12 +01:00
parent 9b81955544
commit 92737bb695
17 changed files with 143 additions and 148 deletions

View File

@ -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) {

View File

@ -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,

View File

@ -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")

View File

@ -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!!)
}

View File

@ -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 {

View File

@ -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
)

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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()
}

View File

@ -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)

View File

@ -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()
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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.
...

View File

@ -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