Skip to content

Commit

Permalink
add %output library
Browse files Browse the repository at this point in the history
  • Loading branch information
irmen committed Jan 24, 2025
1 parent 1e17df5 commit 2478aea
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 243 deletions.
3 changes: 2 additions & 1 deletion codeCore/src/prog8/code/core/Enumerations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ val CpuRegisters = arrayOf(
enum class OutputType {
RAW,
PRG,
XEX
XEX,
LIBRARY
}

enum class CbmPrgLauncherType {
Expand Down
5 changes: 5 additions & 0 deletions codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ internal class AssemblyProgram(
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
OutputType.LIBRARY -> {
command.add("--nostart")
println("\nCreating binary library file for target ${compTarget.name}.")
binFile
}
else -> throw AssemblyError("invalid output type")
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
Expand Down
178 changes: 96 additions & 82 deletions codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,93 +87,107 @@ internal class ProgramAndVarsGen(
}
}

when(options.output) {
OutputType.RAW -> {
asmgen.out("; ---- raw assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
OutputType.PRG -> {
when(options.launcher) {
CbmPrgLauncherType.BASIC -> {
if (options.loadAddress != options.compTarget.PROGRAM_LOAD_ADDRESS) {
errors.err("BASIC output must have load address ${options.compTarget.PROGRAM_LOAD_ADDRESS.toHex()}", program.position)
if(options.output == OutputType.LIBRARY) {

asmgen.out("; ---- library assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out(" jmp p8b_main.p8s_start") // TODO still needed otherwise 64tass removes all .procs

} else {

when (options.output) {
OutputType.LIBRARY -> { }
OutputType.RAW -> {
asmgen.out("; ---- raw assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if (!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
OutputType.PRG -> {
when (options.launcher) {
CbmPrgLauncherType.BASIC -> {
if (options.loadAddress != options.compTarget.PROGRAM_LOAD_ADDRESS) {
errors.err(
"BASIC output must have load address ${options.compTarget.PROGRAM_LOAD_ADDRESS.toHex()}",
program.position
)
}
asmgen.out("; ---- basic program with sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
val year = LocalDate.now().year
asmgen.out(" .word (+), $year")
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
asmgen.out("+\t.word 0")
asmgen.out("prog8_entrypoint")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if (!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}

CbmPrgLauncherType.NONE -> {
// this is the same as RAW
asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if (!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
asmgen.out("; ---- basic program with sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
val year = LocalDate.now().year
asmgen.out(" .word (+), $year")
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
asmgen.out("+\t.word 0")
asmgen.out("prog8_entrypoint")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
CbmPrgLauncherType.NONE -> {
// this is the same as RAW
asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
}
OutputType.XEX -> {
asmgen.out("; ---- atari xex program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if (!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
}
OutputType.XEX -> {
asmgen.out("; ---- atari xex program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
}

if(options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
asmgen.out("""
; zeropage is clobbered so we need to reset the machine at exit
lda #>sys.reset_system
pha
lda #<sys.reset_system
pha""")
}

when(compTarget.name) {
"cx16" -> {
if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
"c128" -> {
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
else -> {
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
if (options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
asmgen.out("""
; zeropage is clobbered so we need to reset the machine at exit
lda #>sys.reset_system
pha
lda #<sys.reset_system
pha""")
}

when (compTarget.name) {
"cx16" -> {
if (options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
"c128" -> {
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
else -> {
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
}
}
}
Expand Down
40 changes: 32 additions & 8 deletions compiler/src/prog8/compiler/Compiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,15 @@ internal fun determineProgramLoadAddress(program: Program, options: CompilationO
throw AssemblyError("atari xex output can't contain BASIC launcher")
loadAddress = options.compTarget.PROGRAM_LOAD_ADDRESS
}
OutputType.LIBRARY -> {
if(options.launcher!=CbmPrgLauncherType.NONE)
throw AssemblyError("library output can't contain BASIC launcher")
if(options.zeropage!=ZeropageType.DONTUSE)
throw AssemblyError("library output can't use zeropage")
if(options.noSysInit==false)
throw AssemblyError("library output can't have sysinit")
// LIBRARY has no predefined load address.
}
}
}

Expand All @@ -264,7 +273,7 @@ internal fun determineProgramLoadAddress(program: Program, options: CompilationO
}

if(loadAddress==null) {
errors.err("load address must be specified with the specifid output/launcher options", program.toplevelModule.position)
errors.err("load address must be specified for the selected output/launcher options", program.toplevelModule.position)
return
}

Expand Down Expand Up @@ -334,10 +343,19 @@ fun parseMainModule(filepath: Path,
importer.importImplicitLibraryModule("verafx")
}

if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG)
errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position)
if(compilerOptions.launcher == CbmPrgLauncherType.BASIC && compTarget.name== AtariTarget.NAME)
errors.err("atari target cannot use CBM BASIC launcher, use NONE", program.toplevelModule.position)
if(compilerOptions.output==OutputType.LIBRARY) {
if(compilerOptions.launcher != CbmPrgLauncherType.NONE)
errors.err("library must not use a launcher", program.toplevelModule.position)
if(compilerOptions.zeropage != ZeropageType.DONTUSE)
errors.err("library cannot use zeropage", program.toplevelModule.position)
if(compilerOptions.noSysInit == false)
errors.err("library cannot use sysinit", program.toplevelModule.position)
} else {
if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG)
errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position)
if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compTarget.name == AtariTarget.NAME)
errors.err("atari target cannot use CBM BASIC launcher, use NONE", program.toplevelModule.position)
}

errors.report()

Expand All @@ -354,8 +372,8 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
as? Directive)?.args?.single()?.name?.uppercase()
val allOptions = program.modules.flatMap { it.options() }.toSet()
val floatsEnabled = "enable_floats" in allOptions
val noSysInit = "no_sysinit" in allOptions
val zpType: ZeropageType =
var noSysInit = "no_sysinit" in allOptions
var zpType: ZeropageType =
if (zpoption == null)
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
else
Expand Down Expand Up @@ -395,7 +413,7 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
OutputType.PRG
}
}
val launcherType = if (launcherTypeStr == null) {
var launcherType = if (launcherTypeStr == null) {
when(compTarget) {
is AtariTarget -> CbmPrgLauncherType.NONE
else -> CbmPrgLauncherType.BASIC
Expand All @@ -409,6 +427,12 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
}
}

if(outputType == OutputType.LIBRARY) {
launcherType = CbmPrgLauncherType.NONE
zpType = ZeropageType.DONTUSE
noSysInit = true
}

return CompilationOptions(
outputType, launcherType,
zpType, zpReserved, zpAllowed, floatsEnabled, noSysInit,
Expand Down
20 changes: 13 additions & 7 deletions docs/source/todo.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
TODO
====

- added %output library, which preselects a bunch of options required to build a library file (rather than an executable program):
%launcher none
%option no_sysinit
%zeropage dontuse
You still have to set %address and %memtop yourself to tell prog8 where the library is meant to be placed in memory
TODO: it's now a raw binary file, should it include the 2-byte PRG header for easy loading from basic as well perhaps?

- Compiling Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine (see translateSubroutine function)
AND there is separate setup logic going on before calling it. Make up our mind!
Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
Need to add some way to generate a stable jump table at a given address.
Library must not include prog8_program_start stuff either. Must not require 'start' entrypoint either? Although they need some initialization entry point?

- Make some of the target machine config externally configurable (for 1 new target, the existing ones should stay as they are for the time being)

- add paypal donation button as well?
Expand All @@ -15,13 +28,6 @@ Future Things and Ideas
- Kotlin: can we use inline value classes in certain spots?
- add float support to the configurable compiler targets
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Compiling Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine (see translateSubroutine function)
AND there is separate setup logic going on before calling it. Make up our mind!
Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
Add a -library $xxxx command line option (and/or some directive) to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?
Need to add some way to generate a stable jump table at a given address.
Need library to not call init_system AND init_system_phase2 not either.
Library must not include prog8_program_start stuff either. Must not require 'start' entrypoint either? Although they need some initialization entry point?
- [problematic due to using 64tass:] better support for building library programs, where unused .proc are NOT deleted from the assembly.
Perhaps replace all uses of .proc/.pend/.endproc by .block/.bend will fix that with a compiler flag?
But all library code written in asm uses .proc already..... (textual search/replace when writing the actual asm?)
Expand Down
Loading

0 comments on commit 2478aea

Please sign in to comment.