Compare commits

...

26 Commits
v1.62 ... v1.70

Author SHA1 Message Date
7facb4f372 correct version 1.70 2020-02-09 01:41:05 +01:00
ee90fed489 readme 2020-02-09 01:33:20 +01:00
4796c56c35 antlr code back 2020-02-09 01:29:58 +01:00
e2cb031386 added 'void' keyword to explicitly ignore subroutine return values (and no longer get a warning) 2020-02-09 01:29:09 +01:00
a0bc97b90c fix byte array iteration for bb in [1,2,3]
improved array literal datatype detection
2020-02-09 00:45:53 +01:00
fd240899bd fix CHROUT in simulator 2020-02-09 00:12:50 +01:00
885b22df40 fixed while and repeat warning messages line number
fixed invalid while and repeat asm label names
fixed boolean checking of numbers
2020-02-08 19:45:30 +01:00
11de3db25f simplified heapId for arrayvalues 2020-02-08 18:49:48 +01:00
14a13da7ec simplified heapId for stringvalue 2020-02-08 15:54:03 +01:00
875a71c786 removed datatype from StringValue classes (is always STR now) 2020-02-08 02:21:18 +01:00
0ff5b79353 code inspection cleanups 2020-02-08 01:31:41 +01:00
8c4d276810 improvements to string encoding/decoding and text output in the simulator 2020-02-08 01:12:30 +01:00
3dd38c0ac8 antlr library updated to 4.8 2020-02-07 23:58:07 +01:00
b8816a0e2f got rid of separate str_s datatype 2020-02-07 20:47:38 +01:00
a01a9e76f9 removed bogus clang target
fixed various simulator bugs regarding strings and chars
2020-02-07 01:22:07 +01:00
357d704aec clean up version specifier 2020-02-02 19:33:40 +01:00
868df1865c got rid of obsolete code 2020-02-02 19:18:40 +01:00
654d74da1e automatic selection of best Vice C64 emulator executable 2020-02-02 13:39:56 +01:00
59939c727a gradle updated 2020-02-02 13:39:25 +01:00
fbcf190324 sync gradle version with my manjaro packaged gradle 2020-01-27 21:32:42 +01:00
b9922a90cc update gradle wrapper to 6.1.1 2020-01-26 18:36:51 +01:00
66e0b07428 gradle updates 2020-01-07 01:29:25 +01:00
01e617ae8f new kotlin version 2019-12-09 16:17:20 +01:00
52769decd4 fix assembler float truncation warning 2019-11-27 22:36:59 +01:00
165eec4054 started a c++ language compiler code target
(meant to be an intermediate step before direct Wasm/binaryen, via clang compilation to wasm)
2019-10-30 00:15:03 +01:00
8c2e602cc7 preparing for multiple compiler backends/targets 2019-10-26 23:41:15 +02:00
80 changed files with 1892 additions and 5442 deletions

9
.idea/libraries/antlr_4_8_complete.xml generated Normal file
View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="antlr-4.8-complete">
<CLASSES>
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-4.8-complete.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

9
.idea/libraries/antlr_runtime_4_8.xml generated Normal file
View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="antlr-runtime-4.8">
<CLASSES>
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-runtime-4.8.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

29
.idea/markdown-navigator-enh.xml generated Normal file
View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownEnhProjectSettings">
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imagePathType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
<component name="MarkdownNavigatorHistory">
<PasteImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
<highlightList />
<directories />
<filenames />
</PasteImageHistory>
<CopyImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
<highlightList />
<directories />
<filenames />
</CopyImageHistory>
<PasteLinkHistory onPasteImageTargetRef="3" onPasteTargetRef="1" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteWikiElement="2" onPasteReferenceElement="2" hideInapplicableOperations="false" preserveLinkFormat="false" useHeadingForLinkText="false" linkFormat="0" saveAsDefaultOnOK="false" />
<TableToJsonHistory>
<entries />
</TableToJsonHistory>
<TableSortHistory>
<entries />
</TableSortHistory>
</component>
</project>

57
.idea/markdown-navigator.xml generated Normal file
View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="FlexmarkProjectSettings">
<FlexmarkHtmlSettings flexmarkSpecExampleRendering="0" flexmarkSpecExampleRenderHtml="false">
<flexmarkSectionLanguages>
<option name="1" value="Markdown" />
<option name="2" value="HTML" />
<option name="3" value="flexmark-ast:1" />
</flexmarkSectionLanguages>
</FlexmarkHtmlSettings>
</component>
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.md.nav.editor.javafx.html.panel" providerName="JavaFX WebView" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" correctedInvalidSettings="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ANCHORLINKS" value="true" />
<option name="ATXHEADERSPACE" value="true" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="INTELLIJ_DUMMY_IDENTIFIER" value="true" />
<option name="RELAXEDHRULES" value="true" />
<option name="STRIKETHROUGH" value="true" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="true" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="PRODUCTION_SPEC_PARSER" value="true" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" addPageHeader="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0">
<GeneratorProvider>
<provider providerId="com.vladsch.md.nav.editor.javafx.html.generator" providerName="JavaFx HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.md.nav.editor.javafx.html.css" providerName="Default JavaFx Stylesheet" />
</StylesheetProvider>
<ScriptProviders>
<provider providerId="com.vladsch.md.nav.editor.hljs.html.script" providerName="HighlightJS Script" />
</ScriptProviders>
<cssText />
<cssUriHistory />
</CssSettings>
</component>
</project>

16
.idea/misc.xml generated
View File

@ -1,5 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ANTLRGenerationPreferences">
<option name="perGrammarGenerationSettings">
<list>
<PerGrammarGenerationSettings>
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/prog8.g4" />
<option name="autoGen" value="true" />
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
<option name="libDir" value="" />
<option name="encoding" value="" />
<option name="pkg" value="" />
<option name="language" value="" />
<option name="generateListener" value="false" />
</PerGrammarGenerationSettings>
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>

1
.idea/modules.xml generated
View File

@ -2,7 +2,6 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/OldCodeGen/OldCodeGen.iml" filepath="$PROJECT_DIR$/OldCodeGen/OldCodeGen.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

View File

@ -1,762 +0,0 @@
package oldcodegen
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
import prog8.ast.antlr.escape
import prog8.ast.base.DataType
import prog8.ast.base.initvarsSubName
import prog8.ast.statements.ZeropageWish
import prog8.compiler.*
import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.LabelInstr
import prog8.compiler.intermediate.Opcode
import prog8.compiler.target.c64.AssemblyProgram
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.vm.RuntimeValue
import java.io.File
import kotlin.math.abs
class AssemblyError(msg: String) : RuntimeException(msg)
internal fun intVal(valueInstr: Instruction) = valueInstr.arg!!.integerValue()
internal fun hexVal(valueInstr: Instruction) = valueInstr.arg!!.integerValue().toHex()
internal fun hexValPlusOne(valueInstr: Instruction) = (valueInstr.arg!!.integerValue()+1).toHex()
internal fun getFloatConst(value: RuntimeValue): String =
globalFloatConsts[value.numericValue().toDouble()]
?: throw AssemblyError("should have a global float const for number $value")
internal val globalFloatConsts = mutableMapOf<Double, String>()
internal fun signExtendA(into: String) =
"""
ora #$7f
bmi +
lda #0
+ sta $into
"""
class AsmGen(private val options: CompilationOptions, private val program: IntermediateProgram,
private val heap: HeapValues, private val zeropage: Zeropage) {
private val assemblyLines = mutableListOf<String>()
private lateinit var block: IntermediateProgram.ProgramBlock
init {
// Convert invalid label names (such as "<anon-1>") to something that's allowed.
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
for(block in program.blocks) {
val newvars = block.variables.map { IntermediateProgram.Variable(symname(it.scopedname, block), it.value, it.params) }.toMutableList()
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
val newinstructions = block.instructions.asSequence().map {
when {
it is LabelInstr -> LabelInstr(symname(it.name, block), it.asmProc)
it.opcode == Opcode.INLINE_ASSEMBLY -> it
else ->
Instruction(it.opcode, it.arg, it.arg2,
callLabel = if (it.callLabel != null) symname(it.callLabel, block) else null,
callLabel2 = if (it.callLabel2 != null) symname(it.callLabel2, block) else null)
}
}.toMutableList()
val newMempointers = block.memoryPointers.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
val newblock = IntermediateProgram.ProgramBlock(
block.name,
block.address,
newinstructions,
newvars,
newMempointers,
newlabels,
force_output = block.force_output)
newblocks.add(newblock)
}
program.blocks.clear()
program.blocks.addAll(newblocks)
val newAllocatedZp = program.allocatedZeropageVariables.map { symname(it.key, null) to it.value}
program.allocatedZeropageVariables.clear()
program.allocatedZeropageVariables.putAll(newAllocatedZp)
// make a list of all const floats that are used
for(block in program.blocks) {
for(ins in block.instructions.filter{it.arg?.type== DataType.FLOAT}) {
val float = ins.arg!!.numericValue().toDouble()
if(float !in globalFloatConsts)
globalFloatConsts[float] = "prog8_const_float_${globalFloatConsts.size}"
}
}
}
fun compileToAssembly(optimize: Boolean): AssemblyProgram {
println("Generating assembly code from intermediate code... ")
assemblyLines.clear()
header()
for(b in program.blocks)
block2asm(b)
if(optimize) {
var optimizationsDone = 1
while (optimizationsDone > 0) {
optimizationsDone = optimizeAssembly(assemblyLines)
}
}
File("${program.name}.asm").printWriter().use {
for (line in assemblyLines) { it.println(line) }
}
return AssemblyProgram(program.name)
}
private fun out(str: String, splitlines: Boolean=true) {
if(splitlines) {
for (line in str.split('\n')) {
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim()
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
assemblyLines.add(trimmed)
}
} else assemblyLines.add(str)
}
// convert a fully scoped name (defined in the given block) to a valid assembly symbol name
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock?): String {
if(' ' in scoped)
return scoped
val blockLocal: Boolean
var name = if (block!=null && scoped.startsWith("${block.name}.")) {
blockLocal = true
scoped.substring(block.name.length+1)
}
else {
blockLocal = false
scoped
}
name = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
if(name=="-")
return "-"
if(blockLocal)
name = name.replace(".", "_")
else {
val parts = name.split(".", limit=2)
if(parts.size>1)
name = "${parts[0]}.${parts[1].replace(".", "_")}"
}
return name.replace("-", "")
}
private fun makeFloatFill(flt: MachineDefinition.Mflpt5): String {
val b0 = "$"+flt.b0.toString(16).padStart(2, '0')
val b1 = "$"+flt.b1.toString(16).padStart(2, '0')
val b2 = "$"+flt.b2.toString(16).padStart(2, '0')
val b3 = "$"+flt.b3.toString(16).padStart(2, '0')
val b4 = "$"+flt.b4.toString(16).padStart(2, '0')
return "$b0, $b1, $b2, $b3, $b4"
}
private fun header() {
val ourName = this.javaClass.name
out("; 6502 assembly code for '${program.name}'")
out("; generated by $ourName on ${Date()}")
out("; assembler syntax is for the 64tasm cross-assembler")
out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
out("\n.cpu '6502'\n.enc 'none'\n")
if(program.loadAddress==0) // fix load address
program.loadAddress = if(options.launcher==LauncherType.BASIC)
MachineDefinition.BASIC_LOAD_ADDRESS else MachineDefinition.RAW_LOAD_ADDRESS
when {
options.launcher == LauncherType.BASIC -> {
if (program.loadAddress != 0x0801)
throw AssemblyError("BASIC output must have load address $0801")
out("; ---- basic program with sys call ----")
out("* = ${program.loadAddress.toHex()}")
val year = Calendar.getInstance().get(Calendar.YEAR)
out(" .word (+), $year")
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
out("+\t.word 0")
out("_prog8_entrypoint\t; assembly code starts here\n")
out(" jsr prog8_lib.init_system")
}
options.output == OutputType.PRG -> {
out("; ---- program without basic sys call ----")
out("* = ${program.loadAddress.toHex()}\n")
out(" jsr prog8_lib.init_system")
}
options.output == OutputType.RAW -> {
out("; ---- raw assembler program ----")
out("* = ${program.loadAddress.toHex()}\n")
}
}
if(zeropage.exitProgramStrategy!=Zeropage.ExitProgramStrategy.CLEAN_EXIT) {
// disable shift-commodore charset switching and run/stop key
out(" lda #$80")
out(" lda #$80")
out(" sta 657\t; disable charset switching")
out(" lda #239")
out(" sta 808\t; disable run/stop key")
}
out(" ldx #\$ff\t; init estack pointer")
out(" ; initialize the variables in each block")
for(block in program.blocks) {
val initVarsLabel = block.instructions.firstOrNull { it is LabelInstr && it.name== initvarsSubName } as? LabelInstr
if(initVarsLabel!=null)
out(" jsr ${block.name}.${initVarsLabel.name}")
}
out(" clc")
when(zeropage.exitProgramStrategy) {
Zeropage.ExitProgramStrategy.CLEAN_EXIT -> {
out(" jmp main.start\t; jump to program entrypoint")
}
Zeropage.ExitProgramStrategy.SYSTEM_RESET -> {
out(" jsr main.start\t; call program entrypoint")
out(" jmp (c64.RESET_VEC)\t; cold reset")
}
}
out("")
// the global list of all floating point constants for the whole program
for(flt in globalFloatConsts) {
val floatFill = makeFloatFill(MachineDefinition.Mflpt5.fromNumber(flt.key))
out("${flt.value}\t.byte $floatFill ; float ${flt.key}")
}
}
private fun block2asm(blk: IntermediateProgram.ProgramBlock) {
block = blk
out("\n\n; ---- block: '${block.name}' ----")
if(!blk.force_output)
out("${block.name}\t.proc\n")
if(block.address!=null) {
out(".cerror * > ${block.address?.toHex()}, 'block address overlaps by ', *-${block.address?.toHex()},' bytes'")
out("* = ${block.address?.toHex()}")
}
// deal with zeropage variables
for(variable in blk.variables) {
val sym = symname(blk.name+"."+variable.scopedname, null)
val zpVar = program.allocatedZeropageVariables[sym]
if(zpVar==null) {
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
if(variable.params.zp != ZeropageWish.NOT_IN_ZEROPAGE &&
variable.value.type in zeropage.allowedDatatypes
&& variable.value.type != DataType.FLOAT) {
try {
val address = zeropage.allocate(sym, variable.value.type, null)
out("${variable.scopedname} = $address\t; auto zp ${variable.value.type}")
// make sure we add the var to the set of zpvars for this block
program.allocatedZeropageVariables[sym] = Pair(address, variable.value.type)
} catch (x: ZeropageDepletedError) {
// leave it as it is.
}
}
}
else {
// it was already allocated on the zp
out("${variable.scopedname} = ${zpVar.first}\t; zp ${zpVar.second}")
}
}
out("\n; memdefs and kernel subroutines")
memdefs2asm(block)
out("\n; non-zeropage variables")
vardecls2asm(block)
out("")
val instructionPatternWindowSize = 8 // increase once patterns occur longer than this.
var processed = 0
for (ins in block.instructions.windowed(instructionPatternWindowSize, partialWindows = true)) {
if (processed == 0) {
processed = instr2asm(ins)
if (processed == 0) {
// the instructions are not recognised yet and can't be translated into assembly
throw CompilerException("no asm translation found for instruction pattern: $ins")
}
}
processed--
}
if(!blk.force_output)
out("\n\t.pend\n")
}
private fun memdefs2asm(block: IntermediateProgram.ProgramBlock) {
for(m in block.memoryPointers) {
out(" ${m.key} = ${m.value.first.toHex()}")
}
}
private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) {
val uniqueNames = block.variables.map { it.scopedname }.toSet()
if (uniqueNames.size != block.variables.size)
throw AssemblyError("not all variables have unique names")
// these are the non-zeropage variables.
// first get all the flattened struct members, they MUST remain in order
out("; flattened struct members")
val (structMembers, normalVars) = block.variables.partition { it.params.memberOfStruct!=null }
structMembers.forEach { vardecl2asm(it.scopedname, it.value, it.params) }
// sort the other variables by type
out("; other variables sorted by type")
val sortedVars = normalVars.sortedBy { it.value.type }
for (variable in sortedVars) {
val sym = symname(block.name + "." + variable.scopedname, null)
if(sym in program.allocatedZeropageVariables)
continue // skip the ones that already belong in the zero page
vardecl2asm(variable.scopedname, variable.value, variable.params)
}
}
private fun vardecl2asm(varname: String, value: RuntimeValue, parameters: IntermediateProgram.VariableParameters) {
when (value.type) {
DataType.UBYTE -> out("$varname\t.byte 0")
DataType.BYTE -> out("$varname\t.char 0")
DataType.UWORD -> out("$varname\t.word 0")
DataType.WORD -> out("$varname\t.sint 0")
DataType.FLOAT -> out("$varname\t.byte 0,0,0,0,0 ; float")
DataType.STR, DataType.STR_S -> {
val rawStr = heap.get(value.heapId!!).str!!
val bytes = encodeStr(rawStr, value.type).map { "$" + it.toString(16).padStart(2, '0') }
out("$varname\t; ${value.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
for (chunk in bytes.chunked(16))
out(" .byte " + chunk.joinToString())
}
DataType.ARRAY_UB -> {
// unsigned integer byte arraysize
val data = makeArrayFillDataUnsigned(value)
if (data.size <= 16)
out("$varname\t.byte ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .byte " + chunk.joinToString())
}
}
DataType.ARRAY_B -> {
// signed integer byte arraysize
val data = makeArrayFillDataSigned(value)
if (data.size <= 16)
out("$varname\t.char ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .char " + chunk.joinToString())
}
}
DataType.ARRAY_UW -> {
// unsigned word arraysize
val data = makeArrayFillDataUnsigned(value)
if (data.size <= 16)
out("$varname\t.word ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .word " + chunk.joinToString())
}
}
DataType.ARRAY_W -> {
// signed word arraysize
val data = makeArrayFillDataSigned(value)
if (data.size <= 16)
out("$varname\t.sint ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_F -> {
// float arraysize
val array = heap.get(value.heapId!!).doubleArray!!
val floatFills = array.map { makeFloatFill(MachineDefinition.Mflpt5.fromNumber(it)) }
out(varname)
for (f in array.zip(floatFills))
out(" .byte ${f.second} ; float ${f.first}")
}
DataType.STRUCT -> throw AssemblyError("vars of type STRUCT should have been removed because flattened")
}
}
private fun encodeStr(str: String, dt: DataType): List<Short> {
return when(dt) {
DataType.STR -> {
val bytes = Petscii.encodePetscii(str, true)
bytes.plus(0)
}
DataType.STR_S -> {
val bytes = Petscii.encodeScreencode(str, true)
bytes.plus(0)
}
else -> throw AssemblyError("invalid str type")
}
}
private fun makeArrayFillDataUnsigned(value: RuntimeValue): List<String> {
val array = heap.get(value.heapId!!).array!!
return when {
value.type== DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map { "$"+it.integer!!.toString(16).padStart(2, '0') }
value.type== DataType.ARRAY_UW -> array.map {
when {
it.integer!=null -> "$"+it.integer.toString(16).padStart(2, '0')
it.addressOf!=null -> symname(it.addressOf.scopedname!!, block)
else -> throw AssemblyError("weird type in array")
}
}
else -> throw AssemblyError("invalid arraysize type")
}
}
private fun makeArrayFillDataSigned(value: RuntimeValue): List<String> {
val array = heap.get(value.heapId!!).array!!
// note: array of signed value can never contain pointer-to type, so simply accept values as being all integers
return if (value.type == DataType.ARRAY_B || value.type == DataType.ARRAY_W) {
array.map {
if(it.integer!!>=0)
"$"+it.integer.toString(16).padStart(2, '0')
else
"-$"+abs(it.integer).toString(16).padStart(2, '0')
}
}
else throw AssemblyError("invalid arraysize type")
}
private fun instr2asm(ins: List<Instruction>): Int {
// find best patterns (matching the most of the lines, then with the smallest weight)
val fragments = findPatterns(ins).sortedByDescending { it.segmentSize }
if(fragments.isEmpty()) {
// we didn't find any matching patterns (complex multi-instruction fragments), try simple ones
val firstIns = ins[0]
val singleAsm = simpleInstr2Asm(firstIns, block)
if(singleAsm != null) {
outputAsmFragment(singleAsm)
return 1
}
return 0
}
val best = fragments[0]
outputAsmFragment(best.asm)
return best.segmentSize
}
private fun outputAsmFragment(singleAsm: String) {
if (singleAsm.isNotEmpty()) {
if(singleAsm.startsWith("@inline@"))
out(singleAsm.substring(8), false)
else {
val withNewlines = singleAsm.replace('|', '\n')
out(withNewlines)
}
}
}
private fun findPatterns(segment: List<Instruction>): List<AsmFragment> {
val opcodes = segment.map { it.opcode }
val result = mutableListOf<AsmFragment>()
// check for operations that modify a single value, by putting it on the stack (and popping it afterwards)
if((opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[2]==Opcode.POP_VAR_BYTE) ||
(opcodes[0]==Opcode.PUSH_VAR_WORD && opcodes[2]==Opcode.POP_VAR_WORD) ||
(opcodes[0]==Opcode.PUSH_VAR_FLOAT && opcodes[2]==Opcode.POP_VAR_FLOAT)) {
if (segment[0].callLabel == segment[2].callLabel) {
val fragment = sameVarOperation(segment[0].callLabel!!, segment[1])
if (fragment != null) {
fragment.segmentSize = 3
result.add(fragment)
}
}
}
else if((opcodes[0]==Opcode.PUSH_BYTE && opcodes[1] in setOf(Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB,
Opcode.INC_INDEXED_VAR_UW, Opcode.INC_INDEXED_VAR_W, Opcode.INC_INDEXED_VAR_FLOAT,
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB, Opcode.DEC_INDEXED_VAR_W,
Opcode.DEC_INDEXED_VAR_UW, Opcode.DEC_INDEXED_VAR_FLOAT))) {
val fragment = sameConstantIndexedVarOperation(segment[1].callLabel!!, segment[0].arg!!.integerValue(), segment[1])
if(fragment!=null) {
fragment.segmentSize=2
result.add(fragment)
}
}
else if((opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[1] in setOf(Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB,
Opcode.INC_INDEXED_VAR_UW, Opcode.INC_INDEXED_VAR_W, Opcode.INC_INDEXED_VAR_FLOAT,
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB, Opcode.DEC_INDEXED_VAR_W,
Opcode.DEC_INDEXED_VAR_UW, Opcode.DEC_INDEXED_VAR_FLOAT))) {
val fragment = sameIndexedVarOperation(segment[1].callLabel!!, segment[0].callLabel!!, segment[1])
if(fragment!=null) {
fragment.segmentSize=2
result.add(fragment)
}
}
else if((opcodes[0]==Opcode.PUSH_MEM_UB && opcodes[2]==Opcode.POP_MEM_BYTE) ||
(opcodes[0]==Opcode.PUSH_MEM_B && opcodes[2]==Opcode.POP_MEM_BYTE) ||
(opcodes[0]==Opcode.PUSH_MEM_UW && opcodes[2]==Opcode.POP_MEM_WORD) ||
(opcodes[0]==Opcode.PUSH_MEM_W && opcodes[2]==Opcode.POP_MEM_WORD) ||
(opcodes[0]==Opcode.PUSH_MEM_FLOAT && opcodes[2]==Opcode.POP_MEM_FLOAT)) {
if(segment[0].arg==segment[2].arg) {
val fragment = sameMemOperation(segment[0].arg!!.integerValue(), segment[1])
if(fragment!=null) {
fragment.segmentSize = 3
result.add(fragment)
}
}
}
else if((opcodes[0]==Opcode.PUSH_BYTE && opcodes[1]==Opcode.READ_INDEXED_VAR_BYTE &&
opcodes[3]==Opcode.PUSH_BYTE && opcodes[4]==Opcode.WRITE_INDEXED_VAR_BYTE) ||
(opcodes[0]==Opcode.PUSH_BYTE && opcodes[1]==Opcode.READ_INDEXED_VAR_WORD &&
opcodes[3]==Opcode.PUSH_BYTE && opcodes[4]==Opcode.WRITE_INDEXED_VAR_WORD)) {
if(segment[0].arg==segment[3].arg && segment[1].callLabel==segment[4].callLabel) {
val fragment = sameConstantIndexedVarOperation(segment[1].callLabel!!, segment[0].arg!!.integerValue(), segment[2])
if(fragment!=null){
fragment.segmentSize = 5
result.add(fragment)
}
}
}
else if((opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[1]==Opcode.READ_INDEXED_VAR_BYTE &&
opcodes[3]==Opcode.PUSH_VAR_BYTE && opcodes[4]==Opcode.WRITE_INDEXED_VAR_BYTE) ||
(opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[1]==Opcode.READ_INDEXED_VAR_WORD &&
opcodes[3]==Opcode.PUSH_VAR_BYTE && opcodes[4]==Opcode.WRITE_INDEXED_VAR_WORD)) {
if(segment[0].callLabel==segment[3].callLabel && segment[1].callLabel==segment[4].callLabel) {
val fragment = sameIndexedVarOperation(segment[1].callLabel!!, segment[0].callLabel!!, segment[2])
if(fragment!=null){
fragment.segmentSize = 5
result.add(fragment)
}
}
}
// add any matching patterns from the big list
for(pattern in Patterns.patterns) {
if(pattern.sequence.size > segment.size || (pattern.altSequence!=null && pattern.altSequence.size > segment.size))
continue // don't accept patterns that don't fit
val opcodesList = opcodes.subList(0, pattern.sequence.size)
if(pattern.sequence == opcodesList) {
val asm = pattern.asm(segment)
if(asm!=null)
result.add(AsmFragment(asm, pattern.sequence.size))
} else if(pattern.altSequence!=null) {
val opcodesListAlt = opcodes.subList(0, pattern.altSequence.size)
if(pattern.altSequence == opcodesListAlt) {
val asm = pattern.asm(segment)
if (asm != null)
result.add(AsmFragment(asm, pattern.sequence.size))
}
}
}
return result
}
private fun sameConstantIndexedVarOperation(variable: String, index: Int, ins: Instruction): AsmFragment? {
// an in place operation that consists of a push-value / op / push-index-value / pop-into-indexed-var
return when(ins.opcode) {
Opcode.SHL_BYTE -> AsmFragment(" asl $variable+$index", 8)
Opcode.SHR_UBYTE -> AsmFragment(" lsr $variable+$index", 8)
Opcode.SHR_SBYTE -> AsmFragment(" lda $variable+$index | asl a | ror $variable+$index")
Opcode.SHL_WORD -> AsmFragment(" asl $variable+${index * 2 + 1} | rol $variable+${index * 2}", 8)
Opcode.SHR_UWORD -> AsmFragment(" lsr $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.SHR_SWORD -> AsmFragment(" lda $variable+${index * 2 + 1} | asl a | ror $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.ROL_BYTE -> AsmFragment(" rol $variable+$index", 8)
Opcode.ROR_BYTE -> AsmFragment(" ror $variable+$index", 8)
Opcode.ROL_WORD -> AsmFragment(" rol $variable+${index * 2 + 1} | rol $variable+${index * 2}", 8)
Opcode.ROR_WORD -> AsmFragment(" ror $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.ROL2_BYTE -> AsmFragment(" lda $variable+$index | cmp #\$80 | rol $variable+$index", 8)
Opcode.ROR2_BYTE -> AsmFragment(" lda $variable+$index | lsr a | bcc + | ora #\$80 |+ | sta $variable+$index", 10)
Opcode.ROL2_WORD -> AsmFragment(" asl $variable+${index * 2 + 1} | rol $variable+${index * 2} | bcc + | inc $variable+${index * 2 + 1} |+", 20)
Opcode.ROR2_WORD -> AsmFragment(" lsr $variable+${index * 2 + 1} | ror $variable+${index * 2} | bcc + | lda $variable+${index * 2 + 1} | ora #\$80 | sta $variable+${index * 2 + 1} |+", 30)
Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB -> AsmFragment(" inc $variable+$index", 2)
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB -> AsmFragment(" dec $variable+$index", 5)
Opcode.INC_INDEXED_VAR_W, Opcode.INC_INDEXED_VAR_UW -> AsmFragment(" inc $variable+${index * 2} | bne + | inc $variable+${index * 2 + 1} |+")
Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_UW -> AsmFragment(" lda $variable+${index * 2} | bne + | dec $variable+${index * 2 + 1} |+ | dec $variable+${index * 2}")
Opcode.INC_INDEXED_VAR_FLOAT -> AsmFragment(
"""
lda #<($variable+${index * MachineDefinition.Mflpt5.MemorySize})
ldy #>($variable+${index * MachineDefinition.Mflpt5.MemorySize})
jsr c64flt.inc_var_f
""")
Opcode.DEC_INDEXED_VAR_FLOAT -> AsmFragment(
"""
lda #<($variable+${index * MachineDefinition.Mflpt5.MemorySize})
ldy #>($variable+${index * MachineDefinition.Mflpt5.MemorySize})
jsr c64flt.dec_var_f
""")
else -> null
}
}
private fun sameIndexedVarOperation(variable: String, indexVar: String, ins: Instruction): AsmFragment? {
// an in place operation that consists of a push-value / op / push-index-var / pop-into-indexed-var
val saveX = " stx ${MachineDefinition.C64Zeropage.SCRATCH_B1} |"
val restoreX = " | ldx ${MachineDefinition.C64Zeropage.SCRATCH_B1}"
val loadXWord: String
val loadX: String
when(indexVar) {
"X" -> {
loadX = ""
loadXWord = " txa | asl a | tax |"
}
"Y" -> {
loadX = " tya | tax |"
loadXWord = " tya | asl a | tax |"
}
"A" -> {
loadX = " tax |"
loadXWord = " asl a | tax |"
}
else -> {
// the indexvar is a real variable, not a register
loadX = " ldx $indexVar |"
loadXWord = " lda $indexVar | asl a | tax |"
}
}
return when (ins.opcode) {
Opcode.SHL_BYTE -> AsmFragment(" txa | $loadX asl $variable,x | tax", 10)
Opcode.SHR_UBYTE -> AsmFragment(" txa | $loadX lsr $variable,x | tax", 10)
Opcode.SHR_SBYTE -> AsmFragment("$saveX $loadX lda $variable,x | asl a | ror $variable,x $restoreX", 10)
Opcode.SHL_WORD -> AsmFragment("$saveX $loadXWord asl $variable,x | rol $variable+1,x $restoreX", 10)
Opcode.SHR_UWORD -> AsmFragment("$saveX $loadXWord lsr $variable+1,x | ror $variable,x $restoreX", 10)
Opcode.SHR_SWORD -> AsmFragment("$saveX $loadXWord lda $variable+1,x | asl a | ror $variable+1,x | ror $variable,x $restoreX", 10)
Opcode.ROL_BYTE -> AsmFragment(" txa | $loadX rol $variable,x | tax", 10)
Opcode.ROR_BYTE -> AsmFragment(" txa | $loadX ror $variable,x | tax", 10)
Opcode.ROL_WORD -> AsmFragment("$saveX $loadXWord rol $variable,x | rol $variable+1,x $restoreX", 10)
Opcode.ROR_WORD -> AsmFragment("$saveX $loadXWord ror $variable+1,x | ror $variable,x $restoreX", 10)
Opcode.ROL2_BYTE -> AsmFragment("$saveX $loadX lda $variable,x | cmp #\$80 | rol $variable,x $restoreX", 10)
Opcode.ROR2_BYTE -> AsmFragment("$saveX $loadX lda $variable,x | lsr a | bcc + | ora #\$80 |+ | sta $variable,x $restoreX", 10)
Opcode.ROL2_WORD -> AsmFragment(" txa | $loadXWord asl $variable,x | rol $variable+1,x | bcc + | inc $variable,x |+ | tax", 30)
Opcode.ROR2_WORD -> AsmFragment("$saveX $loadXWord lsr $variable+1,x | ror $variable,x | bcc + | lda $variable+1,x | ora #\$80 | sta $variable+1,x |+ $restoreX", 30)
Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB -> AsmFragment(" txa | $loadX inc $variable,x | tax", 10)
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB -> AsmFragment(" txa | $loadX dec $variable,x | tax", 10)
Opcode.INC_INDEXED_VAR_W, Opcode.INC_INDEXED_VAR_UW -> AsmFragment("$saveX $loadXWord inc $variable,x | bne + | inc $variable+1,x |+ $restoreX", 10)
Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_UW -> AsmFragment("$saveX $loadXWord lda $variable,x | bne + | dec $variable+1,x |+ | dec $variable,x $restoreX", 10)
Opcode.INC_INDEXED_VAR_FLOAT -> AsmFragment(" lda #<$variable | ldy #>$variable | $saveX $loadX jsr c64flt.inc_indexed_var_f $restoreX")
Opcode.DEC_INDEXED_VAR_FLOAT -> AsmFragment(" lda #<$variable | ldy #>$variable | $saveX $loadX jsr c64flt.dec_indexed_var_f $restoreX")
else -> null
}
}
private fun sameMemOperation(address: Int, ins: Instruction): AsmFragment? {
// an in place operation that consists of push-mem / op / pop-mem
val addr = address.toHex()
val addrHi = (address+1).toHex()
return when(ins.opcode) {
Opcode.SHL_BYTE -> AsmFragment(" asl $addr", 10)
Opcode.SHR_UBYTE -> AsmFragment(" lsr $addr", 10)
Opcode.SHR_SBYTE -> AsmFragment(" lda $addr | asl a | ror $addr", 10)
Opcode.SHL_WORD -> AsmFragment(" asl $addr | rol $addrHi", 10)
Opcode.SHR_UWORD -> AsmFragment(" lsr $addrHi | ror $addr", 10)
Opcode.SHR_SWORD -> AsmFragment(" lda $addrHi | asl a | ror $addrHi | ror $addr", 10)
Opcode.ROL_BYTE -> AsmFragment(" rol $addr", 10)
Opcode.ROR_BYTE -> AsmFragment(" ror $addr", 10)
Opcode.ROL_WORD -> AsmFragment(" rol $addr | rol $addrHi", 10)
Opcode.ROR_WORD -> AsmFragment(" ror $addrHi | ror $addr", 10)
Opcode.ROL2_BYTE -> AsmFragment(" lda $addr | cmp #\$80 | rol $addr", 10)
Opcode.ROR2_BYTE -> AsmFragment(" lda $addr | lsr a | bcc + | ora #\$80 |+ | sta $addr", 10)
Opcode.ROL2_WORD -> AsmFragment(" lda $addr | cmp #\$80 | rol $addr | rol $addrHi", 10)
Opcode.ROR2_WORD -> AsmFragment(" lsr $addrHi | ror $addr | bcc + | lda $addrHi | ora #$80 | sta $addrHi |+", 20)
else -> null
}
}
private fun sameVarOperation(variable: String, ins: Instruction): AsmFragment? {
// an in place operation that consists of a push-var / op / pop-var
return when(ins.opcode) {
Opcode.SHL_BYTE -> {
when (variable) {
"A" -> AsmFragment(" asl a", 10)
"X" -> AsmFragment(" txa | asl a | tax", 10)
"Y" -> AsmFragment(" tya | asl a | tay", 10)
else -> AsmFragment(" asl $variable", 10)
}
}
Opcode.SHR_UBYTE -> {
when (variable) {
"A" -> AsmFragment(" lsr a", 10)
"X" -> AsmFragment(" txa | lsr a | tax", 10)
"Y" -> AsmFragment(" tya | lsr a | tay", 10)
else -> AsmFragment(" lsr $variable", 10)
}
}
Opcode.SHR_SBYTE -> {
// arithmetic shift right (keep sign bit)
when (variable) {
"A" -> AsmFragment(" cmp #$80 | ror a", 10)
"X" -> AsmFragment(" txa | cmp #$80 | ror a | tax", 10)
"Y" -> AsmFragment(" tya | cmp #$80 | ror a | tay", 10)
else -> AsmFragment(" lda $variable | asl a | ror $variable", 10)
}
}
Opcode.SHL_WORD -> {
AsmFragment(" asl $variable | rol $variable+1", 10)
}
Opcode.SHR_UWORD -> {
AsmFragment(" lsr $variable+1 | ror $variable", 10)
}
Opcode.SHR_SWORD -> {
// arithmetic shift right (keep sign bit)
AsmFragment(" lda $variable+1 | asl a | ror $variable+1 | ror $variable", 10)
}
Opcode.ROL_BYTE -> {
when (variable) {
"A" -> AsmFragment(" rol a", 10)
"X" -> AsmFragment(" txa | rol a | tax", 10)
"Y" -> AsmFragment(" tya | rol a | tay", 10)
else -> AsmFragment(" rol $variable", 10)
}
}
Opcode.ROR_BYTE -> {
when (variable) {
"A" -> AsmFragment(" ror a", 10)
"X" -> AsmFragment(" txa | ror a | tax", 10)
"Y" -> AsmFragment(" tya | ror a | tay", 10)
else -> AsmFragment(" ror $variable", 10)
}
}
Opcode.ROL_WORD -> {
AsmFragment(" rol $variable | rol $variable+1", 10)
}
Opcode.ROR_WORD -> {
AsmFragment(" ror $variable+1 | ror $variable", 10)
}
Opcode.ROL2_BYTE -> { // 8-bit rol
when (variable) {
"A" -> AsmFragment(" cmp #\$80 | rol a", 10)
"X" -> AsmFragment(" txa | cmp #\$80 | rol a | tax", 10)
"Y" -> AsmFragment(" tya | cmp #\$80 | rol a | tay", 10)
else -> AsmFragment(" lda $variable | cmp #\$80 | rol $variable", 10)
}
}
Opcode.ROR2_BYTE -> { // 8-bit ror
when (variable) {
"A" -> AsmFragment(" lsr a | bcc + | ora #\$80 |+", 10)
"X" -> AsmFragment(" txa | lsr a | bcc + | ora #\$80 |+ | tax", 10)
"Y" -> AsmFragment(" tya | lsr a | bcc + | ora #\$80 |+ | tay", 10)
else -> AsmFragment(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable", 10)
}
}
Opcode.ROL2_WORD -> {
AsmFragment(" lda $variable | cmp #\$80 | rol $variable | rol $variable+1", 10)
}
Opcode.ROR2_WORD -> {
AsmFragment(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+", 30)
}
else -> null
}
}
private class AsmFragment(val asm: String, var segmentSize: Int=0)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,562 +0,0 @@
package oldcodegen
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
import prog8.compiler.CompilerException
import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.LabelInstr
import prog8.compiler.intermediate.Opcode
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS2_HEX
import prog8.compiler.toHex
import prog8.vm.stackvm.Syscall
import prog8.vm.stackvm.syscallsForStackVm
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
private var breakpointCounter = 0
internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.ProgramBlock): String? {
// a label 'instruction' is simply translated into a asm label
if(ins is LabelInstr) {
val labelresult =
if(ins.name.startsWith("${block.name}."))
ins.name.substring(block.name.length+1)
else
ins.name
return if(ins.asmProc) labelresult+"\t\t.proc" else labelresult
}
// simple opcodes that are translated directly into one or a few asm instructions
return when(ins.opcode) {
Opcode.LINE -> " ;\tsrc line: ${ins.callLabel}"
Opcode.NOP -> " nop" // shouldn't be present anymore though
Opcode.START_PROCDEF -> "" // is done as part of a label
Opcode.END_PROCDEF -> " .pend"
Opcode.TERMINATE -> " brk"
Opcode.SEC -> " sec"
Opcode.CLC -> " clc"
Opcode.SEI -> " sei"
Opcode.CLI -> " cli"
Opcode.CARRY_TO_A -> " lda #0 | adc #0"
Opcode.JUMP -> {
if(ins.callLabel!=null)
" jmp ${ins.callLabel}"
else
" jmp ${hexVal(ins)}"
}
Opcode.CALL -> {
if(ins.callLabel!=null)
" jsr ${ins.callLabel}"
else
" jsr ${hexVal(ins)}"
}
Opcode.RETURN -> " rts"
Opcode.RSAVE -> {
// save cpu status flag and all registers A, X, Y.
// see http://6502.org/tutorials/register_preservation.html
" php | sta ${C64Zeropage.SCRATCH_REG} | pha | txa | pha | tya | pha | lda ${C64Zeropage.SCRATCH_REG}"
}
Opcode.RRESTORE -> {
// restore all registers and cpu status flag
" pla | tay | pla | tax | pla | plp"
}
Opcode.RSAVEX -> " sta ${C64Zeropage.SCRATCH_REG} | txa | pha | lda ${C64Zeropage.SCRATCH_REG}"
Opcode.RRESTOREX -> " sta ${C64Zeropage.SCRATCH_REG} | pla | tax | lda ${C64Zeropage.SCRATCH_REG}"
Opcode.DISCARD_BYTE -> " inx"
Opcode.DISCARD_WORD -> " inx"
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
Opcode.DUP_B -> {
" lda $ESTACK_LO_PLUS1_HEX,x | sta $ESTACK_LO_HEX,x | dex | ;DUP_B "
}
Opcode.DUP_W -> {
" lda $ESTACK_LO_PLUS1_HEX,x | sta $ESTACK_LO_HEX,x | lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_HI_HEX,x | dex "
}
Opcode.CMP_B, Opcode.CMP_UB -> {
" inx | lda $ESTACK_LO_HEX,x | cmp #${ins.arg!!.integerValue().toHex()} | ;CMP_B "
}
Opcode.CMP_W, Opcode.CMP_UW -> {
"""
inx
lda $ESTACK_HI_HEX,x
cmp #>${ins.arg!!.integerValue().toHex()}
bne +
lda $ESTACK_LO_HEX,x
cmp #<${ins.arg.integerValue().toHex()}
; bne + not necessary?
; lda #0 not necessary?
+
"""
}
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to accept it.
Opcode.INCLUDE_FILE -> {
val offset = if(ins.arg==null) "" else ", ${ins.arg.integerValue()}"
val length = if(ins.arg2==null) "" else ", ${ins.arg2.integerValue()}"
" .binary \"${ins.callLabel}\" $offset $length"
}
Opcode.SYSCALL -> {
if (ins.arg!!.numericValue() in syscallsForStackVm.map { it.callNr })
throw CompilerException("cannot translate vm syscalls to real assembly calls - use *real* subroutine calls instead. Syscall ${ins.arg.numericValue()}")
val call = Syscall.values().find { it.callNr==ins.arg.numericValue() }
when(call) {
Syscall.FUNC_SIN,
Syscall.FUNC_COS,
Syscall.FUNC_ABS,
Syscall.FUNC_TAN,
Syscall.FUNC_ATAN,
Syscall.FUNC_LN,
Syscall.FUNC_LOG2,
Syscall.FUNC_SQRT,
Syscall.FUNC_RAD,
Syscall.FUNC_DEG,
Syscall.FUNC_ROUND,
Syscall.FUNC_FLOOR,
Syscall.FUNC_CEIL,
Syscall.FUNC_RNDF,
Syscall.FUNC_ANY_F,
Syscall.FUNC_ALL_F,
Syscall.FUNC_MAX_F,
Syscall.FUNC_MIN_F,
Syscall.FUNC_SUM_F -> " jsr c64flt.${call.name.toLowerCase()}"
null -> ""
else -> " jsr prog8_lib.${call.name.toLowerCase()}"
}
}
Opcode.BREAKPOINT -> {
breakpointCounter++
"_prog8_breakpoint_$breakpointCounter\tnop"
}
Opcode.PUSH_BYTE -> {
" lda #${hexVal(ins)} | sta $ESTACK_LO_HEX,x | dex"
}
Opcode.PUSH_WORD -> {
val value = hexVal(ins)
" lda #<$value | sta $ESTACK_LO_HEX,x | lda #>$value | sta $ESTACK_HI_HEX,x | dex"
}
Opcode.PUSH_FLOAT -> {
val floatConst = getFloatConst(ins.arg!!)
" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float"
}
Opcode.PUSH_VAR_BYTE -> {
when(ins.callLabel) {
"X" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself. You should probably not use the X register (or only in trivial assignments)")
"A" -> " sta $ESTACK_LO_HEX,x | dex"
"Y" -> " tya | sta $ESTACK_LO_HEX,x | dex"
else -> " lda ${ins.callLabel} | sta $ESTACK_LO_HEX,x | dex"
}
}
Opcode.PUSH_VAR_WORD -> {
" lda ${ins.callLabel} | sta $ESTACK_LO_HEX,x | lda ${ins.callLabel}+1 | sta $ESTACK_HI_HEX,x | dex"
}
Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr c64flt.push_float"
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> {
"""
lda ${hexVal(ins)}
sta $ESTACK_LO_HEX,x
dex
"""
}
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> {
"""
lda ${hexVal(ins)}
sta $ESTACK_LO_HEX,x
lda ${hexValPlusOne(ins)}
sta $ESTACK_HI_HEX,x
dex
"""
}
Opcode.PUSH_MEM_FLOAT -> {
" lda #<${hexVal(ins)} | ldy #>${hexVal(ins)}| jsr c64flt.push_float"
}
Opcode.PUSH_MEMREAD -> {
"""
lda $ESTACK_LO_PLUS1_HEX,x
sta (+) +1
lda $ESTACK_HI_PLUS1_HEX,x
sta (+) +2
+ lda 65535 ; modified
sta $ESTACK_LO_PLUS1_HEX,x
"""
}
Opcode.PUSH_REGAY_WORD -> {
" sta $ESTACK_LO_HEX,x | tya | sta $ESTACK_HI_HEX,x | dex "
}
Opcode.PUSH_ADDR_HEAPVAR -> {
" lda #<${ins.callLabel} | sta $ESTACK_LO_HEX,x | lda #>${ins.callLabel} | sta $ESTACK_HI_HEX,x | dex"
}
Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
Opcode.POP_REGAY_WORD -> {
" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x "
}
Opcode.READ_INDEXED_VAR_BYTE -> {
"""
ldy $ESTACK_LO_PLUS1_HEX,x
lda ${ins.callLabel},y
sta $ESTACK_LO_PLUS1_HEX,x
"""
}
Opcode.READ_INDEXED_VAR_WORD -> {
"""
lda $ESTACK_LO_PLUS1_HEX,x
asl a
tay
lda ${ins.callLabel},y
sta $ESTACK_LO_PLUS1_HEX,x
lda ${ins.callLabel}+1,y
sta $ESTACK_HI_PLUS1_HEX,x
"""
}
Opcode.READ_INDEXED_VAR_FLOAT -> {
"""
lda #<${ins.callLabel}
ldy #>${ins.callLabel}
jsr c64flt.push_float_from_indexed_var
"""
}
Opcode.WRITE_INDEXED_VAR_BYTE -> {
"""
inx
ldy $ESTACK_LO_HEX,x
inx
lda $ESTACK_LO_HEX,x
sta ${ins.callLabel},y
"""
}
Opcode.WRITE_INDEXED_VAR_WORD -> {
"""
inx
lda $ESTACK_LO_HEX,x
asl a
tay
inx
lda $ESTACK_LO_HEX,x
sta ${ins.callLabel},y
lda $ESTACK_HI_HEX,x
sta ${ins.callLabel}+1,y
"""
}
Opcode.WRITE_INDEXED_VAR_FLOAT -> {
"""
lda #<${ins.callLabel}
ldy #>${ins.callLabel}
jsr c64flt.pop_float_to_indexed_var
"""
}
Opcode.POP_MEM_BYTE -> {
"""
inx
lda $ESTACK_LO_HEX,x
sta ${hexVal(ins)}
"""
}
Opcode.POP_MEM_WORD -> {
"""
inx
lda $ESTACK_LO_HEX,x
sta ${hexVal(ins)}
lda $ESTACK_HI_HEX,x
sta ${hexValPlusOne(ins)}
"""
}
Opcode.POP_MEM_FLOAT -> {
" lda ${hexVal(ins)} | ldy ${hexValPlusOne(ins)} | jsr c64flt.pop_float"
}
Opcode.POP_MEMWRITE -> {
"""
inx
lda $ESTACK_LO_HEX,x
sta (+) +1
lda $ESTACK_HI_HEX,x
sta (+) +2
inx
lda $ESTACK_LO_HEX,x
+ sta 65535 ; modified
"""
}
Opcode.POP_VAR_BYTE -> {
when (ins.callLabel) {
"X" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself")
"A" -> " inx | lda $ESTACK_LO_HEX,x"
"Y" -> " inx | ldy $ESTACK_LO_HEX,x"
else -> " inx | lda $ESTACK_LO_HEX,x | sta ${ins.callLabel}"
}
}
Opcode.POP_VAR_WORD -> {
" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x | sta ${ins.callLabel} | sty ${ins.callLabel}+1"
}
Opcode.POP_VAR_FLOAT -> {
" lda #<${ins.callLabel} | ldy #>${ins.callLabel} | jsr c64flt.pop_float"
}
Opcode.INC_VAR_UB, Opcode.INC_VAR_B -> {
when (ins.callLabel) {
"A" -> " clc | adc #1"
"X" -> " inx"
"Y" -> " iny"
else -> " inc ${ins.callLabel}"
}
}
Opcode.INC_VAR_UW, Opcode.INC_VAR_W -> {
" inc ${ins.callLabel} | bne + | inc ${ins.callLabel}+1 |+"
}
Opcode.INC_VAR_F -> {
"""
lda #<${ins.callLabel}
ldy #>${ins.callLabel}
jsr c64flt.inc_var_f
"""
}
Opcode.POP_INC_MEMORY -> {
"""
inx
lda $ESTACK_LO_HEX,x
sta (+) +1
lda $ESTACK_HI_HEX,x
sta (+) +2
+ inc 65535 ; modified
"""
}
Opcode.POP_DEC_MEMORY -> {
"""
inx
lda $ESTACK_LO_HEX,x
sta (+) +1
lda $ESTACK_HI_HEX,x
sta (+) +2
+ dec 65535 ; modified
"""
}
Opcode.DEC_VAR_UB, Opcode.DEC_VAR_B -> {
when (ins.callLabel) {
"A" -> " sec | sbc #1"
"X" -> " dex"
"Y" -> " dey"
else -> " dec ${ins.callLabel}"
}
}
Opcode.DEC_VAR_UW, Opcode.DEC_VAR_W -> {
" lda ${ins.callLabel} | bne + | dec ${ins.callLabel}+1 |+ | dec ${ins.callLabel}"
}
Opcode.DEC_VAR_F -> {
"""
lda #<${ins.callLabel}
ldy #>${ins.callLabel}
jsr c64flt.dec_var_f
"""
}
Opcode.INC_MEMORY -> " inc ${hexVal(ins)}"
Opcode.DEC_MEMORY -> " dec ${hexVal(ins)}"
Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB -> " inx | txa | pha | lda $ESTACK_LO_HEX,x | tax | inc ${ins.callLabel},x | pla | tax"
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB -> " inx | txa | pha | lda $ESTACK_LO_HEX,x | tax | dec ${ins.callLabel},x | pla | tax"
Opcode.NEG_B -> " jsr prog8_lib.neg_b"
Opcode.NEG_W -> " jsr prog8_lib.neg_w"
Opcode.NEG_F -> " jsr c64flt.neg_f"
Opcode.ABS_B -> " jsr prog8_lib.abs_b"
Opcode.ABS_W -> " jsr prog8_lib.abs_w"
Opcode.ABS_F -> " jsr c64flt.abs_f"
Opcode.POW_F -> " jsr c64flt.pow_f"
Opcode.INV_BYTE -> {
"""
lda $ESTACK_LO_PLUS1_HEX,x
eor #255
sta $ESTACK_LO_PLUS1_HEX,x
"""
}
Opcode.INV_WORD -> " jsr prog8_lib.inv_word"
Opcode.NOT_BYTE -> " jsr prog8_lib.not_byte"
Opcode.NOT_WORD -> " jsr prog8_lib.not_word"
Opcode.BCS -> {
val label = ins.callLabel ?: hexVal(ins)
" bcs $label"
}
Opcode.BCC -> {
val label = ins.callLabel ?: hexVal(ins)
" bcc $label"
}
Opcode.BNEG -> {
val label = ins.callLabel ?: hexVal(ins)
" bmi $label"
}
Opcode.BPOS -> {
val label = ins.callLabel ?: hexVal(ins)
" bpl $label"
}
Opcode.BVC -> {
val label = ins.callLabel ?: hexVal(ins)
" bvc $label"
}
Opcode.BVS -> {
val label = ins.callLabel ?: hexVal(ins)
" bvs $label"
}
Opcode.BZ -> {
val label = ins.callLabel ?: hexVal(ins)
" beq $label"
}
Opcode.BNZ -> {
val label = ins.callLabel ?: hexVal(ins)
" bne $label"
}
Opcode.JZ -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda $ESTACK_LO_HEX,x
beq $label
"""
}
Opcode.JZW -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda $ESTACK_LO_HEX,x
beq $label
lda $ESTACK_HI_HEX,x
beq $label
"""
}
Opcode.JNZ -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda $ESTACK_LO_HEX,x
bne $label
"""
}
Opcode.JNZW -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda $ESTACK_LO_HEX,x
bne $label
lda $ESTACK_HI_HEX,x
bne $label
"""
}
Opcode.CAST_B_TO_UB -> "" // is a no-op, just carry on with the byte as-is
Opcode.CAST_UB_TO_B -> "" // is a no-op, just carry on with the byte as-is
Opcode.CAST_W_TO_UW -> "" // is a no-op, just carry on with the word as-is
Opcode.CAST_UW_TO_W -> "" // is a no-op, just carry on with the word as-is
Opcode.CAST_W_TO_UB -> "" // is a no-op, just carry on with the lsb of the word as-is
Opcode.CAST_W_TO_B -> "" // is a no-op, just carry on with the lsb of the word as-is
Opcode.CAST_UW_TO_UB -> "" // is a no-op, just carry on with the lsb of the uword as-is
Opcode.CAST_UW_TO_B -> "" // is a no-op, just carry on with the lsb of the uword as-is
Opcode.CAST_UB_TO_F -> " jsr c64flt.stack_ub2float"
Opcode.CAST_B_TO_F -> " jsr c64flt.stack_b2float"
Opcode.CAST_UW_TO_F -> " jsr c64flt.stack_uw2float"
Opcode.CAST_W_TO_F -> " jsr c64flt.stack_w2float"
Opcode.CAST_F_TO_UB -> " jsr c64flt.stack_float2ub"
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2b"
Opcode.CAST_F_TO_UW -> " jsr c64flt.stack_float2uw"
Opcode.CAST_F_TO_W -> " jsr c64flt.stack_float2w"
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta $ESTACK_HI_PLUS1_HEX,x" // clear the msb
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda $ESTACK_LO_PLUS1_HEX,x | ${signExtendA("$ESTACK_HI_PLUS1_HEX,x")}" // sign extend the lsb
Opcode.MSB -> " lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_LO_PLUS1_HEX,x"
Opcode.MKWORD -> " inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x "
Opcode.ADD_UB, Opcode.ADD_B -> { // TODO inline better (pattern with more opcodes)
"""
lda $ESTACK_LO_PLUS2_HEX,x
clc
adc $ESTACK_LO_PLUS1_HEX,x
inx
sta $ESTACK_LO_PLUS1_HEX,x
"""
}
Opcode.SUB_UB, Opcode.SUB_B -> { // TODO inline better (pattern with more opcodes)
"""
lda $ESTACK_LO_PLUS2_HEX,x
sec
sbc $ESTACK_LO_PLUS1_HEX,x
inx
sta $ESTACK_LO_PLUS1_HEX,x
"""
}
Opcode.ADD_W, Opcode.ADD_UW -> " jsr prog8_lib.add_w"
Opcode.SUB_W, Opcode.SUB_UW -> " jsr prog8_lib.sub_w"
Opcode.MUL_B, Opcode.MUL_UB -> " jsr prog8_lib.mul_byte"
Opcode.MUL_W, Opcode.MUL_UW -> " jsr prog8_lib.mul_word"
Opcode.MUL_F -> " jsr c64flt.mul_f"
Opcode.ADD_F -> " jsr c64flt.add_f"
Opcode.SUB_F -> " jsr c64flt.sub_f"
Opcode.DIV_F -> " jsr c64flt.div_f"
Opcode.IDIV_UB -> " jsr prog8_lib.idiv_ub"
Opcode.IDIV_B -> " jsr prog8_lib.idiv_b"
Opcode.IDIV_W -> " jsr prog8_lib.idiv_w"
Opcode.IDIV_UW -> " jsr prog8_lib.idiv_uw"
Opcode.AND_BYTE -> " jsr prog8_lib.and_b"
Opcode.OR_BYTE -> " jsr prog8_lib.or_b"
Opcode.XOR_BYTE -> " jsr prog8_lib.xor_b"
Opcode.AND_WORD -> " jsr prog8_lib.and_w"
Opcode.OR_WORD -> " jsr prog8_lib.or_w"
Opcode.XOR_WORD -> " jsr prog8_lib.xor_w"
Opcode.BITAND_BYTE -> " jsr prog8_lib.bitand_b"
Opcode.BITOR_BYTE -> " jsr prog8_lib.bitor_b"
Opcode.BITXOR_BYTE -> " jsr prog8_lib.bitxor_b"
Opcode.BITAND_WORD -> " jsr prog8_lib.bitand_w"
Opcode.BITOR_WORD -> " jsr prog8_lib.bitor_w"
Opcode.BITXOR_WORD -> " jsr prog8_lib.bitxor_w"
Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub"
Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw"
Opcode.GREATER_B -> " jsr prog8_lib.greater_b"
Opcode.GREATER_UB -> " jsr prog8_lib.greater_ub"
Opcode.GREATER_W -> " jsr prog8_lib.greater_w"
Opcode.GREATER_UW -> " jsr prog8_lib.greater_uw"
Opcode.GREATER_F -> " jsr c64flt.greater_f"
Opcode.GREATEREQ_B -> " jsr prog8_lib.greatereq_b"
Opcode.GREATEREQ_UB -> " jsr prog8_lib.greatereq_ub"
Opcode.GREATEREQ_W -> " jsr prog8_lib.greatereq_w"
Opcode.GREATEREQ_UW -> " jsr prog8_lib.greatereq_uw"
Opcode.GREATEREQ_F -> " jsr c64flt.greatereq_f"
Opcode.EQUAL_BYTE -> " jsr prog8_lib.equal_b"
Opcode.EQUAL_WORD -> " jsr prog8_lib.equal_w"
Opcode.EQUAL_F -> " jsr c64flt.equal_f"
Opcode.NOTEQUAL_BYTE -> " jsr prog8_lib.notequal_b"
Opcode.NOTEQUAL_WORD -> " jsr prog8_lib.notequal_w"
Opcode.NOTEQUAL_F -> " jsr c64flt.notequal_f"
Opcode.LESS_UB -> " jsr prog8_lib.less_ub"
Opcode.LESS_B -> " jsr prog8_lib.less_b"
Opcode.LESS_UW -> " jsr prog8_lib.less_uw"
Opcode.LESS_W -> " jsr prog8_lib.less_w"
Opcode.LESS_F -> " jsr c64flt.less_f"
Opcode.LESSEQ_UB -> " jsr prog8_lib.lesseq_ub"
Opcode.LESSEQ_B -> " jsr prog8_lib.lesseq_b"
Opcode.LESSEQ_UW -> " jsr prog8_lib.lesseq_uw"
Opcode.LESSEQ_W -> " jsr prog8_lib.lesseq_w"
Opcode.LESSEQ_F -> " jsr c64flt.lesseq_f"
Opcode.SHIFTEDL_BYTE -> " asl $ESTACK_LO_PLUS1_HEX,x"
Opcode.SHIFTEDL_WORD -> " asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x"
Opcode.SHIFTEDR_SBYTE -> " lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x"
Opcode.SHIFTEDR_UBYTE -> " lsr $ESTACK_LO_PLUS1_HEX,x"
Opcode.SHIFTEDR_SWORD -> " lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x"
Opcode.SHIFTEDR_UWORD -> " lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x"
else -> null
}
}

View File

@ -30,21 +30,20 @@ which aims to provide many conveniences over raw assembly code (even when using
Rapid edit-compile-run-debug cycle:
- use modern PC to work on
- use modern PC to work on
- quick compilation times (seconds)
- option to automatically run the program in the Vice emulator
- option to automatically run the program in the Vice emulator
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
- virtual machine that can execute compiled code directy on the host system,
without having to actually convert it to assembly to run on a real 6502
without having to actually convert it to assembly to run on a real 6502
It is mainly targeted at the Commodore-64 machine at this time.
Contributions to add support for other 8-bit (or other?!) machines are welcome.
Documentation/manual
--------------------
See https://prog8.readthedocs.io/
This describes the language, but also how to build and run the compiler. See https://prog8.readthedocs.io/
Required tools
--------------

View File

@ -1,14 +1,14 @@
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"
}
}
plugins {
// id "org.jetbrains.kotlin.jvm" version $kotlinVersion
// id "org.jetbrains.kotlin.jvm" version "1.3.61"
id 'application'
id 'org.jetbrains.dokka' version "0.9.18"
id 'com.github.johnrengelman.shadow' version '5.1.0'
id 'com.github.johnrengelman.shadow' version '5.2.0'
id 'java'
}
@ -29,17 +29,17 @@ def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
dependencies {
implementation project(':parser')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
// implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
// runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
runtime 'org.antlr:antlr4-runtime:4.7.2'
runtime project(':parser')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.antlr:antlr4-runtime:4.8'
implementation 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
// implementation 'net.razorvine:ksim65:1.6'
implementation project(':parser')
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
compile 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
}
compileKotlin {
@ -85,8 +85,8 @@ artifacts {
shadowJar {
baseName = 'prog8compiler'
version = prog8version
archiveBaseName = 'prog8compiler'
archiveVersion = prog8version
// minimize()
}

View File

@ -11,9 +11,9 @@
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="antlr-runtime-4.7.2" level="project" />
<orderEntry type="module" module-name="parser" />
<orderEntry type="library" name="unittest-libs" level="project" />
<orderEntry type="library" name="kotlinx-cli-jvm-0.1.0-dev-5" level="project" />
<orderEntry type="library" name="antlr-runtime-4.8" level="project" />
</component>
</module>

View File

@ -716,7 +716,7 @@ func_sin8 .proc
lda _sinecos8,y
sta c64.ESTACK_LO+1,x
rts
_sinecos8 .char 127 * sin(range(256+64) * rad(360.0/256.0))
_sinecos8 .char trunc(127.0 * sin(range(256+64) * rad(360.0/256.0)))
.pend
func_sin8u .proc
@ -724,7 +724,7 @@ func_sin8u .proc
lda _sinecos8u,y
sta c64.ESTACK_LO+1,x
rts
_sinecos8u .byte 128 + 127.5 * sin(range(256+64) * rad(360.0/256.0))
_sinecos8u .byte trunc(128.0 + 127.5 * sin(range(256+64) * rad(360.0/256.0)))
.pend
func_sin16 .proc
@ -735,7 +735,7 @@ func_sin16 .proc
sta c64.ESTACK_HI+1,x
rts
_ := 32767 * sin(range(256+64) * rad(360.0/256.0))
_ := trunc(32767.0 * sin(range(256+64) * rad(360.0/256.0)))
_sinecos8lo .byte <_
_sinecos8hi .byte >_
.pend
@ -748,7 +748,7 @@ func_sin16u .proc
sta c64.ESTACK_HI+1,x
rts
_ := 32768 + 32767.5 * sin(range(256+64) * rad(360.0/256.0))
_ := trunc(32768.0 + 32767.5 * sin(range(256+64) * rad(360.0/256.0)))
_sinecos8ulo .byte <_
_sinecos8uhi .byte >_
.pend

View File

@ -1 +1 @@
1.62
1.70

View File

@ -2,10 +2,14 @@ package prog8
import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.compiler.CompilationResult
import prog8.compiler.compileProgram
import prog8.compiler.*
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.parser.ParsingFailedError
import prog8.vm.astvm.AstVm
import java.io.IOException
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds
@ -31,13 +35,13 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
private fun compileMain(args: Array<String>) {
val cli = CommandLineInterface("prog8compiler")
val startEmulator1 by cli.flagArgument("-emu", "auto-start the 'x64' C-64 emulator after successful compilation")
val startEmulator2 by cli.flagArgument("-emu2", "auto-start the 'x64sc' C-64 emulator after successful compilation")
val startEmulator by cli.flagArgument("-emu", "auto-start the Vice C-64 emulator after successful compilation")
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
val launchSimulator by cli.flagArgument("-sim", "launch the prog8 virtual machine/simulator after compilation")
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes)")
val launchSimulator by cli.flagArgument("-sim", "launch the builtin execution simulator after compilation")
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
val compilationTarget by cli.flagValueArgument("-target", "compilertarget", "target output of the compiler, currently only 'c64' (C64 6502 assembly) available", "c64")
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
try {
@ -46,6 +50,22 @@ private fun compileMain(args: Array<String>) {
exitProcess(1)
}
when(compilationTarget) {
"c64" -> {
with(CompilationTarget) {
name = "c64"
machine = C64MachineDefinition
encodeString = { str -> Petscii.encodePetscii(str, true) }
decodeString = { bytes -> Petscii.decodePetscii(bytes, true) }
asmGenerator = ::AsmGen
}
}
else -> {
System.err.println("invalid compilation target")
exitProcess(1)
}
}
val outputPath = pathFrom(outputDir)
if(!outputPath.toFile().isDirectory) {
System.err.println("Output path doesn't exist")
@ -95,21 +115,35 @@ private fun compileMain(args: Array<String>) {
}
if (launchSimulator) {
// val c64 = razorvine.c64emu.C64Machine("C64 emulator launched from Prog8 compiler")
// c64.cpu.addBreakpoint(0xea31) { cpu, address ->
// println("zz")
// Cpu6502.BreakpointResultAction()
// }
// c64.start()
println("\nLaunching AST-based simulator...")
val vm = AstVm(compilationResult.programAst)
val vm = AstVm(compilationResult.programAst, compilationTarget)
vm.run()
}
if (startEmulator1 || startEmulator2) {
if (startEmulator) {
if (compilationResult.programName.isEmpty())
println("\nCan't start emulator because no program was assembled.")
else {
val emulator = if(startEmulator1) "x64" else "x64sc"
println("\nStarting C-64 emulator $emulator...")
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
val process = ProcessBuilder(cmdline).inheritIO().start()
process.waitFor()
else if(startEmulator) {
for(emulator in listOf("x64sc", "x64")) {
println("\nStarting C-64 emulator $emulator...")
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process
try {
process=processb.start()
} catch(x: IOException) {
continue // try the next emulator executable
}
process.waitFor()
break
}
}
}
}

View File

@ -3,7 +3,6 @@ package prog8.ast
import prog8.ast.antlr.escape
import prog8.ast.base.DataType
import prog8.ast.base.NumericDatatypes
import prog8.ast.base.StringDatatypes
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.*
import prog8.ast.processing.IAstVisitor
@ -79,7 +78,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
private fun datatypeString(dt: DataType): String {
return when(dt) {
in NumericDatatypes -> dt.toString().toLowerCase()
in StringDatatypes -> dt.toString().toLowerCase()
DataType.STR -> dt.toString().toLowerCase()
DataType.ARRAY_UB -> "ubyte["
DataType.ARRAY_B -> "byte["
DataType.ARRAY_UW -> "uword["
@ -197,9 +196,9 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
private fun printout(call: IFunctionCall) {
call.target.accept(this)
output("(")
for(arg in call.arglist) {
for(arg in call.args) {
arg.accept(this)
if(arg!==call.arglist.last())
if(arg!==call.args.last())
output(", ")
}
output(")")

View File

@ -37,7 +37,7 @@ interface Node {
interface IFunctionCall {
var target: IdentifierReference
var arglist: MutableList<Expression>
var args: MutableList<Expression>
}
interface INameScope {
@ -243,8 +243,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
}
}
// lookup something from the module.
val stmt = localContext.definingModule().lookup(scopedName, localContext)
return when (stmt) {
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
is Label, is VarDecl, is Block, is Subroutine -> stmt
null -> null
else -> throw NameError("wrong identifier target: $stmt", stmt.position)

View File

@ -7,7 +7,7 @@ import prog8.ast.Module
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.CompilationTarget
import prog8.parser.CustomLexer
import prog8.parser.prog8Parser
import java.io.CharConversionException
@ -264,11 +264,12 @@ private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
val void = this.VOID() != null
val location = scoped_identifier().toAst()
return if(expression_list() == null)
FunctionCallStatement(location, mutableListOf(), toPosition())
FunctionCallStatement(location, mutableListOf(), void, toPosition())
else
FunctionCallStatement(location, expression_list().toAst().toMutableList(), toPosition())
FunctionCallStatement(location, expression_list().toAst().toMutableList(), void, toPosition())
}
@ -429,10 +430,11 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
else -> throw FatalAstException("invalid datatype for numeric literal")
}
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
litval.stringliteral()!=null -> StringLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), litval.toPosition())
litval.stringliteral()!=null -> StringLiteralValue(unescape(litval.stringliteral().text, litval.toPosition()), litval.toPosition())
litval.charliteral()!=null -> {
try {
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
NumericLiteralValue(DataType.UBYTE, CompilationTarget.encodeString(
unescape(litval.charliteral().text, litval.toPosition()))[0], litval.toPosition())
} catch (ce: CharConversionException) {
throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
}

View File

@ -1,7 +1,8 @@
package prog8.ast.base
import prog8.ast.Node
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.CompilationTarget
/**************************** AST Data classes ****************************/
@ -12,7 +13,6 @@ enum class DataType {
WORD, // pass by value
FLOAT, // pass by value
STR, // pass by reference
STR_S, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference
@ -31,8 +31,7 @@ enum class DataType {
UWORD -> targetType in setOf(UWORD, FLOAT)
WORD -> targetType in setOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
STR -> targetType == STR || targetType==STR_S
STR_S -> targetType == STR || targetType==STR_S
STR -> targetType == STR
in ArrayDatatypes -> targetType == this
else -> false
}
@ -58,7 +57,7 @@ enum class DataType {
return when(this) {
in ByteDatatypes -> 1
in WordDatatypes -> 2
FLOAT -> MachineDefinition.Mflpt5.MemorySize
FLOAT -> CompilationTarget.machine.FLOAT_MEM_SIZE
in PassByReferenceDatatypes -> 2
else -> -9999999
}
@ -112,10 +111,9 @@ val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val StringDatatypes = setOf(DataType.STR, DataType.STR_S)
val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
val IterableDatatypes = setOf(
DataType.STR, DataType.STR_S,
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F)
@ -123,7 +121,6 @@ val PassByValueDatatypes = NumericDatatypes
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
val ArrayElementTypes = mapOf(
DataType.STR to DataType.UBYTE,
DataType.STR_S to DataType.UBYTE,
DataType.ARRAY_B to DataType.BYTE,
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,

View File

@ -4,7 +4,6 @@ import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.processing.*
import prog8.compiler.CompilationOptions
import prog8.compiler.target.c64.codegen.AnonymousScopeVarsCleanup
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops

View File

@ -9,7 +9,7 @@ import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.CompilationTarget
import prog8.functions.BuiltinFunctions
import prog8.functions.NotConstArgumentException
import prog8.functions.builtinFunctionReturnType
@ -63,7 +63,27 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
override fun inferType(program: Program): InferredTypes.InferredType = expression.inferType(program)
override fun inferType(program: Program): InferredTypes.InferredType {
val inferred = expression.inferType(program)
return when(operator) {
"+" -> inferred
"~", "not" -> {
when(inferred.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)
else -> inferred
}
}
"-" -> {
when(inferred.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> InferredTypes.knownFor(DataType.BYTE)
in WordDatatypes -> InferredTypes.knownFor(DataType.WORD)
else -> inferred
}
}
else -> throw FatalAstException("weird prefix expression operator")
}
}
override fun toString(): String {
return "Prefix($operator $expression)"
@ -197,7 +217,7 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
val target = identifier.targetStatement(program.namespace)
if (target is VarDecl) {
return when (target.datatype) {
in StringDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(target.datatype))
else -> InferredTypes.unknown()
}
@ -302,7 +322,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
}
}
val asBooleanValue: Boolean = number!=0
val asBooleanValue: Boolean = number.toDouble() != 0.0
override fun linkParents(parent: Node) {
this.parent = parent
@ -407,11 +427,14 @@ class StructLiteralValue(var values: List<Expression>,
}
}
class StringLiteralValue(val type: DataType, // only string types
val value: String,
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
class StringLiteralValue(val value: String,
override val position: Position) : Expression() {
override lateinit var parent: Node
val heapId = ++heapIdSequence
override fun linkParents(parent: Node) {
this.parent = parent
}
@ -420,35 +443,22 @@ class StringLiteralValue(val type: DataType, // only string types
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun toString(): String = "'${escape(value)}'"
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STR)
operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
override fun hashCode(): Int = Objects.hash(value, type)
override fun hashCode(): Int = value.hashCode()
override fun equals(other: Any?): Boolean {
if(other==null || other !is StringLiteralValue)
return false
return value==other.value && type==other.type
}
var heapId: Int? = null
private set
fun addToHeap() {
if(heapId==null)
heapId = ++heapIdSequence
return value==other.value
}
}
private var heapIdSequence = 0
class ArrayLiteralValue(val type: DataType, // only array types
val value: Array<Expression>,
initHeapId: Int? =null,
override val position: Position) : Expression() {
override lateinit var parent: Node
var heapId = initHeapId
private set
val heapId = ++heapIdSequence
override fun linkParents(parent: Node) {
this.parent = parent
@ -476,9 +486,9 @@ class ArrayLiteralValue(val type: DataType, // only array types
val castArray = value.map{
val num = it as? NumericLiteralValue
if(num==null) {
// an array of UWORDs could possibly also contain AddressOfs
// an array of UWORDs could possibly also contain AddressOfs, other stuff can't be casted
if (elementType != DataType.UWORD || it !is AddressOf)
throw FatalAstException("weird array element $it")
return null
it
} else {
try {
@ -492,11 +502,6 @@ class ArrayLiteralValue(val type: DataType, // only array types
}
return null // invalid type conversion from $this to $targettype
}
fun addToHeap() {
if(heapId==null)
heapId = ++heapIdSequence
}
}
class RangeExpr(var from: Expression,
@ -524,7 +529,6 @@ class RangeExpr(var from: Expression,
fromDt istype DataType.UBYTE && toDt istype DataType.UBYTE -> InferredTypes.knownFor(DataType.ARRAY_UB)
fromDt istype DataType.UWORD && toDt istype DataType.UWORD -> InferredTypes.knownFor(DataType.ARRAY_UW)
fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR)
fromDt istype DataType.STR_S && toDt istype DataType.STR_S -> InferredTypes.knownFor(DataType.STR_S)
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
else -> InferredTypes.knownFor(DataType.ARRAY_UB)
@ -549,8 +553,8 @@ class RangeExpr(var from: Expression,
val toString = to as? StringLiteralValue
if(fromString!=null && toString!=null ) {
// string range -> int range over petscii values
fromVal = Petscii.encodePetscii(fromString.value, true)[0].toInt()
toVal = Petscii.encodePetscii(toString.value, true)[0].toInt()
fromVal = CompilationTarget.encodeString(fromString.value)[0].toInt()
toVal = CompilationTarget.encodeString(toString.value)[0].toInt()
} else {
val fromLv = from as? NumericLiteralValue
val toLv = to as? NumericLiteralValue
@ -650,22 +654,22 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
return when (value) {
is IdentifierReference -> value.heapId(namespace)
is StringLiteralValue -> value.heapId ?: throw FatalAstException("string is not on the heap: $value")
is ArrayLiteralValue -> value.heapId ?: throw FatalAstException("array is not on the heap: $value")
is StringLiteralValue -> value.heapId
is ArrayLiteralValue -> value.heapId
else -> throw FatalAstException("requires a reference value")
}
}
}
class FunctionCall(override var target: IdentifierReference,
override var arglist: MutableList<Expression>,
override var args: MutableList<Expression>,
override val position: Position) : Expression(), IFunctionCall {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
target.linkParents(this)
arglist.forEach { it.linkParents(this) }
args.forEach { it.linkParents(this) }
}
override fun constValue(program: Program) = constValue(program, true)
@ -680,7 +684,7 @@ class FunctionCall(override var target: IdentifierReference,
if(func!=null) {
val exprfunc = func.constExpressionFunc
if(exprfunc!=null)
resultValue = exprfunc(arglist, position, program)
resultValue = exprfunc(args, position, program)
else if(func.returntype==null)
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
}
@ -706,7 +710,7 @@ class FunctionCall(override var target: IdentifierReference,
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || arglist.any{it.referencesIdentifiers(*name)}
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || args.any{it.referencesIdentifiers(*name)}
override fun inferType(program: Program): InferredTypes.InferredType {
val constVal = constValue(program ,false)
@ -719,7 +723,7 @@ class FunctionCall(override var target: IdentifierReference,
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
return InferredTypes.void() // these have no return value
}
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
return builtinFunctionReturnType(target.nameInSource[0], this.args, program)
}
is Subroutine -> {
if(stmt.returntypes.isEmpty())

View File

@ -46,7 +46,6 @@ object InferredTypes {
DataType.WORD to InferredType.known(DataType.WORD),
DataType.FLOAT to InferredType.known(DataType.FLOAT),
DataType.STR to InferredType.known(DataType.STR),
DataType.STR_S to InferredType.known(DataType.STR_S),
DataType.ARRAY_UB to InferredType.known(DataType.ARRAY_UB),
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),
DataType.ARRAY_UW to InferredType.known(DataType.ARRAY_UW),

View File

@ -1,9 +1,8 @@
package prog8.compiler.target.c64.codegen
package prog8.ast.processing
import prog8.ast.Program
import prog8.ast.base.AstException
import prog8.ast.base.NameError
import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.Statement
import prog8.ast.statements.VarDecl

View File

@ -1,5 +1,3 @@
@file:Suppress("DuplicatedCode")
package prog8.ast.processing
import prog8.ast.IFunctionCall
@ -10,8 +8,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.CompilationOptions
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.CompilationTarget
import prog8.functions.BuiltinFunctions
import java.io.File
@ -126,7 +123,7 @@ internal class AstChecker(private val program: Program,
} else {
if (forLoop.loopRegister != null) {
// loop register
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt !in StringDatatypes)
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR)
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
if(forLoop.loopRegister!=Register.A)
checkResult.add(ExpressionError("it's only possible to use A as a loop register", forLoop.position))
@ -138,16 +135,15 @@ internal class AstChecker(private val program: Program,
} else {
when (loopvar.datatype) {
DataType.UBYTE -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt !in StringDatatypes)
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
checkResult.add(ExpressionError("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position))
}
DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt !in StringDatatypes &&
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
checkResult.add(ExpressionError("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position))
}
DataType.BYTE -> {
// TODO fix this, it should allow: for bb in [1,2,3]
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position))
}
@ -246,7 +242,7 @@ internal class AstChecker(private val program: Program,
}
else if(param.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
&& param.first.type !in StringDatatypes && param.first.type !in ArrayDatatypes && param.first.type != DataType.FLOAT)
&& param.first.type != DataType.STR && param.first.type !in ArrayDatatypes && param.first.type != DataType.FLOAT)
err("parameter '${param.first.name}' should be (u)word/address")
}
else if(param.second.statusflag!=null) {
@ -261,7 +257,7 @@ internal class AstChecker(private val program: Program,
}
else if(ret.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (ret.first.value != DataType.UWORD && ret.first.value != DataType.WORD
&& ret.first.value !in StringDatatypes && ret.first.value !in ArrayDatatypes && ret.first.value != DataType.FLOAT)
&& ret.first.value != DataType.STR && ret.first.value !in ArrayDatatypes && ret.first.value != DataType.FLOAT)
err("return value #${ret.first.index + 1} should be (u)word/address")
}
else if(ret.second.statusflag!=null) {
@ -412,7 +408,7 @@ internal class AstChecker(private val program: Program,
}
}
val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR)
if(targetDt in StringDatatypes || targetDt in ArrayDatatypes)
if(targetDt in IterableDatatypes)
checkResult.add(SyntaxError("cannot assign to a string or array type", assignTarget.position))
if (assignment is Assignment) {
@ -448,7 +444,7 @@ internal class AstChecker(private val program: Program,
if(variable==null)
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
else {
if(variable.datatype !in ArrayDatatypes && variable.datatype !in StringDatatypes && variable.datatype!=DataType.STRUCT)
if(variable.datatype !in ArrayDatatypes && variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
checkResult.add(ExpressionError("invalid pointer-of operand type", addressOf.position))
}
super.visit(addressOf)
@ -508,9 +504,9 @@ internal class AstChecker(private val program: Program,
decl.datatype in NumericDatatypes -> {
// initialize numeric var with value zero by default.
val litVal =
when {
decl.datatype in ByteDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
decl.datatype in WordDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
when (decl.datatype) {
in ByteDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
in WordDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
else -> NumericLiteralValue(decl.datatype, 0.0, decl.position)
}
litVal.parent = decl
@ -697,16 +693,11 @@ internal class AstChecker(private val program: Program,
checkValueTypeAndRangeArray(array.type, null, arrayspec, array)
super.visit(array)
if(array.heapId==null && array.parent !is IFunctionCall)
throw FatalAstException("array should have been moved to heap at ${array.position}")
}
override fun visit(string: StringLiteralValue) {
checkValueTypeAndRangeString(string.type, string)
checkValueTypeAndRangeString(DataType.STR, string)
super.visit(string)
if(string.heapId==null)
throw FatalAstException("string should have been moved to heap at ${string.position}")
}
override fun visit(expr: PrefixExpression) {
@ -820,24 +811,24 @@ internal class AstChecker(private val program: Program,
val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression)
if(targetStatement!=null)
checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position)
checkFunctionCall(targetStatement, functionCall.args, functionCall.position)
super.visit(functionCall)
}
override fun visit(functionCallStatement: FunctionCallStatement) {
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
if(targetStatement!=null)
checkFunctionCall(targetStatement, functionCallStatement.arglist, functionCallStatement.position)
if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) {
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
if(!functionCallStatement.void && targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) {
if(targetStatement.returntypes.size==1)
printWarning("result value of subroutine call is discarded", functionCallStatement.position)
printWarning("result value of subroutine call is discarded (use void?)", functionCallStatement.position)
else
printWarning("result values of subroutine call are discarded", functionCallStatement.position)
printWarning("result values of subroutine call are discarded (use void?)", functionCallStatement.position)
}
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
// in-place modification, can't be done on literals
if(functionCallStatement.arglist.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
if(functionCallStatement.args.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
checkResult.add(ExpressionError("can't use that as argument to a in-place modifying function", functionCallStatement.position))
}
}
@ -877,10 +868,10 @@ internal class AstChecker(private val program: Program,
checkResult.add(ExpressionError("swap requires args of numerical type", position))
}
else if(target.name=="all" || target.name=="any") {
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype in StringDatatypes) {
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype == DataType.STR) {
checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position))
}
if(args[0].inferType(program).typeOrElse(DataType.STR) in StringDatatypes) {
if(args[0].inferType(program).typeOrElse(DataType.STR) == DataType.STR) {
checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position))
}
}
@ -897,7 +888,7 @@ internal class AstChecker(private val program: Program,
val argDt=argIDt.typeOrElse(DataType.STRUCT)
if(!(argDt isAssignableTo arg.second.type)) {
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt == DataType.UWORD))
if(!(target.isAsmSubroutine && arg.second.type == DataType.STR && argDt == DataType.UWORD))
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))
}
@ -964,7 +955,7 @@ internal class AstChecker(private val program: Program,
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
if(index!=null && (index<0 || index>=arraysize))
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
} else if(target.datatype in StringDatatypes) {
} else if(target.datatype == DataType.STR) {
if(target.value is StringLiteralValue) {
// check string lengths for non-memory mapped strings
val stringLen = (target.value as StringLiteralValue).value.length
@ -1048,19 +1039,15 @@ internal class AstChecker(private val program: Program,
}
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean {
fun err(msg: String): Boolean {
checkResult.add(ExpressionError(msg, value.position))
return false
}
return when (targetDt) {
in StringDatatypes -> {
return if (value.value.length > 255)
err("string length must be 0-255")
else
true
return if (targetDt == DataType.STR) {
if (value.value.length > 255) {
checkResult.add(ExpressionError("string length must be 0-255", value.position))
false
}
else -> false
else
true
}
else false
}
private fun checkValueTypeAndRangeArray(targetDt: DataType, struct: StructDecl?,
@ -1070,7 +1057,7 @@ internal class AstChecker(private val program: Program,
return false
}
when (targetDt) {
in StringDatatypes -> return err("string value expected")
DataType.STR -> return err("string value expected")
DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
if(value.type==targetDt) {
@ -1136,7 +1123,7 @@ internal class AstChecker(private val program: Program,
// check if the floating point values are all within range
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE })
if(doubles.any { it < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || it > CompilationTarget.machine.FLOAT_MAX_POSITIVE })
return err("floating point value overflow")
return true
}
@ -1207,35 +1194,14 @@ internal class AstChecker(private val program: Program,
}
private fun checkArrayValues(value: ArrayLiteralValue, type: DataType): Boolean {
if (value.heapId == null) {
// hmm weird, array literal that hasn't been moved to the heap yet?
val array = value.value.mapNotNull { it.constValue(program) }
val correct: Boolean
when (type) {
DataType.ARRAY_UB -> {
correct = array.all { it.type == DataType.UBYTE && it.number.toInt() in 0..255 }
}
DataType.ARRAY_B -> {
correct = array.all { it.type == DataType.BYTE && it.number.toInt() in -128..127 }
}
DataType.ARRAY_UW -> {
correct = array.all { it.type == DataType.UWORD && it.number.toInt() in 0..65535 }
}
DataType.ARRAY_W -> {
correct = array.all { it.type == DataType.WORD && it.number.toInt() in -32768..32767 }
}
DataType.ARRAY_F -> correct = true
else -> throw AstException("invalid array type $type")
}
if (!correct)
checkResult.add(ExpressionError("array value out of range for type $type", value.position))
return correct
}
val array = value.value.map {
when (it) {
is NumericLiteralValue -> it.number.toInt()
is AddressOf -> it.identifier.heapId(program.namespace)
is TypecastExpression -> {
val constVal = it.expression.constValue(program)
constVal?.cast(it.type)?.number?.toInt() ?: -9999999
}
else -> -9999999
}
}
@ -1277,7 +1243,6 @@ internal class AstChecker(private val program: Program,
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
DataType.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype== DataType.STR
DataType.STR_S -> sourceDatatype== DataType.STR_S
DataType.STRUCT -> {
if(sourceDatatype==DataType.STRUCT) {
val structLv = sourceValue as StructLiteralValue

View File

@ -7,7 +7,7 @@ import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.target.c64.AssemblyProgram
import prog8.compiler.target.CompilationTarget
import prog8.functions.BuiltinFunctions
@ -50,7 +50,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
override fun visit(functionCall: FunctionCall): Expression {
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, false, functionCall.position)
val typecast = TypecastExpression(functionCall.args.single(), DataType.UBYTE, false, functionCall.position)
typecast.linkParents(functionCall.parent)
return super.visit(typecast)
}
@ -66,7 +66,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// the builtin functions can't be redefined
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
if(decl.name in AssemblyProgram.opcodeNames)
if(decl.name in CompilationTarget.machine.opcodeNames)
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position))
// is it a struct variable? then define all its struct members as mangled names,
@ -103,7 +103,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
}
override fun visit(subroutine: Subroutine): Statement {
if(subroutine.name in AssemblyProgram.opcodeNames) {
if(subroutine.name in CompilationTarget.machine.opcodeNames) {
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position))
} else if(subroutine.name in BuiltinFunctions) {
// the builtin functions can't be redefined
@ -164,7 +164,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
}
override fun visit(label: Label): Statement {
if(label.name in AssemblyProgram.opcodeNames)
if(label.name in CompilationTarget.machine.opcodeNames)
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${label.name}'", label.position))
if(label.name in BuiltinFunctions) {
@ -229,18 +229,18 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
val array = super.visit(arrayLiteral)
if(array is ArrayLiteralValue) {
val vardecl = array.parent as? VarDecl
return when {
vardecl!=null -> fixupArrayDatatype(array, vardecl, program)
array.heapId!=null -> {
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
// (we don't know the desired datatype here exactly so we guess)
val datatype = determineArrayDt(array.value)
val litval2 = array.cast(datatype)!!
return if(vardecl!=null)
fixupArrayEltDatatypesFromVardecl(array, vardecl)
else {
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
// (we don't know the desired datatype here exactly so we guess)
val datatype = determineArrayDt(array.value)
val litval2 = array.cast(datatype)
if(litval2!=null) {
litval2.parent = array.parent
// finally, replace the literal array by a identifier reference.
makeIdentifierFromRefLv(litval2)
}
else -> array
} else array
}
}
return array
@ -253,9 +253,6 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// intern the string; move it into the heap
if (string.value.length !in 1..255)
checkResult.add(ExpressionError("string literal length must be between 1 and 255", string.position))
else {
string.addToHeap()
}
return if (vardecl != null)
string
else
@ -282,7 +279,6 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// a referencetype literal value that's not declared as a variable
// we need to introduce an auto-generated variable for this to be able to refer to the value
// note: if the var references the same literal value, it is not yet de-duplicated here.
array.addToHeap()
val scope = array.definingScope()
val variable = VarDecl.createAuto(array)
return replaceWithIdentifier(variable, scope, array.parent)
@ -292,7 +288,6 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// a referencetype literal value that's not declared as a variable
// we need to introduce an auto-generated variable for this to be able to refer to the value
// note: if the var references the same literal value, it is not yet de-duplicated here.
string.addToHeap()
val scope = string.definingScope()
val variable = VarDecl.createAuto(string)
return replaceWithIdentifier(variable, scope, string.parent)
@ -331,16 +326,12 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
if(constvalue!=null) {
if (expr.operator == "*") {
// repeat a string a number of times
val idt = string.inferType(program)
return StringLiteralValue(idt.typeOrElse(DataType.STR),
string.value.repeat(constvalue.number.toInt()), expr.position)
return StringLiteralValue(string.value.repeat(constvalue.number.toInt()), expr.position)
}
}
if(expr.operator == "+" && operand is StringLiteralValue) {
// concatenate two strings
val idt = string.inferType(program)
return StringLiteralValue(idt.typeOrElse(DataType.STR),
"${string.value}${operand.value}", expr.position)
return StringLiteralValue("${string.value}${operand.value}", expr.position)
}
return expr
}
@ -360,7 +351,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
}
internal fun fixupArrayDatatype(array: ArrayLiteralValue, program: Program): ArrayLiteralValue {
internal fun fixupArrayEltDatatypes(array: ArrayLiteralValue, program: Program): ArrayLiteralValue {
val dts = array.value.map {it.inferType(program).typeOrElse(DataType.STRUCT)}.toSet()
if(dts.any { it !in NumericDatatypes }) {
return array
@ -377,51 +368,28 @@ internal fun fixupArrayDatatype(array: ArrayLiteralValue, program: Program): Arr
// convert values and array type
val elementType = ArrayElementTypes.getValue(dt)
val values = array.value.map { (it as NumericLiteralValue).cast(elementType) as Expression}.toTypedArray()
val array2 = ArrayLiteralValue(dt, values, array.heapId, array.position)
array2.linkParents(array.parent)
return array2
val allNumerics = array.value.all { it is NumericLiteralValue }
if(allNumerics) {
val values = array.value.map { (it as NumericLiteralValue).cast(elementType) as Expression }.toTypedArray()
val array2 = ArrayLiteralValue(dt, values, array.position)
array2.linkParents(array.parent)
return array2
}
return array
}
internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, program: Program): ArrayLiteralValue {
if(array.heapId!=null) {
val arrayDt = array.type
if(arrayDt!=vardecl.datatype) {
// fix the datatype of the array (also on the heap) to match the vardecl
val litval2 =
try {
val result = array.cast(vardecl.datatype)
if(result==null) {
val constElements = array.value.mapNotNull { it.constValue(program) }
val elementDts = constElements.map { it.type }
if(DataType.FLOAT in elementDts) {
array.cast(DataType.ARRAY_F) ?: ArrayLiteralValue(DataType.ARRAY_F, array.value, array.heapId, array.position)
} else {
val numbers = constElements.map { it.number.toInt() }
val minValue = numbers.min()!!
val maxValue = numbers.max()!!
if (minValue >= 0) {
// only positive values, so uword or ubyte
val dt = if(maxValue<256) DataType.ARRAY_UB else DataType.ARRAY_UW
array.cast(dt) ?: ArrayLiteralValue(dt, array.value, array.heapId, array.position)
} else {
// negative value present, so word or byte
val dt = if(minValue >= -128 && maxValue<=127) DataType.ARRAY_B else DataType.ARRAY_W
array.cast(dt) ?: ArrayLiteralValue(dt, array.value, array.heapId, array.position)
}
}
}
else result
} catch(x: ExpressionError) {
// couldn't cast permanently.
// instead, simply adjust the array type and trust the AstChecker to report the exact error
ArrayLiteralValue(vardecl.datatype, array.value, array.heapId, array.position)
}
vardecl.value = litval2
litval2.linkParents(vardecl)
litval2.addToHeap()
return litval2
internal fun fixupArrayEltDatatypesFromVardecl(array: ArrayLiteralValue, vardecl: VarDecl): ArrayLiteralValue {
val arrayDt = array.type
if(arrayDt!=vardecl.datatype) {
// fix the datatype of the array (also on the heap) to match the vardecl
val cast = array.cast(vardecl.datatype)
if (cast != null) {
vardecl.value = cast
cast.linkParents(vardecl)
return cast
}
// can't be casted yet, attempt again later
}
return array
}

View File

@ -52,7 +52,7 @@ interface IAstModifyingVisitor {
functionCall.target = newtarget
else
throw FatalAstException("cannot change class of function call target")
functionCall.arglist = functionCall.arglist.map { it.accept(this) }.toMutableList()
functionCall.args = functionCall.args.map { it.accept(this) }.toMutableList()
return functionCall
}
@ -62,7 +62,7 @@ interface IAstModifyingVisitor {
functionCallStatement.target = newtarget
else
throw FatalAstException("cannot change class of function call target")
functionCallStatement.arglist = functionCallStatement.arglist.map { it.accept(this) }.toMutableList()
functionCallStatement.args = functionCallStatement.args.map { it.accept(this) }.toMutableList()
return functionCallStatement
}

View File

@ -41,12 +41,12 @@ interface IAstVisitor {
fun visit(functionCall: FunctionCall) {
functionCall.target.accept(this)
functionCall.arglist.forEach { it.accept(this) }
functionCall.args.forEach { it.accept(this) }
}
fun visit(functionCallStatement: FunctionCallStatement) {
functionCallStatement.target.accept(this)
functionCallStatement.arglist.forEach { it.accept(this) }
functionCallStatement.args.forEach { it.accept(this) }
}
fun visit(identifier: IdentifierReference) {

View File

@ -13,8 +13,8 @@ private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment,
val identifierName = identifier.nameInSource.single()
val targetVar = identifier.targetVarDecl(program.namespace)!!
val struct = targetVar.struct!!
when {
structAssignment.value is IdentifierReference -> {
when (structAssignment.value) {
is IdentifierReference -> {
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
if (sourceVar.struct == null)
throw FatalAstException("can only assign arrays or structs to structs")
@ -39,7 +39,7 @@ private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment,
assign
}
}
structAssignment.value is StructLiteralValue -> {
is StructLiteralValue -> {
throw IllegalArgumentException("not going to flatten a structLv assignment here")
}
else -> throw FatalAstException("strange struct value")

View File

@ -78,7 +78,7 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
// see if a typecast is needed to convert the arguments into the required parameter's type
when(val sub = call.target.targetStatement(scope)) {
is Subroutine -> {
for(arg in sub.parameters.zip(call.arglist.withIndex())) {
for(arg in sub.parameters.zip(call.args.withIndex())) {
val argItype = arg.second.value.inferType(program)
if(argItype.isKnown) {
val argtype = argItype.typeOrElse(DataType.STRUCT)
@ -87,7 +87,7 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
if (argtype isAssignableTo requiredType) {
val typecasted = TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position)
typecasted.linkParents(arg.second.value.parent)
call.arglist[arg.second.index] = typecasted
call.args[arg.second.index] = typecasted
}
// if they're not assignable, we'll get a proper error later from the AstChecker
}
@ -98,7 +98,7 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
val func = BuiltinFunctions.getValue(sub.name)
if(func.pure) {
// non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters
for (arg in func.parameters.zip(call.arglist.withIndex())) {
for (arg in func.parameters.zip(call.args.withIndex())) {
val argItype = arg.second.value.inferType(program)
if (argItype.isKnown) {
val argtype = argItype.typeOrElse(DataType.STRUCT)
@ -108,7 +108,7 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
if (argtype isAssignableTo possibleType) {
val typecasted = TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position)
typecasted.linkParents(arg.second.value.parent)
call.arglist[arg.second.index] = typecasted
call.args[arg.second.index] = typecasted
break
}
}

View File

@ -44,8 +44,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
val arraysize = decl.arraysize!!.size()!!
val array = ArrayLiteralValue(decl.datatype,
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
null, decl.position)
array.addToHeap()
decl.position)
decl.value = array
}
@ -79,11 +78,11 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
parentStatement = parentStatement.parent
val targetStatement = functionCall.target.targetSubroutine(program.namespace)
if(targetStatement!=null) {
addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, parentStatement)
addAddressOfExprIfNeeded(targetStatement, functionCall.args, parentStatement)
} else {
val builtinFunc = BuiltinFunctions[functionCall.target.nameInSource.joinToString (".")]
if(builtinFunc!=null)
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCall.arglist, parentStatement)
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCall.args, parentStatement)
}
return functionCall
}
@ -91,11 +90,11 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
val targetStatement = functionCallStatement.target.targetSubroutine(program.namespace)
if(targetStatement!=null) {
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.args, functionCallStatement)
} else {
val builtinFunc = BuiltinFunctions[functionCallStatement.target.nameInSource.joinToString (".")]
if(builtinFunc!=null)
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCallStatement.arglist, functionCallStatement)
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCallStatement.args, functionCallStatement)
}
return functionCallStatement
}
@ -103,14 +102,14 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<Expression>, parent: Statement) {
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
for(argparam in subroutine.parameters.withIndex().zip(arglist)) {
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type in StringDatatypes) {
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type == DataType.STR) {
if(argparam.second is AddressOf)
continue
val idref = argparam.second as? IdentifierReference
val strvalue = argparam.second as? StringLiteralValue
if(idref!=null) {
val variable = idref.targetVarDecl(program.namespace)
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
if(variable!=null && variable.datatype in IterableDatatypes) {
val pointerExpr = AddressOf(idref, idref.position)
pointerExpr.linkParents(arglist[argparam.first.index].parent)
arglist[argparam.first.index] = pointerExpr

View File

@ -192,17 +192,12 @@ class VarDecl(val type: VarDeclType,
private var autoHeapValueSequenceNumber = 0
fun createAuto(string: StringLiteralValue): VarDecl {
if(string.heapId==null)
throw FatalAstException("can only create autovar for a string that has a heapid $string")
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
return VarDecl(VarDeclType.VAR, string.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
return VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
isArray = false, autogeneratedDontRemove = true, position = string.position)
}
fun createAuto(array: ArrayLiteralValue): VarDecl {
if(array.heapId==null)
throw FatalAstException("can only create autovar for an array that has a heapid $array")
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
val declaredType = ArrayElementTypes.getValue(array.type)
val arraysize = ArrayIndex.forArray(array)
@ -477,16 +472,17 @@ class Jump(val address: Int?,
}
class FunctionCallStatement(override var target: IdentifierReference,
override var arglist: MutableList<Expression>,
override var args: MutableList<Expression>,
val void: Boolean,
override val position: Position) : Statement(), IFunctionCall {
override lateinit var parent: Node
override val expensiveToInline
get() = arglist.any { it !is NumericLiteralValue }
get() = args.any { it !is NumericLiteralValue }
override fun linkParents(parent: Node) {
this.parent = parent
target.linkParents(this)
arglist.forEach { it.linkParents(this) }
args.forEach { it.linkParents(this) }
}
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)

View File

@ -0,0 +1,3 @@
package prog8.compiler
internal class AssemblyError(msg: String) : RuntimeException(msg)

View File

@ -4,8 +4,7 @@ import prog8.ast.AstToSourceCode
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.statements.Directive
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.CompilationTarget
import prog8.optimizer.constantFold
import prog8.optimizer.optimizeStatements
import prog8.optimizer.simplifyExpressions
@ -101,9 +100,9 @@ fun compileProgram(filepath: Path,
if(writeAssembly) {
// asm generation directly from the Ast, no need for intermediate code
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
programAst.anonscopeVarsCleanup()
val assembly = AsmGen(programAst, zeropage, compilerOptions, outputDir).compileToAssembly(optimize)
val assembly = CompilationTarget.asmGenerator(programAst, zeropage, compilerOptions, outputDir).compileToAssembly(optimize)
assembly.assemble(compilerOptions)
programName = assembly.name
}

View File

@ -0,0 +1,16 @@
package prog8.compiler.target
import prog8.ast.Program
import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage
import java.nio.file.Path
internal interface CompilationTarget {
companion object {
lateinit var name: String
lateinit var machine: IMachineDefinition
lateinit var encodeString: (str: String) -> List<Short>
lateinit var decodeString: (bytes: List<Short>) -> String
lateinit var asmGenerator: (Program, Zeropage, CompilationOptions, Path) -> IAssemblyGenerator
}
}

View File

@ -0,0 +1,12 @@
package prog8.compiler.target
import prog8.compiler.CompilationOptions
internal interface IAssemblyGenerator {
fun compileToAssembly(optimize: Boolean): IAssemblyProgram
}
internal interface IAssemblyProgram {
val name: String
fun assemble(options: CompilationOptions)
}

View File

@ -0,0 +1,15 @@
package prog8.compiler.target
import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage
interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
val opcodeNames: Set<String>
fun getZeropage(compilerOptions: CompilationOptions): Zeropage
}

View File

@ -2,30 +2,17 @@ package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions
import prog8.compiler.OutputType
import prog8.compiler.target.IAssemblyProgram
import java.nio.file.Path
import kotlin.system.exitProcess
class AssemblyProgram(val name: String, outputDir: Path) {
class AssemblyProgram(override val name: String, outputDir: Path): IAssemblyProgram {
private val assemblyFile = outputDir.resolve("$name.asm")
private val prgFile = outputDir.resolve("$name.prg")
private val binFile = outputDir.resolve("$name.bin")
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
companion object {
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
}
fun assemble(options: CompilationOptions) {
override fun assemble(options: CompilationOptions) {
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",

View File

@ -4,18 +4,19 @@ import prog8.compiler.CompilationOptions
import prog8.compiler.CompilerException
import prog8.compiler.Zeropage
import prog8.compiler.ZeropageType
import prog8.compiler.target.IMachineDefinition
import java.awt.Color
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import kotlin.math.absoluteValue
import kotlin.math.pow
object MachineDefinition {
object C64MachineDefinition: IMachineDefinition {
// 5-byte cbm MFLPT format limitations:
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
override val FLOAT_MEM_SIZE = 5
const val BASIC_LOAD_ADDRESS = 0x0801
const val RAW_LOAD_ADDRESS = 0xc000
@ -30,6 +31,19 @@ object MachineDefinition {
const val ESTACK_HI_PLUS1_HEX = "\$cf01"
const val ESTACK_HI_PLUS2_HEX = "\$cf02"
override fun getZeropage(compilerOptions: CompilationOptions) = C64Zeropage(compilerOptions)
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
@ -110,8 +124,6 @@ object MachineDefinition {
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
companion object {
const val MemorySize = 5
val zero = Mflpt5(0, 0, 0, 0, 0)
fun fromNumber(num: Number): Mflpt5 {
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
@ -232,7 +244,6 @@ object MachineDefinition {
return bcopy
}
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
Color(0x000000), // 0 = black
Color(0xFFFFFF), // 1 = white

View File

@ -1058,7 +1058,7 @@ object Petscii {
0.toShort()
else {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}case Petscii character for '$it'")
throw CharConversionException("no ${case}case Petscii character for '$it' (${it.toShort()})")
}
}
}
@ -1076,7 +1076,7 @@ object Petscii {
0.toShort()
else {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}Screencode character for '$it'")
throw CharConversionException("no ${case}Screencode character for '$it' (${it.toShort()})")
}
}
}

View File

@ -7,10 +7,12 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.*
import prog8.compiler.target.IAssemblyGenerator
import prog8.compiler.target.IAssemblyProgram
import prog8.compiler.target.c64.AssemblyProgram
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import prog8.functions.FunctionSignature
@ -22,13 +24,10 @@ import java.util.ArrayDeque
import kotlin.math.absoluteValue
internal class AssemblyError(msg: String) : RuntimeException(msg)
internal class AsmGen(private val program: Program,
private val zeropage: Zeropage,
private val options: CompilationOptions,
private val outputDir: Path) {
private val zeropage: Zeropage,
private val options: CompilationOptions,
private val outputDir: Path): IAssemblyGenerator {
private val assemblyLines = mutableListOf<String>()
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
@ -43,7 +42,7 @@ internal class AsmGen(private val program: Program,
internal val loopEndLabels = ArrayDeque<String>()
internal val loopContinueLabels = ArrayDeque<String>()
internal fun compileToAssembly(optimize: Boolean): AssemblyProgram {
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
assemblyLines.clear()
loopEndLabels.clear()
loopContinueLabels.clear()
@ -84,7 +83,7 @@ internal class AsmGen(private val program: Program,
program.actualLoadAddress = program.definedLoadAddress
if (program.actualLoadAddress == 0) // fix load address
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC)
MachineDefinition.BASIC_LOAD_ADDRESS else MachineDefinition.RAW_LOAD_ADDRESS
C64MachineDefinition.BASIC_LOAD_ADDRESS else C64MachineDefinition.RAW_LOAD_ADDRESS
when {
options.launcher == LauncherType.BASIC -> {
@ -145,7 +144,7 @@ internal class AsmGen(private val program: Program,
// the global list of all floating point constants for the whole program
out("; global float constants")
for (flt in globalFloatConsts) {
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(flt.key)
val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(flt.key)
val floatFill = makeFloatFill(mflpt5)
val floatvalue = flt.key
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
@ -198,7 +197,7 @@ internal class AsmGen(private val program: Program,
} else assemblyLines.add(fragment)
}
private fun makeFloatFill(flt: MachineDefinition.Mflpt5): String {
private fun makeFloatFill(flt: C64MachineDefinition.Mflpt5): String {
val b0 = "$" + flt.b0.toString(16).padStart(2, '0')
val b1 = "$" + flt.b1.toString(16).padStart(2, '0')
val b2 = "$" + flt.b2.toString(16).padStart(2, '0')
@ -207,18 +206,9 @@ internal class AsmGen(private val program: Program,
return "$b0, $b1, $b2, $b3, $b4"
}
private fun encodeStr(str: String, dt: DataType): List<Short> {
return when(dt) {
DataType.STR -> {
val bytes = Petscii.encodePetscii(str, true)
bytes.plus(0)
}
DataType.STR_S -> {
val bytes = Petscii.encodeScreencode(str, true)
bytes.plus(0)
}
else -> throw AssemblyError("invalid str type")
}
private fun petscii(str: String): List<Short> {
val bytes = Petscii.encodePetscii(str, true)
return bytes.plus(0)
}
private fun zeropagevars2asm(statements: List<Statement>) {
@ -255,10 +245,9 @@ internal class AsmGen(private val program: Program,
DataType.WORD -> out("${decl.name}\t.sint 0")
DataType.FLOAT -> out("${decl.name}\t.byte 0,0,0,0,0 ; float")
DataType.STRUCT -> {} // is flattened
DataType.STR, DataType.STR_S -> {
DataType.STR -> {
val string = (decl.value as StringLiteralValue).value
val encoded = encodeStr(string, decl.datatype)
outputStringvar(decl, encoded)
outputStringvar(decl, petscii(string))
}
DataType.ARRAY_UB -> {
val data = makeArrayFillDataUnsigned(decl)
@ -304,7 +293,7 @@ internal class AsmGen(private val program: Program,
val array = (decl.value as ArrayLiteralValue).value
val floatFills = array.map {
val number = (it as NumericLiteralValue).number
makeFloatFill(MachineDefinition.Mflpt5.fromNumber(number))
makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number))
}
out(decl.name)
for (f in array.zip(floatFills))
@ -341,8 +330,8 @@ internal class AsmGen(private val program: Program,
// special treatment for string types: merge strings that are identical
val encodedstringVars = normalVars
.filter {it.datatype in StringDatatypes }
.map { it to encodeStr((it.value as StringLiteralValue).value, it.datatype) }
.filter {it.datatype == DataType.STR }
.map { it to petscii((it.value as StringLiteralValue).value) }
.groupBy({it.second}, {it.first})
for((encoded, variables) in encodedstringVars) {
variables.dropLast(1).forEach { out(it.name) }
@ -351,7 +340,7 @@ internal class AsmGen(private val program: Program,
}
// non-string variables
normalVars.filter{ it.datatype !in StringDatatypes}.sortedBy { it.datatype }.forEach {
normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
if(it.scopedname !in allocatedZeropageVariables)
vardecl2asm(it)
}
@ -367,14 +356,14 @@ internal class AsmGen(private val program: Program,
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
val array = (decl.value as ArrayLiteralValue).value
return when {
decl.datatype == DataType.ARRAY_UB ->
return when (decl.datatype) {
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = (it as NumericLiteralValue).number.toInt()
"$"+number.toString(16).padStart(2, '0')
}
decl.datatype== DataType.ARRAY_UW -> array.map {
DataType.ARRAY_UW -> array.map {
if(it is NumericLiteralValue) {
"$" + it.number.toInt().toString(16).padStart(4, '0')
} else {
@ -425,7 +414,7 @@ internal class AsmGen(private val program: Program,
internal fun getFloatConst(number: Double): String {
// try to match the ROM float constants to save memory
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(number)
val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(number)
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
when {
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_ZERO"
@ -511,7 +500,7 @@ internal class AsmGen(private val program: Program,
internal fun readAndPushArrayvalueWithIndexA(arrayDt: DataType, variable: IdentifierReference) {
val variablename = asmIdentifierName(variable)
when (arrayDt) {
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
out(" tay | lda $variablename,y | sta $ESTACK_LO_HEX,x | dex")
DataType.ARRAY_UW, DataType.ARRAY_W ->
out(" asl a | tay | lda $variablename,y | sta $ESTACK_LO_HEX,x | lda $variablename+1,y | sta $ESTACK_HI_HEX,x | dex")

View File

@ -1,7 +1,7 @@
package prog8.compiler.target.c64.codegen
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations

View File

@ -7,8 +7,13 @@ import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.VarDecl
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.toHex
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -78,10 +83,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
val indexValue = index.number.toInt() * ArrayElementTypes.getValue(arrayDt).memorySize()
when (arrayDt) {
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | dex")
DataType.ARRAY_UW, DataType.ARRAY_W ->
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $arrayVarName+$indexValue+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | lda $arrayVarName+$indexValue+1 | sta $ESTACK_HI_HEX,x | dex")
DataType.ARRAY_F ->
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
else ->
@ -124,21 +129,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
target.register!=null -> {
if(target.register== Register.X)
throw AssemblyError("can't pop into X register - use variable instead")
asmgen.out(" inx | ld${target.register.name.toLowerCase()} ${MachineDefinition.ESTACK_LO_HEX},x ")
asmgen.out(" inx | ld${target.register.name.toLowerCase()} $ESTACK_LO_HEX,x ")
}
targetIdent!=null -> {
val targetName = asmgen.asmIdentifierName(targetIdent)
val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $targetName")
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $targetName")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
sta $targetName
lda ${MachineDefinition.ESTACK_HI_HEX},x
lda $ESTACK_HI_HEX,x
sta $targetName+1
""")
}
@ -153,14 +158,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
target.memoryAddress!=null -> {
asmgen.out(" inx | ldy ${MachineDefinition.ESTACK_LO_HEX},x")
asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
}
target.arrayindexed!=null -> {
val arrayDt = target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!.datatype
val arrayVarName = asmgen.asmIdentifierName(target.arrayindexed!!.identifier)
asmgen.translateExpression(target.arrayindexed!!.arrayspec.index)
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
popAndWriteArrayvalueWithIndexA(arrayDt, arrayVarName)
}
else -> throw AssemblyError("weird assignment target $target")
@ -225,9 +230,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
targetArrayIdx!=null -> {
val index = targetArrayIdx.arrayspec.index
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $sourceName+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | lda $sourceName+1 | sta $ESTACK_HI_HEX,x | dex")
asmgen.translateExpression(index)
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
}
@ -285,9 +290,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val index = targetArrayIdx.arrayspec.index
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | dex")
asmgen.translateExpression(index)
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
}
target.memoryAddress != null -> {
@ -303,8 +308,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.translateExpression(addressExpr)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
ldy ${MachineDefinition.ESTACK_HI_HEX},x
lda $ESTACK_LO_HEX,x
ldy $ESTACK_HI_HEX,x
sta (+) +1
sty (+) +2
lda $sourceName
@ -361,9 +366,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
is RegisterExpr -> {
when(register) {
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
}
when(index.register) {
Register.A -> {}
@ -372,20 +377,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
asmgen.out("""
tay
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
lda ${C64Zeropage.SCRATCH_B1}
sta $targetName,y
""")
}
is IdentifierReference -> {
when(register) {
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
}
asmgen.out("""
lda ${asmgen.asmIdentifierName(index)}
tay
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
lda ${C64Zeropage.SCRATCH_B1}
sta $targetName,y
""")
}
@ -394,15 +399,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.translateExpression(index)
asmgen.restoreRegister(register)
when(register) {
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
}
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
tay
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
lda ${C64Zeropage.SCRATCH_B1}
sta $targetName,y
""")
}
@ -423,29 +428,29 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(register) {
Register.A -> asmgen.out("""
ldy $targetName
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
sty ${C64Zeropage.SCRATCH_W1}
ldy $targetName+1
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
sty ${C64Zeropage.SCRATCH_W1+1}
ldy #0
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
sta (${C64Zeropage.SCRATCH_W1}),y
""")
Register.X -> asmgen.out("""
txa
ldy $targetName
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
sty ${C64Zeropage.SCRATCH_W1}
ldy $targetName+1
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
sty ${C64Zeropage.SCRATCH_W1+1}
ldy #0
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
sta (${C64Zeropage.SCRATCH_W1}),y
""")
Register.Y -> asmgen.out("""
tya
ldy $targetName
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
sty ${C64Zeropage.SCRATCH_W1}
ldy $targetName+1
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
sty ${C64Zeropage.SCRATCH_W1+1}
ldy #0
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
sta (${C64Zeropage.SCRATCH_W1}),y
""")
}
}
@ -460,9 +465,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
sta (+) +1
lda ${MachineDefinition.ESTACK_HI_HEX},x
lda $ESTACK_HI_HEX,x
sta (+) +2
+ sty ${65535.toHex()} ; modified
""")
@ -502,7 +507,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.translateExpression(index)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
asl a
tay
lda #<${word.toHex()}
@ -537,7 +542,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.translateExpression(index)
asmgen.out("""
inx
ldy ${MachineDefinition.ESTACK_LO_HEX},x
ldy $ESTACK_LO_HEX,x
lda #${byte.toHex()}
sta $targetName,y
""")
@ -567,7 +572,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val index = targetArrayIdx.arrayspec.index
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
if(index is NumericLiteralValue) {
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
val indexValue = index.number.toInt() * C64MachineDefinition.FLOAT_MEM_SIZE
asmgen.out("""
lda #0
sta $targetName+$indexValue
@ -580,11 +585,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.translateExpression(index)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
asl a
asl a
clc
adc ${MachineDefinition.ESTACK_LO_HEX},x
adc $ESTACK_LO_HEX,x
tay
lda #0
sta $targetName,y
@ -620,7 +625,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val index = targetArrayIdx.arrayspec.index
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
if(index is NumericLiteralValue) {
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
val indexValue = index.number.toInt() * C64MachineDefinition.FLOAT_MEM_SIZE
asmgen.out("""
lda $constFloat
sta $arrayVarName+$indexValue
@ -636,11 +641,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} else {
asmgen.translateArrayIndexIntoA(targetArrayIdx)
asmgen.out("""
sta ${MachineDefinition.C64Zeropage.SCRATCH_REG}
sta ${C64Zeropage.SCRATCH_REG}
asl a
asl a
clc
adc ${MachineDefinition.C64Zeropage.SCRATCH_REG}
adc ${C64Zeropage.SCRATCH_REG}
tay
lda $constFloat
sta $arrayVarName,y
@ -725,14 +730,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
private fun popAndWriteArrayvalueWithIndexA(arrayDt: DataType, variablename: String) {
when (arrayDt) {
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
asmgen.out(" tay | inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $variablename,y")
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
asmgen.out(" tay | inx | lda $ESTACK_LO_HEX,x | sta $variablename,y")
DataType.ARRAY_UW, DataType.ARRAY_W ->
asmgen.out(" asl a | tay | inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $variablename,y | lda ${MachineDefinition.ESTACK_HI_HEX},x | sta $variablename+1,y")
asmgen.out(" asl a | tay | inx | lda $ESTACK_LO_HEX,x | sta $variablename,y | lda $ESTACK_HI_HEX,x | sta $variablename+1,y")
DataType.ARRAY_F ->
// index * 5 is done in the subroutine that's called
asmgen.out("""
sta ${MachineDefinition.ESTACK_LO_HEX},x
sta $ESTACK_LO_HEX,x
dex
lda #<$variablename
ldy #>$variablename

View File

@ -9,12 +9,13 @@ import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.FunctionCallStatement
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.toHex
import prog8.compiler.AssemblyError
import prog8.functions.FunctionSignature
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -38,7 +39,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (functionName) {
"msb" -> {
val arg = fcall.arglist.single()
val arg = fcall.args.single()
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
throw AssemblyError("msb required word argument")
if (arg is NumericLiteralValue)
@ -52,12 +53,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
"mkword" -> {
translateFunctionArguments(fcall.arglist, func)
translateFunctionArguments(fcall.args, func)
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
}
"abs" -> {
translateFunctionArguments(fcall.arglist, func)
val dt = fcall.arglist.single().inferType(program)
translateFunctionArguments(fcall.args, func)
val dt = fcall.args.single().inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
@ -66,8 +67,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
"swap" -> {
val first = fcall.arglist[0]
val second = fcall.arglist[1]
val first = fcall.args[0]
val second = fcall.args[1]
asmgen.translateExpression(first)
asmgen.translateExpression(second)
// pop in reverse order
@ -77,14 +78,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.assignFromEvalResult(secondTarget)
}
"strlen" -> {
outputPushAddressOfIdentifier(fcall.arglist[0])
outputPushAddressOfIdentifier(fcall.args[0])
asmgen.out(" jsr prog8_lib.func_strlen")
}
"min", "max", "sum" -> {
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
val dt = fcall.arglist.single().inferType(program)
outputPushAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
@ -93,18 +94,18 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
"any", "all" -> {
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
val dt = fcall.arglist.single().inferType(program)
outputPushAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
else -> throw AssemblyError("weird type $dt")
}
}
"sgn" -> {
translateFunctionArguments(fcall.arglist, func)
val dt = fcall.arglist.single().inferType(program)
translateFunctionArguments(fcall.args, func)
val dt = fcall.args.single().inferType(program)
when(dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> asmgen.out(" jsr math.sign_ub")
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
@ -118,12 +119,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"ln", "log2", "sqrt", "rad",
"deg", "round", "floor", "ceil",
"rdnf" -> {
translateFunctionArguments(fcall.arglist, func)
translateFunctionArguments(fcall.args, func)
asmgen.out(" jsr c64flt.func_$functionName")
}
"lsl" -> {
// in-place
val what = fcall.arglist.single()
val what = fcall.args.single()
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> {
@ -179,7 +180,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
"lsr" -> {
// in-place
val what = fcall.arglist.single()
val what = fcall.args.single()
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
@ -263,7 +264,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
"rol" -> {
// in-place
val what = fcall.arglist.single()
val what = fcall.args.single()
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
@ -322,7 +323,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
"rol2" -> {
// in-place
val what = fcall.arglist.single()
val what = fcall.args.single()
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
@ -374,7 +375,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
"ror" -> {
// in-place
val what = fcall.arglist.single()
val what = fcall.args.single()
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
@ -432,7 +433,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
"ror2" -> {
// in-place
val what = fcall.arglist.single()
val what = fcall.args.single()
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
@ -483,7 +484,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
"sort" -> {
val variable = fcall.arglist.single()
val variable = fcall.args.single()
if(variable is IdentifierReference) {
val decl = variable.targetVarDecl(program.namespace)!!
val varName = asmgen.asmIdentifierName(variable)
@ -519,7 +520,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("weird type")
}
"reverse" -> {
val variable = fcall.arglist.single()
val variable = fcall.args.single()
if (variable is IdentifierReference) {
val decl = variable.targetVarDecl(program.namespace)!!
val varName = asmgen.asmIdentifierName(variable)
@ -560,7 +561,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" pla | tay | pla | tax | pla | plp")
}
else -> {
translateFunctionArguments(fcall.arglist, func)
translateFunctionArguments(fcall.args, func)
asmgen.out(" jsr prog8_lib.func_$functionName")
}
}

View File

@ -3,8 +3,13 @@ package prog8.compiler.target.c64.codegen
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.toHex
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS2_HEX
import prog8.functions.BuiltinFunctions
import kotlin.math.absoluteValue
@ -42,9 +47,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
// result value in cpu or status registers, put it on the stack
if (reg.registerOrPair != null) {
when (reg.registerOrPair) {
RegisterOrPair.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
RegisterOrPair.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
RegisterOrPair.AY -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | tya | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
RegisterOrPair.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
RegisterOrPair.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
RegisterOrPair.AY -> asmgen.out(" sta $ESTACK_LO_HEX,x | tya | sta $ESTACK_HI_HEX,x | dex")
RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't push X register - use a variable")
}
}
@ -60,7 +65,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
DataType.UBYTE -> {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> asmgen.out(" lda #0 | sta ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x")
DataType.UWORD, DataType.WORD -> asmgen.out(" lda #0 | sta $ESTACK_HI_PLUS1_HEX,x")
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
@ -69,7 +74,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
DataType.BYTE -> {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> asmgen.out(" lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | ${asmgen.signExtendAtoMsb("${MachineDefinition.ESTACK_HI_PLUS1_HEX},x")}")
DataType.UWORD, DataType.WORD -> asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | ${asmgen.signExtendAtoMsb("$ESTACK_HI_PLUS1_HEX,x")}")
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_b2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
@ -111,35 +116,35 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
private fun translateExpression(expr: AddressOf) {
val name = asmgen.asmIdentifierName(expr.identifier)
asmgen.out(" lda #<$name | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda #>$name | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
asmgen.out(" lda #<$name | sta $ESTACK_LO_HEX,x | lda #>$name | sta $ESTACK_HI_HEX,x | dex")
}
private fun translateExpression(expr: DirectMemoryRead) {
when(expr.addressExpression) {
is NumericLiteralValue -> {
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
asmgen.out(" lda ${address.toHex()} | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
asmgen.out(" lda ${address.toHex()} | sta $ESTACK_LO_HEX,x | dex")
}
is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | dex")
}
else -> {
translateExpression(expr.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address")
asmgen.out(" sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x")
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
}
}
}
private fun translateExpression(expr: NumericLiteralValue) {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta $ESTACK_LO_HEX,x | dex")
DataType.UWORD, DataType.WORD -> asmgen.out("""
lda #<${expr.number.toHex()}
sta ${MachineDefinition.ESTACK_LO_HEX},x
sta $ESTACK_LO_HEX,x
lda #>${expr.number.toHex()}
sta ${MachineDefinition.ESTACK_HI_HEX},x
sta $ESTACK_HI_HEX,x
dex
""")
DataType.FLOAT -> {
@ -152,9 +157,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
private fun translateExpression(expr: RegisterExpr) {
when(expr.register) {
Register.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
Register.X -> throw AssemblyError("cannot push X - use a variable instead of the X register")
Register.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
Register.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
}
}
@ -162,17 +167,17 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
val varname = asmgen.asmIdentifierName(expr)
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
asmgen.out(" lda $varname | sta $ESTACK_LO_HEX,x | dex")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $varname+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
}
in ArrayDatatypes, in StringDatatypes -> {
asmgen.out(" lda #<$varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda #>$varname | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
asmgen.out(" lda $varname | sta $ESTACK_LO_HEX,x | lda $varname+1 | sta $ESTACK_HI_HEX,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
}
in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta $ESTACK_LO_HEX,x | lda #>$varname | sta $ESTACK_HI_HEX,x | dex")
}
else -> throw AssemblyError("stack push weird variable type $expr")
}
}
@ -196,10 +201,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
translateExpression(expr.left)
val amount = expr.right.constValue(program)!!.number.toInt()
when (leftDt) {
DataType.UBYTE -> repeat(amount) { asmgen.out(" lsr ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
DataType.BYTE -> repeat(amount) { asmgen.out(" lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | asl a | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
DataType.UWORD -> repeat(amount) { asmgen.out(" lsr ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
DataType.WORD -> repeat(amount) { asmgen.out(" lda ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | asl a | ror ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
DataType.UBYTE -> repeat(amount) { asmgen.out(" lsr $ESTACK_LO_PLUS1_HEX,x") }
DataType.BYTE -> repeat(amount) { asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x") }
DataType.UWORD -> repeat(amount) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
DataType.WORD -> repeat(amount) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
else -> throw AssemblyError("weird type")
}
return
@ -209,9 +214,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
translateExpression(expr.left)
val amount = expr.right.constValue(program)!!.number.toInt()
if (leftDt in ByteDatatypes)
repeat(amount) { asmgen.out(" asl ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x") }
else
repeat(amount) { asmgen.out(" asl ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | rol ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x") }
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
return
}
"*" -> {
@ -297,9 +302,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
when(type) {
in ByteDatatypes ->
asmgen.out("""
lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
lda $ESTACK_LO_PLUS1_HEX,x
eor #255
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
sta $ESTACK_LO_PLUS1_HEX,x
""")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
else -> throw AssemblyError("weird type")
@ -326,10 +331,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
val indexValue = index.number.toInt() * elementDt.memorySize()
when(elementDt) {
in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | dex")
}
in WordDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $arrayVarName+$indexValue+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | lda $arrayVarName+$indexValue+1 | sta $ESTACK_HI_HEX,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
@ -353,18 +358,18 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(" jsr prog8_lib.remainder_ub")
}
"+" -> asmgen.out("""
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
lda $ESTACK_LO_PLUS2_HEX,x
clc
adc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
adc $ESTACK_LO_PLUS1_HEX,x
inx
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
sta $ESTACK_LO_PLUS1_HEX,x
""")
"-" -> asmgen.out("""
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
lda $ESTACK_LO_PLUS2_HEX,x
sec
sbc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
sbc $ESTACK_LO_PLUS1_HEX,x
inx
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
sta $ESTACK_LO_PLUS1_HEX,x
""")
"<<", ">>" -> throw AssemblyError("bit-shifts not via stack")
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")

View File

@ -8,10 +8,11 @@ import prog8.ast.expressions.RangeExpr
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.ForLoop
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.toHex
import prog8.compiler.AssemblyError
import kotlin.math.absoluteValue
// todo choose more efficient comparisons to avoid needless lda's
@ -317,7 +318,7 @@ $endLabel inx""")
val iterableName = asmgen.asmIdentifierName(ident)
val decl = ident.targetVarDecl(program.namespace)!!
when(iterableDt) {
DataType.STR, DataType.STR_S -> {
DataType.STR -> {
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.out("""

View File

@ -7,8 +7,11 @@ import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Subroutine
import prog8.ast.statements.SubroutineParameter
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.toHex
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -20,8 +23,8 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
val subName = asmgen.asmIdentifierName(stmt.target)
if(stmt.arglist.isNotEmpty()) {
for(arg in sub.parameters.withIndex().zip(stmt.arglist)) {
if(stmt.args.isNotEmpty()) {
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
translateFuncArguments(arg.first, arg.second, sub)
}
}
@ -139,7 +142,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.translateExpression(value)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda $ESTACK_LO_HEX,x
beq +
sec
bcs ++
@ -166,9 +169,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
else -> {
asmgen.translateExpression(value)
when(register) {
RegisterOrPair.A -> asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
RegisterOrPair.Y -> asmgen.out(" inx | ldy ${MachineDefinition.ESTACK_LO_HEX},x")
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
else -> throw AssemblyError("cannot assign to register pair")
}
}
@ -211,7 +214,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
throw AssemblyError("can't use X register here - use a variable")
else if (register == RegisterOrPair.AY)
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | ldy ${MachineDefinition.ESTACK_HI_HEX},x")
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
}
}
}
@ -225,9 +228,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// we have a special rule for some types.
// strings are assignable to UWORD, for example, and vice versa
if(argType in StringDatatypes && paramType==DataType.UWORD)
if(argType==DataType.STR && paramType==DataType.UWORD)
return true
if(argType==DataType.UWORD && paramType in StringDatatypes)
if(argType==DataType.UWORD && paramType == DataType.STR)
return true
return false

View File

@ -6,8 +6,10 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.RegisterExpr
import prog8.ast.statements.PostIncrDecr
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.toHex
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translate(stmt: PostIncrDecr) {
@ -119,9 +121,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
}
private fun incrDecrArrayvalueWithIndexA(incr: Boolean, arrayDt: DataType, arrayVarName: String) {
asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_REG_X} | tax")
asmgen.out(" stx ${C64Zeropage.SCRATCH_REG_X} | tax")
when(arrayDt) {
DataType.STR, DataType.STR_S,
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out(if(incr) " inc $arrayVarName,x" else " dec $arrayVarName,x")
}
@ -142,7 +144,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
}
else -> throw AssemblyError("weird array dt")
}
asmgen.out(" ldx ${MachineDefinition.C64Zeropage.SCRATCH_REG_X}")
asmgen.out(" ldx ${C64Zeropage.SCRATCH_REG_X}")
}
}

View File

@ -88,7 +88,7 @@ val BuiltinFunctions = mapOf(
BuiltinFunctionParam("address", IterableDatatypes + DataType.UWORD),
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.UBYTE, ::builtinStrlen)
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen)
)
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
@ -121,8 +121,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
if(!idt.isKnown)
throw FatalAstException("couldn't determine type of iterable $arglist")
return when(val dt = idt.typeOrElse(DataType.STRUCT)) {
in NumericDatatypes -> dt
in StringDatatypes -> dt
DataType.STR, in NumericDatatypes -> dt
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
}
@ -145,8 +144,8 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
}
"max", "min" -> {
when(val dt = datatypeFromIterableArg(args.single())) {
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
in NumericDatatypes -> InferredTypes.knownFor(dt)
in StringDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(dt))
else -> InferredTypes.unknown()
}
@ -159,7 +158,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
DataType.ARRAY_UB, DataType.ARRAY_UW -> InferredTypes.knownFor(DataType.UWORD)
DataType.ARRAY_B, DataType.ARRAY_W -> InferredTypes.knownFor(DataType.WORD)
DataType.ARRAY_F -> InferredTypes.knownFor(DataType.FLOAT)
in StringDatatypes -> InferredTypes.knownFor(DataType.UWORD)
DataType.STR -> InferredTypes.knownFor(DataType.UWORD)
else -> InferredTypes.unknown()
}
}
@ -232,7 +231,7 @@ private fun builtinStrlen(args: List<Expression>, position: Position, program: P
if (args.size != 1)
throw SyntaxError("strlen requires one argument", position)
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
if(argument.type !in StringDatatypes)
if(argument.type != DataType.STR)
throw SyntaxError("strlen must have string argument", position)
throw NotConstArgumentException() // this function is not considering the string argument a constant
@ -269,7 +268,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
throw CompilerException("array length exceeds byte limit ${target.position}")
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
}
in StringDatatypes -> {
DataType.STR -> {
val refLv = target.value as StringLiteralValue
if(refLv.value.length>255)
throw CompilerException("string length exceeds byte limit ${refLv.position}")

View File

@ -54,15 +54,15 @@ class ConstExprEvaluator {
private fun logicalxor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute $left locical-bitxor $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toDouble() != 0.0), left.position)
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toDouble() != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toInt() != 0), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toDouble() != 0.0), left.position)
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toDouble() != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -71,15 +71,15 @@ class ConstExprEvaluator {
private fun logicalor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute $left locical-or $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toDouble() != 0.0, left.position)
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toDouble() != 0.0, left.position)
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -88,15 +88,15 @@ class ConstExprEvaluator {
private fun logicaland(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute $left locical-and $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toDouble() != 0.0, left.position)
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toDouble() != 0.0, left.position)
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -144,15 +144,15 @@ class ConstExprEvaluator {
private fun power(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot calculate $left ** $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number.toDouble()), left.position)
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number.toDouble()), left.position)
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toInt()), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toDouble()), left.position)
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toInt()), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toDouble()), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -161,15 +161,15 @@ class ConstExprEvaluator {
private fun plus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot add $left and $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position)
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -178,15 +178,15 @@ class ConstExprEvaluator {
private fun minus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot subtract $left and $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toDouble(), left.position)
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -195,15 +195,15 @@ class ConstExprEvaluator {
private fun multiply(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot multiply ${left.type} and ${right.type}"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toDouble(), left.position)
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -215,25 +215,25 @@ class ConstExprEvaluator {
private fun divide(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot divide $left by $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> {
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
val result: Int = left.number.toInt() / right.number.toInt()
NumericLiteralValue.optimalNumeric(result, left.position)
}
right.type == DataType.FLOAT -> {
DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toInt() / right.number.toDouble(), left.position)
}
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> {
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toInt(), left.position)
}
right.type == DataType.FLOAT -> {
DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toDouble(), left.position)
}
@ -245,24 +245,24 @@ class ConstExprEvaluator {
private fun remainder(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute remainder of $left by $right"
return when {
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> {
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
}
right.type == DataType.FLOAT -> {
DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toInt() % right.number.toDouble(), left.position)
}
else -> throw ExpressionError(error, left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> {
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toInt(), left.position)
}
right.type == DataType.FLOAT -> {
DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toDouble(), left.position)
}

View File

@ -5,11 +5,10 @@ import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.processing.fixupArrayDatatype
import prog8.ast.processing.fixupArrayEltDatatypesFromVardecl
import prog8.ast.processing.fixupArrayEltDatatypes
import prog8.ast.statements.*
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.codegen.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.functions.BuiltinFunctions
import kotlin.math.floor
@ -65,12 +64,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return super.visit(decl)
}
}
in StringDatatypes -> {
// nothing to do for strings
}
DataType.STRUCT -> {
// struct defintions don't have anything else in them
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
val numericLv = decl.value as? NumericLiteralValue
val rangeExpr = decl.value as? RangeExpr
@ -124,7 +117,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
val refValue = ArrayLiteralValue(decl.datatype, array, position = numericLv.position)
refValue.addToHeap()
decl.value = refValue
refValue.parent=decl
optimizationsDone++
@ -140,13 +132,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
} else {
// arraysize initializer is a single int, and we know the size.
val fillvalue = litval.number.toDouble()
if (fillvalue < FLOAT_MAX_NEGATIVE || fillvalue > FLOAT_MAX_POSITIVE)
if (fillvalue < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.machine.FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval.position))
else {
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
val refValue = ArrayLiteralValue(DataType.ARRAY_F, array, position = litval.position)
refValue.addToHeap()
decl.value = refValue
refValue.parent=decl
optimizationsDone++
@ -156,6 +147,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
}
else -> {
// nothing to do for this type
// this includes strings and structs
}
}
}
@ -178,13 +170,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return try {
val cval = identifier.constValue(program) ?: return identifier
return when {
cval.type in NumericDatatypes -> {
return when (cval.type) {
in NumericDatatypes -> {
val copy = NumericLiteralValue(cval.type, cval.number, identifier.position)
copy.parent = identifier.parent
copy
}
cval.type in PassByReferenceDatatypes -> throw AssemblyError("pass-by-reference type should not be considered a constant")
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
else -> identifier
}
} catch (ax: AstException) {
@ -215,12 +207,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
val builtinFunction = BuiltinFunctions[functionCall.target.nameInSource.single()]
if(builtinFunction!=null) {
// match the arguments of a builtin function signature.
for(arg in functionCall.arglist.withIndex().zip(builtinFunction.parameters)) {
for(arg in functionCall.args.withIndex().zip(builtinFunction.parameters)) {
val possibleDts = arg.second.possibleDatatypes
val argConst = arg.first.value.constValue(program)
if(argConst!=null && argConst.type !in possibleDts) {
val convertedValue = argConst.cast(possibleDts.first())
functionCall.arglist[arg.first.index] = convertedValue
functionCall.args[arg.first.index] = convertedValue
optimizationsDone++
}
}
@ -231,12 +223,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
val subroutine = functionCall.target.targetSubroutine(program.namespace)
if(subroutine!=null) {
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
for(arg in functionCall.arglist.withIndex().zip(subroutine.parameters)) {
for(arg in functionCall.args.withIndex().zip(subroutine.parameters)) {
val expectedDt = arg.second.type
val argConst = arg.first.value.constValue(program)
if(argConst!=null && argConst.type!=expectedDt) {
val convertedValue = argConst.cast(expectedDt)
functionCall.arglist[arg.first.index] = convertedValue
functionCall.args[arg.first.index] = convertedValue
optimizationsDone++
}
}
@ -265,27 +257,27 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
val subexpr = prefixExpr.expression
if (subexpr is NumericLiteralValue) {
// accept prefixed literal values (such as -3, not true)
return when {
prefixExpr.operator == "+" -> subexpr
prefixExpr.operator == "-" -> when {
subexpr.type in IntegerDatatypes -> {
return when (prefixExpr.operator) {
"+" -> subexpr
"-" -> when (subexpr.type) {
in IntegerDatatypes -> {
optimizationsDone++
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
}
subexpr.type == DataType.FLOAT -> {
DataType.FLOAT -> {
optimizationsDone++
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position)
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
prefixExpr.operator == "~" -> when {
subexpr.type in IntegerDatatypes -> {
"~" -> when (subexpr.type) {
in IntegerDatatypes -> {
optimizationsDone++
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
prefixExpr.operator == "not" -> {
"not" -> {
optimizationsDone++
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
}
@ -593,7 +585,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
if(array2!=null && array2!==array) {
forLoop2.iterable = array2
array2.linkParents(forLoop2)
array2.addToHeap()
}
}
@ -640,13 +631,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
val array = super.visit(arrayLiteral)
if(array is ArrayLiteralValue) {
array.addToHeap()
val vardecl = array.parent as? VarDecl
return if (vardecl!=null) {
fixupArrayDatatype(array, vardecl, program)
fixupArrayEltDatatypesFromVardecl(array, vardecl)
} else {
// it's not an array associated with a vardecl, attempt to guess the data type from the array values
fixupArrayDatatype(array, program)
fixupArrayEltDatatypes(array, program)
}
}
return array

View File

@ -9,8 +9,7 @@ import prog8.ast.expressions.*
import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.processing.IAstVisitor
import prog8.ast.statements.*
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.functions.BuiltinFunctions
import kotlin.math.floor
@ -170,7 +169,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
val arg = functionCallStatement.arglist.single()
val arg = functionCallStatement.args.single()
val stringVar: IdentifierReference?
stringVar = if(arg is AddressOf) {
arg.identifier
@ -180,20 +179,23 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
if(stringVar!=null) {
val vardecl = stringVar.targetVarDecl(program.namespace)!!
val string = vardecl.value!! as StringLiteralValue
val encodedString = Petscii.encodePetscii(string.value, true)
if(string.value.length==1) {
functionCallStatement.arglist.clear()
functionCallStatement.arglist.add(NumericLiteralValue.optimalInteger(encodedString[0].toInt(), functionCallStatement.position))
val firstCharEncoded = CompilationTarget.encodeString(string.value)[0]
functionCallStatement.args.clear()
functionCallStatement.args.add(NumericLiteralValue.optimalInteger(firstCharEncoded.toInt(), functionCallStatement.position))
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
vardeclsToRemove.add(vardecl)
optimizationsDone++
return functionCallStatement
} else if(string.value.length==2) {
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2))
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(NumericLiteralValue.optimalInteger(encodedString[0].toInt(), functionCallStatement.position)), functionCallStatement.position))
mutableListOf(NumericLiteralValue.optimalInteger(firstTwoCharsEncoded[0].toInt(), functionCallStatement.position)),
functionCallStatement.void, functionCallStatement.position))
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(NumericLiteralValue.optimalInteger(encodedString[1].toInt(), functionCallStatement.position)), functionCallStatement.position))
mutableListOf(NumericLiteralValue.optimalInteger(firstTwoCharsEncoded[1].toInt(), functionCallStatement.position)),
functionCallStatement.void, functionCallStatement.position))
vardeclsToRemove.add(vardecl)
optimizationsDone++
return scope
@ -209,7 +211,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Jump && first.identifier!=null) {
optimizationsDone++
return FunctionCallStatement(first.identifier, functionCallStatement.arglist, functionCallStatement.position)
return FunctionCallStatement(first.identifier, functionCallStatement.args, functionCallStatement.void, functionCallStatement.position)
}
if(first is ReturnFromIrq || first is Return) {
optimizationsDone++
@ -229,7 +231,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Jump && first.identifier!=null) {
optimizationsDone++
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
return FunctionCall(first.identifier, functionCall.args, functionCall.position)
}
if(first is Return && first.value!=null) {
val constval = first.value?.constValue(program)
@ -261,7 +263,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
// always true -> keep only if-part
printWarning("condition is always true", ifStatement.position)
printWarning("condition is always true", ifStatement.position) // TODO don't warn this if the condition is just the single value 'true'
optimizationsDone++
ifStatement.truepart
} else {
@ -310,19 +312,20 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
// always true -> print a warning, and optimize into body + jump (if there are no continue and break statements)
printWarning("condition is always true", whileLoop.position)
printWarning("condition is always true", whileLoop.condition.position)
if(hasContinueOrBreak(whileLoop.body))
return whileLoop
val label = Label("_prog8_back", whileLoop.condition.position)
val backLabelName = "_prog8_back${whileLoop.position.line}"
val label = Label(backLabelName, whileLoop.condition.position)
whileLoop.body.statements.add(0, label)
whileLoop.body.statements.add(Jump(null,
IdentifierReference(listOf("_prog8_back"), whileLoop.condition.position),
IdentifierReference(listOf(backLabelName), whileLoop.condition.position),
null, whileLoop.condition.position))
optimizationsDone++
return whileLoop.body
} else {
// always false -> ditch whole statement
printWarning("condition is always false", whileLoop.position)
printWarning("condition is always false", whileLoop.condition.position)
optimizationsDone++
NopStatement.insteadOf(whileLoop)
}
@ -336,7 +339,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
// always true -> keep only the statement block (if there are no continue and break statements)
printWarning("condition is always true", repeatLoop.position)
printWarning("condition is always true", repeatLoop.untilCondition.position)
if(hasContinueOrBreak(repeatLoop.body))
repeatLoop
else {
@ -345,13 +348,14 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
}
} else {
// always false -> print a warning, and optimize into body + jump (if there are no continue and break statements)
printWarning("condition is always false", repeatLoop.position)
printWarning("condition is always false", repeatLoop.untilCondition.position)
if(hasContinueOrBreak(repeatLoop.body))
return repeatLoop
val label = Label("__back", repeatLoop.untilCondition.position)
val backLabelName = "_prog8_back${repeatLoop.position.line}"
val label = Label(backLabelName, repeatLoop.untilCondition.position)
repeatLoop.body.statements.add(0, label)
repeatLoop.body.statements.add(Jump(null,
IdentifierReference(listOf("__back"), repeatLoop.untilCondition.position),
IdentifierReference(listOf(backLabelName), repeatLoop.untilCondition.position),
null, repeatLoop.untilCondition.position))
optimizationsDone++
return repeatLoop.body
@ -430,7 +434,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
}
val targetIDt = assignment.target.inferType(program, assignment)
if(!targetIDt.isKnown)
throw AssemblyError("can't infer type of assignment target")
throw FatalAstException("can't infer type of assignment target")
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
val bexpr=assignment.value as? BinaryExpression
if(bexpr!=null) {
@ -516,7 +520,8 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
val scope = AnonymousScope(mutableListOf(), assignment.position)
var numshifts = cv.toInt()
while (numshifts > 0) {
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsl"), assignment.position), mutableListOf(bexpr.left), assignment.position))
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsl"), assignment.position),
mutableListOf(bexpr.left), true, assignment.position))
numshifts--
}
optimizationsDone++
@ -537,7 +542,8 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
val scope = AnonymousScope(mutableListOf(), assignment.position)
var numshifts = cv.toInt()
while (numshifts > 0) {
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsr"), assignment.position), mutableListOf(bexpr.left), assignment.position))
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsr"), assignment.position),
mutableListOf(bexpr.left), true, assignment.position))
numshifts--
}
optimizationsDone++

View File

@ -6,7 +6,6 @@ import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.StringLiteralValue
import prog8.compiler.target.c64.Petscii
import prog8.vm.astvm.VmExecutionException
import java.util.Objects
import kotlin.math.abs
@ -571,20 +570,14 @@ class RuntimeValueNumeric(type: DataType, num: Number): RuntimeValueBase(type) {
}
class RuntimeValueString(type: DataType, val str: String, val heapId: Int?): RuntimeValueBase(type) {
class RuntimeValueString(val str: String, val heapId: Int?): RuntimeValueBase(DataType.STR) {
companion object {
fun fromLv(string: StringLiteralValue): RuntimeValueString {
return RuntimeValueString(string.type, string.value, string.heapId!!)
return RuntimeValueString(string.value, string.heapId)
}
}
override fun toString(): String {
return when (type) {
DataType.STR -> "str:$str"
DataType.STR_S -> "str_s:$str"
else -> "???"
}
}
override fun toString(): String = if(type==DataType.STR) "str:$str" else "???"
override fun hashCode(): Int = Objects.hash(type, str)
@ -594,7 +587,7 @@ class RuntimeValueString(type: DataType, val str: String, val heapId: Int?): Run
return type == other.type && str == other.str
}
fun iterator(): Iterator<Number> = Petscii.encodePetscii(str, true).iterator()
fun iterator(): Iterator<Number> = str.map { it.toShort() }.iterator()
override fun numericValue(): Number {
throw VmExecutionException("string is not a number")
@ -612,17 +605,17 @@ open class RuntimeValueArray(type: DataType, val array: Array<Number>, val heapI
fun fromLv(array: ArrayLiteralValue): RuntimeValueArray {
return if (array.type == DataType.ARRAY_F) {
val doubleArray = array.value.map { (it as NumericLiteralValue).number }.toTypedArray()
RuntimeValueArray(array.type, doubleArray, array.heapId!!)
RuntimeValueArray(array.type, doubleArray, array.heapId)
} else {
val resultArray = mutableListOf<Number>()
for (elt in array.value.withIndex()) {
if (elt.value is NumericLiteralValue)
resultArray.add((elt.value as NumericLiteralValue).number.toInt())
else {
TODO("ADDRESSOF ${elt.value}")
resultArray.add((elt.hashCode())) // ...poor man's implementation of ADDRESSOF(array), it probably won't work very well
}
}
RuntimeValueArray(array.type, resultArray.toTypedArray(), array.heapId!!)
RuntimeValueArray(array.type, resultArray.toTypedArray(), array.heapId)
}
}
}

View File

@ -7,11 +7,9 @@ import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.*
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.vm.*
import java.awt.EventQueue
import java.io.CharConversionException
import java.util.ArrayDeque
import kotlin.NoSuchElementException
import kotlin.concurrent.fixedRateTimer
@ -129,7 +127,7 @@ class RuntimeVariables {
}
class AstVm(val program: Program) {
class AstVm(val program: Program, compilationTarget: String) {
val mem = Memory(::memread, ::memwrite)
val statusflags = StatusFlags()
@ -147,6 +145,8 @@ class AstVm(val program: Program) {
init {
require(compilationTarget == "c64") {"using the AstVm only works for the C64 compiler target"}
// observe the jiffyclock and screen matrix
mem.observe(0xa0, 0xa1, 0xa2)
for(i in 1024..2023)
@ -182,7 +182,7 @@ class AstVm(val program: Program) {
if(address in 1024..2023) {
// write to the screen matrix
val scraddr = address-1024
dialog.canvas.setChar(scraddr % 40, scraddr / 40, value, 1)
dialog.canvas.setScreenChar(scraddr % 40, scraddr / 40, value, 1)
}
return value
}
@ -238,7 +238,7 @@ class AstVm(val program: Program) {
}
}
}
dialog.canvas.printText("\n<program ended>", true)
dialog.canvas.printAsciiText("\n<program ended>")
println("PROGRAM EXITED!")
dialog.title = "PROGRAM EXITED"
} catch (tx: VmTerminationException) {
@ -339,10 +339,9 @@ class AstVm(val program: Program) {
// should have been defined already when the program started
}
is FunctionCallStatement -> {
val target = stmt.target.targetStatement(program.namespace)
when (target) {
when (val target = stmt.target.targetStatement(program.namespace)) {
is Subroutine -> {
val args = evaluate(stmt.arglist).map { it as RuntimeValueNumeric }
val args = evaluate(stmt.args).map { it as RuntimeValueNumeric }
if (target.isAsmSubroutine) {
performSyscall(target, args)
} else {
@ -355,7 +354,7 @@ class AstVm(val program: Program) {
// swap cannot be implemented as a function, so inline it here
executeSwap(stmt)
} else {
val args = evaluate(stmt.arglist).map { it as RuntimeValueNumeric }
val args = evaluate(stmt.args)
performBuiltinFunction(target.name, args, statusflags)
}
}
@ -388,18 +387,18 @@ class AstVm(val program: Program) {
when(ident.type){
VarDeclType.VAR -> {
var value = runtimeVariables.get(identScope, ident.name) as RuntimeValueNumeric
value = when {
stmt.operator == "++" -> value.add(RuntimeValueNumeric(value.type, 1))
stmt.operator == "--" -> value.sub(RuntimeValueNumeric(value.type, 1))
value = when (stmt.operator) {
"++" -> value.add(RuntimeValueNumeric(value.type, 1))
"--" -> value.sub(RuntimeValueNumeric(value.type, 1))
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
runtimeVariables.set(identScope, ident.name, value)
}
VarDeclType.MEMORY -> {
val addr=ident.value!!.constValue(program)!!.number.toInt()
val newval = when {
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
val newval = when (stmt.operator) {
"++" -> mem.getUByte(addr)+1 and 255
"--" -> mem.getUByte(addr)-1 and 255
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
mem.setUByte(addr,newval.toShort())
@ -409,9 +408,9 @@ class AstVm(val program: Program) {
}
stmt.target.memoryAddress != null -> {
val addr = (evaluate(stmt.target.memoryAddress!!.addressExpression, evalCtx) as RuntimeValueNumeric).integerValue()
val newval = when {
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
val newval = when (stmt.operator) {
"++" -> mem.getUByte(addr)+1 and 255
"--" -> mem.getUByte(addr)-1 and 255
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
mem.setUByte(addr,newval.toShort())
@ -424,18 +423,18 @@ class AstVm(val program: Program) {
if(!elementType.isKnown)
throw VmExecutionException("unknown/void elt type")
var value = RuntimeValueNumeric(elementType.typeOrElse(DataType.BYTE), arrayvalue.array[index].toInt())
value = when {
stmt.operator == "++" -> value.inc()
stmt.operator == "--" -> value.dec()
value = when (stmt.operator) {
"++" -> value.inc()
"--" -> value.dec()
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
arrayvalue.array[index] = value.numericValue()
}
stmt.target.register != null -> {
var value = runtimeVariables.get(program.namespace, stmt.target.register!!.name) as RuntimeValueNumeric
value = when {
stmt.operator == "++" -> value.add(RuntimeValueNumeric(value.type, 1))
stmt.operator == "--" -> value.sub(RuntimeValueNumeric(value.type, 1))
value = when (stmt.operator) {
"++" -> value.add(RuntimeValueNumeric(value.type, 1))
"--" -> value.sub(RuntimeValueNumeric(value.type, 1))
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
runtimeVariables.set(program.namespace, stmt.target.register!!.name, value)
@ -551,8 +550,8 @@ class AstVm(val program: Program) {
}
private fun executeSwap(swap: FunctionCallStatement) {
val v1 = swap.arglist[0]
val v2 = swap.arglist[1]
val v1 = swap.args[0]
val v2 = swap.args[1]
val value1 = evaluate(v1, evalCtx)
val value2 = evaluate(v2, evalCtx)
val target1 = AssignTarget.fromExpr(v1)
@ -577,7 +576,6 @@ class AstVm(val program: Program) {
DataType.WORD -> mem.setSWord(address, (value as RuntimeValueNumeric).wordval!!)
DataType.FLOAT -> mem.setFloat(address, (value as RuntimeValueNumeric).floatval!!)
DataType.STR -> mem.setString(address, (value as RuntimeValueString).str)
DataType.STR_S -> mem.setScreencodeString(address, (value as RuntimeValueString).str)
else -> throw VmExecutionException("weird memaddress type $decl")
}
} else
@ -613,7 +611,7 @@ class AstVm(val program: Program) {
if (value.type != DataType.FLOAT)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
DataType.STR, DataType.STR_S -> {
DataType.STR -> {
if (value.type !in ByteDatatypes)
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
}
@ -621,14 +619,14 @@ class AstVm(val program: Program) {
}
if (array.type in ArrayDatatypes)
(array as RuntimeValueArray).array[index.integerValue()] = value.numericValue()
else if (array.type in StringDatatypes) {
else if (array.type == DataType.STR) {
val indexInt = index.integerValue()
val newchr = Petscii.decodePetscii(listOf(value.numericValue().toShort()), true)
val newchr = value.numericValue().toChar().toString()
val newstr = (array as RuntimeValueString).str.replaceRange(indexInt, indexInt + 1, newchr)
val ident = contextStmt.definingScope().lookup(targetArrayIndexed.identifier.nameInSource, contextStmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
val identScope = ident.definingScope()
runtimeVariables.set(identScope, ident.name, RuntimeValueString(array.type, newstr, array.heapId))
runtimeVariables.set(identScope, ident.name, RuntimeValueString(newstr, array.heapId))
}
}
else {
@ -643,7 +641,7 @@ class AstVm(val program: Program) {
DataType.BYTE -> mem.setSByte(address+index, value.byteval!!)
DataType.UWORD -> mem.setUWord(address+index*2, value.wordval!!)
DataType.WORD -> mem.setSWord(address+index*2, value.wordval!!)
DataType.FLOAT -> mem.setFloat(address+index* MachineDefinition.Mflpt5.MemorySize, value.floatval!!)
DataType.FLOAT -> mem.setFloat(address+index* C64MachineDefinition.FLOAT_MEM_SIZE, value.floatval!!)
else -> throw VmExecutionException("strange array elt type $elementType")
}
}
@ -670,48 +668,48 @@ class AstVm(val program: Program) {
"c64scr.print" -> {
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId)
if (args[0].wordval != null) {
val encodedStr = getEncodedStringFromRuntimeVars(args[0].wordval!!)
dialog.canvas.printText(encodedStr)
val string = getAsciiStringFromRuntimeVars(args[0].wordval!!)
dialog.canvas.printAsciiText(string)
} else
throw VmExecutionException("print non-heap string")
}
"c64scr.print_ub" -> {
dialog.canvas.printText(args[0].byteval!!.toString(), true)
dialog.canvas.printAsciiText(args[0].byteval!!.toString())
}
"c64scr.print_ub0" -> {
dialog.canvas.printText("%03d".format(args[0].byteval!!), true)
dialog.canvas.printAsciiText("%03d".format(args[0].byteval!!))
}
"c64scr.print_b" -> {
dialog.canvas.printText(args[0].byteval!!.toString(), true)
dialog.canvas.printAsciiText(args[0].byteval!!.toString())
}
"c64scr.print_uw" -> {
dialog.canvas.printText(args[0].wordval!!.toString(), true)
dialog.canvas.printAsciiText(args[0].wordval!!.toString())
}
"c64scr.print_uw0" -> {
dialog.canvas.printText("%05d".format(args[0].wordval!!), true)
dialog.canvas.printAsciiText("%05d".format(args[0].wordval!!))
}
"c64scr.print_w" -> {
dialog.canvas.printText(args[0].wordval!!.toString(), true)
dialog.canvas.printAsciiText(args[0].wordval!!.toString())
}
"c64scr.print_ubhex" -> {
val number = args[0].byteval!!
val prefix = if (args[1].asBoolean) "$" else ""
dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", true)
dialog.canvas.printAsciiText("$prefix${number.toString(16).padStart(2, '0')}")
}
"c64scr.print_uwhex" -> {
val number = args[0].wordval!!
val prefix = if (args[1].asBoolean) "$" else ""
dialog.canvas.printText("$prefix${number.toString(16).padStart(4, '0')}", true)
dialog.canvas.printAsciiText("$prefix${number.toString(16).padStart(4, '0')}")
}
"c64scr.print_uwbin" -> {
val number = args[0].wordval!!
val prefix = if (args[1].asBoolean) "%" else ""
dialog.canvas.printText("$prefix${number.toString(2).padStart(16, '0')}", true)
dialog.canvas.printAsciiText("$prefix${number.toString(2).padStart(16, '0')}")
}
"c64scr.print_ubbin" -> {
val number = args[0].byteval!!
val prefix = if (args[1].asBoolean) "%" else ""
dialog.canvas.printText("$prefix${number.toString(2).padStart(8, '0')}", true)
dialog.canvas.printAsciiText("$prefix${number.toString(2).padStart(8, '0')}")
}
"c64scr.clear_screenchars" -> {
dialog.canvas.clearScreen(6)
@ -720,7 +718,7 @@ class AstVm(val program: Program) {
dialog.canvas.clearScreen(args[0].integerValue().toShort())
}
"c64scr.setcc" -> {
dialog.canvas.setChar(args[0].integerValue(), args[1].integerValue(), args[2].integerValue().toShort(), args[3].integerValue().toShort())
dialog.canvas.setScreenChar(args[0].integerValue(), args[1].integerValue(), args[2].integerValue().toShort(), args[3].integerValue().toShort())
}
"c64scr.plot" -> {
dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue())
@ -736,27 +734,23 @@ class AstVm(val program: Program) {
break
else {
input.add(char)
val printChar = try {
Petscii.encodePetscii("" + char, true).first()
} catch (cv: CharConversionException) {
0x3f.toShort()
}
dialog.canvas.printPetscii(printChar)
dialog.canvas.printAsciiText(char.toString())
}
}
val inputStr = input.joinToString("")
var inputStr = input.joinToString("")
val inputLength = inputStr.length
val heapId = args[0].wordval!!
val origStrLength = getEncodedStringFromRuntimeVars(heapId).size
val encodedStr = Petscii.encodePetscii(inputStr, true).take(origStrLength).toMutableList()
while(encodedStr.size < origStrLength)
encodedStr.add(0)
result = RuntimeValueNumeric(DataType.UBYTE, encodedStr.indexOf(0))
val origStrLength = getAsciiStringFromRuntimeVars(heapId).length
while(inputStr.length < origStrLength) {
inputStr += '\u0000'
}
result = RuntimeValueNumeric(DataType.UBYTE, inputLength)
}
"c64flt.print_f" -> {
dialog.canvas.printText(args[0].floatval.toString(), false)
dialog.canvas.printAsciiText(args[0].floatval.toString())
}
"c64.CHROUT" -> {
dialog.canvas.printPetscii(args[0].byteval!!)
dialog.canvas.printPetsciiChar(args[0].byteval!!)
}
"c64.CLEARSCR" -> {
dialog.canvas.clearScreen(6)
@ -768,10 +762,17 @@ class AstVm(val program: Program) {
val char=dialog.keyboardBuffer.pop()
result = RuntimeValueNumeric(DataType.UBYTE, char.toShort())
}
"c64.GETIN" -> {
Thread.sleep(1)
result = if(dialog.keyboardBuffer.isEmpty())
RuntimeValueNumeric(DataType.UBYTE, 0)
else
RuntimeValueNumeric(DataType.UBYTE, dialog.keyboardBuffer.pop().toShort())
}
"c64utils.str2uword" -> {
val heapId = args[0].wordval!!
val argString = getEncodedStringFromRuntimeVars(heapId)
val numericpart = argString.takeWhile { it.toChar().isDigit() }.toString()
val argString = getAsciiStringFromRuntimeVars(heapId)
val numericpart = argString.takeWhile { it.isDigit() }
result = RuntimeValueNumeric(DataType.UWORD, numericpart.toInt() and 65535)
}
else -> TODO("syscall ${sub.scopedname} $sub")
@ -780,14 +781,8 @@ class AstVm(val program: Program) {
return result
}
private fun getEncodedStringFromRuntimeVars(heapId: Int): List<Short> {
val stringvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueString
return when {
stringvar.type==DataType.STR -> Petscii.encodePetscii(stringvar.str, true)
stringvar.type==DataType.STR_S -> Petscii.encodeScreencode(stringvar.str, true)
else -> throw VmExecutionException("weird string type")
}
}
private fun getAsciiStringFromRuntimeVars(heapId: Int): String =
(runtimeVariables.getByHeapId(heapId) as RuntimeValueString).str
private fun getArrayFromRuntimeVars(heapId: Int): IntArray {
val arrayvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueArray

View File

@ -124,8 +124,7 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValueBase {
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, ctx.mem.getUWord(address))
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, ctx.mem.getSWord(address))
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, ctx.mem.getFloat(address))
DataType.STR -> RuntimeValueString(DataType.STR, ctx.mem.getString(address), null)
DataType.STR_S -> RuntimeValueString(DataType.STR_S, ctx.mem.getScreencodeString(address)!!, null)
DataType.STR -> RuntimeValueString(ctx.mem.getString(address), null)
else -> throw VmExecutionException("unexpected datatype $variable")
}
}
@ -135,7 +134,7 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValueBase {
}
is FunctionCall -> {
val sub = expr.target.targetStatement(ctx.program.namespace)
val args = expr.arglist.map { evaluate(it, ctx) as RuntimeValueNumeric }
val args = expr.args.map { evaluate(it, ctx) as RuntimeValueNumeric }
return when(sub) {
is Subroutine -> {
val result = ctx.executeSubroutine(sub, args, null)

View File

@ -1,7 +1,7 @@
package prog8.vm.astvm
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.c64.C64MachineDefinition
import kotlin.math.abs
class Memory(private val readObserver: (address: Int, value: Short) -> Short,
@ -80,7 +80,7 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
}
fun setFloat(address: Int, value: Double) {
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(value)
val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(value)
setUByte(address, mflpt5.b0)
setUByte(address+1, mflpt5.b1)
setUByte(address+2, mflpt5.b2)
@ -89,28 +89,26 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
}
fun getFloat(address: Int): Double {
return MachineDefinition.Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2),
return C64MachineDefinition.Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2),
getUByte(address + 3), getUByte(address + 4)).toDouble()
}
fun setString(address: Int, str: String) {
// lowercase PETSCII
val petscii = Petscii.encodePetscii(str, true)
val encoded = CompilationTarget.encodeString(str)
var addr = address
for (c in petscii) setUByte(addr++, c)
for (c in encoded) setUByte(addr++, c)
setUByte(addr, 0)
}
fun getString(strAddress: Int): String {
// lowercase PETSCII
val petscii = mutableListOf<Short>()
val encoded = mutableListOf<Short>()
var addr = strAddress
while(true) {
val byte = getUByte(addr++)
if(byte==0.toShort()) break
petscii.add(byte)
encoded.add(byte)
}
return Petscii.decodePetscii(petscii, true)
return CompilationTarget.decodeString(encoded)
}
fun clear() {
@ -121,24 +119,4 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
for(i in 0 until numbytes)
setUByte(to+i, getUByte(from+i))
}
fun getScreencodeString(strAddress: Int): String? {
// lowercase Screencodes
val screencodes = mutableListOf<Short>()
var addr = strAddress
while(true) {
val byte = getUByte(addr++)
if(byte==0.toShort()) break
screencodes.add(byte)
}
return Petscii.decodeScreencode(screencodes, true)
}
fun setScreencodeString(address: Int, str: String) {
// lowercase screencodes
val screencodes = Petscii.encodeScreencode(str, true)
var addr = address
for (c in screencodes) setUByte(addr++, c)
setUByte(addr, 0)
}
}

View File

@ -1,6 +1,6 @@
package prog8.vm.astvm
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.Petscii
import java.awt.*
import java.awt.event.KeyEvent
@ -50,41 +50,33 @@ class BitmapScreenPanel : KeyListener, JPanel() {
}
fun clearScreen(color: Short) {
g2d.background = MachineDefinition.colorPalette[color % MachineDefinition.colorPalette.size]
g2d.background = C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size]
g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT)
cursorX = 0
cursorY = 0
}
fun setPixel(x: Int, y: Int, color: Short) {
image.setRGB(x, y, MachineDefinition.colorPalette[color % MachineDefinition.colorPalette.size].rgb)
image.setRGB(x, y, C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size].rgb)
}
fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) {
g2d.color = MachineDefinition.colorPalette[color % MachineDefinition.colorPalette.size]
g2d.color = C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size]
g2d.drawLine(x1, y1, x2, y2)
}
fun printText(text: String, lowercase: Boolean, inverseVideo: Boolean=false) {
fun printAsciiText(text: String) {
val t2 = text.substringBefore(0.toChar())
val lines = t2.split('\n')
for(line in lines.withIndex()) {
val petscii = Petscii.encodePetscii(line.value, lowercase)
petscii.forEach { printPetscii(it, inverseVideo) }
if(line.index<lines.size-1) {
printPetscii(13) // newline
}
}
val petscii = Petscii.encodePetscii(t2, true)
petscii.forEach { printPetsciiChar(it) }
}
fun printText(text: Iterable<Short>) {
text.forEach { printPetscii(it, false) }
}
fun printPetscii(char: Short, inverseVideo: Boolean=false) {
if(char==13.toShort() || char==141.toShort()) {
fun printPetsciiChar(petscii: Short) {
if(petscii in listOf(0x0d.toShort(), 0x8d.toShort())) {
// Return and shift-Return
cursorX=0
cursorY++
} else {
setPetscii(cursorX, cursorY, char, 1, inverseVideo)
val scr = Petscii.petscii2scr(petscii, false)
setScreenChar(cursorX, cursorY, scr, 1)
cursorX++
if (cursorX >= (SCREENWIDTH / 8)) {
cursorY++
@ -98,38 +90,17 @@ class BitmapScreenPanel : KeyListener, JPanel() {
val graphics = image.graphics as Graphics2D
graphics.drawImage(screen, 0, -8, null)
val color = graphics.color
graphics.color = MachineDefinition.colorPalette[6]
graphics.color = C64MachineDefinition.colorPalette[6]
graphics.fillRect(0, 24*8, SCREENWIDTH, 25*8)
graphics.color=color
cursorY--
}
}
fun writeTextAt(x: Int, y: Int, text: String, color: Short, lowercase: Boolean, inverseVideo: Boolean=false) {
val colorIdx = (color % MachineDefinition.colorPalette.size).toShort()
var xx=x
for(clearx in xx until xx+text.length) {
g2d.clearRect(8*clearx, 8*y, 8, 8)
}
for(sc in Petscii.encodePetscii(text, lowercase)) {
if(sc==0.toShort())
break
setPetscii(xx++, y, sc, colorIdx, inverseVideo)
}
}
fun setPetscii(x: Int, y: Int, petscii: Short, color: Short, inverseVideo: Boolean) {
fun setScreenChar(x: Int, y: Int, screencode: Short, color: Short) {
g2d.clearRect(8*x, 8*y, 8, 8)
val colorIdx = (color % MachineDefinition.colorPalette.size).toShort()
val screencode = Petscii.petscii2scr(petscii, inverseVideo)
val coloredImage = MachineDefinition.Charset.getColoredChar(screencode, colorIdx)
g2d.drawImage(coloredImage, 8*x, 8*y , null)
}
fun setChar(x: Int, y: Int, screencode: Short, color: Short) {
g2d.clearRect(8*x, 8*y, 8, 8)
val colorIdx = (color % MachineDefinition.colorPalette.size).toShort()
val coloredImage = MachineDefinition.Charset.getColoredChar(screencode, colorIdx)
val colorIdx = (color % C64MachineDefinition.colorPalette.size).toShort()
val coloredImage = C64MachineDefinition.Charset.getColoredChar(screencode, colorIdx)
g2d.drawImage(coloredImage, 8*x, 8*y , null)
}
@ -163,19 +134,19 @@ class ScreenDialog(title: String) : JFrame(title) {
// the borders (top, left, right, bottom)
val borderTop = JPanel().apply {
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
background = MachineDefinition.colorPalette[14]
background = C64MachineDefinition.colorPalette[14]
}
val borderBottom = JPanel().apply {
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
background = MachineDefinition.colorPalette[14]
background = C64MachineDefinition.colorPalette[14]
}
val borderLeft = JPanel().apply {
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
background = MachineDefinition.colorPalette[14]
background = C64MachineDefinition.colorPalette[14]
}
val borderRight = JPanel().apply {
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
background = MachineDefinition.colorPalette[14]
background = C64MachineDefinition.colorPalette[14]
}
var c = GridBagConstraints()
c.gridx=0; c.gridy=1; c.gridwidth=3

View File

@ -83,8 +83,8 @@ class TestParserNumericLiteralValue {
@Test
fun testEqualsRef() {
assertTrue(StringLiteralValue(DataType.STR, "hello", dummyPos) == StringLiteralValue(DataType.STR, "hello", dummyPos))
assertFalse(StringLiteralValue(DataType.STR, "hello", dummyPos) == StringLiteralValue(DataType.STR, "bye", dummyPos))
assertEquals(StringLiteralValue("hello", dummyPos), StringLiteralValue("hello", dummyPos))
assertNotEquals(StringLiteralValue("hello", dummyPos), StringLiteralValue("bye", dummyPos))
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
@ -93,9 +93,9 @@ class TestParserNumericLiteralValue {
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), null, dummyPos)
val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), null, dummyPos)
val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), null, dummyPos)
val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), dummyPos)
val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), dummyPos)
val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), dummyPos)
assertEquals(lv1, lv2)
assertNotEquals(lv1, lv3)
}

View File

@ -10,10 +10,10 @@ import prog8.ast.base.Position
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.StringLiteralValue
import prog8.compiler.*
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.MachineDefinition.Mflpt5
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
import prog8.compiler.target.c64.Petscii
import prog8.vm.RuntimeValueNumeric
import java.io.CharConversionException
@ -95,29 +95,29 @@ class TestCompiler {
@Test
fun testMflpt5ToFloat() {
val PRECISION=0.000000001
val epsilon=0.000000001
assertThat(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(0.0))
assertThat(Mflpt5(0x82, 0x49, 0x0F, 0xDA, 0xA1).toDouble(), closeTo(3.141592653, PRECISION))
assertThat(Mflpt5(0x82, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(3.141592653589793, PRECISION))
assertThat(Mflpt5(0x82, 0x49, 0x0F, 0xDA, 0xA1).toDouble(), closeTo(3.141592653, epsilon))
assertThat(Mflpt5(0x82, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(3.141592653589793, epsilon))
assertThat(Mflpt5(0x90, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(32768.0))
assertThat(Mflpt5(0x90, 0x80, 0x00, 0x00, 0x00).toDouble(), equalTo(-32768.0))
assertThat(Mflpt5(0x81, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(1.0))
assertThat(Mflpt5(0x80, 0x35, 0x04, 0xF3, 0x34).toDouble(), closeTo(0.7071067812, PRECISION))
assertThat(Mflpt5(0x80, 0x35, 0x04, 0xF3, 0x33).toDouble(), closeTo(0.7071067811865476, PRECISION))
assertThat(Mflpt5(0x81, 0x35, 0x04, 0xF3, 0x34).toDouble(), closeTo(1.4142135624, PRECISION))
assertThat(Mflpt5(0x81, 0x35, 0x04, 0xF3, 0x33).toDouble(), closeTo(1.4142135623730951, PRECISION))
assertThat(Mflpt5(0x80, 0x35, 0x04, 0xF3, 0x34).toDouble(), closeTo(0.7071067812, epsilon))
assertThat(Mflpt5(0x80, 0x35, 0x04, 0xF3, 0x33).toDouble(), closeTo(0.7071067811865476, epsilon))
assertThat(Mflpt5(0x81, 0x35, 0x04, 0xF3, 0x34).toDouble(), closeTo(1.4142135624, epsilon))
assertThat(Mflpt5(0x81, 0x35, 0x04, 0xF3, 0x33).toDouble(), closeTo(1.4142135623730951, epsilon))
assertThat(Mflpt5(0x80, 0x80, 0x00, 0x00, 0x00).toDouble(), equalTo(-.5))
assertThat(Mflpt5(0x80, 0x31, 0x72, 0x17, 0xF8).toDouble(), closeTo(0.69314718061, PRECISION))
assertThat(Mflpt5(0x80, 0x31, 0x72, 0x17, 0xF7).toDouble(), closeTo(0.6931471805599453, PRECISION))
assertThat(Mflpt5(0x80, 0x31, 0x72, 0x17, 0xF8).toDouble(), closeTo(0.69314718061, epsilon))
assertThat(Mflpt5(0x80, 0x31, 0x72, 0x17, 0xF7).toDouble(), closeTo(0.6931471805599453, epsilon))
assertThat(Mflpt5(0x84, 0x20, 0x00, 0x00, 0x00).toDouble(), equalTo(10.0))
assertThat(Mflpt5(0x9E, 0x6E, 0x6B, 0x28, 0x00).toDouble(), equalTo(1000000000.0))
assertThat(Mflpt5(0x80, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(.5))
assertThat(Mflpt5(0x81, 0x38, 0xAA, 0x3B, 0x29).toDouble(), closeTo(1.4426950408889634, PRECISION))
assertThat(Mflpt5(0x81, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(1.5707963267948966, PRECISION))
assertThat(Mflpt5(0x83, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(6.283185307179586, PRECISION))
assertThat(Mflpt5(0x81, 0x38, 0xAA, 0x3B, 0x29).toDouble(), closeTo(1.4426950408889634, epsilon))
assertThat(Mflpt5(0x81, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(1.5707963267948966, epsilon))
assertThat(Mflpt5(0x83, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(6.283185307179586, epsilon))
assertThat(Mflpt5(0x7F, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(.25))
assertThat(Mflpt5(0xd1, 0x02, 0xb7, 0x06, 0xfb).toDouble(), closeTo(123.45678e22, 1.0e15))
assertThat(Mflpt5(0x3e, 0xe9, 0x34, 0x09, 0x1b).toDouble(), closeTo(-123.45678e-22, PRECISION))
assertThat(Mflpt5(0x3e, 0xe9, 0x34, 0x09, 0x1b).toDouble(), closeTo(-123.45678e-22, epsilon))
}
}
@ -369,8 +369,8 @@ class TestPetscii {
assertTrue(ten <= ten)
assertFalse(ten < ten)
val abc = StringLiteralValue(DataType.STR, "abc", Position("", 0, 0, 0))
val abd = StringLiteralValue(DataType.STR, "abd", Position("", 0, 0, 0))
val abc = StringLiteralValue("abc", Position("", 0, 0, 0))
val abd = StringLiteralValue("abd", Position("", 0, 0, 0))
assertEquals(abc, abc)
assertTrue(abc!=abd)
assertFalse(abc!=abc)

View File

@ -5,7 +5,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="jdk" jdkName="Python 3.7 (py3)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.8 virtualenv" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -271,9 +271,8 @@ Strings
Strings are a sequence of characters enclosed in ``"`` quotes. The length is limited to 255 characters.
They're stored and treated much the same as a byte array,
but they have some special properties because they are considered to be *text*.
Strings in your source code files will be encoded (translated from ASCII/UTF-8) into either CBM PETSCII or C-64 screencodes.
PETSCII is the default choice. If you need screencodes (also called 'poke' codes) instead,
you have to use the ``str_s`` variants of the string type identifier.
Strings in your source code files will be encoded (translated from ASCII/UTF-8) into the byte-encoding
that is used on the target platform. For the C-64, this is CBM PETSCII.
You can concatenate two string literals using '+' (not very useful though) or repeat
a string literal a given number of times using '*'::
@ -283,10 +282,10 @@ a string literal a given number of times using '*'::
.. caution::
It's probably best that you don't change strings after they're created.
Avoid changing strings after they've been created.
This is because if your program exits and is restarted (without loading it again),
it will then operate on the changed strings instead of the original ones.
The same is true for arrays by the way.
it will then start working with the changed strings instead of the original ones.
The same is true for arrays.
Structs
@ -608,8 +607,8 @@ Calling a subroutine
^^^^^^^^^^^^^^^^^^^^
The arguments in parentheses after the function name, should match the parameters in the subroutine definition.
It is possible to not store the return value but the compiler
will issue a warning then telling you the result values of a subroutine call are discarded.
If you want to ignore a return value of a subroutine, you should prefix the call with the ``void`` keyword.
Otherwise the compiler will issue a warning about discarding a result value.
.. caution::
Note that due to the way parameters are processed by the compiler,

View File

@ -268,8 +268,6 @@ type identifier type storage size example var declara
``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]``
``str`` string (petscii) varies ``str myvar = "hello."``
implicitly terminated by a 0-byte
``str_s`` string (screencodes) varies ``str_s myvar = "hello."``
implicitly terminated by a 0-byte
=============== ======================= ================= =========================================
**arrays:** you can split an array initializer list over several lines if you want. When an initialization
@ -453,14 +451,17 @@ Subroutine / function calls
You call a subroutine like this::
[ result = ] subroutinename_or_address ( [argument...] )
[ void / result = ] subroutinename_or_address ( [argument...] )
; example:
resultvariable = subroutine(arg1, arg2, arg3)
void noresultvaluesub(arg)
Arguments are separated by commas. The argument list can also be empty if the subroutine
takes no parameters. If the subroutine returns a value, you can still omit the assignment to
a result variable (but the compiler will warn you about discarding the result of the call).
takes no parameters. If the subroutine returns a value, usually you assign it to a variable.
If you're not interested in the return value, prefix the function call with the ``void`` keyword.
Otherwise the compiler will warn you about discarding the result of the call.
Normal subroutines can only return zero or one return values.
However, the special ``asmsub`` routines (implemented in assembly code or referencing

View File

@ -2,6 +2,10 @@
TODO
====
- option to load library files from a directory instead of the embedded ones
- @"zzz" screencode encoded strings + add this to docs too
Memory Block Operations integrated in language?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -13,7 +13,7 @@ sub start() {
c64.MVOL = 15
c64scr.print("will play the music from boulderdash,\nmade in 1984 by peter liepa.\npress enter to start: ")
c64.CHRIN()
void c64.CHRIN()
c64.CLEARSCR()
while(true) {

View File

@ -23,7 +23,7 @@ main {
; use indexed loop to write characters
str bye = "Goodbye!\n"
for char in 0 to len(bye)
for char in 0 to len(bye)-1
c64.CHROUT(bye[char])
@ -40,6 +40,8 @@ main {
c64.CHROUT(':')
c64flt.print_f(clock_seconds)
c64.CHROUT('\n')
c64scr.print("bye!\n")
}
}

View File

@ -14,7 +14,7 @@ main {
c64.VMCSB |= 2 ; switch lowercase chars
c64scr.print("Please introduce yourself: ")
c64scr.input_chars(name)
void c64scr.input_chars(name)
c64scr.print("\n\nHello, ")
c64scr.print(name)
c64scr.print(".\nLet's play a number guessing game.\nI am thinking of a number from 1 to 100!You'll have to guess it!\n")
@ -27,7 +27,7 @@ main {
if attempts_left>1
c64scr.print("es")
c64scr.print(" left.\nWhat is your next guess? ")
c64scr.input_chars(input)
void c64scr.input_chars(input)
ubyte guess = lsb(c64utils.str2uword(input))
if guess==secretnumber {

27
examples/screencodes.p8 Normal file
View File

@ -0,0 +1,27 @@
%import c64lib
%import c64utils
%zeropage basicsafe
main {
sub start() {
c64.VMCSB |= 2 ; switch to lowercase charset
str s1 = "HELLO hello 1234 @[/]\n" ; regular strings have default encoding (petscii on c64)
str s2 = @"HELLO hello 1234 @[/]\n" ; TODO @-strings for alternate encoding (screencode on c64)
c64scr.print("\n\n\n\nString output via print:\n")
c64scr.print(s1)
c64scr.print(s2)
c64scr.print("\nThe top two screen lines are set via screencodes.\n")
ubyte i
for i in 0 to len(s1)-1
@($0400+i) = s1[i]
for i in 0 to len(s2)-1
@($0400+40+i) = s2[i]
}
}

View File

@ -1,13 +1,19 @@
%import c64lib
%import c64flt
%zeropage basicsafe
%option enable_floats
main {
sub start() {
; this is only a parser/compiler test, there's no actual working program
sub start() {
c64scr.print("this is only a parser/compiler test\n")
return
str s1 = "hello"
str s2 = "screencodes" ; TODO as c64sc
str s1 = "irmen"
str_s s2 = "hello"
&str ms1 = $c000
@ -32,7 +38,6 @@ main {
; read array
A=s1[2]
ub=s1[2]
ub=s2[2]
bb=barray[2]
ub=ubarray[2]
ww=warray[2]
@ -47,8 +52,7 @@ main {
fl=mflarray[2]
A=s1[A]
ub=s2[A]
ub=s2[A]
ub=s1[A]
bb=barray[A]
ub=ubarray[A]
ww=warray[A]
@ -64,7 +68,6 @@ main {
A=s1[bb]
ub=s1[bb]
ub=s2[bb]
bb=barray[bb]
ub=ubarray[bb]
ww=warray[bb]
@ -80,7 +83,6 @@ main {
A=s1[bb*3]
ub=s1[bb*3]
ub=s2[bb*3]
bb=barray[bb*3]
ub=ubarray[bb*3]
ww=warray[bb*3]
@ -99,7 +101,6 @@ main {
barray[2]--
s1[2] = A
s1[2] = ub
s2[2] = ub
barray[2] = bb
ubarray[2] = ub
warray[2] = ww
@ -116,7 +117,6 @@ main {
mflarray[2] = fl
s1[A] = ub
s2[A] = ub
barray[A] = bb
ubarray[A] = ub
warray[A] = ww
@ -124,7 +124,6 @@ main {
flarray[A] = fl
s1[bb] = ub
s2[bb] = ub
barray[bb] = bb
ubarray[bb] = ub
warray[bb] = ww
@ -132,7 +131,6 @@ main {
flarray[bb] = fl
s1[bb*3] = ub
s2[bb*3] = ub
barray[bb*3] = bb
ubarray[bb*3] = ub
warray[bb*3] = ww

View File

@ -1,7 +1,4 @@
org.gradle.caching=true
org.gradle.console=rich
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx2500M
org.gradle.daemon=true
kotlinVersion=1.3.50

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

51
gradlew vendored
View File

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
@ -138,19 +154,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@ -159,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

18
gradlew.bat vendored
View File

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

View File

@ -19,6 +19,7 @@ COMMENT : ';' ~[\r\n]* -> channel(HIDDEN) ;
WS : [ \t] -> skip ;
EOL : [\r\n]+ ;
// WS2 : '\\' EOL -> skip;
VOID: 'void';
NAME : [a-zA-Z_][a-zA-Z0-9_]* ;
DEC_INTEGER : ('0'..'9') | (('1'..'9')('0'..'9')+);
HEX_INTEGER : '$' (('a'..'f') | ('A'..'F') | ('0'..'9'))+ ;
@ -126,7 +127,7 @@ memoryvardecl: ADDRESS_OF varinitializer;
structdecl: 'struct' identifier '{' EOL vardecl ( EOL vardecl)* EOL? '}' EOL;
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'str_s' ;
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' ;
arrayindex: '[' expression ']' ;
@ -183,10 +184,9 @@ directmemory : '@' '(' expression ')';
addressof : <assoc=right> ADDRESS_OF scoped_identifier ;
functioncall : scoped_identifier '(' expression_list? ')' ;
functioncall : scoped_identifier '(' expression_list? ')' ;
functioncall_stmt : scoped_identifier '(' expression_list? ')' ;
functioncall_stmt : VOID? scoped_identifier '(' expression_list? ')' ;
expression_list :
expression (',' EOL? expression)* // you can split the expression list over several lines

View File

@ -20,10 +20,10 @@ configurations {
}
dependencies {
antlr 'org.antlr:antlr4:4.7.2'
compile 'org.antlr:antlr4-runtime:4.7.2'
antlr 'org.antlr:antlr4:4.8'
implementation 'org.antlr:antlr4-runtime:4.8'
// antlr('org.antlr:antlr4:4.7.2') {
// antlr('org.antlr:antlr4:4.8') {
// exclude group: 'com.ibm.icu', module: 'icu4j'
// }
}

View File

@ -8,6 +8,6 @@
</content>
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="antlr-4.7.2-complete" level="project" />
<orderEntry type="library" name="antlr-4.8-complete" level="project" />
</component>
</module>

View File

@ -1,4 +1,4 @@
// Generated from prog8.g4 by ANTLR 4.7.2
// Generated from prog8.g4 by ANTLR 4.8
package prog8.parser;
@ -13,7 +13,7 @@ import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class prog8Lexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.7.2", RuntimeMetaData.VERSION); }
static { RuntimeMetaData.checkVersion("4.8", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
@ -34,8 +34,8 @@ public class prog8Lexer extends Lexer {
T__87=88, T__88=89, T__89=90, T__90=91, T__91=92, T__92=93, T__93=94,
T__94=95, T__95=96, T__96=97, T__97=98, T__98=99, T__99=100, T__100=101,
T__101=102, T__102=103, T__103=104, T__104=105, T__105=106, T__106=107,
T__107=108, T__108=109, T__109=110, LINECOMMENT=111, COMMENT=112, WS=113,
EOL=114, NAME=115, DEC_INTEGER=116, HEX_INTEGER=117, BIN_INTEGER=118,
T__107=108, T__108=109, LINECOMMENT=110, COMMENT=111, WS=112, EOL=113,
VOID=114, NAME=115, DEC_INTEGER=116, HEX_INTEGER=117, BIN_INTEGER=118,
ADDRESS_OF=119, FLOAT_NUMBER=120, STRING=121, INLINEASMBLOCK=122, SINGLECHAR=123,
ZEROPAGE=124, ARRAYSIG=125;
public static String[] channelNames = {
@ -61,8 +61,8 @@ public class prog8Lexer extends Lexer {
"T__81", "T__82", "T__83", "T__84", "T__85", "T__86", "T__87", "T__88",
"T__89", "T__90", "T__91", "T__92", "T__93", "T__94", "T__95", "T__96",
"T__97", "T__98", "T__99", "T__100", "T__101", "T__102", "T__103", "T__104",
"T__105", "T__106", "T__107", "T__108", "T__109", "LINECOMMENT", "COMMENT",
"WS", "EOL", "NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF",
"T__105", "T__106", "T__107", "T__108", "LINECOMMENT", "COMMENT", "WS",
"EOL", "VOID", "NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF",
"FLOAT_NUMBER", "FNUMBER", "STRING_ESCAPE_SEQ", "STRING", "INLINEASMBLOCK",
"SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
};
@ -74,18 +74,18 @@ public class prog8Lexer extends Lexer {
null, "':'", "'goto'", "'%output'", "'%launcher'", "'%zeropage'", "'%zpreserved'",
"'%address'", "'%import'", "'%breakpoint'", "'%asminclude'", "'%asmbinary'",
"'%option'", "','", "'='", "'const'", "'struct'", "'{'", "'}'", "'ubyte'",
"'byte'", "'uword'", "'word'", "'float'", "'str'", "'str_s'", "'['",
"']'", "'+='", "'-='", "'/='", "'*='", "'**='", "'&='", "'|='", "'^='",
"'%='", "'<<='", "'>>='", "'++'", "'--'", "'+'", "'-'", "'~'", "'**'",
"'*'", "'/'", "'%'", "'<<'", "'>>'", "'<'", "'>'", "'<='", "'>='", "'=='",
"'!='", "'^'", "'|'", "'to'", "'step'", "'and'", "'or'", "'xor'", "'not'",
"'('", "')'", "'as'", "'@'", "'return'", "'break'", "'continue'", "'.'",
"'A'", "'X'", "'Y'", "'AX'", "'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'",
"'Pv'", "'.w'", "'true'", "'false'", "'%asm'", "'sub'", "'->'", "'asmsub'",
"'stack'", "'clobbers'", "'if'", "'else'", "'if_cs'", "'if_cc'", "'if_eq'",
"'if_z'", "'if_ne'", "'if_nz'", "'if_pl'", "'if_pos'", "'if_mi'", "'if_neg'",
"'if_vs'", "'if_vc'", "'for'", "'in'", "'while'", "'repeat'", "'until'",
"'when'", null, null, null, null, null, null, null, null, "'&'", null,
"'byte'", "'uword'", "'word'", "'float'", "'str'", "'['", "']'", "'+='",
"'-='", "'/='", "'*='", "'**='", "'&='", "'|='", "'^='", "'%='", "'<<='",
"'>>='", "'++'", "'--'", "'+'", "'-'", "'~'", "'**'", "'*'", "'/'", "'%'",
"'<<'", "'>>'", "'<'", "'>'", "'<='", "'>='", "'=='", "'!='", "'^'",
"'|'", "'to'", "'step'", "'and'", "'or'", "'xor'", "'not'", "'('", "')'",
"'as'", "'@'", "'return'", "'break'", "'continue'", "'.'", "'A'", "'X'",
"'Y'", "'AX'", "'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'", "'Pv'", "'.w'",
"'true'", "'false'", "'%asm'", "'sub'", "'->'", "'asmsub'", "'stack'",
"'clobbers'", "'if'", "'else'", "'if_cs'", "'if_cc'", "'if_eq'", "'if_z'",
"'if_ne'", "'if_nz'", "'if_pl'", "'if_pos'", "'if_mi'", "'if_neg'", "'if_vs'",
"'if_vc'", "'for'", "'in'", "'while'", "'repeat'", "'until'", "'when'",
null, null, null, null, "'void'", null, null, null, null, "'&'", null,
null, null, null, "'@zp'", "'[]'"
};
}
@ -101,7 +101,7 @@ public class prog8Lexer extends Lexer {
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, "LINECOMMENT", "COMMENT", "WS", "EOL", "NAME", "DEC_INTEGER",
null, null, "LINECOMMENT", "COMMENT", "WS", "EOL", "VOID", "NAME", "DEC_INTEGER",
"HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF", "FLOAT_NUMBER", "STRING",
"INLINEASMBLOCK", "SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
};
@ -213,7 +213,7 @@ public class prog8Lexer extends Lexer {
}
public static final String _serializedATN =
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\177\u036e\b\1\4\2"+
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\177\u036d\b\1\4\2"+
"\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4"+
"\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22"+
"\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31"+
@ -237,31 +237,31 @@ public class prog8Lexer extends Lexer {
"\3\20\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\23\3\23\3\24\3\24"+
"\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26"+
"\3\26\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31"+
"\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\34\3\34\3\35\3\35"+
"\3\35\3\36\3\36\3\36\3\37\3\37\3\37\3 \3 \3 \3!\3!\3!\3!\3\"\3\"\3\"\3"+
"#\3#\3#\3$\3$\3$\3%\3%\3%\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3(\3(\3(\3)\3)\3"+
")\3*\3*\3+\3+\3,\3,\3-\3-\3-\3.\3.\3/\3/\3\60\3\60\3\61\3\61\3\61\3\62"+
"\3\62\3\62\3\63\3\63\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\66\3\67\3\67"+
"\3\67\38\38\38\39\39\3:\3:\3;\3;\3;\3<\3<\3<\3<\3<\3=\3=\3=\3=\3>\3>\3"+
">\3?\3?\3?\3?\3@\3@\3@\3@\3A\3A\3B\3B\3C\3C\3C\3D\3D\3E\3E\3E\3E\3E\3"+
"E\3E\3F\3F\3F\3F\3F\3F\3G\3G\3G\3G\3G\3G\3G\3G\3G\3H\3H\3I\3I\3J\3J\3"+
"K\3K\3L\3L\3L\3M\3M\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3"+
"S\3S\3S\3T\3T\3T\3T\3T\3U\3U\3U\3U\3U\3U\3V\3V\3V\3V\3V\3W\3W\3W\3W\3"+
"X\3X\3X\3Y\3Y\3Y\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3[\3[\3[\3[\3"+
"[\3[\3\\\3\\\3\\\3]\3]\3]\3]\3]\3^\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3"+
"`\3`\3`\3`\3`\3`\3a\3a\3a\3a\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3"+
"d\3d\3d\3d\3d\3d\3e\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3"+
"g\3g\3g\3h\3h\3h\3h\3h\3h\3i\3i\3i\3i\3i\3i\3j\3j\3j\3j\3k\3k\3k\3l\3"+
"l\3l\3l\3l\3l\3m\3m\3m\3m\3m\3m\3m\3n\3n\3n\3n\3n\3n\3o\3o\3o\3o\3o\3"+
"p\3p\7p\u02f4\np\fp\16p\u02f7\13p\3p\3p\3p\3p\3q\3q\7q\u02ff\nq\fq\16"+
"q\u0302\13q\3q\3q\3r\3r\3r\3r\3s\6s\u030b\ns\rs\16s\u030c\3t\3t\7t\u0311"+
"\nt\ft\16t\u0314\13t\3u\3u\3u\6u\u0319\nu\ru\16u\u031a\5u\u031d\nu\3v"+
"\3v\6v\u0321\nv\rv\16v\u0322\3w\3w\6w\u0327\nw\rw\16w\u0328\3x\3x\3y\3"+
"y\3y\5y\u0330\ny\3y\5y\u0333\ny\3z\6z\u0336\nz\rz\16z\u0337\3z\3z\6z\u033c"+
"\nz\rz\16z\u033d\5z\u0340\nz\3{\3{\3{\3{\5{\u0346\n{\3|\3|\3|\7|\u034b"+
"\n|\f|\16|\u034e\13|\3|\3|\3|\3}\3}\3}\3}\6}\u0357\n}\r}\16}\u0358\3}"+
"\3}\3}\3}\3}\3~\3~\3~\5~\u0363\n~\3~\3~\3~\3\177\3\177\3\177\3\177\3\u0080"+
"\3\u0080\3\u0080\3\u0358\2\u0081\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23"+
"\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\36\3\36"+
"\3\36\3\37\3\37\3\37\3 \3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3$\3$\3"+
"$\3%\3%\3%\3%\3&\3&\3&\3&\3\'\3\'\3\'\3(\3(\3(\3)\3)\3*\3*\3+\3+\3,\3"+
",\3,\3-\3-\3.\3.\3/\3/\3\60\3\60\3\60\3\61\3\61\3\61\3\62\3\62\3\63\3"+
"\63\3\64\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\66\3\67\3\67\3\67\38\38"+
"\39\39\3:\3:\3:\3;\3;\3;\3;\3;\3<\3<\3<\3<\3=\3=\3=\3>\3>\3>\3>\3?\3?"+
"\3?\3?\3@\3@\3A\3A\3B\3B\3B\3C\3C\3D\3D\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E"+
"\3E\3F\3F\3F\3F\3F\3F\3F\3F\3F\3G\3G\3H\3H\3I\3I\3J\3J\3K\3K\3K\3L\3L"+
"\3L\3M\3M\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3S\3S\3S\3S"+
"\3S\3T\3T\3T\3T\3T\3T\3U\3U\3U\3U\3U\3V\3V\3V\3V\3W\3W\3W\3X\3X\3X\3X"+
"\3X\3X\3X\3Y\3Y\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3\\\3"+
"\\\3\\\3\\\3\\\3]\3]\3]\3]\3]\3]\3^\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3"+
"`\3`\3`\3`\3`\3a\3a\3a\3a\3a\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3"+
"d\3d\3d\3d\3d\3d\3d\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3f\3f\3f\3g\3g\3g\3"+
"g\3g\3g\3h\3h\3h\3h\3h\3h\3i\3i\3i\3i\3j\3j\3j\3k\3k\3k\3k\3k\3k\3l\3"+
"l\3l\3l\3l\3l\3l\3m\3m\3m\3m\3m\3m\3n\3n\3n\3n\3n\3o\3o\7o\u02ee\no\f"+
"o\16o\u02f1\13o\3o\3o\3o\3o\3p\3p\7p\u02f9\np\fp\16p\u02fc\13p\3p\3p\3"+
"q\3q\3q\3q\3r\6r\u0305\nr\rr\16r\u0306\3s\3s\3s\3s\3s\3t\3t\7t\u0310\n"+
"t\ft\16t\u0313\13t\3u\3u\3u\6u\u0318\nu\ru\16u\u0319\5u\u031c\nu\3v\3"+
"v\6v\u0320\nv\rv\16v\u0321\3w\3w\6w\u0326\nw\rw\16w\u0327\3x\3x\3y\3y"+
"\3y\5y\u032f\ny\3y\5y\u0332\ny\3z\6z\u0335\nz\rz\16z\u0336\3z\3z\6z\u033b"+
"\nz\rz\16z\u033c\5z\u033f\nz\3{\3{\3{\3{\5{\u0345\n{\3|\3|\3|\7|\u034a"+
"\n|\f|\16|\u034d\13|\3|\3|\3|\3}\3}\3}\3}\6}\u0356\n}\r}\16}\u0357\3}"+
"\3}\3}\3}\3}\3~\3~\3~\5~\u0362\n~\3~\3~\3~\3\177\3\177\3\177\3\177\3\u0080"+
"\3\u0080\3\u0080\3\u0357\2\u0081\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23"+
"\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31"+
"\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60"+
"_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085"+
@ -272,7 +272,7 @@ public class prog8Lexer extends Lexer {
"l\u00d7m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1r\u00e3s\u00e5t\u00e7u\u00e9"+
"v\u00ebw\u00edx\u00efy\u00f1z\u00f3\2\u00f5\2\u00f7{\u00f9|\u00fb}\u00fd"+
"~\u00ff\177\3\2\n\4\2\f\f\17\17\4\2\13\13\"\"\5\2C\\aac|\6\2\62;C\\aa"+
"c|\5\2\62;CHch\4\2GGgg\4\2--//\6\2\f\f\16\17$$^^\2\u037d\2\3\3\2\2\2\2"+
"c|\5\2\62;CHch\4\2GGgg\4\2--//\6\2\f\f\16\17$$^^\2\u037c\2\3\3\2\2\2\2"+
"\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2"+
"\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2"+
"\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2"+
@ -302,34 +302,34 @@ public class prog8Lexer extends Lexer {
"\23\u0141\3\2\2\2\25\u014d\3\2\2\2\27\u0159\3\2\2\2\31\u0164\3\2\2\2\33"+
"\u016c\3\2\2\2\35\u016e\3\2\2\2\37\u0170\3\2\2\2!\u0176\3\2\2\2#\u017d"+
"\3\2\2\2%\u017f\3\2\2\2\'\u0181\3\2\2\2)\u0187\3\2\2\2+\u018c\3\2\2\2"+
"-\u0192\3\2\2\2/\u0197\3\2\2\2\61\u019d\3\2\2\2\63\u01a1\3\2\2\2\65\u01a7"+
"\3\2\2\2\67\u01a9\3\2\2\29\u01ab\3\2\2\2;\u01ae\3\2\2\2=\u01b1\3\2\2\2"+
"?\u01b4\3\2\2\2A\u01b7\3\2\2\2C\u01bb\3\2\2\2E\u01be\3\2\2\2G\u01c1\3"+
"\2\2\2I\u01c4\3\2\2\2K\u01c7\3\2\2\2M\u01cb\3\2\2\2O\u01cf\3\2\2\2Q\u01d2"+
"\3\2\2\2S\u01d5\3\2\2\2U\u01d7\3\2\2\2W\u01d9\3\2\2\2Y\u01db\3\2\2\2["+
"\u01de\3\2\2\2]\u01e0\3\2\2\2_\u01e2\3\2\2\2a\u01e4\3\2\2\2c\u01e7\3\2"+
"\2\2e\u01ea\3\2\2\2g\u01ec\3\2\2\2i\u01ee\3\2\2\2k\u01f1\3\2\2\2m\u01f4"+
"\3\2\2\2o\u01f7\3\2\2\2q\u01fa\3\2\2\2s\u01fc\3\2\2\2u\u01fe\3\2\2\2w"+
"\u0201\3\2\2\2y\u0206\3\2\2\2{\u020a\3\2\2\2}\u020d\3\2\2\2\177\u0211"+
"\3\2\2\2\u0081\u0215\3\2\2\2\u0083\u0217\3\2\2\2\u0085\u0219\3\2\2\2\u0087"+
"\u021c\3\2\2\2\u0089\u021e\3\2\2\2\u008b\u0225\3\2\2\2\u008d\u022b\3\2"+
"\2\2\u008f\u0234\3\2\2\2\u0091\u0236\3\2\2\2\u0093\u0238\3\2\2\2\u0095"+
"\u023a\3\2\2\2\u0097\u023c\3\2\2\2\u0099\u023f\3\2\2\2\u009b\u0242\3\2"+
"\2\2\u009d\u0245\3\2\2\2\u009f\u0248\3\2\2\2\u00a1\u024b\3\2\2\2\u00a3"+
"\u024e\3\2\2\2\u00a5\u0251\3\2\2\2\u00a7\u0254\3\2\2\2\u00a9\u0259\3\2"+
"\2\2\u00ab\u025f\3\2\2\2\u00ad\u0264\3\2\2\2\u00af\u0268\3\2\2\2\u00b1"+
"\u026b\3\2\2\2\u00b3\u0272\3\2\2\2\u00b5\u0278\3\2\2\2\u00b7\u0281\3\2"+
"\2\2\u00b9\u0284\3\2\2\2\u00bb\u0289\3\2\2\2\u00bd\u028f\3\2\2\2\u00bf"+
"\u0295\3\2\2\2\u00c1\u029b\3\2\2\2\u00c3\u02a0\3\2\2\2\u00c5\u02a6\3\2"+
"\2\2\u00c7\u02ac\3\2\2\2\u00c9\u02b2\3\2\2\2\u00cb\u02b9\3\2\2\2\u00cd"+
"\u02bf\3\2\2\2\u00cf\u02c6\3\2\2\2\u00d1\u02cc\3\2\2\2\u00d3\u02d2\3\2"+
"\2\2\u00d5\u02d6\3\2\2\2\u00d7\u02d9\3\2\2\2\u00d9\u02df\3\2\2\2\u00db"+
"\u02e6\3\2\2\2\u00dd\u02ec\3\2\2\2\u00df\u02f1\3\2\2\2\u00e1\u02fc\3\2"+
"\2\2\u00e3\u0305\3\2\2\2\u00e5\u030a\3\2\2\2\u00e7\u030e\3\2\2\2\u00e9"+
"\u031c\3\2\2\2\u00eb\u031e\3\2\2\2\u00ed\u0324\3\2\2\2\u00ef\u032a\3\2"+
"\2\2\u00f1\u032c\3\2\2\2\u00f3\u0335\3\2\2\2\u00f5\u0345\3\2\2\2\u00f7"+
"\u0347\3\2\2\2\u00f9\u0352\3\2\2\2\u00fb\u035f\3\2\2\2\u00fd\u0367\3\2"+
"\2\2\u00ff\u036b\3\2\2\2\u0101\u0102\7<\2\2\u0102\4\3\2\2\2\u0103\u0104"+
"-\u0192\3\2\2\2/\u0197\3\2\2\2\61\u019d\3\2\2\2\63\u01a1\3\2\2\2\65\u01a3"+
"\3\2\2\2\67\u01a5\3\2\2\29\u01a8\3\2\2\2;\u01ab\3\2\2\2=\u01ae\3\2\2\2"+
"?\u01b1\3\2\2\2A\u01b5\3\2\2\2C\u01b8\3\2\2\2E\u01bb\3\2\2\2G\u01be\3"+
"\2\2\2I\u01c1\3\2\2\2K\u01c5\3\2\2\2M\u01c9\3\2\2\2O\u01cc\3\2\2\2Q\u01cf"+
"\3\2\2\2S\u01d1\3\2\2\2U\u01d3\3\2\2\2W\u01d5\3\2\2\2Y\u01d8\3\2\2\2["+
"\u01da\3\2\2\2]\u01dc\3\2\2\2_\u01de\3\2\2\2a\u01e1\3\2\2\2c\u01e4\3\2"+
"\2\2e\u01e6\3\2\2\2g\u01e8\3\2\2\2i\u01eb\3\2\2\2k\u01ee\3\2\2\2m\u01f1"+
"\3\2\2\2o\u01f4\3\2\2\2q\u01f6\3\2\2\2s\u01f8\3\2\2\2u\u01fb\3\2\2\2w"+
"\u0200\3\2\2\2y\u0204\3\2\2\2{\u0207\3\2\2\2}\u020b\3\2\2\2\177\u020f"+
"\3\2\2\2\u0081\u0211\3\2\2\2\u0083\u0213\3\2\2\2\u0085\u0216\3\2\2\2\u0087"+
"\u0218\3\2\2\2\u0089\u021f\3\2\2\2\u008b\u0225\3\2\2\2\u008d\u022e\3\2"+
"\2\2\u008f\u0230\3\2\2\2\u0091\u0232\3\2\2\2\u0093\u0234\3\2\2\2\u0095"+
"\u0236\3\2\2\2\u0097\u0239\3\2\2\2\u0099\u023c\3\2\2\2\u009b\u023f\3\2"+
"\2\2\u009d\u0242\3\2\2\2\u009f\u0245\3\2\2\2\u00a1\u0248\3\2\2\2\u00a3"+
"\u024b\3\2\2\2\u00a5\u024e\3\2\2\2\u00a7\u0253\3\2\2\2\u00a9\u0259\3\2"+
"\2\2\u00ab\u025e\3\2\2\2\u00ad\u0262\3\2\2\2\u00af\u0265\3\2\2\2\u00b1"+
"\u026c\3\2\2\2\u00b3\u0272\3\2\2\2\u00b5\u027b\3\2\2\2\u00b7\u027e\3\2"+
"\2\2\u00b9\u0283\3\2\2\2\u00bb\u0289\3\2\2\2\u00bd\u028f\3\2\2\2\u00bf"+
"\u0295\3\2\2\2\u00c1\u029a\3\2\2\2\u00c3\u02a0\3\2\2\2\u00c5\u02a6\3\2"+
"\2\2\u00c7\u02ac\3\2\2\2\u00c9\u02b3\3\2\2\2\u00cb\u02b9\3\2\2\2\u00cd"+
"\u02c0\3\2\2\2\u00cf\u02c6\3\2\2\2\u00d1\u02cc\3\2\2\2\u00d3\u02d0\3\2"+
"\2\2\u00d5\u02d3\3\2\2\2\u00d7\u02d9\3\2\2\2\u00d9\u02e0\3\2\2\2\u00db"+
"\u02e6\3\2\2\2\u00dd\u02eb\3\2\2\2\u00df\u02f6\3\2\2\2\u00e1\u02ff\3\2"+
"\2\2\u00e3\u0304\3\2\2\2\u00e5\u0308\3\2\2\2\u00e7\u030d\3\2\2\2\u00e9"+
"\u031b\3\2\2\2\u00eb\u031d\3\2\2\2\u00ed\u0323\3\2\2\2\u00ef\u0329\3\2"+
"\2\2\u00f1\u032b\3\2\2\2\u00f3\u0334\3\2\2\2\u00f5\u0344\3\2\2\2\u00f7"+
"\u0346\3\2\2\2\u00f9\u0351\3\2\2\2\u00fb\u035e\3\2\2\2\u00fd\u0366\3\2"+
"\2\2\u00ff\u036a\3\2\2\2\u0101\u0102\7<\2\2\u0102\4\3\2\2\2\u0103\u0104"+
"\7i\2\2\u0104\u0105\7q\2\2\u0105\u0106\7v\2\2\u0106\u0107\7q\2\2\u0107"+
"\6\3\2\2\2\u0108\u0109\7\'\2\2\u0109\u010a\7q\2\2\u010a\u010b\7w\2\2\u010b"+
"\u010c\7v\2\2\u010c\u010d\7r\2\2\u010d\u010e\7w\2\2\u010e\u010f\7v\2\2"+
@ -371,139 +371,139 @@ public class prog8Lexer extends Lexer {
"y\2\2\u0193\u0194\7q\2\2\u0194\u0195\7t\2\2\u0195\u0196\7f\2\2\u0196."+
"\3\2\2\2\u0197\u0198\7h\2\2\u0198\u0199\7n\2\2\u0199\u019a\7q\2\2\u019a"+
"\u019b\7c\2\2\u019b\u019c\7v\2\2\u019c\60\3\2\2\2\u019d\u019e\7u\2\2\u019e"+
"\u019f\7v\2\2\u019f\u01a0\7t\2\2\u01a0\62\3\2\2\2\u01a1\u01a2\7u\2\2\u01a2"+
"\u01a3\7v\2\2\u01a3\u01a4\7t\2\2\u01a4\u01a5\7a\2\2\u01a5\u01a6\7u\2\2"+
"\u01a6\64\3\2\2\2\u01a7\u01a8\7]\2\2\u01a8\66\3\2\2\2\u01a9\u01aa\7_\2"+
"\2\u01aa8\3\2\2\2\u01ab\u01ac\7-\2\2\u01ac\u01ad\7?\2\2\u01ad:\3\2\2\2"+
"\u01ae\u01af\7/\2\2\u01af\u01b0\7?\2\2\u01b0<\3\2\2\2\u01b1\u01b2\7\61"+
"\2\2\u01b2\u01b3\7?\2\2\u01b3>\3\2\2\2\u01b4\u01b5\7,\2\2\u01b5\u01b6"+
"\7?\2\2\u01b6@\3\2\2\2\u01b7\u01b8\7,\2\2\u01b8\u01b9\7,\2\2\u01b9\u01ba"+
"\7?\2\2\u01baB\3\2\2\2\u01bb\u01bc\7(\2\2\u01bc\u01bd\7?\2\2\u01bdD\3"+
"\2\2\2\u01be\u01bf\7~\2\2\u01bf\u01c0\7?\2\2\u01c0F\3\2\2\2\u01c1\u01c2"+
"\7`\2\2\u01c2\u01c3\7?\2\2\u01c3H\3\2\2\2\u01c4\u01c5\7\'\2\2\u01c5\u01c6"+
"\7?\2\2\u01c6J\3\2\2\2\u01c7\u01c8\7>\2\2\u01c8\u01c9\7>\2\2\u01c9\u01ca"+
"\7?\2\2\u01caL\3\2\2\2\u01cb\u01cc\7@\2\2\u01cc\u01cd\7@\2\2\u01cd\u01ce"+
"\7?\2\2\u01ceN\3\2\2\2\u01cf\u01d0\7-\2\2\u01d0\u01d1\7-\2\2\u01d1P\3"+
"\2\2\2\u01d2\u01d3\7/\2\2\u01d3\u01d4\7/\2\2\u01d4R\3\2\2\2\u01d5\u01d6"+
"\7-\2\2\u01d6T\3\2\2\2\u01d7\u01d8\7/\2\2\u01d8V\3\2\2\2\u01d9\u01da\7"+
"\u0080\2\2\u01daX\3\2\2\2\u01db\u01dc\7,\2\2\u01dc\u01dd\7,\2\2\u01dd"+
"Z\3\2\2\2\u01de\u01df\7,\2\2\u01df\\\3\2\2\2\u01e0\u01e1\7\61\2\2\u01e1"+
"^\3\2\2\2\u01e2\u01e3\7\'\2\2\u01e3`\3\2\2\2\u01e4\u01e5\7>\2\2\u01e5"+
"\u01e6\7>\2\2\u01e6b\3\2\2\2\u01e7\u01e8\7@\2\2\u01e8\u01e9\7@\2\2\u01e9"+
"d\3\2\2\2\u01ea\u01eb\7>\2\2\u01ebf\3\2\2\2\u01ec\u01ed\7@\2\2\u01edh"+
"\3\2\2\2\u01ee\u01ef\7>\2\2\u01ef\u01f0\7?\2\2\u01f0j\3\2\2\2\u01f1\u01f2"+
"\7@\2\2\u01f2\u01f3\7?\2\2\u01f3l\3\2\2\2\u01f4\u01f5\7?\2\2\u01f5\u01f6"+
"\7?\2\2\u01f6n\3\2\2\2\u01f7\u01f8\7#\2\2\u01f8\u01f9\7?\2\2\u01f9p\3"+
"\2\2\2\u01fa\u01fb\7`\2\2\u01fbr\3\2\2\2\u01fc\u01fd\7~\2\2\u01fdt\3\2"+
"\2\2\u01fe\u01ff\7v\2\2\u01ff\u0200\7q\2\2\u0200v\3\2\2\2\u0201\u0202"+
"\7u\2\2\u0202\u0203\7v\2\2\u0203\u0204\7g\2\2\u0204\u0205\7r\2\2\u0205"+
"x\3\2\2\2\u0206\u0207\7c\2\2\u0207\u0208\7p\2\2\u0208\u0209\7f\2\2\u0209"+
"z\3\2\2\2\u020a\u020b\7q\2\2\u020b\u020c\7t\2\2\u020c|\3\2\2\2\u020d\u020e"+
"\7z\2\2\u020e\u020f\7q\2\2\u020f\u0210\7t\2\2\u0210~\3\2\2\2\u0211\u0212"+
"\7p\2\2\u0212\u0213\7q\2\2\u0213\u0214\7v\2\2\u0214\u0080\3\2\2\2\u0215"+
"\u0216\7*\2\2\u0216\u0082\3\2\2\2\u0217\u0218\7+\2\2\u0218\u0084\3\2\2"+
"\2\u0219\u021a\7c\2\2\u021a\u021b\7u\2\2\u021b\u0086\3\2\2\2\u021c\u021d"+
"\7B\2\2\u021d\u0088\3\2\2\2\u021e\u021f\7t\2\2\u021f\u0220\7g\2\2\u0220"+
"\u0221\7v\2\2\u0221\u0222\7w\2\2\u0222\u0223\7t\2\2\u0223\u0224\7p\2\2"+
"\u0224\u008a\3\2\2\2\u0225\u0226\7d\2\2\u0226\u0227\7t\2\2\u0227\u0228"+
"\7g\2\2\u0228\u0229\7c\2\2\u0229\u022a\7m\2\2\u022a\u008c\3\2\2\2\u022b"+
"\u022c\7e\2\2\u022c\u022d\7q\2\2\u022d\u022e\7p\2\2\u022e\u022f\7v\2\2"+
"\u022f\u0230\7k\2\2\u0230\u0231\7p\2\2\u0231\u0232\7w\2\2\u0232\u0233"+
"\7g\2\2\u0233\u008e\3\2\2\2\u0234\u0235\7\60\2\2\u0235\u0090\3\2\2\2\u0236"+
"\u0237\7C\2\2\u0237\u0092\3\2\2\2\u0238\u0239\7Z\2\2\u0239\u0094\3\2\2"+
"\2\u023a\u023b\7[\2\2\u023b\u0096\3\2\2\2\u023c\u023d\7C\2\2\u023d\u023e"+
"\7Z\2\2\u023e\u0098\3\2\2\2\u023f\u0240\7C\2\2\u0240\u0241\7[\2\2\u0241"+
"\u009a\3\2\2\2\u0242\u0243\7Z\2\2\u0243\u0244\7[\2\2\u0244\u009c\3\2\2"+
"\2\u0245\u0246\7R\2\2\u0246\u0247\7e\2\2\u0247\u009e\3\2\2\2\u0248\u0249"+
"\7R\2\2\u0249\u024a\7|\2\2\u024a\u00a0\3\2\2\2\u024b\u024c\7R\2\2\u024c"+
"\u024d\7p\2\2\u024d\u00a2\3\2\2\2\u024e\u024f\7R\2\2\u024f\u0250\7x\2"+
"\2\u0250\u00a4\3\2\2\2\u0251\u0252\7\60\2\2\u0252\u0253\7y\2\2\u0253\u00a6"+
"\3\2\2\2\u0254\u0255\7v\2\2\u0255\u0256\7t\2\2\u0256\u0257\7w\2\2\u0257"+
"\u0258\7g\2\2\u0258\u00a8\3\2\2\2\u0259\u025a\7h\2\2\u025a\u025b\7c\2"+
"\2\u025b\u025c\7n\2\2\u025c\u025d\7u\2\2\u025d\u025e\7g\2\2\u025e\u00aa"+
"\3\2\2\2\u025f\u0260\7\'\2\2\u0260\u0261\7c\2\2\u0261\u0262\7u\2\2\u0262"+
"\u0263\7o\2\2\u0263\u00ac\3\2\2\2\u0264\u0265\7u\2\2\u0265\u0266\7w\2"+
"\2\u0266\u0267\7d\2\2\u0267\u00ae\3\2\2\2\u0268\u0269\7/\2\2\u0269\u026a"+
"\7@\2\2\u026a\u00b0\3\2\2\2\u026b\u026c\7c\2\2\u026c\u026d\7u\2\2\u026d"+
"\u026e\7o\2\2\u026e\u026f\7u\2\2\u026f\u0270\7w\2\2\u0270\u0271\7d\2\2"+
"\u0271\u00b2\3\2\2\2\u0272\u0273\7u\2\2\u0273\u0274\7v\2\2\u0274\u0275"+
"\7c\2\2\u0275\u0276\7e\2\2\u0276\u0277\7m\2\2\u0277\u00b4\3\2\2\2\u0278"+
"\u0279\7e\2\2\u0279\u027a\7n\2\2\u027a\u027b\7q\2\2\u027b\u027c\7d\2\2"+
"\u027c\u027d\7d\2\2\u027d\u027e\7g\2\2\u027e\u027f\7t\2\2\u027f\u0280"+
"\7u\2\2\u0280\u00b6\3\2\2\2\u0281\u0282\7k\2\2\u0282\u0283\7h\2\2\u0283"+
"\u00b8\3\2\2\2\u0284\u0285\7g\2\2\u0285\u0286\7n\2\2\u0286\u0287\7u\2"+
"\2\u0287\u0288\7g\2\2\u0288\u00ba\3\2\2\2\u0289\u028a\7k\2\2\u028a\u028b"+
"\7h\2\2\u028b\u028c\7a\2\2\u028c\u028d\7e\2\2\u028d\u028e\7u\2\2\u028e"+
"\u00bc\3\2\2\2\u028f\u0290\7k\2\2\u0290\u0291\7h\2\2\u0291\u0292\7a\2"+
"\2\u0292\u0293\7e\2\2\u0293\u0294\7e\2\2\u0294\u00be\3\2\2\2\u0295\u0296"+
"\7k\2\2\u0296\u0297\7h\2\2\u0297\u0298\7a\2\2\u0298\u0299\7g\2\2\u0299"+
"\u029a\7s\2\2\u029a\u00c0\3\2\2\2\u029b\u029c\7k\2\2\u029c\u029d\7h\2"+
"\2\u029d\u029e\7a\2\2\u029e\u029f\7|\2\2\u029f\u00c2\3\2\2\2\u02a0\u02a1"+
"\7k\2\2\u02a1\u02a2\7h\2\2\u02a2\u02a3\7a\2\2\u02a3\u02a4\7p\2\2\u02a4"+
"\u02a5\7g\2\2\u02a5\u00c4\3\2\2\2\u02a6\u02a7\7k\2\2\u02a7\u02a8\7h\2"+
"\2\u02a8\u02a9\7a\2\2\u02a9\u02aa\7p\2\2\u02aa\u02ab\7|\2\2\u02ab\u00c6"+
"\3\2\2\2\u02ac\u02ad\7k\2\2\u02ad\u02ae\7h\2\2\u02ae\u02af\7a\2\2\u02af"+
"\u02b0\7r\2\2\u02b0\u02b1\7n\2\2\u02b1\u00c8\3\2\2\2\u02b2\u02b3\7k\2"+
"\2\u02b3\u02b4\7h\2\2\u02b4\u02b5\7a\2\2\u02b5\u02b6\7r\2\2\u02b6\u02b7"+
"\7q\2\2\u02b7\u02b8\7u\2\2\u02b8\u00ca\3\2\2\2\u02b9\u02ba\7k\2\2\u02ba"+
"\u02bb\7h\2\2\u02bb\u02bc\7a\2\2\u02bc\u02bd\7o\2\2\u02bd\u02be\7k\2\2"+
"\u02be\u00cc\3\2\2\2\u02bf\u02c0\7k\2\2\u02c0\u02c1\7h\2\2\u02c1\u02c2"+
"\7a\2\2\u02c2\u02c3\7p\2\2\u02c3\u02c4\7g\2\2\u02c4\u02c5\7i\2\2\u02c5"+
"\u00ce\3\2\2\2\u02c6\u02c7\7k\2\2\u02c7\u02c8\7h\2\2\u02c8\u02c9\7a\2"+
"\2\u02c9\u02ca\7x\2\2\u02ca\u02cb\7u\2\2\u02cb\u00d0\3\2\2\2\u02cc\u02cd"+
"\7k\2\2\u02cd\u02ce\7h\2\2\u02ce\u02cf\7a\2\2\u02cf\u02d0\7x\2\2\u02d0"+
"\u02d1\7e\2\2\u02d1\u00d2\3\2\2\2\u02d2\u02d3\7h\2\2\u02d3\u02d4\7q\2"+
"\2\u02d4\u02d5\7t\2\2\u02d5\u00d4\3\2\2\2\u02d6\u02d7\7k\2\2\u02d7\u02d8"+
"\7p\2\2\u02d8\u00d6\3\2\2\2\u02d9\u02da\7y\2\2\u02da\u02db\7j\2\2\u02db"+
"\u02dc\7k\2\2\u02dc\u02dd\7n\2\2\u02dd\u02de\7g\2\2\u02de\u00d8\3\2\2"+
"\2\u02df\u02e0\7t\2\2\u02e0\u02e1\7g\2\2\u02e1\u02e2\7r\2\2\u02e2\u02e3"+
"\7g\2\2\u02e3\u02e4\7c\2\2\u02e4\u02e5\7v\2\2\u02e5\u00da\3\2\2\2\u02e6"+
"\u02e7\7w\2\2\u02e7\u02e8\7p\2\2\u02e8\u02e9\7v\2\2\u02e9\u02ea\7k\2\2"+
"\u02ea\u02eb\7n\2\2\u02eb\u00dc\3\2\2\2\u02ec\u02ed\7y\2\2\u02ed\u02ee"+
"\7j\2\2\u02ee\u02ef\7g\2\2\u02ef\u02f0\7p\2\2\u02f0\u00de\3\2\2\2\u02f1"+
"\u02f5\t\2\2\2\u02f2\u02f4\t\3\2\2\u02f3\u02f2\3\2\2\2\u02f4\u02f7\3\2"+
"\2\2\u02f5\u02f3\3\2\2\2\u02f5\u02f6\3\2\2\2\u02f6\u02f8\3\2\2\2\u02f7"+
"\u02f5\3\2\2\2\u02f8\u02f9\5\u00e1q\2\u02f9\u02fa\3\2\2\2\u02fa\u02fb"+
"\bp\2\2\u02fb\u00e0\3\2\2\2\u02fc\u0300\7=\2\2\u02fd\u02ff\n\2\2\2\u02fe"+
"\u02fd\3\2\2\2\u02ff\u0302\3\2\2\2\u0300\u02fe\3\2\2\2\u0300\u0301\3\2"+
"\2\2\u0301\u0303\3\2\2\2\u0302\u0300\3\2\2\2\u0303\u0304\bq\2\2\u0304"+
"\u00e2\3\2\2\2\u0305\u0306\t\3\2\2\u0306\u0307\3\2\2\2\u0307\u0308\br"+
"\3\2\u0308\u00e4\3\2\2\2\u0309\u030b\t\2\2\2\u030a\u0309\3\2\2\2\u030b"+
"\u030c\3\2\2\2\u030c\u030a\3\2\2\2\u030c\u030d\3\2\2\2\u030d\u00e6\3\2"+
"\2\2\u030e\u0312\t\4\2\2\u030f\u0311\t\5\2\2\u0310\u030f\3\2\2\2\u0311"+
"\u0314\3\2\2\2\u0312\u0310\3\2\2\2\u0312\u0313\3\2\2\2\u0313\u00e8\3\2"+
"\2\2\u0314\u0312\3\2\2\2\u0315\u031d\4\62;\2\u0316\u0318\4\63;\2\u0317"+
"\u0319\4\62;\2\u0318\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u0318\3\2"+
"\2\2\u031a\u031b\3\2\2\2\u031b\u031d\3\2\2\2\u031c\u0315\3\2\2\2\u031c"+
"\u0316\3\2\2\2\u031d\u00ea\3\2\2\2\u031e\u0320\7&\2\2\u031f\u0321\t\6"+
"\2\2\u0320\u031f\3\2\2\2\u0321\u0322\3\2\2\2\u0322\u0320\3\2\2\2\u0322"+
"\u0323\3\2\2\2\u0323\u00ec\3\2\2\2\u0324\u0326\7\'\2\2\u0325\u0327\4\62"+
"\63\2\u0326\u0325\3\2\2\2\u0327\u0328\3\2\2\2\u0328\u0326\3\2\2\2\u0328"+
"\u0329\3\2\2\2\u0329\u00ee\3\2\2\2\u032a\u032b\7(\2\2\u032b\u00f0\3\2"+
"\2\2\u032c\u0332\5\u00f3z\2\u032d\u032f\t\7\2\2\u032e\u0330\t\b\2\2\u032f"+
"\u032e\3\2\2\2\u032f\u0330\3\2\2\2\u0330\u0331\3\2\2\2\u0331\u0333\5\u00f3"+
"z\2\u0332\u032d\3\2\2\2\u0332\u0333\3\2\2\2\u0333\u00f2\3\2\2\2\u0334"+
"\u0336\4\62;\2\u0335\u0334\3\2\2\2\u0336\u0337\3\2\2\2\u0337\u0335\3\2"+
"\2\2\u0337\u0338\3\2\2\2\u0338\u033f\3\2\2\2\u0339\u033b\7\60\2\2\u033a"+
"\u033c\4\62;\2\u033b\u033a\3\2\2\2\u033c\u033d\3\2\2\2\u033d\u033b\3\2"+
"\2\2\u033d\u033e\3\2\2\2\u033e\u0340\3\2\2\2\u033f\u0339\3\2\2\2\u033f"+
"\u0340\3\2\2\2\u0340\u00f4\3\2\2\2\u0341\u0342\7^\2\2\u0342\u0346\13\2"+
"\2\2\u0343\u0344\7^\2\2\u0344\u0346\5\u00e5s\2\u0345\u0341\3\2\2\2\u0345"+
"\u0343\3\2\2\2\u0346\u00f6\3\2\2\2\u0347\u034c\7$\2\2\u0348\u034b\5\u00f5"+
"{\2\u0349\u034b\n\t\2\2\u034a\u0348\3\2\2\2\u034a\u0349\3\2\2\2\u034b"+
"\u034e\3\2\2\2\u034c\u034a\3\2\2\2\u034c\u034d\3\2\2\2\u034d\u034f\3\2"+
"\2\2\u034e\u034c\3\2\2\2\u034f\u0350\7$\2\2\u0350\u0351\b|\4\2\u0351\u00f8"+
"\3\2\2\2\u0352\u0353\7}\2\2\u0353\u0354\7}\2\2\u0354\u0356\3\2\2\2\u0355"+
"\u0357\13\2\2\2\u0356\u0355\3\2\2\2\u0357\u0358\3\2\2\2\u0358\u0359\3"+
"\2\2\2\u0358\u0356\3\2\2\2\u0359\u035a\3\2\2\2\u035a\u035b\7\177\2\2\u035b"+
"\u035c\7\177\2\2\u035c\u035d\3\2\2\2\u035d\u035e\b}\5\2\u035e\u00fa\3"+
"\2\2\2\u035f\u0362\7)\2\2\u0360\u0363\5\u00f5{\2\u0361\u0363\n\t\2\2\u0362"+
"\u0360\3\2\2\2\u0362\u0361\3\2\2\2\u0363\u0364\3\2\2\2\u0364\u0365\7)"+
"\2\2\u0365\u0366\b~\6\2\u0366\u00fc\3\2\2\2\u0367\u0368\7B\2\2\u0368\u0369"+
"\7|\2\2\u0369\u036a\7r\2\2\u036a\u00fe\3\2\2\2\u036b\u036c\7]\2\2\u036c"+
"\u036d\7_\2\2\u036d\u0100\3\2\2\2\26\2\u02f5\u0300\u030c\u0312\u031a\u031c"+
"\u0320\u0322\u0328\u032f\u0332\u0337\u033d\u033f\u0345\u034a\u034c\u0358"+
"\u0362\7\2\3\2\b\2\2\3|\2\3}\3\3~\4";
"\u019f\7v\2\2\u019f\u01a0\7t\2\2\u01a0\62\3\2\2\2\u01a1\u01a2\7]\2\2\u01a2"+
"\64\3\2\2\2\u01a3\u01a4\7_\2\2\u01a4\66\3\2\2\2\u01a5\u01a6\7-\2\2\u01a6"+
"\u01a7\7?\2\2\u01a78\3\2\2\2\u01a8\u01a9\7/\2\2\u01a9\u01aa\7?\2\2\u01aa"+
":\3\2\2\2\u01ab\u01ac\7\61\2\2\u01ac\u01ad\7?\2\2\u01ad<\3\2\2\2\u01ae"+
"\u01af\7,\2\2\u01af\u01b0\7?\2\2\u01b0>\3\2\2\2\u01b1\u01b2\7,\2\2\u01b2"+
"\u01b3\7,\2\2\u01b3\u01b4\7?\2\2\u01b4@\3\2\2\2\u01b5\u01b6\7(\2\2\u01b6"+
"\u01b7\7?\2\2\u01b7B\3\2\2\2\u01b8\u01b9\7~\2\2\u01b9\u01ba\7?\2\2\u01ba"+
"D\3\2\2\2\u01bb\u01bc\7`\2\2\u01bc\u01bd\7?\2\2\u01bdF\3\2\2\2\u01be\u01bf"+
"\7\'\2\2\u01bf\u01c0\7?\2\2\u01c0H\3\2\2\2\u01c1\u01c2\7>\2\2\u01c2\u01c3"+
"\7>\2\2\u01c3\u01c4\7?\2\2\u01c4J\3\2\2\2\u01c5\u01c6\7@\2\2\u01c6\u01c7"+
"\7@\2\2\u01c7\u01c8\7?\2\2\u01c8L\3\2\2\2\u01c9\u01ca\7-\2\2\u01ca\u01cb"+
"\7-\2\2\u01cbN\3\2\2\2\u01cc\u01cd\7/\2\2\u01cd\u01ce\7/\2\2\u01ceP\3"+
"\2\2\2\u01cf\u01d0\7-\2\2\u01d0R\3\2\2\2\u01d1\u01d2\7/\2\2\u01d2T\3\2"+
"\2\2\u01d3\u01d4\7\u0080\2\2\u01d4V\3\2\2\2\u01d5\u01d6\7,\2\2\u01d6\u01d7"+
"\7,\2\2\u01d7X\3\2\2\2\u01d8\u01d9\7,\2\2\u01d9Z\3\2\2\2\u01da\u01db\7"+
"\61\2\2\u01db\\\3\2\2\2\u01dc\u01dd\7\'\2\2\u01dd^\3\2\2\2\u01de\u01df"+
"\7>\2\2\u01df\u01e0\7>\2\2\u01e0`\3\2\2\2\u01e1\u01e2\7@\2\2\u01e2\u01e3"+
"\7@\2\2\u01e3b\3\2\2\2\u01e4\u01e5\7>\2\2\u01e5d\3\2\2\2\u01e6\u01e7\7"+
"@\2\2\u01e7f\3\2\2\2\u01e8\u01e9\7>\2\2\u01e9\u01ea\7?\2\2\u01eah\3\2"+
"\2\2\u01eb\u01ec\7@\2\2\u01ec\u01ed\7?\2\2\u01edj\3\2\2\2\u01ee\u01ef"+
"\7?\2\2\u01ef\u01f0\7?\2\2\u01f0l\3\2\2\2\u01f1\u01f2\7#\2\2\u01f2\u01f3"+
"\7?\2\2\u01f3n\3\2\2\2\u01f4\u01f5\7`\2\2\u01f5p\3\2\2\2\u01f6\u01f7\7"+
"~\2\2\u01f7r\3\2\2\2\u01f8\u01f9\7v\2\2\u01f9\u01fa\7q\2\2\u01fat\3\2"+
"\2\2\u01fb\u01fc\7u\2\2\u01fc\u01fd\7v\2\2\u01fd\u01fe\7g\2\2\u01fe\u01ff"+
"\7r\2\2\u01ffv\3\2\2\2\u0200\u0201\7c\2\2\u0201\u0202\7p\2\2\u0202\u0203"+
"\7f\2\2\u0203x\3\2\2\2\u0204\u0205\7q\2\2\u0205\u0206\7t\2\2\u0206z\3"+
"\2\2\2\u0207\u0208\7z\2\2\u0208\u0209\7q\2\2\u0209\u020a\7t\2\2\u020a"+
"|\3\2\2\2\u020b\u020c\7p\2\2\u020c\u020d\7q\2\2\u020d\u020e\7v\2\2\u020e"+
"~\3\2\2\2\u020f\u0210\7*\2\2\u0210\u0080\3\2\2\2\u0211\u0212\7+\2\2\u0212"+
"\u0082\3\2\2\2\u0213\u0214\7c\2\2\u0214\u0215\7u\2\2\u0215\u0084\3\2\2"+
"\2\u0216\u0217\7B\2\2\u0217\u0086\3\2\2\2\u0218\u0219\7t\2\2\u0219\u021a"+
"\7g\2\2\u021a\u021b\7v\2\2\u021b\u021c\7w\2\2\u021c\u021d\7t\2\2\u021d"+
"\u021e\7p\2\2\u021e\u0088\3\2\2\2\u021f\u0220\7d\2\2\u0220\u0221\7t\2"+
"\2\u0221\u0222\7g\2\2\u0222\u0223\7c\2\2\u0223\u0224\7m\2\2\u0224\u008a"+
"\3\2\2\2\u0225\u0226\7e\2\2\u0226\u0227\7q\2\2\u0227\u0228\7p\2\2\u0228"+
"\u0229\7v\2\2\u0229\u022a\7k\2\2\u022a\u022b\7p\2\2\u022b\u022c\7w\2\2"+
"\u022c\u022d\7g\2\2\u022d\u008c\3\2\2\2\u022e\u022f\7\60\2\2\u022f\u008e"+
"\3\2\2\2\u0230\u0231\7C\2\2\u0231\u0090\3\2\2\2\u0232\u0233\7Z\2\2\u0233"+
"\u0092\3\2\2\2\u0234\u0235\7[\2\2\u0235\u0094\3\2\2\2\u0236\u0237\7C\2"+
"\2\u0237\u0238\7Z\2\2\u0238\u0096\3\2\2\2\u0239\u023a\7C\2\2\u023a\u023b"+
"\7[\2\2\u023b\u0098\3\2\2\2\u023c\u023d\7Z\2\2\u023d\u023e\7[\2\2\u023e"+
"\u009a\3\2\2\2\u023f\u0240\7R\2\2\u0240\u0241\7e\2\2\u0241\u009c\3\2\2"+
"\2\u0242\u0243\7R\2\2\u0243\u0244\7|\2\2\u0244\u009e\3\2\2\2\u0245\u0246"+
"\7R\2\2\u0246\u0247\7p\2\2\u0247\u00a0\3\2\2\2\u0248\u0249\7R\2\2\u0249"+
"\u024a\7x\2\2\u024a\u00a2\3\2\2\2\u024b\u024c\7\60\2\2\u024c\u024d\7y"+
"\2\2\u024d\u00a4\3\2\2\2\u024e\u024f\7v\2\2\u024f\u0250\7t\2\2\u0250\u0251"+
"\7w\2\2\u0251\u0252\7g\2\2\u0252\u00a6\3\2\2\2\u0253\u0254\7h\2\2\u0254"+
"\u0255\7c\2\2\u0255\u0256\7n\2\2\u0256\u0257\7u\2\2\u0257\u0258\7g\2\2"+
"\u0258\u00a8\3\2\2\2\u0259\u025a\7\'\2\2\u025a\u025b\7c\2\2\u025b\u025c"+
"\7u\2\2\u025c\u025d\7o\2\2\u025d\u00aa\3\2\2\2\u025e\u025f\7u\2\2\u025f"+
"\u0260\7w\2\2\u0260\u0261\7d\2\2\u0261\u00ac\3\2\2\2\u0262\u0263\7/\2"+
"\2\u0263\u0264\7@\2\2\u0264\u00ae\3\2\2\2\u0265\u0266\7c\2\2\u0266\u0267"+
"\7u\2\2\u0267\u0268\7o\2\2\u0268\u0269\7u\2\2\u0269\u026a\7w\2\2\u026a"+
"\u026b\7d\2\2\u026b\u00b0\3\2\2\2\u026c\u026d\7u\2\2\u026d\u026e\7v\2"+
"\2\u026e\u026f\7c\2\2\u026f\u0270\7e\2\2\u0270\u0271\7m\2\2\u0271\u00b2"+
"\3\2\2\2\u0272\u0273\7e\2\2\u0273\u0274\7n\2\2\u0274\u0275\7q\2\2\u0275"+
"\u0276\7d\2\2\u0276\u0277\7d\2\2\u0277\u0278\7g\2\2\u0278\u0279\7t\2\2"+
"\u0279\u027a\7u\2\2\u027a\u00b4\3\2\2\2\u027b\u027c\7k\2\2\u027c\u027d"+
"\7h\2\2\u027d\u00b6\3\2\2\2\u027e\u027f\7g\2\2\u027f\u0280\7n\2\2\u0280"+
"\u0281\7u\2\2\u0281\u0282\7g\2\2\u0282\u00b8\3\2\2\2\u0283\u0284\7k\2"+
"\2\u0284\u0285\7h\2\2\u0285\u0286\7a\2\2\u0286\u0287\7e\2\2\u0287\u0288"+
"\7u\2\2\u0288\u00ba\3\2\2\2\u0289\u028a\7k\2\2\u028a\u028b\7h\2\2\u028b"+
"\u028c\7a\2\2\u028c\u028d\7e\2\2\u028d\u028e\7e\2\2\u028e\u00bc\3\2\2"+
"\2\u028f\u0290\7k\2\2\u0290\u0291\7h\2\2\u0291\u0292\7a\2\2\u0292\u0293"+
"\7g\2\2\u0293\u0294\7s\2\2\u0294\u00be\3\2\2\2\u0295\u0296\7k\2\2\u0296"+
"\u0297\7h\2\2\u0297\u0298\7a\2\2\u0298\u0299\7|\2\2\u0299\u00c0\3\2\2"+
"\2\u029a\u029b\7k\2\2\u029b\u029c\7h\2\2\u029c\u029d\7a\2\2\u029d\u029e"+
"\7p\2\2\u029e\u029f\7g\2\2\u029f\u00c2\3\2\2\2\u02a0\u02a1\7k\2\2\u02a1"+
"\u02a2\7h\2\2\u02a2\u02a3\7a\2\2\u02a3\u02a4\7p\2\2\u02a4\u02a5\7|\2\2"+
"\u02a5\u00c4\3\2\2\2\u02a6\u02a7\7k\2\2\u02a7\u02a8\7h\2\2\u02a8\u02a9"+
"\7a\2\2\u02a9\u02aa\7r\2\2\u02aa\u02ab\7n\2\2\u02ab\u00c6\3\2\2\2\u02ac"+
"\u02ad\7k\2\2\u02ad\u02ae\7h\2\2\u02ae\u02af\7a\2\2\u02af\u02b0\7r\2\2"+
"\u02b0\u02b1\7q\2\2\u02b1\u02b2\7u\2\2\u02b2\u00c8\3\2\2\2\u02b3\u02b4"+
"\7k\2\2\u02b4\u02b5\7h\2\2\u02b5\u02b6\7a\2\2\u02b6\u02b7\7o\2\2\u02b7"+
"\u02b8\7k\2\2\u02b8\u00ca\3\2\2\2\u02b9\u02ba\7k\2\2\u02ba\u02bb\7h\2"+
"\2\u02bb\u02bc\7a\2\2\u02bc\u02bd\7p\2\2\u02bd\u02be\7g\2\2\u02be\u02bf"+
"\7i\2\2\u02bf\u00cc\3\2\2\2\u02c0\u02c1\7k\2\2\u02c1\u02c2\7h\2\2\u02c2"+
"\u02c3\7a\2\2\u02c3\u02c4\7x\2\2\u02c4\u02c5\7u\2\2\u02c5\u00ce\3\2\2"+
"\2\u02c6\u02c7\7k\2\2\u02c7\u02c8\7h\2\2\u02c8\u02c9\7a\2\2\u02c9\u02ca"+
"\7x\2\2\u02ca\u02cb\7e\2\2\u02cb\u00d0\3\2\2\2\u02cc\u02cd\7h\2\2\u02cd"+
"\u02ce\7q\2\2\u02ce\u02cf\7t\2\2\u02cf\u00d2\3\2\2\2\u02d0\u02d1\7k\2"+
"\2\u02d1\u02d2\7p\2\2\u02d2\u00d4\3\2\2\2\u02d3\u02d4\7y\2\2\u02d4\u02d5"+
"\7j\2\2\u02d5\u02d6\7k\2\2\u02d6\u02d7\7n\2\2\u02d7\u02d8\7g\2\2\u02d8"+
"\u00d6\3\2\2\2\u02d9\u02da\7t\2\2\u02da\u02db\7g\2\2\u02db\u02dc\7r\2"+
"\2\u02dc\u02dd\7g\2\2\u02dd\u02de\7c\2\2\u02de\u02df\7v\2\2\u02df\u00d8"+
"\3\2\2\2\u02e0\u02e1\7w\2\2\u02e1\u02e2\7p\2\2\u02e2\u02e3\7v\2\2\u02e3"+
"\u02e4\7k\2\2\u02e4\u02e5\7n\2\2\u02e5\u00da\3\2\2\2\u02e6\u02e7\7y\2"+
"\2\u02e7\u02e8\7j\2\2\u02e8\u02e9\7g\2\2\u02e9\u02ea\7p\2\2\u02ea\u00dc"+
"\3\2\2\2\u02eb\u02ef\t\2\2\2\u02ec\u02ee\t\3\2\2\u02ed\u02ec\3\2\2\2\u02ee"+
"\u02f1\3\2\2\2\u02ef\u02ed\3\2\2\2\u02ef\u02f0\3\2\2\2\u02f0\u02f2\3\2"+
"\2\2\u02f1\u02ef\3\2\2\2\u02f2\u02f3\5\u00dfp\2\u02f3\u02f4\3\2\2\2\u02f4"+
"\u02f5\bo\2\2\u02f5\u00de\3\2\2\2\u02f6\u02fa\7=\2\2\u02f7\u02f9\n\2\2"+
"\2\u02f8\u02f7\3\2\2\2\u02f9\u02fc\3\2\2\2\u02fa\u02f8\3\2\2\2\u02fa\u02fb"+
"\3\2\2\2\u02fb\u02fd\3\2\2\2\u02fc\u02fa\3\2\2\2\u02fd\u02fe\bp\2\2\u02fe"+
"\u00e0\3\2\2\2\u02ff\u0300\t\3\2\2\u0300\u0301\3\2\2\2\u0301\u0302\bq"+
"\3\2\u0302\u00e2\3\2\2\2\u0303\u0305\t\2\2\2\u0304\u0303\3\2\2\2\u0305"+
"\u0306\3\2\2\2\u0306\u0304\3\2\2\2\u0306\u0307\3\2\2\2\u0307\u00e4\3\2"+
"\2\2\u0308\u0309\7x\2\2\u0309\u030a\7q\2\2\u030a\u030b\7k\2\2\u030b\u030c"+
"\7f\2\2\u030c\u00e6\3\2\2\2\u030d\u0311\t\4\2\2\u030e\u0310\t\5\2\2\u030f"+
"\u030e\3\2\2\2\u0310\u0313\3\2\2\2\u0311\u030f\3\2\2\2\u0311\u0312\3\2"+
"\2\2\u0312\u00e8\3\2\2\2\u0313\u0311\3\2\2\2\u0314\u031c\4\62;\2\u0315"+
"\u0317\4\63;\2\u0316\u0318\4\62;\2\u0317\u0316\3\2\2\2\u0318\u0319\3\2"+
"\2\2\u0319\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u031c\3\2\2\2\u031b"+
"\u0314\3\2\2\2\u031b\u0315\3\2\2\2\u031c\u00ea\3\2\2\2\u031d\u031f\7&"+
"\2\2\u031e\u0320\t\6\2\2\u031f\u031e\3\2\2\2\u0320\u0321\3\2\2\2\u0321"+
"\u031f\3\2\2\2\u0321\u0322\3\2\2\2\u0322\u00ec\3\2\2\2\u0323\u0325\7\'"+
"\2\2\u0324\u0326\4\62\63\2\u0325\u0324\3\2\2\2\u0326\u0327\3\2\2\2\u0327"+
"\u0325\3\2\2\2\u0327\u0328\3\2\2\2\u0328\u00ee\3\2\2\2\u0329\u032a\7("+
"\2\2\u032a\u00f0\3\2\2\2\u032b\u0331\5\u00f3z\2\u032c\u032e\t\7\2\2\u032d"+
"\u032f\t\b\2\2\u032e\u032d\3\2\2\2\u032e\u032f\3\2\2\2\u032f\u0330\3\2"+
"\2\2\u0330\u0332\5\u00f3z\2\u0331\u032c\3\2\2\2\u0331\u0332\3\2\2\2\u0332"+
"\u00f2\3\2\2\2\u0333\u0335\4\62;\2\u0334\u0333\3\2\2\2\u0335\u0336\3\2"+
"\2\2\u0336\u0334\3\2\2\2\u0336\u0337\3\2\2\2\u0337\u033e\3\2\2\2\u0338"+
"\u033a\7\60\2\2\u0339\u033b\4\62;\2\u033a\u0339\3\2\2\2\u033b\u033c\3"+
"\2\2\2\u033c\u033a\3\2\2\2\u033c\u033d\3\2\2\2\u033d\u033f\3\2\2\2\u033e"+
"\u0338\3\2\2\2\u033e\u033f\3\2\2\2\u033f\u00f4\3\2\2\2\u0340\u0341\7^"+
"\2\2\u0341\u0345\13\2\2\2\u0342\u0343\7^\2\2\u0343\u0345\5\u00e3r\2\u0344"+
"\u0340\3\2\2\2\u0344\u0342\3\2\2\2\u0345\u00f6\3\2\2\2\u0346\u034b\7$"+
"\2\2\u0347\u034a\5\u00f5{\2\u0348\u034a\n\t\2\2\u0349\u0347\3\2\2\2\u0349"+
"\u0348\3\2\2\2\u034a\u034d\3\2\2\2\u034b\u0349\3\2\2\2\u034b\u034c\3\2"+
"\2\2\u034c\u034e\3\2\2\2\u034d\u034b\3\2\2\2\u034e\u034f\7$\2\2\u034f"+
"\u0350\b|\4\2\u0350\u00f8\3\2\2\2\u0351\u0352\7}\2\2\u0352\u0353\7}\2"+
"\2\u0353\u0355\3\2\2\2\u0354\u0356\13\2\2\2\u0355\u0354\3\2\2\2\u0356"+
"\u0357\3\2\2\2\u0357\u0358\3\2\2\2\u0357\u0355\3\2\2\2\u0358\u0359\3\2"+
"\2\2\u0359\u035a\7\177\2\2\u035a\u035b\7\177\2\2\u035b\u035c\3\2\2\2\u035c"+
"\u035d\b}\5\2\u035d\u00fa\3\2\2\2\u035e\u0361\7)\2\2\u035f\u0362\5\u00f5"+
"{\2\u0360\u0362\n\t\2\2\u0361\u035f\3\2\2\2\u0361\u0360\3\2\2\2\u0362"+
"\u0363\3\2\2\2\u0363\u0364\7)\2\2\u0364\u0365\b~\6\2\u0365\u00fc\3\2\2"+
"\2\u0366\u0367\7B\2\2\u0367\u0368\7|\2\2\u0368\u0369\7r\2\2\u0369\u00fe"+
"\3\2\2\2\u036a\u036b\7]\2\2\u036b\u036c\7_\2\2\u036c\u0100\3\2\2\2\26"+
"\2\u02ef\u02fa\u0306\u0311\u0319\u031b\u031f\u0321\u0327\u032e\u0331\u0336"+
"\u033c\u033e\u0344\u0349\u034b\u0357\u0361\7\2\3\2\b\2\2\3|\2\3}\3\3~"+
"\4";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {

File diff suppressed because it is too large Load Diff