package prog8.codegen.cpu6502 import prog8.code.GENERATED_LABEL_PREFIX import prog8.code.IAssemblyProgram import prog8.code.core.CompilationOptions import prog8.code.core.ICompilationTarget import prog8.code.core.IErrorReporter import prog8.code.core.OutputType import prog8.code.target.C128Target import prog8.code.target.C64Target import prog8.code.target.PETTarget import java.nio.file.Path internal class AssemblyProgram( override val name: String, outputDir: Path, private val compTarget: ICompilationTarget) : IAssemblyProgram { private val assemblyFile = outputDir.resolve("$name.asm") private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program private val binFile = outputDir.resolve("$name.bin") private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name)) private val listFile = outputDir.resolve("$name.list") override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean { val assemblerCommand: List fun addRemainingOptions(command: MutableList, program: Path, assembly: Path): List { if(options.compTarget.additionalAssemblerOptions.isNotEmpty()) command.addAll(options.compTarget.additionalAssemblerOptions) command.addAll(listOf("--output", program.toString(), assembly.toString())) return command } when(options.output) { OutputType.PRG -> { // CBM machines .prg generation. val command = mutableListOf("64tass", "--cbm-prg", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile") if(options.warnSymbolShadowing) command.add("-Wshadow") else command.add("-Wno-shadow") if(options.asmQuiet) command.add("--quiet") if(options.asmListfile) { command.add("--list=$listFile") } assemblerCommand = addRemainingOptions(command, prgFile, assemblyFile) if(!options.quiet) println("\nCreating prg for target ${compTarget.name}.") } OutputType.XEX -> { // Atari800XL .xex generation. val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch", "-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile") if(options.warnSymbolShadowing) command.add("-Wshadow") else command.add("-Wno-shadow") if(options.asmQuiet) command.add("--quiet") if(options.asmListfile) command.add("--list=$listFile") assemblerCommand = addRemainingOptions(command,xexFile, assemblyFile) if(!options.quiet) println("\nCreating xex for target ${compTarget.name}.") } OutputType.RAW -> { // Neo6502/headerless raw program generation. val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch", "-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile") if(options.warnSymbolShadowing) command.add("-Wshadow") else command.add("-Wno-shadow") if(options.asmQuiet) command.add("--quiet") if(options.asmListfile) command.add("--list=$listFile") assemblerCommand = addRemainingOptions(command, binFile, assemblyFile) if(!options.quiet) println("\nCreating raw binary for target ${compTarget.name}.") } OutputType.LIBRARY -> { // CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine) val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile") if(options.warnSymbolShadowing) command.add("-Wshadow") else command.add("-Wno-shadow") if(options.asmQuiet) command.add("--quiet") if(options.asmListfile) command.add("--list=$listFile") if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) { if(!options.quiet) println("\nCreating binary library file with header for target ${compTarget.name}.") command.add("--cbm-prg") } else { if(!options.quiet) println("\nCreating binary library file without header for target ${compTarget.name}.") command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD } assemblerCommand = addRemainingOptions(command, binFile, assemblyFile) } } val proc = ProcessBuilder(assemblerCommand) if(!options.quiet) proc.inheritIO() val process = proc.start() val result = process.waitFor() if (result == 0) { removeGeneratedLabelsFromMonlist() generateBreakpointList() } return result==0 } private fun removeGeneratedLabelsFromMonlist() { val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""") val lines = viceMonListFile.toFile().readLines() viceMonListFile.toFile().outputStream().bufferedWriter().use { for (line in lines) { if(pattern.matchEntire(line)==null) it.write(line+"\n") } } } private fun generateBreakpointList() { // builds list of breakpoints, appends to monitor list file val breakpoints = mutableListOf() val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them for (line in viceMonListFile.toFile().readLines()) { val match = pattern.matchEntire(line) if (match != null) breakpoints.add("break $" + match.groupValues[1]) } val num = breakpoints.size breakpoints.add(0, "; breakpoint list now follows") breakpoints.add(1, "; $num breakpoints have been defined") breakpoints.add(2, "del") viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n") } }