mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
7facb4f372 | |||
ee90fed489 | |||
4796c56c35 | |||
e2cb031386 | |||
a0bc97b90c | |||
fd240899bd | |||
885b22df40 | |||
11de3db25f | |||
14a13da7ec | |||
875a71c786 | |||
0ff5b79353 | |||
8c4d276810 | |||
3dd38c0ac8 | |||
b8816a0e2f | |||
a01a9e76f9 | |||
357d704aec | |||
868df1865c | |||
654d74da1e | |||
59939c727a | |||
fbcf190324 | |||
b9922a90cc | |||
66e0b07428 | |||
01e617ae8f | |||
52769decd4 | |||
165eec4054 | |||
8c2e602cc7 |
9
.idea/libraries/antlr_4_8_complete.xml
generated
Normal file
9
.idea/libraries/antlr_4_8_complete.xml
generated
Normal 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
9
.idea/libraries/antlr_runtime_4_8.xml
generated
Normal 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
29
.idea/markdown-navigator-enh.xml
generated
Normal 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
57
.idea/markdown-navigator.xml
generated
Normal 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
16
.idea/misc.xml
generated
@ -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
1
.idea/modules.xml
generated
@ -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" />
|
||||
|
@ -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>
|
@ -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
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
--------------
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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>
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
1.62
|
||||
1.70
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(")")
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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),
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
3
compiler/src/prog8/compiler/AssemblyError.kt
Normal file
3
compiler/src/prog8/compiler/AssemblyError.kt
Normal file
@ -0,0 +1,3 @@
|
||||
package prog8.compiler
|
||||
|
||||
internal class AssemblyError(msg: String) : RuntimeException(msg)
|
@ -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
|
||||
}
|
||||
|
16
compiler/src/prog8/compiler/target/CompilationTarget.kt
Normal file
16
compiler/src/prog8/compiler/target/CompilationTarget.kt
Normal 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
|
||||
}
|
||||
}
|
12
compiler/src/prog8/compiler/target/IAssemblyGenerator.kt
Normal file
12
compiler/src/prog8/compiler/target/IAssemblyGenerator.kt
Normal 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)
|
||||
}
|
15
compiler/src/prog8/compiler/target/IMachineDefinition.kt
Normal file
15
compiler/src/prog8/compiler/target/IMachineDefinition.kt
Normal 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
|
||||
}
|
@ -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",
|
||||
|
@ -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
|
@ -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()})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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("""
|
||||
|
@ -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
|
||||
|
@ -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}")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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}")
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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++
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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>
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
27
examples/screencodes.p8
Normal 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]
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
51
gradlew
vendored
@ -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
18
gradlew.bat
vendored
@ -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
|
||||
|
Binary file not shown.
Binary file not shown.
@ -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
|
||||
|
@ -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'
|
||||
// }
|
||||
}
|
||||
|
@ -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>
|
@ -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
Reference in New Issue
Block a user