Compare commits

...

3 Commits

Author SHA1 Message Date
Irmen de Jong 7a6f2ecc8c add symboldumps to doc makefile 2024-04-09 19:53:36 +02:00
Irmen de Jong f5d556a7f9 added missing options to doc 2024-04-09 19:30:04 +02:00
Irmen de Jong 2aae46d632 added -dumpsymbols option to print a dump of all the variables and subroutine signatures 2024-04-09 19:19:13 +02:00
20 changed files with 330 additions and 35 deletions

View File

@ -52,7 +52,7 @@ What does Prog8 provide?
------------------------
- all advantages of a higher level language over having to write assembly code manually
- programs run very fast because compilation to native machine code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects.
- programs run very fast because compilation to native machine code
- modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings)
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
@ -71,7 +71,7 @@ What does Prog8 provide?
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
- encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
*Rapid edit-compile-run-debug cycle:*

View File

@ -20,6 +20,7 @@ class CompilationOptions(val output: OutputType,
var asmListfile: Boolean = false,
var includeSourcelines: Boolean = false,
var dumpVariables: Boolean = false,
var dumpSymbols: Boolean = false,
var experimentalCodegen: Boolean = false,
var varsHighBank: Int? = null,
var varsGolden: Boolean = false,

View File

@ -43,6 +43,7 @@ private fun compileMain(args: Array<String>): Boolean {
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
val dumpSymbols by cli.option(ArgType.Boolean, fullName = "dumpsymbols", description = "print a dump of the variable declarations and subroutine signatures")
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
val noStrictBool by cli.option(ArgType.Boolean, fullName = "nostrictbool", description = "allow implicit conversions between bool and bytes")
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations")
@ -155,6 +156,7 @@ private fun compileMain(args: Array<String>): Boolean {
includeSourcelines == true,
experimentalCodegen == true,
dumpVariables == true,
dumpSymbols == true,
varsHighBank,
varsGolden == true,
slabsHighBank,
@ -235,6 +237,7 @@ private fun compileMain(args: Array<String>): Boolean {
includeSourcelines == true,
experimentalCodegen == true,
dumpVariables == true,
dumpSymbols==true,
varsHighBank,
varsGolden == true,
slabsHighBank,

View File

@ -7,6 +7,7 @@ import prog8.ast.base.AstException
import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteral
import prog8.ast.printProgram
import prog8.ast.printSymbols
import prog8.ast.statements.Directive
import prog8.code.SymbolTableMaker
import prog8.code.ast.PtProgram
@ -22,6 +23,7 @@ import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.nameWithoutExtension
import kotlin.math.round
import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis
@ -39,6 +41,7 @@ class CompilerArguments(val filepath: Path,
val includeSourcelines: Boolean,
val experimentalCodegen: Boolean,
val dumpVariables: Boolean,
val dumpSymbols: Boolean,
val varsHighBank: Int?,
val varsGolden: Boolean,
val slabsHighBank: Int?,
@ -71,7 +74,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
var compilationOptions: CompilationOptions
var ast: PtProgram? = null
var resultingProgram: Program? = null
var importedFiles: List<Path> = emptyList()
var importedFiles: List<Path>
try {
val totalTime = measureTimeMillis {
@ -86,6 +89,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
includeSourcelines = args.includeSourcelines
experimentalCodegen = args.experimentalCodegen
dumpVariables = args.dumpVariables
dumpSymbols = args.dumpSymbols
breakpointCpuInstruction = args.breakpointCpuInstruction
varsHighBank = args.varsHighBank
varsGolden = args.varsGolden
@ -402,6 +406,12 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
program.preprocessAst(errors, compilerOptions)
if(compilerOptions.dumpSymbols) {
printSymbols(program)
exitProcess(0)
}
program.checkIdentifiers(errors, compilerOptions)
errors.report()
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)

View File

@ -33,6 +33,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
includeSourcelines = false,
experimentalCodegen = false,
dumpVariables = false,
dumpSymbols = false,
varsHighBank = null,
varsGolden = false,
slabsHighBank = null,

View File

@ -31,6 +31,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
includeSourcelines = false,
experimentalCodegen = false,
dumpVariables = false,
dumpSymbols = false,
varsHighBank = null,
varsGolden = false,
slabsHighBank = null,

View File

@ -22,7 +22,7 @@ class TestAstToSourceText: AnnotationSpec() {
.addModule(module)
var generatedText = ""
val it = AstToSourceTextConverter({ str -> generatedText += str }, program)
val it = AstToSourceTextConverter({ str -> generatedText += str }, program, true)
it.visit(program)
return generatedText

View File

@ -30,6 +30,7 @@ internal fun compileFile(
includeSourcelines = false,
experimentalCodegen = false,
dumpVariables = false,
dumpSymbols = false,
varsHighBank = null,
varsGolden = false,
slabsHighBank = null,

View File

@ -8,7 +8,7 @@ import prog8.code.core.*
fun printProgram(program: Program) {
println()
val printer = AstToSourceTextConverter(::print, program)
val printer = AstToSourceTextConverter(::print, program, true)
printer.visit(program)
println()
}
@ -18,7 +18,7 @@ fun printProgram(program: Program) {
* Produces Prog8 source text from a [Program] (AST node),
* passing it as a String to the specified receiver function.
*/
class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: Program, val skipLibraries: Boolean): IAstVisitor {
private var scopelevel = 0
private fun indent(s: String) = " ".repeat(scopelevel) + s
@ -33,7 +33,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
}
override fun visit(module: Module) {
if(!module.isLibrary) {
if(!module.isLibrary || !skipLibraries) {
outputln("; ----------- module: ${module.name} -----------")
super.visit(module)
}

View File

@ -0,0 +1,155 @@
package prog8.ast
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.core.DataType
import prog8.code.core.ZeropageWish
import prog8.code.core.toHex
fun printSymbols(program: Program) {
println()
val printer = SymbolPrinter(::print, program, false)
printer.visit(program)
println()
}
class SymbolPrinter(val output: (text: String) -> Unit, val program: Program, val skipLibraries: Boolean): IAstVisitor {
private fun outputln(text: String) = output(text + "\n")
override fun visit(module: Module) {
if(!module.isLibrary || !skipLibraries) {
if(module.source.isFromFilesystem || module.source.isFromResources) {
outputln("MODULE FILE: ${module.source.origin}")
super.visit(module)
output("\n")
}
}
}
override fun visit(block: Block) {
val (vars, subs) = block.statements.filter{ it is Subroutine || it is VarDecl }.partition { it is VarDecl }
if(vars.isNotEmpty() || subs.isNotEmpty()) {
outputln("${block.name} {")
for (variable in vars.sortedBy { (it as VarDecl).name }) {
output(" ")
variable.accept(this)
}
for (subroutine in subs.sortedBy { (it as Subroutine).name }) {
output(" ")
subroutine.accept(this)
}
outputln("}\n")
}
}
private fun datatypeString(dt: DataType): String {
return when (dt) {
DataType.BOOL -> "bool"
DataType.UBYTE -> "ubyte"
DataType.BYTE -> "byte"
DataType.UWORD -> "uword"
DataType.WORD -> "word"
DataType.LONG -> "long"
DataType.FLOAT -> "float"
DataType.STR -> "str"
DataType.ARRAY_UB -> "ubyte["
DataType.ARRAY_B -> "byte["
DataType.ARRAY_UW -> "uword["
DataType.ARRAY_W -> "word["
DataType.ARRAY_F -> "float["
DataType.ARRAY_BOOL -> "bool["
DataType.ARRAY_UW_SPLIT -> "@split uword["
DataType.ARRAY_W_SPLIT -> "@split word["
DataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
}
}
override fun visit(decl: VarDecl) {
if(decl.origin==VarDeclOrigin.SUBROUTINEPARAM)
return
when(decl.type) {
VarDeclType.VAR -> {}
VarDeclType.CONST -> output("const ")
VarDeclType.MEMORY -> output("&")
}
output(datatypeString(decl.datatype))
if(decl.arraysize!=null) {
decl.arraysize!!.indexExpr.accept(this)
}
if(decl.isArray)
output("]")
if(decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE)
output(" @requirezp")
else if(decl.zeropage == ZeropageWish.PREFER_ZEROPAGE)
output(" @zp")
if(decl.sharedWithAsm)
output(" @shared")
output(" ")
if(decl.names.size>1)
output(decl.names.joinToString(prefix=" "))
else
output(" ${decl.name} ")
output("\n")
}
override fun visit(subroutine: Subroutine) {
if(subroutine.isAsmSubroutine) {
output("${subroutine.name} (")
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
val reg =
when {
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
param.second.statusflag!=null -> param.second.statusflag.toString()
else -> "?????"
}
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
if(param.first!==subroutine.parameters.last())
output(", ")
}
}
else {
output("${subroutine.name} (")
for(param in subroutine.parameters) {
output("${datatypeString(param.type)} ${param.name}")
if(param!==subroutine.parameters.last())
output(", ")
}
}
output(") ")
if(subroutine.asmClobbers.isNotEmpty()) {
output("-> clobbers (")
val regs = subroutine.asmClobbers.toList().sorted()
for(r in regs) {
output(r.toString())
if(r!==regs.last())
output(",")
}
output(") ")
}
if(subroutine.returntypes.any()) {
if(subroutine.asmReturnvaluesRegisters.isNotEmpty()) {
val rts = subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).joinToString(", ") {
val dtstr = datatypeString(it.first)
if(it.second.registerOrPair!=null)
"$dtstr @${it.second.registerOrPair}"
else
"$dtstr @${it.second.statusflag}"
}
output("-> $rts ")
} else {
val rts = subroutine.returntypes.joinToString(", ") { datatypeString(it) }
output("-> $rts ")
}
}
if(subroutine.asmAddress!=null)
output("= ${subroutine.asmAddress.toHex()}")
output("\n")
}
}

View File

@ -12,9 +12,19 @@ BUILDDIR = build
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
.PHONY: help Makefile symboldumps
symboldumps:
p8compile -target atari import-all-atari.p8 -dumpsymbols > skeletons-atari.txt
p8compile -target c64 import-all-c64.p8 -dumpsymbols > skeletons-c64.txt
p8compile -target c128 import-all-c128.p8 -dumpsymbols > skeletons-c128.txt
p8compile -target cx16 import-all-cx16.p8 -dumpsymbols > skeletons-cx16.txt
p8compile -target pet32 import-all-pet32.p8 -dumpsymbols > skeletons-pet32.txt
p8compile -target virtual import-all-virtual.p8 -dumpsymbols > skeletons-virtual.txt
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

11
docs/import-all-atari.p8 Normal file
View File

@ -0,0 +1,11 @@
; all library modules for the atari compiler target
%import conv
%import cx16logo
%import diskio
%import math
%import prog8_lib
%import string
%import syslib
%import test_stack
%import textio

12
docs/import-all-c128.p8 Normal file
View File

@ -0,0 +1,12 @@
; all library modules for the c128 compiler target
%import conv
%import cx16logo
%import diskio
;;%import floats
%import math
%import prog8_lib
%import string
%import syslib
%import test_stack
%import textio

13
docs/import-all-c64.p8 Normal file
View File

@ -0,0 +1,13 @@
; all library modules for the c64 compiler target
%import conv
%import cx16logo
%import diskio
%import floats
%import graphics
%import math
%import prog8_lib
%import string
%import syslib
%import test_stack
%import textio

21
docs/import-all-cx16.p8 Normal file
View File

@ -0,0 +1,21 @@
; all library modules for the cx16 compiler target
%import bmx
%import conv
%import cx16logo
%import diskio
%import emudbg
%import floats
%import gfx2
%import graphics
%import math
%import monogfx
%import palette
%import prog8_lib
%import psg
%import sprites
%import string
%import syslib
%import test_stack
%import textio
%import verafx

11
docs/import-all-pet32.p8 Normal file
View File

@ -0,0 +1,11 @@
; all library modules for the pet32 compiler target
%import conv
%import cx16logo
%import diskio
%import math
%import prog8_lib
%import string
%import syslib
%import test_stack
%import textio

View File

@ -0,0 +1,14 @@
; all library modules for the virtual compiler target
%import conv
%import cx16logo
%import diskio
%import emudbg
%import floats
%import math
%import monogfx
%import prog8_lib
%import string
%import syslib
%import test_stack
%import textio

View File

@ -208,6 +208,12 @@ One or more .p8 module files
Prints the "intermediate AST" which is the reduced representation of the program.
This is what is used in the code generators, to generate the executable code from.
``-dumpvars``
print a dump of the variables in the program
``-dumpsymbols``
print a dump of the variable declarations and subroutine signatures
``-sourcelines``
Also include the original prog8 source code lines as comments in the generated assembly code file,
mixed in between the actual generated assembly code.
@ -246,6 +252,15 @@ One or more .p8 module files
Because this is in normal system memory, there are no bank switching issues.
This mode is only available on the Commander X16.
``-slabshigh``
put memory() slabs in high memory area instead of at the end of the program.
On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.
``-slabsgolden``
put memory() slabs in 'golden ram' memory area instead of at the end of the program.
On the cx16 target this is $0400-07ff. This is unavailable on other systems.
Module source code files
------------------------

View File

@ -26,12 +26,13 @@ You can compile programs for various machines with this CPU:
* Commodore PET (limited support)
* Atari 800 XL (limited support)
The source code is on github: https://github.com/irmen/prog8.git
Prog8 is copyright © Irmen de Jong (irmen@razorvine.net | http://www.razorvine.net).
The project is on github: https://github.com/irmen/prog8.git
Software License
^^^^^^^^^^^^^^^^
Prog8 is copyright © Irmen de Jong (irmen@razorvine.net | http://www.razorvine.net).
This is free software, as defined in the GNU GPL 3.0 (https://www.gnu.org/licenses/gpl.html)
*Exception:* All output files generated by the compiler (intermediary files and compiled binary programs)
are excluded from this particular license: you can do with those *whatever you want*.
@ -65,36 +66,38 @@ you can help me out a little bit over at https://ko-fi.com/irmen .
:alt: Chess program for the X16
Features
--------
Language features
-----------------
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
- Programs run very fast because compilation to native machine code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects.
- Provides a very convenient edit/compile/run cycle by being able to directly launch
- it is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
- the compiled programs run very fast, because compilation to highly efficient native machine code.
- Provides a convenient and fast edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
- Modular programming and scoping via modules, code blocks, and subroutines.
- No need for forward declarations.
- the language looks like a mix of Python and C so should be quite easy to learn
- Modular programming, scoping via modules, code blocks, and subroutines. No need for forward declarations.
- Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Subroutines with parameters and return values
- Subroutines with parameters and return values of various types
- Complex nested expressions are possible
- Variables are all allocated statically
- Conditional branches for status flags that map 1:1 to processor branch instructions
- Variables are all allocated statically, no memory allocator overhead
- Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
- ``when`` statement to avoid if-else chains
- ``in`` expression for concise and efficient multi-value/containment test
- Several powerful built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``, ``sort`` and ``reverse``
- Variable data types include signed and unsigned bytes and words, arrays, strings.
- Various powerful built-in libraries to do I/O, number conversions, graphics and more
- Floating point math is supported on select compiler targets.
- Easy and highly efficient integration with external subroutines and ROM routines on the target systems.
- Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents.
- Encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
- Identifiers can contain Unicode Letters, so ``knäckebröd``, ``приблизительно``, ``見せしめ`` and ``π`` are all valid identifiers.
- Advanced code optimizations, such as const-folding (zero-allocation constants that are optimized away in expressions), expression and statement simplifications/rewriting.
- Programs can be run multiple times without reloading because of automatic variable (re)initializations.
- Supports the sixteen 'virtual' 16-bit registers R0 to R15 as defined on the Commander X16, also on the other machines.
- Support for low level system features such as Vera Fx hardware word multiplication on the Commander X16
- If you only use standard Kernal and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines (just change the compilation target flag)
- Advanced code optimizations to make the resulting program smaller and faster
- Programs can be restarted after exiting (i.e. run them multiple times without having to reload everything), due to automatic variable (re)initializations.
- Supports the sixteen 'virtual' 16-bit registers R0 to R15 as defined on the Commander X16. These are also available on the other compilation targets!
- On the Commander X16: Support for low level system features such as Vera Fx, which includes 16x16 bits multiplication in hardware and fast memory copy and fill.
- Many library routines are available across compiler targets. This means that as long as you only use standard Kernal
and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines (just change the compilation target flag).
Code example

View File

@ -1,15 +1,28 @@
%import bmx
%import diskio
%import emudbg
%import floats
%import gfx2
%import graphics
%import monogfx
%import palette
%import psg
%import sprites
%import syslib
%import textio
%import verafx
%import conv
%import cx16logo
%import math
%import prog8_lib
%import string
%import test_stack
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
ubyte @shared x,y = multi()
}
asmsub multi() -> ubyte @A, ubyte @Y {
%asm {{
rts
}}
}
}