mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 16:23:42 +00:00
Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
b036e5ed72 | |||
5f1ec80ae0 | |||
fbecedaf41 | |||
aa36acd65a | |||
8d1a4588d3 | |||
66d2af4453 | |||
ef6c731bb3 | |||
98a638a2f3 | |||
96d8a7f0d7 | |||
3162b10392 | |||
e2358de27c | |||
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 |
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,8 +1,8 @@
|
|||||||
.idea/workspace.xml
|
.idea/workspace.xml
|
||||||
.idea/discord.xml
|
.idea/discord.xml
|
||||||
/build/
|
build/
|
||||||
/dist/
|
dist/
|
||||||
/output/
|
output/
|
||||||
.*cache/
|
.*cache/
|
||||||
*.directory
|
*.directory
|
||||||
*.prg
|
*.prg
|
||||||
@ -12,8 +12,8 @@
|
|||||||
*.vice-mon-list
|
*.vice-mon-list
|
||||||
docs/build
|
docs/build
|
||||||
out/
|
out/
|
||||||
**/*.interp
|
parser/**/*.interp
|
||||||
**/*.tokens
|
parser/**/*.tokens
|
||||||
|
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*.egg
|
*.egg
|
||||||
@ -28,5 +28,4 @@ parsetab.py
|
|||||||
.attach_pid*
|
.attach_pid*
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
build/
|
|
||||||
/prog8compiler.jar
|
/prog8compiler.jar
|
||||||
|
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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<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">
|
<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" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
|
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@ -2,7 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<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$/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$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.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:
|
Rapid edit-compile-run-debug cycle:
|
||||||
|
|
||||||
- use modern PC to work on
|
- use modern PC to work on
|
||||||
- quick compilation times (seconds)
|
- 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
|
- 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
|
- 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,
|
- 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.
|
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.
|
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
||||||
|
|
||||||
Documentation/manual
|
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
|
Required tools
|
||||||
--------------
|
--------------
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.70"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// id "org.jetbrains.kotlin.jvm" version $kotlinVersion
|
// id "org.jetbrains.kotlin.jvm" version "1.3.70"
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'org.jetbrains.dokka' version "0.9.18"
|
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'
|
id 'java'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,17 +29,17 @@ def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':parser')
|
implementation project(':parser')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
// runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
implementation 'org.antlr:antlr4-runtime:4.8'
|
||||||
runtime 'org.antlr:antlr4-runtime:4.7.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
|
||||||
runtime project(':parser')
|
// 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.junit.jupiter:junit-jupiter-api:5.3.2'
|
||||||
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
|
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
|
||||||
compile 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
@ -85,8 +85,8 @@ artifacts {
|
|||||||
|
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
baseName = 'prog8compiler'
|
archiveBaseName = 'prog8compiler'
|
||||||
version = prog8version
|
archiveVersion = prog8version
|
||||||
// minimize()
|
// minimize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<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="module" module-name="parser" />
|
||||||
<orderEntry type="library" name="unittest-libs" level="project" />
|
<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="kotlinx-cli-jvm-0.1.0-dev-5" level="project" />
|
||||||
|
<orderEntry type="library" name="antlr-runtime-4.8" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
741
compiler/res/prog8lib/c64floats.asm
Normal file
741
compiler/res/prog8lib/c64floats.asm
Normal file
@ -0,0 +1,741 @@
|
|||||||
|
; --- low level floating point assembly routines for the C64
|
||||||
|
|
||||||
|
ub2float .proc
|
||||||
|
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
|
; clobbers A, Y
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
sty c64.SCRATCH_ZPWORD2+1
|
||||||
|
ldy c64.SCRATCH_ZPB1
|
||||||
|
jsr FREADUY
|
||||||
|
_fac_to_mem ldx c64.SCRATCH_ZPWORD2
|
||||||
|
ldy c64.SCRATCH_ZPWORD2+1
|
||||||
|
jsr MOVMF
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
b2float .proc
|
||||||
|
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
|
; clobbers A, Y
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
sty c64.SCRATCH_ZPWORD2+1
|
||||||
|
lda c64.SCRATCH_ZPB1
|
||||||
|
jsr FREADSA
|
||||||
|
jmp ub2float._fac_to_mem
|
||||||
|
.pend
|
||||||
|
|
||||||
|
uw2float .proc
|
||||||
|
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
sty c64.SCRATCH_ZPWORD2+1
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr GIVUAYFAY
|
||||||
|
jmp ub2float._fac_to_mem
|
||||||
|
.pend
|
||||||
|
|
||||||
|
w2float .proc
|
||||||
|
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
sty c64.SCRATCH_ZPWORD2+1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr GIVAYF
|
||||||
|
jmp ub2float._fac_to_mem
|
||||||
|
.pend
|
||||||
|
|
||||||
|
stack_b2float .proc
|
||||||
|
; -- b2float operating on the stack
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr FREADSA
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
stack_w2float .proc
|
||||||
|
; -- w2float operating on the stack
|
||||||
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr GIVAYF
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
stack_ub2float .proc
|
||||||
|
; -- ub2float operating on the stack
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
tay
|
||||||
|
jsr FREADUY
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
stack_uw2float .proc
|
||||||
|
; -- uw2float operating on the stack
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
ldy c64.ESTACK_HI,x
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr GIVUAYFAY
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
stack_float2w .proc ; also used for float2b
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr AYINT
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
lda $64
|
||||||
|
sta c64.ESTACK_HI,x
|
||||||
|
lda $65
|
||||||
|
sta c64.ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
stack_float2uw .proc ; also used for float2ub
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr GETADR
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
sta c64.ESTACK_HI,x
|
||||||
|
tya
|
||||||
|
sta c64.ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
push_float .proc
|
||||||
|
; ---- push mflpt5 in A/Y onto stack
|
||||||
|
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
sty c64.SCRATCH_ZPWORD1+1
|
||||||
|
ldy #0
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta c64.ESTACK_LO,x
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta c64.ESTACK_HI,x
|
||||||
|
dex
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta c64.ESTACK_LO,x
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta c64.ESTACK_HI,x
|
||||||
|
dex
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta c64.ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_rndf .proc
|
||||||
|
; -- put a random floating point value on the stack
|
||||||
|
stx c64.SCRATCH_ZPREG
|
||||||
|
lda #1
|
||||||
|
jsr FREADSA
|
||||||
|
jsr RND ; rng into fac1
|
||||||
|
ldx #<_rndf_rnum5
|
||||||
|
ldy #>_rndf_rnum5
|
||||||
|
jsr MOVMF ; fac1 to mem X/Y
|
||||||
|
ldx c64.SCRATCH_ZPREG
|
||||||
|
lda #<_rndf_rnum5
|
||||||
|
ldy #>_rndf_rnum5
|
||||||
|
jmp push_float
|
||||||
|
_rndf_rnum5 .byte 0,0,0,0,0
|
||||||
|
.pend
|
||||||
|
|
||||||
|
push_float_from_indexed_var .proc
|
||||||
|
; -- push the float from the array at A/Y with index on stack, onto the stack.
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
sty c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr prog8_lib.pop_index_times_5
|
||||||
|
jsr prog8_lib.add_a_to_zpword
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jmp push_float
|
||||||
|
.pend
|
||||||
|
|
||||||
|
pop_float .proc
|
||||||
|
; ---- pops mflpt5 from stack to memory A/Y
|
||||||
|
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
sty c64.SCRATCH_ZPWORD1+1
|
||||||
|
ldy #4
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
pop_float_fac1 .proc
|
||||||
|
; -- pops float from stack into FAC1
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr pop_float
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jmp MOVFM
|
||||||
|
.pend
|
||||||
|
|
||||||
|
pop_float_to_indexed_var .proc
|
||||||
|
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
sty c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr prog8_lib.pop_index_times_5
|
||||||
|
jsr prog8_lib.add_a_to_zpword
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jmp pop_float
|
||||||
|
.pend
|
||||||
|
|
||||||
|
copy_float .proc
|
||||||
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||||
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
sty c64.SCRATCH_ZPWORD2+1
|
||||||
|
ldy #0
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
inc_var_f .proc
|
||||||
|
; -- add 1 to float pointed to by A/Y
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
sty c64.SCRATCH_ZPWORD1+1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr MOVFM
|
||||||
|
lda #<FL_FONE
|
||||||
|
ldy #>FL_FONE
|
||||||
|
jsr FADD
|
||||||
|
ldx c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr MOVMF
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
dec_var_f .proc
|
||||||
|
; -- subtract 1 from float pointed to by A/Y
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
sty c64.SCRATCH_ZPWORD1+1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<FL_FONE
|
||||||
|
ldy #>FL_FONE
|
||||||
|
jsr MOVFM
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr FSUB
|
||||||
|
ldx c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr MOVMF
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
inc_indexed_var_f .proc
|
||||||
|
; -- add 1 to float in array pointed to by A/Y, at index X
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPB1
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPB1
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jmp inc_var_f
|
||||||
|
.pend
|
||||||
|
|
||||||
|
dec_indexed_var_f .proc
|
||||||
|
; -- subtract 1 to float in array pointed to by A/Y, at index X
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPB1
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPB1
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jmp dec_var_f
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
pop_2_floats_f2_in_fac1 .proc
|
||||||
|
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||||
|
lda #<fmath_float2
|
||||||
|
ldy #>fmath_float2
|
||||||
|
jsr pop_float
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr pop_float
|
||||||
|
lda #<fmath_float2
|
||||||
|
ldy #>fmath_float2
|
||||||
|
jmp MOVFM
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||||
|
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||||
|
|
||||||
|
push_fac1_as_result .proc
|
||||||
|
; -- push the float in FAC1 onto the stack, and return from calculation
|
||||||
|
ldx #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr MOVMF
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
jmp push_float
|
||||||
|
.pend
|
||||||
|
|
||||||
|
pow_f .proc
|
||||||
|
; -- push f1 ** f2 on stack
|
||||||
|
lda #<fmath_float2
|
||||||
|
ldy #>fmath_float2
|
||||||
|
jsr pop_float
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr pop_float
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr CONUPK ; fac2 = float1
|
||||||
|
lda #<fmath_float2
|
||||||
|
ldy #>fmath_float2
|
||||||
|
jsr FPWR
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
div_f .proc
|
||||||
|
; -- push f1/f2 on stack
|
||||||
|
jsr pop_2_floats_f2_in_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr FDIV
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
add_f .proc
|
||||||
|
; -- push f1+f2 on stack
|
||||||
|
jsr pop_2_floats_f2_in_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr FADD
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
sub_f .proc
|
||||||
|
; -- push f1-f2 on stack
|
||||||
|
jsr pop_2_floats_f2_in_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr FSUB
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
mul_f .proc
|
||||||
|
; -- push f1*f2 on stack
|
||||||
|
jsr pop_2_floats_f2_in_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr FMULT
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
neg_f .proc
|
||||||
|
; -- push -flt back on stack
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr NEGOP
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
abs_f .proc
|
||||||
|
; -- push abs(float) on stack (as float)
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr ABS
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
equal_f .proc
|
||||||
|
; -- are the two mflpt5 numbers on the stack identical?
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO-3,x
|
||||||
|
cmp c64.ESTACK_LO,x
|
||||||
|
bne _equals_false
|
||||||
|
lda c64.ESTACK_LO-2,x
|
||||||
|
cmp c64.ESTACK_LO+1,x
|
||||||
|
bne _equals_false
|
||||||
|
lda c64.ESTACK_LO-1,x
|
||||||
|
cmp c64.ESTACK_LO+2,x
|
||||||
|
bne _equals_false
|
||||||
|
lda c64.ESTACK_HI-2,x
|
||||||
|
cmp c64.ESTACK_HI+1,x
|
||||||
|
bne _equals_false
|
||||||
|
lda c64.ESTACK_HI-1,x
|
||||||
|
cmp c64.ESTACK_HI+2,x
|
||||||
|
bne _equals_false
|
||||||
|
_equals_true lda #1
|
||||||
|
_equals_store inx
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
_equals_false lda #0
|
||||||
|
beq _equals_store
|
||||||
|
.pend
|
||||||
|
|
||||||
|
notequal_f .proc
|
||||||
|
; -- are the two mflpt5 numbers on the stack different?
|
||||||
|
jsr equal_f
|
||||||
|
eor #1 ; invert the result
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
less_f .proc
|
||||||
|
; -- is f1 < f2?
|
||||||
|
jsr compare_floats
|
||||||
|
cmp #255
|
||||||
|
beq compare_floats._return_true
|
||||||
|
bne compare_floats._return_false
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
lesseq_f .proc
|
||||||
|
; -- is f1 <= f2?
|
||||||
|
jsr compare_floats
|
||||||
|
cmp #255
|
||||||
|
beq compare_floats._return_true
|
||||||
|
cmp #0
|
||||||
|
beq compare_floats._return_true
|
||||||
|
bne compare_floats._return_false
|
||||||
|
.pend
|
||||||
|
|
||||||
|
greater_f .proc
|
||||||
|
; -- is f1 > f2?
|
||||||
|
jsr compare_floats
|
||||||
|
cmp #1
|
||||||
|
beq compare_floats._return_true
|
||||||
|
bne compare_floats._return_false
|
||||||
|
.pend
|
||||||
|
|
||||||
|
greatereq_f .proc
|
||||||
|
; -- is f1 >= f2?
|
||||||
|
jsr compare_floats
|
||||||
|
cmp #1
|
||||||
|
beq compare_floats._return_true
|
||||||
|
cmp #0
|
||||||
|
beq compare_floats._return_true
|
||||||
|
bne compare_floats._return_false
|
||||||
|
.pend
|
||||||
|
|
||||||
|
compare_floats .proc
|
||||||
|
lda #<fmath_float2
|
||||||
|
ldy #>fmath_float2
|
||||||
|
jsr pop_float
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr pop_float
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr MOVFM ; fac1 = flt1
|
||||||
|
lda #<fmath_float2
|
||||||
|
ldy #>fmath_float2
|
||||||
|
stx c64.SCRATCH_ZPREG
|
||||||
|
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||||
|
ldx c64.SCRATCH_ZPREG
|
||||||
|
rts
|
||||||
|
_return_false lda #0
|
||||||
|
_return_result sta c64.ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
_return_true lda #1
|
||||||
|
bne _return_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_sin .proc
|
||||||
|
; -- push sin(f) back onto stack
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr SIN
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_cos .proc
|
||||||
|
; -- push cos(f) back onto stack
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr COS
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_tan .proc
|
||||||
|
; -- push tan(f) back onto stack
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr TAN
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_atan .proc
|
||||||
|
; -- push atan(f) back onto stack
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr ATN
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_ln .proc
|
||||||
|
; -- push ln(f) back onto stack
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr LOG
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_log2 .proc
|
||||||
|
; -- push log base 2, ln(f)/ln(2), back onto stack
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr LOG
|
||||||
|
jsr MOVEF
|
||||||
|
lda #<c64.FL_LOG2
|
||||||
|
ldy #>c64.FL_LOG2
|
||||||
|
jsr MOVFM
|
||||||
|
jsr FDIVT
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_sqrt .proc
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr SQR
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_rad .proc
|
||||||
|
; -- convert degrees to radians (d * pi / 180)
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<_pi_div_180
|
||||||
|
ldy #>_pi_div_180
|
||||||
|
jsr FMULT
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_deg .proc
|
||||||
|
; -- convert radians to degrees (d * (1/ pi * 180))
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda #<_one_over_pi_div_180
|
||||||
|
ldy #>_one_over_pi_div_180
|
||||||
|
jsr FMULT
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_round .proc
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr FADDH
|
||||||
|
jsr INT
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_floor .proc
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
jsr INT
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_ceil .proc
|
||||||
|
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
||||||
|
jsr pop_float_fac1
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
ldx #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr MOVMF
|
||||||
|
jsr INT
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr FCOMP
|
||||||
|
cmp #0
|
||||||
|
beq +
|
||||||
|
lda #<FL_FONE
|
||||||
|
ldy #>FL_FONE
|
||||||
|
jsr FADD
|
||||||
|
+ jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_any_f .proc
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x ; array size
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
||||||
|
jmp prog8_lib.func_any_b._entry
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_all_f .proc
|
||||||
|
inx
|
||||||
|
jsr prog8_lib.peek_address
|
||||||
|
lda c64.ESTACK_LO,x ; array size
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
||||||
|
tay
|
||||||
|
dey
|
||||||
|
- lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
clc
|
||||||
|
dey
|
||||||
|
adc (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
adc (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
adc (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
adc (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
cmp #0
|
||||||
|
beq +
|
||||||
|
cpy #255
|
||||||
|
bne -
|
||||||
|
lda #1
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
+ sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_max_f .proc
|
||||||
|
lda #255
|
||||||
|
sta _minmax_cmp+1
|
||||||
|
lda #<_largest_neg_float
|
||||||
|
ldy #>_largest_neg_float
|
||||||
|
_minmax_entry jsr MOVFM
|
||||||
|
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
- sty c64.SCRATCH_ZPREG
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr FCOMP
|
||||||
|
_minmax_cmp cmp #255 ; modified
|
||||||
|
bne +
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr MOVFM
|
||||||
|
+ lda c64.SCRATCH_ZPWORD1
|
||||||
|
clc
|
||||||
|
adc #5
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
bcc +
|
||||||
|
inc c64.SCRATCH_ZPWORD1+1
|
||||||
|
+ ldy c64.SCRATCH_ZPREG
|
||||||
|
dey
|
||||||
|
cpy #255
|
||||||
|
bne -
|
||||||
|
jmp push_fac1_as_result
|
||||||
|
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_min_f .proc
|
||||||
|
lda #1
|
||||||
|
sta func_max_f._minmax_cmp+1
|
||||||
|
lda #<_largest_pos_float
|
||||||
|
ldy #>_largest_pos_float
|
||||||
|
jmp func_max_f._minmax_entry
|
||||||
|
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_sum_f .proc
|
||||||
|
lda #<FL_ZERO
|
||||||
|
ldy #>FL_ZERO
|
||||||
|
jsr MOVFM
|
||||||
|
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
- sty c64.SCRATCH_ZPREG
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
ldy c64.SCRATCH_ZPWORD1+1
|
||||||
|
jsr FADD
|
||||||
|
ldy c64.SCRATCH_ZPREG
|
||||||
|
dey
|
||||||
|
cpy #255
|
||||||
|
beq +
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
clc
|
||||||
|
adc #5
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
bcc -
|
||||||
|
inc c64.SCRATCH_ZPWORD1+1
|
||||||
|
bne -
|
||||||
|
+ jmp push_fac1_as_result
|
||||||
|
.pend
|
||||||
|
|
||||||
|
sign_f .proc
|
||||||
|
jsr pop_float_fac1
|
||||||
|
jsr SIGN
|
||||||
|
sta c64.ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
.pend
|
@ -41,25 +41,25 @@ c64flt {
|
|||||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||||
|
|
||||||
; checked functions below:
|
; checked functions below:
|
||||||
asmsub MOVFM (uword mflpt @ AY) clobbers(A,Y) = $bba2 ; load mflpt value from memory in A/Y into fac1
|
romsub $bba2 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
||||||
asmsub FREADMEM () clobbers(A,Y) = $bba6 ; load mflpt value from memory in $22/$23 into fac1
|
romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac1
|
||||||
asmsub CONUPK (uword mflpt @ AY) clobbers(A,Y) = $ba8c ; load mflpt value from memory in A/Y into fac2
|
romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
||||||
asmsub FAREADMEM () clobbers(A,Y) = $ba90 ; load mflpt value from memory in $22/$23 into fac2
|
romsub $ba90 = FAREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac2
|
||||||
asmsub MOVFA () clobbers(A,X) = $bbfc ; copy fac2 to fac1
|
romsub $bbfc = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||||
asmsub MOVAF () clobbers(A,X) = $bc0c ; copy fac1 to fac2 (rounded)
|
romsub $bc0c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||||
asmsub MOVEF () clobbers(A,X) = $bc0f ; copy fac1 to fac2
|
romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||||
asmsub MOVMF (uword mflpt @ XY) clobbers(A,Y) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
|
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||||
|
|
||||||
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
||||||
; (tip: use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
; (tip: use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
asmsub FTOSWORDYA () clobbers(X) -> ubyte @ Y, ubyte @ A = $b1aa ; note: calls AYINT.
|
romsub $b1aa = FTOSWORDYA() clobbers(X) -> ubyte @ Y, ubyte @ A ; note: calls AYINT.
|
||||||
|
|
||||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||||
; (tip: use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
; (tip: use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
asmsub GETADR () clobbers(X) -> ubyte @ Y, ubyte @ A = $b7f7
|
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||||
|
|
||||||
asmsub QINT () clobbers(A,X,Y) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
romsub $bc9b = QINT() clobbers(A,X,Y) ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||||
asmsub AYINT () clobbers(A,X,Y) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
romsub $b1bf = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||||
|
|
||||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||||
; (tip: use c64flt.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
; (tip: use c64flt.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||||
@ -67,50 +67,49 @@ asmsub AYINT () clobbers(A,X,Y) = $b1bf ; fac1-> signed word in 100-101 ($64
|
|||||||
; there is also c64flt.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
; there is also c64flt.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||||
; there is also c64flt.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
; there is also c64flt.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||||
; there is also c64flt.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
; there is also c64flt.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||||
asmsub GIVAYF (ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y) = $b391
|
romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||||
|
|
||||||
asmsub FREADUY (ubyte value @ Y) clobbers(A,X,Y) = $b3a2 ; 8 bit unsigned Y -> float in fac1
|
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||||
asmsub FREADSA (byte value @ A) clobbers(A,X,Y) = $bc3c ; 8 bit signed A -> float in fac1
|
romsub $bc3c = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||||
asmsub FREADSTR (ubyte length @ A) clobbers(A,X,Y) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
|
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length
|
||||||
asmsub FPRINTLN () clobbers(A,X,Y) = $aabc ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||||
asmsub FOUT () clobbers(X) -> uword @ AY = $bddd ; fac1 -> string, address returned in AY ($0100)
|
romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100)
|
||||||
|
|
||||||
asmsub FADDH () clobbers(A,X,Y) = $b849 ; fac1 += 0.5, for rounding- call this before INT
|
romsub $b849 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
||||||
asmsub MUL10 () clobbers(A,X,Y) = $bae2 ; fac1 *= 10
|
romsub $bae2 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||||
asmsub DIV10 () clobbers(A,X,Y) = $bafe ; fac1 /= 10 , CAUTION: result is always positive!
|
romsub $bafe = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||||
asmsub FCOMP (uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
romsub $bc5b = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||||
|
|
||||||
asmsub FADDT () clobbers(A,X,Y) = $b86a ; fac1 += fac2
|
romsub $b86a = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
||||||
asmsub FADD (uword mflpt @ AY) clobbers(A,X,Y) = $b867 ; fac1 += mflpt value from A/Y
|
romsub $b867 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
|
||||||
asmsub FSUBT () clobbers(A,X,Y) = $b853 ; fac1 = fac2-fac1 mind the order of the operands
|
romsub $b853 = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands
|
||||||
asmsub FSUB (uword mflpt @ AY) clobbers(A,X,Y) = $b850 ; fac1 = mflpt from A/Y - fac1
|
romsub $b850 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||||
asmsub FMULTT () clobbers(A,X,Y) = $ba2b ; fac1 *= fac2
|
romsub $ba2b = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||||
asmsub FMULT (uword mflpt @ AY) clobbers(A,X,Y) = $ba28 ; fac1 *= mflpt value from A/Y
|
romsub $ba28 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||||
asmsub FDIVT () clobbers(A,X,Y) = $bb12 ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||||
asmsub FDIV (uword mflpt @ AY) clobbers(A,X,Y) = $bb0f ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||||
asmsub FPWRT () clobbers(A,X,Y) = $bf7b ; fac1 = fac2 ** fac1
|
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||||
asmsub FPWR (uword mflpt @ AY) clobbers(A,X,Y) = $bf78 ; fac1 = fac2 ** mflpt from A/Y
|
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
||||||
|
|
||||||
asmsub NOTOP () clobbers(A,X,Y) = $aed4 ; fac1 = NOT(fac1)
|
romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1)
|
||||||
asmsub INT () clobbers(A,X,Y) = $bccc ; INT() truncates, use FADDH first to round instead of trunc
|
romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||||
asmsub LOG () clobbers(A,X,Y) = $b9ea ; fac1 = LN(fac1) (natural log)
|
romsub $b9ea = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||||
asmsub SGN () clobbers(A,X,Y) = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
romsub $bc39 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||||
asmsub SIGN () -> ubyte @ A = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
romsub $bc2b = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||||
asmsub ABS () = $bc58 ; fac1 = ABS(fac1)
|
romsub $bc58 = ABS() ; fac1 = ABS(fac1)
|
||||||
asmsub SQR () clobbers(A,X,Y) = $bf71 ; fac1 = SQRT(fac1)
|
romsub $bf71 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
asmsub SQRA () clobbers(A,X,Y) = $bf74 ; fac1 = SQRT(fac2)
|
romsub $bf74 = SQRA() clobbers(A,X,Y) ; fac1 = SQRT(fac2)
|
||||||
asmsub EXP () clobbers(A,X,Y) = $bfed ; fac1 = EXP(fac1) (e ** fac1)
|
romsub $bfed = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||||
asmsub NEGOP () clobbers(A) = $bfb4 ; switch the sign of fac1
|
romsub $bfb4 = NEGOP() clobbers(A) ; switch the sign of fac1
|
||||||
asmsub RND () clobbers(A,X,Y) = $e097 ; fac1 = RND(fac1) float random number generator
|
romsub $e097 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||||
asmsub COS () clobbers(A,X,Y) = $e264 ; fac1 = COS(fac1)
|
romsub $e264 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||||
asmsub SIN () clobbers(A,X,Y) = $e26b ; fac1 = SIN(fac1)
|
romsub $e26b = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||||
asmsub TAN () clobbers(A,X,Y) = $e2b4 ; fac1 = TAN(fac1)
|
romsub $e2b4 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||||
asmsub ATN () clobbers(A,X,Y) = $e30e ; fac1 = ATN(fac1)
|
romsub $e30e = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
asmsub FREADS32() clobbers(A,X,Y) {
|
||||||
asmsub FREADS32 () clobbers(A,X,Y) {
|
|
||||||
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
||||||
%asm {{
|
%asm {{
|
||||||
lda $62
|
lda $62
|
||||||
@ -220,750 +219,6 @@ sub print_fln (float value) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%asminclude "library:c64floats.asm", ""
|
||||||
; --- low level floating point assembly routines
|
|
||||||
%asm {{
|
|
||||||
ub2float .proc
|
|
||||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
|
||||||
; clobbers A, Y
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
sta c64.SCRATCH_ZPWORD2
|
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
|
||||||
ldy c64.SCRATCH_ZPB1
|
|
||||||
jsr FREADUY
|
|
||||||
_fac_to_mem ldx c64.SCRATCH_ZPWORD2
|
|
||||||
ldy c64.SCRATCH_ZPWORD2+1
|
|
||||||
jsr MOVMF
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
b2float .proc
|
|
||||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
|
||||||
; clobbers A, Y
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
sta c64.SCRATCH_ZPWORD2
|
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
|
||||||
lda c64.SCRATCH_ZPB1
|
|
||||||
jsr FREADSA
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
uw2float .proc
|
|
||||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
sta c64.SCRATCH_ZPWORD2
|
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr GIVUAYFAY
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
w2float .proc
|
|
||||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
sta c64.SCRATCH_ZPWORD2
|
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1
|
|
||||||
lda c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr GIVAYF
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
stack_b2float .proc
|
|
||||||
; -- b2float operating on the stack
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_LO,x
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr FREADSA
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
stack_w2float .proc
|
|
||||||
; -- w2float operating on the stack
|
|
||||||
inx
|
|
||||||
ldy c64.ESTACK_LO,x
|
|
||||||
lda c64.ESTACK_HI,x
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr GIVAYF
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
stack_ub2float .proc
|
|
||||||
; -- ub2float operating on the stack
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_LO,x
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
tay
|
|
||||||
jsr FREADUY
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
stack_uw2float .proc
|
|
||||||
; -- uw2float operating on the stack
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_LO,x
|
|
||||||
ldy c64.ESTACK_HI,x
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr GIVUAYFAY
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
stack_float2w .proc ; also used for float2b
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr AYINT
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
lda $64
|
|
||||||
sta c64.ESTACK_HI,x
|
|
||||||
lda $65
|
|
||||||
sta c64.ESTACK_LO,x
|
|
||||||
dex
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
stack_float2uw .proc ; also used for float2ub
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr GETADR
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
sta c64.ESTACK_HI,x
|
|
||||||
tya
|
|
||||||
sta c64.ESTACK_LO,x
|
|
||||||
dex
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
push_float .proc
|
|
||||||
; ---- push mflpt5 in A/Y onto stack
|
|
||||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
|
||||||
ldy #0
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta c64.ESTACK_LO,x
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta c64.ESTACK_HI,x
|
|
||||||
dex
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta c64.ESTACK_LO,x
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta c64.ESTACK_HI,x
|
|
||||||
dex
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta c64.ESTACK_LO,x
|
|
||||||
dex
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_rndf .proc
|
|
||||||
; -- put a random floating point value on the stack
|
|
||||||
stx c64.SCRATCH_ZPREG
|
|
||||||
lda #1
|
|
||||||
jsr FREADSA
|
|
||||||
jsr RND ; rng into fac1
|
|
||||||
ldx #<_rndf_rnum5
|
|
||||||
ldy #>_rndf_rnum5
|
|
||||||
jsr MOVMF ; fac1 to mem X/Y
|
|
||||||
ldx c64.SCRATCH_ZPREG
|
|
||||||
lda #<_rndf_rnum5
|
|
||||||
ldy #>_rndf_rnum5
|
|
||||||
jmp push_float
|
|
||||||
_rndf_rnum5 .byte 0,0,0,0,0
|
|
||||||
.pend
|
|
||||||
|
|
||||||
push_float_from_indexed_var .proc
|
|
||||||
; -- push the float from the array at A/Y with index on stack, onto the stack.
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr prog8_lib.pop_index_times_5
|
|
||||||
jsr prog8_lib.add_a_to_zpword
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jmp push_float
|
|
||||||
.pend
|
|
||||||
|
|
||||||
pop_float .proc
|
|
||||||
; ---- pops mflpt5 from stack to memory A/Y
|
|
||||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
|
||||||
ldy #4
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_LO,x
|
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_HI,x
|
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
lda c64.ESTACK_LO,x
|
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_HI,x
|
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
lda c64.ESTACK_LO,x
|
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
pop_float_fac1 .proc
|
|
||||||
; -- pops float from stack into FAC1
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr pop_float
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jmp MOVFM
|
|
||||||
.pend
|
|
||||||
|
|
||||||
pop_float_to_indexed_var .proc
|
|
||||||
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr prog8_lib.pop_index_times_5
|
|
||||||
jsr prog8_lib.add_a_to_zpword
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jmp pop_float
|
|
||||||
.pend
|
|
||||||
|
|
||||||
copy_float .proc
|
|
||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
|
||||||
sta c64.SCRATCH_ZPWORD2
|
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
|
||||||
ldy #0
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
inc_var_f .proc
|
|
||||||
; -- add 1 to float pointed to by A/Y
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr MOVFM
|
|
||||||
lda #<FL_FONE
|
|
||||||
ldy #>FL_FONE
|
|
||||||
jsr FADD
|
|
||||||
ldx c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr MOVMF
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
dec_var_f .proc
|
|
||||||
; -- subtract 1 from float pointed to by A/Y
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<FL_FONE
|
|
||||||
ldy #>FL_FONE
|
|
||||||
jsr MOVFM
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr FSUB
|
|
||||||
ldx c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr MOVMF
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
inc_indexed_var_f .proc
|
|
||||||
; -- add 1 to float in array pointed to by A/Y, at index X
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
pla
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jmp inc_var_f
|
|
||||||
.pend
|
|
||||||
|
|
||||||
dec_indexed_var_f .proc
|
|
||||||
; -- subtract 1 to float in array pointed to by A/Y, at index X
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
pla
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jmp dec_var_f
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
pop_2_floats_f2_in_fac1 .proc
|
|
||||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
|
||||||
lda #<fmath_float2
|
|
||||||
ldy #>fmath_float2
|
|
||||||
jsr pop_float
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr pop_float
|
|
||||||
lda #<fmath_float2
|
|
||||||
ldy #>fmath_float2
|
|
||||||
jmp MOVFM
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
|
||||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
|
||||||
|
|
||||||
push_fac1_as_result .proc
|
|
||||||
; -- push the float in FAC1 onto the stack, and return from calculation
|
|
||||||
ldx #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr MOVMF
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
jmp push_float
|
|
||||||
.pend
|
|
||||||
|
|
||||||
pow_f .proc
|
|
||||||
; -- push f1 ** f2 on stack
|
|
||||||
lda #<fmath_float2
|
|
||||||
ldy #>fmath_float2
|
|
||||||
jsr pop_float
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr pop_float
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr CONUPK ; fac2 = float1
|
|
||||||
lda #<fmath_float2
|
|
||||||
ldy #>fmath_float2
|
|
||||||
jsr FPWR
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
div_f .proc
|
|
||||||
; -- push f1/f2 on stack
|
|
||||||
jsr pop_2_floats_f2_in_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr FDIV
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
add_f .proc
|
|
||||||
; -- push f1+f2 on stack
|
|
||||||
jsr pop_2_floats_f2_in_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr FADD
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
sub_f .proc
|
|
||||||
; -- push f1-f2 on stack
|
|
||||||
jsr pop_2_floats_f2_in_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr FSUB
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
mul_f .proc
|
|
||||||
; -- push f1*f2 on stack
|
|
||||||
jsr pop_2_floats_f2_in_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr FMULT
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
neg_f .proc
|
|
||||||
; -- push -flt back on stack
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr NEGOP
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
abs_f .proc
|
|
||||||
; -- push abs(float) on stack (as float)
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr ABS
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
equal_f .proc
|
|
||||||
; -- are the two mflpt5 numbers on the stack identical?
|
|
||||||
inx
|
|
||||||
inx
|
|
||||||
inx
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_LO-3,x
|
|
||||||
cmp c64.ESTACK_LO,x
|
|
||||||
bne _equals_false
|
|
||||||
lda c64.ESTACK_LO-2,x
|
|
||||||
cmp c64.ESTACK_LO+1,x
|
|
||||||
bne _equals_false
|
|
||||||
lda c64.ESTACK_LO-1,x
|
|
||||||
cmp c64.ESTACK_LO+2,x
|
|
||||||
bne _equals_false
|
|
||||||
lda c64.ESTACK_HI-2,x
|
|
||||||
cmp c64.ESTACK_HI+1,x
|
|
||||||
bne _equals_false
|
|
||||||
lda c64.ESTACK_HI-1,x
|
|
||||||
cmp c64.ESTACK_HI+2,x
|
|
||||||
bne _equals_false
|
|
||||||
_equals_true lda #1
|
|
||||||
_equals_store inx
|
|
||||||
sta c64.ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
_equals_false lda #0
|
|
||||||
beq _equals_store
|
|
||||||
.pend
|
|
||||||
|
|
||||||
notequal_f .proc
|
|
||||||
; -- are the two mflpt5 numbers on the stack different?
|
|
||||||
jsr equal_f
|
|
||||||
eor #1 ; invert the result
|
|
||||||
sta c64.ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
less_f .proc
|
|
||||||
; -- is f1 < f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #255
|
|
||||||
beq compare_floats._return_true
|
|
||||||
bne compare_floats._return_false
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
lesseq_f .proc
|
|
||||||
; -- is f1 <= f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #255
|
|
||||||
beq compare_floats._return_true
|
|
||||||
cmp #0
|
|
||||||
beq compare_floats._return_true
|
|
||||||
bne compare_floats._return_false
|
|
||||||
.pend
|
|
||||||
|
|
||||||
greater_f .proc
|
|
||||||
; -- is f1 > f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #1
|
|
||||||
beq compare_floats._return_true
|
|
||||||
bne compare_floats._return_false
|
|
||||||
.pend
|
|
||||||
|
|
||||||
greatereq_f .proc
|
|
||||||
; -- is f1 >= f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #1
|
|
||||||
beq compare_floats._return_true
|
|
||||||
cmp #0
|
|
||||||
beq compare_floats._return_true
|
|
||||||
bne compare_floats._return_false
|
|
||||||
.pend
|
|
||||||
|
|
||||||
compare_floats .proc
|
|
||||||
lda #<fmath_float2
|
|
||||||
ldy #>fmath_float2
|
|
||||||
jsr pop_float
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr pop_float
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr MOVFM ; fac1 = flt1
|
|
||||||
lda #<fmath_float2
|
|
||||||
ldy #>fmath_float2
|
|
||||||
stx c64.SCRATCH_ZPREG
|
|
||||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
|
||||||
ldx c64.SCRATCH_ZPREG
|
|
||||||
rts
|
|
||||||
_return_false lda #0
|
|
||||||
_return_result sta c64.ESTACK_LO,x
|
|
||||||
dex
|
|
||||||
rts
|
|
||||||
_return_true lda #1
|
|
||||||
bne _return_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sin .proc
|
|
||||||
; -- push sin(f) back onto stack
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr SIN
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_cos .proc
|
|
||||||
; -- push cos(f) back onto stack
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr COS
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_tan .proc
|
|
||||||
; -- push tan(f) back onto stack
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr TAN
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_atan .proc
|
|
||||||
; -- push atan(f) back onto stack
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr ATN
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_ln .proc
|
|
||||||
; -- push ln(f) back onto stack
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr LOG
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_log2 .proc
|
|
||||||
; -- push log base 2, ln(f)/ln(2), back onto stack
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr LOG
|
|
||||||
jsr MOVEF
|
|
||||||
lda #<c64.FL_LOG2
|
|
||||||
ldy #>c64.FL_LOG2
|
|
||||||
jsr MOVFM
|
|
||||||
jsr FDIVT
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sqrt .proc
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr SQR
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_rad .proc
|
|
||||||
; -- convert degrees to radians (d * pi / 180)
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<_pi_div_180
|
|
||||||
ldy #>_pi_div_180
|
|
||||||
jsr FMULT
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_deg .proc
|
|
||||||
; -- convert radians to degrees (d * (1/ pi * 180))
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<_one_over_pi_div_180
|
|
||||||
ldy #>_one_over_pi_div_180
|
|
||||||
jsr FMULT
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_round .proc
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr FADDH
|
|
||||||
jsr INT
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_floor .proc
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
jsr INT
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_ceil .proc
|
|
||||||
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
|
||||||
jsr pop_float_fac1
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
ldx #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr MOVMF
|
|
||||||
jsr INT
|
|
||||||
lda #<fmath_float1
|
|
||||||
ldy #>fmath_float1
|
|
||||||
jsr FCOMP
|
|
||||||
cmp #0
|
|
||||||
beq +
|
|
||||||
lda #<FL_FONE
|
|
||||||
ldy #>FL_FONE
|
|
||||||
jsr FADD
|
|
||||||
+ jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_any_f .proc
|
|
||||||
inx
|
|
||||||
lda c64.ESTACK_LO,x ; array size
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
|
||||||
jmp prog8_lib.func_any_b._entry
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_all_f .proc
|
|
||||||
inx
|
|
||||||
jsr prog8_lib.peek_address
|
|
||||||
lda c64.ESTACK_LO,x ; array size
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
|
||||||
tay
|
|
||||||
dey
|
|
||||||
- lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
clc
|
|
||||||
dey
|
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
|
||||||
dey
|
|
||||||
cmp #0
|
|
||||||
beq +
|
|
||||||
cpy #255
|
|
||||||
bne -
|
|
||||||
lda #1
|
|
||||||
sta c64.ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
+ sta c64.ESTACK_LO+1,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_max_f .proc
|
|
||||||
lda #255
|
|
||||||
sta _minmax_cmp+1
|
|
||||||
lda #<_largest_neg_float
|
|
||||||
ldy #>_largest_neg_float
|
|
||||||
_minmax_entry jsr MOVFM
|
|
||||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
- sty c64.SCRATCH_ZPREG
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr FCOMP
|
|
||||||
_minmax_cmp cmp #255 ; modified
|
|
||||||
bne +
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr MOVFM
|
|
||||||
+ lda c64.SCRATCH_ZPWORD1
|
|
||||||
clc
|
|
||||||
adc #5
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
bcc +
|
|
||||||
inc c64.SCRATCH_ZPWORD1+1
|
|
||||||
+ ldy c64.SCRATCH_ZPREG
|
|
||||||
dey
|
|
||||||
cpy #255
|
|
||||||
bne -
|
|
||||||
jmp push_fac1_as_result
|
|
||||||
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_min_f .proc
|
|
||||||
lda #1
|
|
||||||
sta func_max_f._minmax_cmp+1
|
|
||||||
lda #<_largest_pos_float
|
|
||||||
ldy #>_largest_pos_float
|
|
||||||
jmp func_max_f._minmax_entry
|
|
||||||
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
func_sum_f .proc
|
|
||||||
lda #<FL_ZERO
|
|
||||||
ldy #>FL_ZERO
|
|
||||||
jsr MOVFM
|
|
||||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
- sty c64.SCRATCH_ZPREG
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr FADD
|
|
||||||
ldy c64.SCRATCH_ZPREG
|
|
||||||
dey
|
|
||||||
cpy #255
|
|
||||||
beq +
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
clc
|
|
||||||
adc #5
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
bcc -
|
|
||||||
inc c64.SCRATCH_ZPWORD1+1
|
|
||||||
bne -
|
|
||||||
+ jmp push_fac1_as_result
|
|
||||||
.pend
|
|
||||||
|
|
||||||
sign_f .proc
|
|
||||||
jsr pop_float_fac1
|
|
||||||
jsr SIGN
|
|
||||||
sta c64.ESTACK_LO,x
|
|
||||||
dex
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
||||||
} ; ------ end of block c64flt
|
} ; ------ end of block c64flt
|
||||||
|
@ -7,178 +7,178 @@
|
|||||||
|
|
||||||
|
|
||||||
c64 {
|
c64 {
|
||||||
const uword ESTACK_LO = $ce00 ; evaluation stack (lsb)
|
const uword ESTACK_LO = $ce00 ; evaluation stack (lsb)
|
||||||
const uword ESTACK_HI = $cf00 ; evaluation stack (msb)
|
const uword ESTACK_HI = $cf00 ; evaluation stack (msb)
|
||||||
&ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP
|
&ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP
|
||||||
&ubyte SCRATCH_ZPREG = $03 ; scratch register in ZP
|
&ubyte SCRATCH_ZPREG = $03 ; scratch register in ZP
|
||||||
&ubyte SCRATCH_ZPREGX = $fa ; temp storage for X register (stack pointer)
|
&ubyte SCRATCH_ZPREGX = $fa ; temp storage for X register (stack pointer)
|
||||||
&uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
&uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
||||||
&uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
&uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
||||||
|
|
||||||
|
|
||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||||
|
|
||||||
&ubyte COLOR = $0286 ; cursor color
|
&ubyte COLOR = $0286 ; cursor color
|
||||||
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
||||||
&uword CINV = $0314 ; IRQ vector
|
&uword CINV = $0314 ; IRQ vector
|
||||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||||
|
|
||||||
; the default addresses for the character screen chars and colors
|
; the default addresses for the character screen chars and colors
|
||||||
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||||
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||||
|
|
||||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||||
&ubyte SPRPTR0 = 2040
|
&ubyte SPRPTR0 = 2040
|
||||||
&ubyte SPRPTR1 = 2041
|
&ubyte SPRPTR1 = 2041
|
||||||
&ubyte SPRPTR2 = 2042
|
&ubyte SPRPTR2 = 2042
|
||||||
&ubyte SPRPTR3 = 2043
|
&ubyte SPRPTR3 = 2043
|
||||||
&ubyte SPRPTR4 = 2044
|
&ubyte SPRPTR4 = 2044
|
||||||
&ubyte SPRPTR5 = 2045
|
&ubyte SPRPTR5 = 2045
|
||||||
&ubyte SPRPTR6 = 2046
|
&ubyte SPRPTR6 = 2046
|
||||||
&ubyte SPRPTR7 = 2047
|
&ubyte SPRPTR7 = 2047
|
||||||
&ubyte[8] SPRPTR = 2040 ; the 8 sprite pointers as an array.
|
&ubyte[8] SPRPTR = 2040 ; the 8 sprite pointers as an array.
|
||||||
|
|
||||||
|
|
||||||
; ---- VIC-II 6567/6569/856x registers ----
|
; ---- VIC-II 6567/6569/856x registers ----
|
||||||
|
|
||||||
&ubyte SP0X = $d000
|
&ubyte SP0X = $d000
|
||||||
&ubyte SP0Y = $d001
|
&ubyte SP0Y = $d001
|
||||||
&ubyte SP1X = $d002
|
&ubyte SP1X = $d002
|
||||||
&ubyte SP1Y = $d003
|
&ubyte SP1Y = $d003
|
||||||
&ubyte SP2X = $d004
|
&ubyte SP2X = $d004
|
||||||
&ubyte SP2Y = $d005
|
&ubyte SP2Y = $d005
|
||||||
&ubyte SP3X = $d006
|
&ubyte SP3X = $d006
|
||||||
&ubyte SP3Y = $d007
|
&ubyte SP3Y = $d007
|
||||||
&ubyte SP4X = $d008
|
&ubyte SP4X = $d008
|
||||||
&ubyte SP4Y = $d009
|
&ubyte SP4Y = $d009
|
||||||
&ubyte SP5X = $d00a
|
&ubyte SP5X = $d00a
|
||||||
&ubyte SP5Y = $d00b
|
&ubyte SP5Y = $d00b
|
||||||
&ubyte SP6X = $d00c
|
&ubyte SP6X = $d00c
|
||||||
&ubyte SP6Y = $d00d
|
&ubyte SP6Y = $d00d
|
||||||
&ubyte SP7X = $d00e
|
&ubyte SP7X = $d00e
|
||||||
&ubyte SP7Y = $d00f
|
&ubyte SP7Y = $d00f
|
||||||
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
|
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
|
||||||
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
|
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
|
||||||
|
|
||||||
&ubyte MSIGX = $d010
|
&ubyte MSIGX = $d010
|
||||||
&ubyte SCROLY = $d011
|
&ubyte SCROLY = $d011
|
||||||
&ubyte RASTER = $d012
|
&ubyte RASTER = $d012
|
||||||
&ubyte LPENX = $d013
|
&ubyte LPENX = $d013
|
||||||
&ubyte LPENY = $d014
|
&ubyte LPENY = $d014
|
||||||
&ubyte SPENA = $d015
|
&ubyte SPENA = $d015
|
||||||
&ubyte SCROLX = $d016
|
&ubyte SCROLX = $d016
|
||||||
&ubyte YXPAND = $d017
|
&ubyte YXPAND = $d017
|
||||||
&ubyte VMCSB = $d018
|
&ubyte VMCSB = $d018
|
||||||
&ubyte VICIRQ = $d019
|
&ubyte VICIRQ = $d019
|
||||||
&ubyte IREQMASK = $d01a
|
&ubyte IREQMASK = $d01a
|
||||||
&ubyte SPBGPR = $d01b
|
&ubyte SPBGPR = $d01b
|
||||||
&ubyte SPMC = $d01c
|
&ubyte SPMC = $d01c
|
||||||
&ubyte XXPAND = $d01d
|
&ubyte XXPAND = $d01d
|
||||||
&ubyte SPSPCL = $d01e
|
&ubyte SPSPCL = $d01e
|
||||||
&ubyte SPBGCL = $d01f
|
&ubyte SPBGCL = $d01f
|
||||||
|
|
||||||
&ubyte EXTCOL = $d020 ; border color
|
&ubyte EXTCOL = $d020 ; border color
|
||||||
&ubyte BGCOL0 = $d021 ; screen color
|
&ubyte BGCOL0 = $d021 ; screen color
|
||||||
&ubyte BGCOL1 = $d022
|
&ubyte BGCOL1 = $d022
|
||||||
&ubyte BGCOL2 = $d023
|
&ubyte BGCOL2 = $d023
|
||||||
&ubyte BGCOL4 = $d024
|
&ubyte BGCOL4 = $d024
|
||||||
&ubyte SPMC0 = $d025
|
&ubyte SPMC0 = $d025
|
||||||
&ubyte SPMC1 = $d026
|
&ubyte SPMC1 = $d026
|
||||||
&ubyte SP0COL = $d027
|
&ubyte SP0COL = $d027
|
||||||
&ubyte SP1COL = $d028
|
&ubyte SP1COL = $d028
|
||||||
&ubyte SP2COL = $d029
|
&ubyte SP2COL = $d029
|
||||||
&ubyte SP3COL = $d02a
|
&ubyte SP3COL = $d02a
|
||||||
&ubyte SP4COL = $d02b
|
&ubyte SP4COL = $d02b
|
||||||
&ubyte SP5COL = $d02c
|
&ubyte SP5COL = $d02c
|
||||||
&ubyte SP6COL = $d02d
|
&ubyte SP6COL = $d02d
|
||||||
&ubyte SP7COL = $d02e
|
&ubyte SP7COL = $d02e
|
||||||
&ubyte[8] SPCOL = $d027
|
&ubyte[8] SPCOL = $d027
|
||||||
|
|
||||||
|
|
||||||
; ---- end of VIC-II registers ----
|
; ---- end of VIC-II registers ----
|
||||||
|
|
||||||
; ---- CIA 6526 1 & 2 registers ----
|
; ---- CIA 6526 1 & 2 registers ----
|
||||||
|
|
||||||
&ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive (and joystick control port #2)
|
&ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive (and joystick control port #2)
|
||||||
&ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port (and joystick control port #1)
|
&ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port (and joystick control port #1)
|
||||||
&ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column
|
&ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column
|
||||||
&ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row
|
&ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row
|
||||||
&ubyte CIA1TAL = $DC04 ; CIA 1 timer A low byte
|
&ubyte CIA1TAL = $DC04 ; CIA 1 timer A low byte
|
||||||
&ubyte CIA1TAH = $DC05 ; CIA 1 timer A high byte
|
&ubyte CIA1TAH = $DC05 ; CIA 1 timer A high byte
|
||||||
&ubyte CIA1TBL = $DC06 ; CIA 1 timer B low byte
|
&ubyte CIA1TBL = $DC06 ; CIA 1 timer B low byte
|
||||||
&ubyte CIA1TBH = $DC07 ; CIA 1 timer B high byte
|
&ubyte CIA1TBH = $DC07 ; CIA 1 timer B high byte
|
||||||
&ubyte CIA1TOD10 = $DC08 ; time of day, 1/10 sec.
|
&ubyte CIA1TOD10 = $DC08 ; time of day, 1/10 sec.
|
||||||
&ubyte CIA1TODSEC = $DC09 ; time of day, seconds
|
&ubyte CIA1TODSEC = $DC09 ; time of day, seconds
|
||||||
&ubyte CIA1TODMMIN = $DC0A ; time of day, minutes
|
&ubyte CIA1TODMMIN = $DC0A ; time of day, minutes
|
||||||
&ubyte CIA1TODHR = $DC0B ; time of day, hours
|
&ubyte CIA1TODHR = $DC0B ; time of day, hours
|
||||||
&ubyte CIA1SDR = $DC0C ; Serial Data Register
|
&ubyte CIA1SDR = $DC0C ; Serial Data Register
|
||||||
&ubyte CIA1ICR = $DC0D
|
&ubyte CIA1ICR = $DC0D
|
||||||
&ubyte CIA1CRA = $DC0E
|
&ubyte CIA1CRA = $DC0E
|
||||||
&ubyte CIA1CRB = $DC0F
|
&ubyte CIA1CRB = $DC0F
|
||||||
|
|
||||||
&ubyte CIA2PRA = $DD00 ; CIA 2 DRA, serial port and video address
|
&ubyte CIA2PRA = $DD00 ; CIA 2 DRA, serial port and video address
|
||||||
&ubyte CIA2PRB = $DD01 ; CIA 2 DRB, RS232 port / USERPORT
|
&ubyte CIA2PRB = $DD01 ; CIA 2 DRB, RS232 port / USERPORT
|
||||||
&ubyte CIA2DDRA = $DD02 ; CIA 2 DDRA, serial port and video address
|
&ubyte CIA2DDRA = $DD02 ; CIA 2 DDRA, serial port and video address
|
||||||
&ubyte CIA2DDRB = $DD03 ; CIA 2 DDRB, RS232 port / USERPORT
|
&ubyte CIA2DDRB = $DD03 ; CIA 2 DDRB, RS232 port / USERPORT
|
||||||
&ubyte CIA2TAL = $DD04 ; CIA 2 timer A low byte
|
&ubyte CIA2TAL = $DD04 ; CIA 2 timer A low byte
|
||||||
&ubyte CIA2TAH = $DD05 ; CIA 2 timer A high byte
|
&ubyte CIA2TAH = $DD05 ; CIA 2 timer A high byte
|
||||||
&ubyte CIA2TBL = $DD06 ; CIA 2 timer B low byte
|
&ubyte CIA2TBL = $DD06 ; CIA 2 timer B low byte
|
||||||
&ubyte CIA2TBH = $DD07 ; CIA 2 timer B high byte
|
&ubyte CIA2TBH = $DD07 ; CIA 2 timer B high byte
|
||||||
&ubyte CIA2TOD10 = $DD08 ; time of day, 1/10 sec.
|
&ubyte CIA2TOD10 = $DD08 ; time of day, 1/10 sec.
|
||||||
&ubyte CIA2TODSEC = $DD09 ; time of day, seconds
|
&ubyte CIA2TODSEC = $DD09 ; time of day, seconds
|
||||||
&ubyte CIA2TODMIN = $DD0A ; time of day, minutes
|
&ubyte CIA2TODMIN = $DD0A ; time of day, minutes
|
||||||
&ubyte CIA2TODHR = $DD0B ; time of day, hours
|
&ubyte CIA2TODHR = $DD0B ; time of day, hours
|
||||||
&ubyte CIA2SDR = $DD0C ; Serial Data Register
|
&ubyte CIA2SDR = $DD0C ; Serial Data Register
|
||||||
&ubyte CIA2ICR = $DD0D
|
&ubyte CIA2ICR = $DD0D
|
||||||
&ubyte CIA2CRA = $DD0E
|
&ubyte CIA2CRA = $DD0E
|
||||||
&ubyte CIA2CRB = $DD0F
|
&ubyte CIA2CRB = $DD0F
|
||||||
|
|
||||||
; ---- end of CIA registers ----
|
; ---- end of CIA registers ----
|
||||||
|
|
||||||
; ---- SID 6581/8580 registers ----
|
; ---- SID 6581/8580 registers ----
|
||||||
|
|
||||||
&ubyte FREQLO1 = $D400 ; channel 1 freq lo
|
&ubyte FREQLO1 = $D400 ; channel 1 freq lo
|
||||||
&ubyte FREQHI1 = $D401 ; channel 1 freq hi
|
&ubyte FREQHI1 = $D401 ; channel 1 freq hi
|
||||||
&uword FREQ1 = $D400 ; channel 1 freq (word)
|
&uword FREQ1 = $D400 ; channel 1 freq (word)
|
||||||
&ubyte PWLO1 = $D402 ; channel 1 pulse width lo (7-0)
|
&ubyte PWLO1 = $D402 ; channel 1 pulse width lo (7-0)
|
||||||
&ubyte PWHI1 = $D403 ; channel 1 pulse width hi (11-8)
|
&ubyte PWHI1 = $D403 ; channel 1 pulse width hi (11-8)
|
||||||
&uword PW1 = $D402 ; channel 1 pulse width (word)
|
&uword PW1 = $D402 ; channel 1 pulse width (word)
|
||||||
&ubyte CR1 = $D404 ; channel 1 voice control register
|
&ubyte CR1 = $D404 ; channel 1 voice control register
|
||||||
&ubyte AD1 = $D405 ; channel 1 attack & decay
|
&ubyte AD1 = $D405 ; channel 1 attack & decay
|
||||||
&ubyte SR1 = $D406 ; channel 1 sustain & release
|
&ubyte SR1 = $D406 ; channel 1 sustain & release
|
||||||
&ubyte FREQLO2 = $D407 ; channel 2 freq lo
|
&ubyte FREQLO2 = $D407 ; channel 2 freq lo
|
||||||
&ubyte FREQHI2 = $D408 ; channel 2 freq hi
|
&ubyte FREQHI2 = $D408 ; channel 2 freq hi
|
||||||
&uword FREQ2 = $D407 ; channel 2 freq (word)
|
&uword FREQ2 = $D407 ; channel 2 freq (word)
|
||||||
&ubyte PWLO2 = $D409 ; channel 2 pulse width lo (7-0)
|
&ubyte PWLO2 = $D409 ; channel 2 pulse width lo (7-0)
|
||||||
&ubyte PWHI2 = $D40A ; channel 2 pulse width hi (11-8)
|
&ubyte PWHI2 = $D40A ; channel 2 pulse width hi (11-8)
|
||||||
&uword PW2 = $D409 ; channel 2 pulse width (word)
|
&uword PW2 = $D409 ; channel 2 pulse width (word)
|
||||||
&ubyte CR2 = $D40B ; channel 2 voice control register
|
&ubyte CR2 = $D40B ; channel 2 voice control register
|
||||||
&ubyte AD2 = $D40C ; channel 2 attack & decay
|
&ubyte AD2 = $D40C ; channel 2 attack & decay
|
||||||
&ubyte SR2 = $D40D ; channel 2 sustain & release
|
&ubyte SR2 = $D40D ; channel 2 sustain & release
|
||||||
&ubyte FREQLO3 = $D40E ; channel 3 freq lo
|
&ubyte FREQLO3 = $D40E ; channel 3 freq lo
|
||||||
&ubyte FREQHI3 = $D40F ; channel 3 freq hi
|
&ubyte FREQHI3 = $D40F ; channel 3 freq hi
|
||||||
&uword FREQ3 = $D40E ; channel 3 freq (word)
|
&uword FREQ3 = $D40E ; channel 3 freq (word)
|
||||||
&ubyte PWLO3 = $D410 ; channel 3 pulse width lo (7-0)
|
&ubyte PWLO3 = $D410 ; channel 3 pulse width lo (7-0)
|
||||||
&ubyte PWHI3 = $D411 ; channel 3 pulse width hi (11-8)
|
&ubyte PWHI3 = $D411 ; channel 3 pulse width hi (11-8)
|
||||||
&uword PW3 = $D410 ; channel 3 pulse width (word)
|
&uword PW3 = $D410 ; channel 3 pulse width (word)
|
||||||
&ubyte CR3 = $D412 ; channel 3 voice control register
|
&ubyte CR3 = $D412 ; channel 3 voice control register
|
||||||
&ubyte AD3 = $D413 ; channel 3 attack & decay
|
&ubyte AD3 = $D413 ; channel 3 attack & decay
|
||||||
&ubyte SR3 = $D414 ; channel 3 sustain & release
|
&ubyte SR3 = $D414 ; channel 3 sustain & release
|
||||||
&ubyte FCLO = $D415 ; filter cutoff lo (2-0)
|
&ubyte FCLO = $D415 ; filter cutoff lo (2-0)
|
||||||
&ubyte FCHI = $D416 ; filter cutoff hi (10-3)
|
&ubyte FCHI = $D416 ; filter cutoff hi (10-3)
|
||||||
&uword FC = $D415 ; filter cutoff (word)
|
&uword FC = $D415 ; filter cutoff (word)
|
||||||
&ubyte RESFILT = $D417 ; filter resonance and routing
|
&ubyte RESFILT = $D417 ; filter resonance and routing
|
||||||
&ubyte MVOL = $D418 ; filter mode and main volume control
|
&ubyte MVOL = $D418 ; filter mode and main volume control
|
||||||
&ubyte POTX = $D419 ; potentiometer X
|
&ubyte POTX = $D419 ; potentiometer X
|
||||||
&ubyte POTY = $D41A ; potentiometer Y
|
&ubyte POTY = $D41A ; potentiometer Y
|
||||||
&ubyte OSC3 = $D41B ; channel 3 oscillator value read
|
&ubyte OSC3 = $D41B ; channel 3 oscillator value read
|
||||||
&ubyte ENV3 = $D41C ; channel 3 envelope value read
|
&ubyte ENV3 = $D41C ; channel 3 envelope value read
|
||||||
|
|
||||||
; ---- end of SID registers ----
|
; ---- end of SID registers ----
|
||||||
|
|
||||||
@ -186,8 +186,8 @@ c64 {
|
|||||||
|
|
||||||
; ---- C64 basic routines ----
|
; ---- C64 basic routines ----
|
||||||
|
|
||||||
asmsub CLEARSCR () clobbers(A,X,Y) = $E544 ; clear the screen
|
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||||
asmsub HOMECRSR () clobbers(A,X,Y) = $E566 ; cursor to top left of screen
|
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||||
|
|
||||||
|
|
||||||
; ---- end of C64 basic routines ----
|
; ---- end of C64 basic routines ----
|
||||||
@ -195,48 +195,48 @@ asmsub HOMECRSR () clobbers(A,X,Y) = $E566 ; cursor to top left of screen
|
|||||||
|
|
||||||
; ---- C64 kernal routines ----
|
; ---- C64 kernal routines ----
|
||||||
|
|
||||||
asmsub STROUT (uword strptr @ AY) clobbers(A, X, Y) = $AB1E ; print null-terminated string (use c64scr.print instead)
|
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
||||||
asmsub IRQDFRT () clobbers(A,X,Y) = $EA31 ; default IRQ routine
|
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||||
asmsub IRQDFEND () clobbers(A,X,Y) = $EA81 ; default IRQ end/cleanup
|
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||||
asmsub CINT () clobbers(A,X,Y) = $FF81 ; (alias: SCINIT) initialize screen editor and video chip
|
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||||
asmsub IOINIT () clobbers(A, X) = $FF84 ; initialize I/O devices (CIA, SID, IRQ)
|
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||||
asmsub RAMTAS () clobbers(A,X,Y) = $FF87 ; initialize RAM, tape buffer, screen
|
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||||
asmsub RESTOR () clobbers(A,X,Y) = $FF8A ; restore default I/O vectors
|
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||||
asmsub VECTOR (uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) = $FF8D ; read/set I/O vector table
|
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||||
asmsub SETMSG (ubyte value @ A) = $FF90 ; set Kernal message control flag
|
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||||
asmsub SECOND (ubyte address @ A) clobbers(A) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
|
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||||
asmsub TKSA (ubyte address @ A) clobbers(A) = $FF96 ; (alias: TALKSA) send secondary address after TALK
|
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||||
asmsub MEMTOP (uword address @ XY, ubyte dir @ Pc) -> uword @ XY = $FF99 ; read/set top of memory pointer
|
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||||
asmsub MEMBOT (uword address @ XY, ubyte dir @ Pc) -> uword @ XY = $FF9C ; read/set bottom of memory pointer
|
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||||
asmsub SCNKEY () clobbers(A,X,Y) = $FF9F ; scan the keyboard
|
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
asmsub SETTMO (ubyte timeout @ A) = $FFA2 ; set time-out flag for IEEE bus
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
asmsub ACPTR () -> ubyte @ A = $FFA5 ; (alias: IECIN) input byte from serial bus
|
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||||
asmsub CIOUT (ubyte databyte @ A) = $FFA8 ; (alias: IECOUT) output byte to serial bus
|
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||||
asmsub UNTLK () clobbers(A) = $FFAB ; command serial bus device to UNTALK
|
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||||
asmsub UNLSN () clobbers(A) = $FFAE ; command serial bus device to UNLISTEN
|
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||||
asmsub LISTEN (ubyte device @ A) clobbers(A) = $FFB1 ; command serial bus device to LISTEN
|
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||||
asmsub TALK (ubyte device @ A) clobbers(A) = $FFB4 ; command serial bus device to TALK
|
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
asmsub READST () -> ubyte @ A = $FFB7 ; read I/O status word
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
asmsub SETLFS (ubyte logical @ A, ubyte device @ X, ubyte address @ Y) = $FFBA ; set logical file parameters
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
||||||
asmsub SETNAM (ubyte namelen @ A, str filename @ XY) = $FFBD ; set filename parameters
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
asmsub OPEN () clobbers(A,X,Y) = $FFC0 ; (via 794 ($31A)) open a logical file
|
romsub $FFC0 = OPEN() clobbers(A,X,Y) ; (via 794 ($31A)) open a logical file
|
||||||
asmsub CLOSE (ubyte logical @ A) clobbers(A,X,Y) = $FFC3 ; (via 796 ($31C)) close a logical file
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
asmsub CHKIN (ubyte logical @ X) clobbers(A,X) = $FFC6 ; (via 798 ($31E)) define an input channel
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) ; (via 798 ($31E)) define an input channel
|
||||||
asmsub CHKOUT (ubyte logical @ X) clobbers(A,X) = $FFC9 ; (via 800 ($320)) define an output channel
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
asmsub CLRCHN () clobbers(A,X) = $FFCC ; (via 802 ($322)) restore default devices
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
asmsub CHRIN () clobbers(Y) -> ubyte @ A = $FFCF ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
romsub $FFCF = CHRIN() clobbers(Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
asmsub CHROUT (ubyte char @ A) = $FFD2 ; (via 806 ($326)) output a character
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
asmsub LOAD (ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y = $FFD5 ; (via 816 ($330)) load from device
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
||||||
asmsub SAVE (ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A = $FFD8 ; (via 818 ($332)) save to a device
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
asmsub SETTIM (ubyte low @ A, ubyte middle @ X, ubyte high @ Y) = $FFDB ; set the software clock
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
asmsub RDTIM () -> ubyte @ A, ubyte @ X, ubyte @ Y = $FFDE ; read the software clock
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
||||||
asmsub STOP () clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc = $FFE1 ; (via 808 ($328)) check the STOP key
|
romsub $FFE1 = STOP() clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc ; (via 808 ($328)) check the STOP key
|
||||||
asmsub GETIN () clobbers(X,Y) -> ubyte @ A = $FFE4 ; (via 810 ($32A)) get a character
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
asmsub CLALL () clobbers(A,X) = $FFE7 ; (via 812 ($32C)) close all files
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
asmsub UDTIM () clobbers(A,X) = $FFEA ; update the software clock
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
asmsub SCREEN () -> ubyte @ X, ubyte @ Y = $FFED ; read number of screen rows and columns
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
asmsub PLOT (ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||||
asmsub IOBASE () -> uword @ XY = $FFF3 ; read base address of I/O devices
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
; ---- end of C64 kernal routines ----
|
; ---- end of C64 kernal routines ----
|
||||||
|
|
||||||
|
@ -716,7 +716,7 @@ func_sin8 .proc
|
|||||||
lda _sinecos8,y
|
lda _sinecos8,y
|
||||||
sta c64.ESTACK_LO+1,x
|
sta c64.ESTACK_LO+1,x
|
||||||
rts
|
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
|
.pend
|
||||||
|
|
||||||
func_sin8u .proc
|
func_sin8u .proc
|
||||||
@ -724,7 +724,7 @@ func_sin8u .proc
|
|||||||
lda _sinecos8u,y
|
lda _sinecos8u,y
|
||||||
sta c64.ESTACK_LO+1,x
|
sta c64.ESTACK_LO+1,x
|
||||||
rts
|
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
|
.pend
|
||||||
|
|
||||||
func_sin16 .proc
|
func_sin16 .proc
|
||||||
@ -735,7 +735,7 @@ func_sin16 .proc
|
|||||||
sta c64.ESTACK_HI+1,x
|
sta c64.ESTACK_HI+1,x
|
||||||
rts
|
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 <_
|
_sinecos8lo .byte <_
|
||||||
_sinecos8hi .byte >_
|
_sinecos8hi .byte >_
|
||||||
.pend
|
.pend
|
||||||
@ -748,7 +748,7 @@ func_sin16u .proc
|
|||||||
sta c64.ESTACK_HI+1,x
|
sta c64.ESTACK_HI+1,x
|
||||||
rts
|
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 <_
|
_sinecos8ulo .byte <_
|
||||||
_sinecos8uhi .byte >_
|
_sinecos8uhi .byte >_
|
||||||
.pend
|
.pend
|
||||||
@ -1577,96 +1577,182 @@ _work3 .word 0
|
|||||||
reverse_b .proc
|
reverse_b .proc
|
||||||
; --- reverse an array of bytes (in-place)
|
; --- reverse an array of bytes (in-place)
|
||||||
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
|
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
|
||||||
_left_index = c64.SCRATCH_ZPWORD2
|
_index_right = c64.SCRATCH_ZPWORD2
|
||||||
_right_index = c64.SCRATCH_ZPWORD2+1
|
_index_left = c64.SCRATCH_ZPWORD2+1
|
||||||
pha
|
_loop_count = c64.SCRATCH_ZPREG
|
||||||
|
sta _loop_count
|
||||||
|
lsr _loop_count
|
||||||
sec
|
sec
|
||||||
sbc #1
|
sbc #1
|
||||||
sta _left_index
|
sta _index_right
|
||||||
lda #0
|
lda #0
|
||||||
|
sta _index_left
|
||||||
|
_loop ldy _index_right
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pha
|
||||||
|
ldy _index_left
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ldy _index_right
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pla
|
||||||
|
ldy _index_left
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
inc _index_left
|
||||||
|
dec _index_right
|
||||||
|
dec _loop_count
|
||||||
|
bne _loop
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
reverse_f .proc
|
||||||
|
; --- reverse an array of floats
|
||||||
|
_left_index = c64.SCRATCH_ZPWORD2
|
||||||
|
_right_index = c64.SCRATCH_ZPWORD2+1
|
||||||
|
_loop_count = c64.SCRATCH_ZPREG
|
||||||
|
pha
|
||||||
|
sta c64.SCRATCH_ZPREG
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPREG ; *5 because float
|
||||||
|
sec
|
||||||
|
sbc #5
|
||||||
sta _right_index
|
sta _right_index
|
||||||
|
lda #0
|
||||||
|
sta _left_index
|
||||||
pla
|
pla
|
||||||
lsr a
|
lsr a
|
||||||
tay
|
sta _loop_count
|
||||||
_loop sty c64.SCRATCH_ZPREG
|
_loop ; push the left indexed float on the stack
|
||||||
ldy _left_index
|
ldy _left_index
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
pha
|
pha
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pha
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pha
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pha
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pha
|
||||||
|
; copy right index float to left index float
|
||||||
ldy _right_index
|
ldy _right_index
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
ldy _left_index
|
ldy _left_index
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
pla
|
inc _left_index
|
||||||
ldy _right_index
|
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
|
||||||
inc _right_index
|
inc _right_index
|
||||||
dec _left_index
|
ldy _right_index
|
||||||
ldy c64.SCRATCH_ZPREG
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ldy _left_index
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
inc _left_index
|
||||||
|
inc _right_index
|
||||||
|
ldy _right_index
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ldy _left_index
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
inc _left_index
|
||||||
|
inc _right_index
|
||||||
|
ldy _right_index
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ldy _left_index
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
inc _left_index
|
||||||
|
inc _right_index
|
||||||
|
ldy _right_index
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ldy _left_index
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
; pop the float off the stack into the right index float
|
||||||
|
ldy _right_index
|
||||||
|
pla
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
dey
|
dey
|
||||||
|
pla
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
pla
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
pla
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
pla
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
inc _left_index
|
||||||
|
lda _right_index
|
||||||
|
sec
|
||||||
|
sbc #9
|
||||||
|
sta _right_index
|
||||||
|
dec _loop_count
|
||||||
bne _loop
|
bne _loop
|
||||||
rts
|
rts
|
||||||
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
reverse_w .proc
|
reverse_w .proc
|
||||||
; --- reverse an array of words (in-place)
|
; --- reverse an array of words (in-place)
|
||||||
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
|
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
|
||||||
_left_index = c64.SCRATCH_ZPWORD2
|
_index_first = c64.SCRATCH_ZPWORD2
|
||||||
_right_index = c64.SCRATCH_ZPWORD2+1
|
_index_second = c64.SCRATCH_ZPWORD2+1
|
||||||
|
_loop_count = c64.SCRATCH_ZPREG
|
||||||
pha
|
pha
|
||||||
asl a ; *2 because words
|
asl a ; *2 because words
|
||||||
sec
|
sec
|
||||||
sbc #2
|
sbc #2
|
||||||
sta _left_index
|
sta _index_first
|
||||||
lda #0
|
lda #0
|
||||||
sta _right_index
|
sta _index_second
|
||||||
pla
|
pla
|
||||||
lsr a
|
lsr a
|
||||||
pha
|
pha
|
||||||
tay
|
sta _loop_count
|
||||||
; first reverse the lsbs
|
; first reverse the lsbs
|
||||||
_loop_lo sty c64.SCRATCH_ZPREG
|
_loop_lo ldy _index_first
|
||||||
ldy _left_index
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
pha
|
pha
|
||||||
ldy _right_index
|
ldy _index_second
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
ldy _left_index
|
ldy _index_first
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
pla
|
pla
|
||||||
ldy _right_index
|
ldy _index_second
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
inc _right_index
|
inc _index_second
|
||||||
inc _right_index
|
inc _index_second
|
||||||
dec _left_index
|
dec _index_first
|
||||||
dec _left_index
|
dec _index_first
|
||||||
ldy c64.SCRATCH_ZPREG
|
dec _loop_count
|
||||||
dey
|
|
||||||
bne _loop_lo
|
bne _loop_lo
|
||||||
; now reverse the msbs
|
; now reverse the msbs
|
||||||
dec _right_index
|
dec _index_second
|
||||||
inc _left_index
|
inc _index_first
|
||||||
inc _left_index
|
inc _index_first
|
||||||
inc _left_index
|
inc _index_first
|
||||||
pla
|
pla
|
||||||
tay
|
sta _loop_count
|
||||||
_loop_hi sty c64.SCRATCH_ZPREG
|
_loop_hi ldy _index_first
|
||||||
ldy _left_index
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
pha
|
pha
|
||||||
ldy _right_index
|
ldy _index_second
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
ldy _left_index
|
ldy _index_first
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
pla
|
pla
|
||||||
ldy _right_index
|
ldy _index_second
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
dec _right_index
|
dec _index_second
|
||||||
dec _right_index
|
dec _index_second
|
||||||
inc _left_index
|
inc _index_first
|
||||||
inc _left_index
|
inc _index_first
|
||||||
ldy c64.SCRATCH_ZPREG
|
dec _loop_count
|
||||||
dey
|
|
||||||
bne _loop_hi
|
bne _loop_hi
|
||||||
|
|
||||||
rts
|
rts
|
||||||
|
@ -1 +1 @@
|
|||||||
1.62
|
1.80
|
||||||
|
@ -2,10 +2,14 @@ package prog8
|
|||||||
|
|
||||||
import kotlinx.cli.*
|
import kotlinx.cli.*
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.compiler.CompilationResult
|
import prog8.compiler.*
|
||||||
import prog8.compiler.compileProgram
|
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.parser.ParsingFailedError
|
||||||
import prog8.vm.astvm.AstVm
|
import prog8.vm.astvm.AstVm
|
||||||
|
import java.io.IOException
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardWatchEventKinds
|
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>) {
|
private fun compileMain(args: Array<String>) {
|
||||||
val cli = CommandLineInterface("prog8compiler")
|
val cli = CommandLineInterface("prog8compiler")
|
||||||
val startEmulator1 by cli.flagArgument("-emu", "auto-start the 'x64' C-64 emulator after successful compilation")
|
val startEmulator by cli.flagArgument("-emu", "auto-start the Vice C-64 emulator after successful compilation")
|
||||||
val startEmulator2 by cli.flagArgument("-emu2", "auto-start the 'x64sc' C-64 emulator after successful compilation")
|
|
||||||
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
|
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 dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
||||||
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
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 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)")
|
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)
|
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -46,6 +50,26 @@ private fun compileMain(args: Array<String>) {
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when(compilationTarget) {
|
||||||
|
"c64" -> {
|
||||||
|
with(CompilationTarget) {
|
||||||
|
name = "c64"
|
||||||
|
machine = C64MachineDefinition
|
||||||
|
encodeString = { str, altEncoding ->
|
||||||
|
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
|
}
|
||||||
|
decodeString = { bytes, altEncoding ->
|
||||||
|
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
|
}
|
||||||
|
asmGenerator = ::AsmGen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
System.err.println("invalid compilation target")
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val outputPath = pathFrom(outputDir)
|
val outputPath = pathFrom(outputDir)
|
||||||
if(!outputPath.toFile().isDirectory) {
|
if(!outputPath.toFile().isDirectory) {
|
||||||
System.err.println("Output path doesn't exist")
|
System.err.println("Output path doesn't exist")
|
||||||
@ -95,21 +119,35 @@ private fun compileMain(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (launchSimulator) {
|
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...")
|
println("\nLaunching AST-based simulator...")
|
||||||
val vm = AstVm(compilationResult.programAst)
|
val vm = AstVm(compilationResult.programAst, compilationTarget)
|
||||||
vm.run()
|
vm.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startEmulator1 || startEmulator2) {
|
if (startEmulator) {
|
||||||
if (compilationResult.programName.isEmpty())
|
if (compilationResult.programName.isEmpty())
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
else {
|
else if(startEmulator) {
|
||||||
val emulator = if(startEmulator1) "x64" else "x64sc"
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
|
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
|
||||||
val process = ProcessBuilder(cmdline).inheritIO().start()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
process.waitFor()
|
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.antlr.escape
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.NumericDatatypes
|
import prog8.ast.base.NumericDatatypes
|
||||||
import prog8.ast.base.StringDatatypes
|
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstVisitor
|
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 {
|
private fun datatypeString(dt: DataType): String {
|
||||||
return when(dt) {
|
return when(dt) {
|
||||||
in NumericDatatypes -> dt.toString().toLowerCase()
|
in NumericDatatypes -> dt.toString().toLowerCase()
|
||||||
in StringDatatypes -> dt.toString().toLowerCase()
|
DataType.STR -> dt.toString().toLowerCase()
|
||||||
DataType.ARRAY_UB -> "ubyte["
|
DataType.ARRAY_UB -> "ubyte["
|
||||||
DataType.ARRAY_B -> "byte["
|
DataType.ARRAY_B -> "byte["
|
||||||
DataType.ARRAY_UW -> "uword["
|
DataType.ARRAY_UW -> "uword["
|
||||||
@ -197,9 +196,9 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
private fun printout(call: IFunctionCall) {
|
private fun printout(call: IFunctionCall) {
|
||||||
call.target.accept(this)
|
call.target.accept(this)
|
||||||
output("(")
|
output("(")
|
||||||
for(arg in call.arglist) {
|
for(arg in call.args) {
|
||||||
arg.accept(this)
|
arg.accept(this)
|
||||||
if(arg!==call.arglist.last())
|
if(arg!==call.args.last())
|
||||||
output(", ")
|
output(", ")
|
||||||
}
|
}
|
||||||
output(")")
|
output(")")
|
||||||
|
@ -37,7 +37,7 @@ interface Node {
|
|||||||
|
|
||||||
interface IFunctionCall {
|
interface IFunctionCall {
|
||||||
var target: IdentifierReference
|
var target: IdentifierReference
|
||||||
var arglist: MutableList<Expression>
|
var args: MutableList<Expression>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface INameScope {
|
interface INameScope {
|
||||||
@ -243,8 +243,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// lookup something from the module.
|
// lookup something from the module.
|
||||||
val stmt = localContext.definingModule().lookup(scopedName, localContext)
|
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
||||||
return when (stmt) {
|
|
||||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||||
null -> null
|
null -> null
|
||||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
||||||
|
@ -7,7 +7,7 @@ import prog8.ast.Module
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.parser.CustomLexer
|
import prog8.parser.CustomLexer
|
||||||
import prog8.parser.prog8Parser
|
import prog8.parser.prog8Parser
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
@ -202,6 +202,9 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
val asmsubstmt = asmsubroutine()?.toAst()
|
val asmsubstmt = asmsubroutine()?.toAst()
|
||||||
if(asmsubstmt!=null) return asmsubstmt
|
if(asmsubstmt!=null) return asmsubstmt
|
||||||
|
|
||||||
|
val romsubstmt = romsubroutine()?.toAst()
|
||||||
|
if(romsubstmt!=null) return romsubstmt
|
||||||
|
|
||||||
val whenstmt = whenstmt()?.toAst()
|
val whenstmt = whenstmt()?.toAst()
|
||||||
if(whenstmt!=null) return whenstmt
|
if(whenstmt!=null) return whenstmt
|
||||||
|
|
||||||
@ -215,20 +218,43 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.AsmsubroutineContext.toAst(): Statement {
|
private fun prog8Parser.AsmsubroutineContext.toAst(): Statement {
|
||||||
|
val subdecl = asmsub_decl().toAst()
|
||||||
|
val statements = statement_block()?.toAst() ?: mutableListOf()
|
||||||
|
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
|
||||||
|
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||||
|
subdecl.asmClobbers, null, true, statements, toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prog8Parser.RomsubroutineContext.toAst(): Statement {
|
||||||
|
val subdecl = asmsub_decl().toAst()
|
||||||
|
val address = integerliteral().toAst().number.toInt()
|
||||||
|
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
|
||||||
|
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||||
|
subdecl.asmClobbers, address, true, mutableListOf(), toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class AsmsubDecl(val name: String,
|
||||||
|
val parameters: List<SubroutineParameter>,
|
||||||
|
val returntypes: List<DataType>,
|
||||||
|
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||||
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
|
val asmClobbers: Set<Register>)
|
||||||
|
|
||||||
|
|
||||||
|
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
||||||
val name = identifier().text
|
val name = identifier().text
|
||||||
val address = asmsub_address()?.address?.toAst()?.number?.toInt()
|
|
||||||
val params = asmsub_params()?.toAst() ?: emptyList()
|
val params = asmsub_params()?.toAst() ?: emptyList()
|
||||||
val returns = asmsub_returns()?.toAst() ?: emptyList()
|
val returns = asmsub_returns()?.toAst() ?: emptyList()
|
||||||
|
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
||||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
|
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
|
||||||
val normalReturnvalues = returns.map { it.type }
|
val normalReturntypes = returns.map { it.type }
|
||||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
||||||
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
||||||
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
|
||||||
val statements = statement_block()?.toAst() ?: mutableListOf()
|
|
||||||
return Subroutine(name, normalParameters, normalReturnvalues,
|
|
||||||
paramRegisters, returnRegisters, clobbers, address, true, statements, toPosition())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class AsmSubroutineParameter(name: String,
|
private class AsmSubroutineParameter(name: String,
|
||||||
type: DataType,
|
type: DataType,
|
||||||
val registerOrPair: RegisterOrPair?,
|
val registerOrPair: RegisterOrPair?,
|
||||||
@ -264,11 +290,12 @@ private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
|||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
||||||
|
val void = this.VOID() != null
|
||||||
val location = scoped_identifier().toAst()
|
val location = scoped_identifier().toAst()
|
||||||
return if(expression_list() == null)
|
return if(expression_list() == null)
|
||||||
FunctionCallStatement(location, mutableListOf(), toPosition())
|
FunctionCallStatement(location, mutableListOf(), void, toPosition())
|
||||||
else
|
else
|
||||||
FunctionCallStatement(location, expression_list().toAst().toMutableList(), toPosition())
|
FunctionCallStatement(location, expression_list().toAst().toMutableList(), void, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -353,8 +380,13 @@ private fun prog8Parser.DirectiveContext.toAst() : Directive =
|
|||||||
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg =
|
private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg {
|
||||||
DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
|
val str = stringliteral()
|
||||||
|
if(str?.ALT_STRING_ENCODING() != null)
|
||||||
|
throw AstException("${toPosition()} can't use alternate string encodings for directive arguments")
|
||||||
|
|
||||||
|
return DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||||
@ -429,10 +461,13 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
else -> throw FatalAstException("invalid datatype for numeric literal")
|
else -> throw FatalAstException("invalid datatype for numeric literal")
|
||||||
}
|
}
|
||||||
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
|
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 -> litval.stringliteral().toAst()
|
||||||
litval.charliteral()!=null -> {
|
litval.charliteral()!=null -> {
|
||||||
try {
|
try {
|
||||||
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
|
val cc=litval.charliteral()
|
||||||
|
NumericLiteralValue(DataType.UBYTE, CompilationTarget.encodeString(
|
||||||
|
unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()),
|
||||||
|
litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition())
|
||||||
} catch (ce: CharConversionException) {
|
} catch (ce: CharConversionException) {
|
||||||
throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
|
throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
|
||||||
}
|
}
|
||||||
@ -441,7 +476,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
val array = litval.arrayliteral().toAst()
|
val array = litval.arrayliteral().toAst()
|
||||||
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
|
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
|
||||||
// the ConstantFold takes care of that and converts the type if needed.
|
// the ConstantFold takes care of that and converts the type if needed.
|
||||||
ArrayLiteralValue(DataType.ARRAY_UB, array, position = litval.toPosition())
|
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
|
||||||
}
|
}
|
||||||
litval.structliteral()!=null -> {
|
litval.structliteral()!=null -> {
|
||||||
val values = litval.structliteral().expression().map { it.toAst() }
|
val values = litval.structliteral().expression().map { it.toAst() }
|
||||||
@ -468,7 +503,8 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
if(funcall!=null) return funcall
|
if(funcall!=null) return funcall
|
||||||
|
|
||||||
if (rangefrom!=null && rangeto!=null) {
|
if (rangefrom!=null && rangeto!=null) {
|
||||||
val step = rangestep?.toAst() ?: NumericLiteralValue(DataType.UBYTE, 1, toPosition())
|
val defaultstep = if(rto.text == "to") 1 else -1
|
||||||
|
val step = rangestep?.toAst() ?: NumericLiteralValue(DataType.UBYTE, defaultstep, toPosition())
|
||||||
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
|
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,6 +527,10 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun prog8Parser.StringliteralContext.toAst(): StringLiteralValue =
|
||||||
|
StringLiteralValue(unescape(this.STRING().text, toPosition()), ALT_STRING_ENCODING()!=null, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
|
private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
|
||||||
return ArrayIndexedExpression(scoped_identifier().toAst(),
|
return ArrayIndexedExpression(scoped_identifier().toAst(),
|
||||||
arrayindex().toAst(),
|
arrayindex().toAst(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package prog8.ast.base
|
package prog8.ast.base
|
||||||
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
|
||||||
|
|
||||||
/**************************** AST Data classes ****************************/
|
/**************************** AST Data classes ****************************/
|
||||||
|
|
||||||
@ -12,7 +13,6 @@ enum class DataType {
|
|||||||
WORD, // pass by value
|
WORD, // pass by value
|
||||||
FLOAT, // pass by value
|
FLOAT, // pass by value
|
||||||
STR, // pass by reference
|
STR, // pass by reference
|
||||||
STR_S, // pass by reference
|
|
||||||
ARRAY_UB, // pass by reference
|
ARRAY_UB, // pass by reference
|
||||||
ARRAY_B, // pass by reference
|
ARRAY_B, // pass by reference
|
||||||
ARRAY_UW, // pass by reference
|
ARRAY_UW, // pass by reference
|
||||||
@ -31,8 +31,7 @@ enum class DataType {
|
|||||||
UWORD -> targetType in setOf(UWORD, FLOAT)
|
UWORD -> targetType in setOf(UWORD, FLOAT)
|
||||||
WORD -> targetType in setOf(WORD, FLOAT)
|
WORD -> targetType in setOf(WORD, FLOAT)
|
||||||
FLOAT -> targetType == FLOAT
|
FLOAT -> targetType == FLOAT
|
||||||
STR -> targetType == STR || targetType==STR_S
|
STR -> targetType == STR
|
||||||
STR_S -> targetType == STR || targetType==STR_S
|
|
||||||
in ArrayDatatypes -> targetType == this
|
in ArrayDatatypes -> targetType == this
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@ -58,7 +57,7 @@ enum class DataType {
|
|||||||
return when(this) {
|
return when(this) {
|
||||||
in ByteDatatypes -> 1
|
in ByteDatatypes -> 1
|
||||||
in WordDatatypes -> 2
|
in WordDatatypes -> 2
|
||||||
FLOAT -> MachineDefinition.Mflpt5.MemorySize
|
FLOAT -> CompilationTarget.machine.FLOAT_MEM_SIZE
|
||||||
in PassByReferenceDatatypes -> 2
|
in PassByReferenceDatatypes -> 2
|
||||||
else -> -9999999
|
else -> -9999999
|
||||||
}
|
}
|
||||||
@ -112,10 +111,9 @@ val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
|
|||||||
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
|
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
|
||||||
val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, 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 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 ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
||||||
val IterableDatatypes = setOf(
|
val IterableDatatypes = setOf(
|
||||||
DataType.STR, DataType.STR_S,
|
DataType.STR,
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||||
DataType.ARRAY_F)
|
DataType.ARRAY_F)
|
||||||
@ -123,12 +121,18 @@ val PassByValueDatatypes = NumericDatatypes
|
|||||||
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
|
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
|
||||||
val ArrayElementTypes = mapOf(
|
val ArrayElementTypes = mapOf(
|
||||||
DataType.STR to DataType.UBYTE,
|
DataType.STR to DataType.UBYTE,
|
||||||
DataType.STR_S to DataType.UBYTE,
|
|
||||||
DataType.ARRAY_B to DataType.BYTE,
|
DataType.ARRAY_B to DataType.BYTE,
|
||||||
DataType.ARRAY_UB to DataType.UBYTE,
|
DataType.ARRAY_UB to DataType.UBYTE,
|
||||||
DataType.ARRAY_W to DataType.WORD,
|
DataType.ARRAY_W to DataType.WORD,
|
||||||
DataType.ARRAY_UW to DataType.UWORD,
|
DataType.ARRAY_UW to DataType.UWORD,
|
||||||
DataType.ARRAY_F to DataType.FLOAT)
|
DataType.ARRAY_F to DataType.FLOAT)
|
||||||
|
val ElementArrayTypes = mapOf(
|
||||||
|
DataType.BYTE to DataType.ARRAY_B,
|
||||||
|
DataType.UBYTE to DataType.ARRAY_UB,
|
||||||
|
DataType.WORD to DataType.ARRAY_W,
|
||||||
|
DataType.UWORD to DataType.ARRAY_UW,
|
||||||
|
DataType.FLOAT to DataType.ARRAY_F
|
||||||
|
)
|
||||||
|
|
||||||
// find the parent node of a specific type or interface
|
// find the parent node of a specific type or interface
|
||||||
// (useful to figure out in what namespace/block something is defined, etc)
|
// (useful to figure out in what namespace/block something is defined, etc)
|
||||||
|
@ -4,7 +4,6 @@ import prog8.ast.Module
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.*
|
import prog8.ast.processing.*
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.target.c64.codegen.AnonymousScopeVarsCleanup
|
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
|
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,11 +5,8 @@ import prog8.ast.antlr.escape
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.ArrayIndex
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.compiler.target.c64.Petscii
|
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.functions.NotConstArgumentException
|
import prog8.functions.NotConstArgumentException
|
||||||
import prog8.functions.builtinFunctionReturnType
|
import prog8.functions.builtinFunctionReturnType
|
||||||
@ -63,7 +60,27 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
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 {
|
override fun toString(): String {
|
||||||
return "Prefix($operator $expression)"
|
return "Prefix($operator $expression)"
|
||||||
@ -197,7 +214,7 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
|
|||||||
val target = identifier.targetStatement(program.namespace)
|
val target = identifier.targetStatement(program.namespace)
|
||||||
if (target is VarDecl) {
|
if (target is VarDecl) {
|
||||||
return when (target.datatype) {
|
return when (target.datatype) {
|
||||||
in StringDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(target.datatype))
|
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(target.datatype))
|
||||||
else -> InferredTypes.unknown()
|
else -> InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
@ -302,7 +319,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) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -407,11 +424,15 @@ class StructLiteralValue(var values: List<Expression>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StringLiteralValue(val type: DataType, // only string types
|
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
|
||||||
val value: String,
|
|
||||||
|
class StringLiteralValue(val value: String,
|
||||||
|
val altEncoding: Boolean, // such as: screencodes instead of Petscii for the C64
|
||||||
override val position: Position) : Expression() {
|
override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
val heapId = ++heapIdSequence
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
@ -420,35 +441,22 @@ class StringLiteralValue(val type: DataType, // only string types
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun toString(): String = "'${escape(value)}'"
|
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)
|
operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
|
||||||
override fun hashCode(): Int = Objects.hash(value, type)
|
override fun hashCode(): Int = Objects.hash(value, altEncoding)
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is StringLiteralValue)
|
if(other==null || other !is StringLiteralValue)
|
||||||
return false
|
return false
|
||||||
return value==other.value && type==other.type
|
return value==other.value && altEncoding == other.altEncoding
|
||||||
}
|
|
||||||
|
|
||||||
var heapId: Int? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
fun addToHeap() {
|
|
||||||
if(heapId==null)
|
|
||||||
heapId = ++heapIdSequence
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var heapIdSequence = 0
|
class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred because not all array literals hava a known type yet
|
||||||
|
|
||||||
|
|
||||||
class ArrayLiteralValue(val type: DataType, // only array types
|
|
||||||
val value: Array<Expression>,
|
val value: Array<Expression>,
|
||||||
initHeapId: Int? =null,
|
|
||||||
override val position: Position) : Expression() {
|
override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
var heapId = initHeapId
|
val heapId = ++heapIdSequence
|
||||||
private set
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -459,7 +467,8 @@ class ArrayLiteralValue(val type: DataType, // only array types
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun toString(): String = "$value"
|
override fun toString(): String = "$value"
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
override fun inferType(program: Program): InferredTypes.InferredType = if(type.isUnknown) type else guessDatatype(program)
|
||||||
|
|
||||||
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
|
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
|
||||||
override fun hashCode(): Int = Objects.hash(value, type)
|
override fun hashCode(): Int = Objects.hash(value, type)
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@ -468,17 +477,45 @@ class ArrayLiteralValue(val type: DataType, // only array types
|
|||||||
return type==other.type && value.contentEquals(other.value)
|
return type==other.type && value.contentEquals(other.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun guessDatatype(program: Program): InferredTypes.InferredType {
|
||||||
|
// Educated guess of the desired array literal's datatype.
|
||||||
|
// If it's inside a for loop, assume the data type of the loop variable is what we want.
|
||||||
|
val forloop = parent as? ForLoop
|
||||||
|
if(forloop != null) {
|
||||||
|
val loopvarDt = forloop.loopVarDt(program)
|
||||||
|
if(loopvarDt.isKnown) {
|
||||||
|
return if(loopvarDt.typeOrElse(DataType.STRUCT) !in ElementArrayTypes)
|
||||||
|
InferredTypes.InferredType.unknown()
|
||||||
|
else
|
||||||
|
InferredTypes.InferredType.known(ElementArrayTypes.getValue(loopvarDt.typeOrElse(DataType.STRUCT)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, select the "biggegst" datatype based on the elements in the array.
|
||||||
|
val datatypesInArray = value.map { it.inferType(program) }
|
||||||
|
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
|
||||||
|
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
|
||||||
|
return when {
|
||||||
|
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
||||||
|
DataType.WORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_W)
|
||||||
|
DataType.UWORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||||
|
DataType.BYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_B)
|
||||||
|
DataType.UBYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UB)
|
||||||
|
else -> InferredTypes.InferredType.unknown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun cast(targettype: DataType): ArrayLiteralValue? {
|
fun cast(targettype: DataType): ArrayLiteralValue? {
|
||||||
if(type==targettype)
|
if(type.istype(targettype))
|
||||||
return this
|
return this
|
||||||
if(targettype in ArrayDatatypes) {
|
if(targettype in ArrayDatatypes) {
|
||||||
val elementType = ArrayElementTypes.getValue(targettype)
|
val elementType = ArrayElementTypes.getValue(targettype)
|
||||||
val castArray = value.map{
|
val castArray = value.map{
|
||||||
val num = it as? NumericLiteralValue
|
val num = it as? NumericLiteralValue
|
||||||
if(num==null) {
|
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)
|
if (elementType != DataType.UWORD || it !is AddressOf)
|
||||||
throw FatalAstException("weird array element $it")
|
return null
|
||||||
it
|
it
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@ -488,15 +525,10 @@ class ArrayLiteralValue(val type: DataType, // only array types
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
return ArrayLiteralValue(targettype, castArray, position = position)
|
return ArrayLiteralValue(InferredTypes.InferredType.known(targettype), castArray, position = position)
|
||||||
}
|
}
|
||||||
return null // invalid type conversion from $this to $targettype
|
return null // invalid type conversion from $this to $targettype
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addToHeap() {
|
|
||||||
if(heapId==null)
|
|
||||||
heapId = ++heapIdSequence
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RangeExpr(var from: Expression,
|
class RangeExpr(var from: Expression,
|
||||||
@ -524,7 +556,6 @@ class RangeExpr(var from: Expression,
|
|||||||
fromDt istype DataType.UBYTE && toDt istype DataType.UBYTE -> InferredTypes.knownFor(DataType.ARRAY_UB)
|
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.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 && 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.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
||||||
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
||||||
else -> InferredTypes.knownFor(DataType.ARRAY_UB)
|
else -> InferredTypes.knownFor(DataType.ARRAY_UB)
|
||||||
@ -548,9 +579,9 @@ class RangeExpr(var from: Expression,
|
|||||||
val fromString = from as? StringLiteralValue
|
val fromString = from as? StringLiteralValue
|
||||||
val toString = to as? StringLiteralValue
|
val toString = to as? StringLiteralValue
|
||||||
if(fromString!=null && toString!=null ) {
|
if(fromString!=null && toString!=null ) {
|
||||||
// string range -> int range over petscii values
|
// string range -> int range over character values
|
||||||
fromVal = Petscii.encodePetscii(fromString.value, true)[0].toInt()
|
fromVal = CompilationTarget.encodeString(fromString.value, fromString.altEncoding)[0].toInt()
|
||||||
toVal = Petscii.encodePetscii(toString.value, true)[0].toInt()
|
toVal = CompilationTarget.encodeString(toString.value, fromString.altEncoding)[0].toInt()
|
||||||
} else {
|
} else {
|
||||||
val fromLv = from as? NumericLiteralValue
|
val fromLv = from as? NumericLiteralValue
|
||||||
val toLv = to as? NumericLiteralValue
|
val toLv = to as? NumericLiteralValue
|
||||||
@ -650,22 +681,22 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
|
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
|
||||||
return when (value) {
|
return when (value) {
|
||||||
is IdentifierReference -> value.heapId(namespace)
|
is IdentifierReference -> value.heapId(namespace)
|
||||||
is StringLiteralValue -> value.heapId ?: throw FatalAstException("string is not on the heap: $value")
|
is StringLiteralValue -> value.heapId
|
||||||
is ArrayLiteralValue -> value.heapId ?: throw FatalAstException("array is not on the heap: $value")
|
is ArrayLiteralValue -> value.heapId
|
||||||
else -> throw FatalAstException("requires a reference value")
|
else -> throw FatalAstException("requires a reference value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCall(override var target: IdentifierReference,
|
class FunctionCall(override var target: IdentifierReference,
|
||||||
override var arglist: MutableList<Expression>,
|
override var args: MutableList<Expression>,
|
||||||
override val position: Position) : Expression(), IFunctionCall {
|
override val position: Position) : Expression(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
target.linkParents(this)
|
target.linkParents(this)
|
||||||
arglist.forEach { it.linkParents(this) }
|
args.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program) = constValue(program, true)
|
override fun constValue(program: Program) = constValue(program, true)
|
||||||
@ -680,7 +711,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
if(func!=null) {
|
if(func!=null) {
|
||||||
val exprfunc = func.constExpressionFunc
|
val exprfunc = func.constExpressionFunc
|
||||||
if(exprfunc!=null)
|
if(exprfunc!=null)
|
||||||
resultValue = exprfunc(arglist, position, program)
|
resultValue = exprfunc(args, position, program)
|
||||||
else if(func.returntype==null)
|
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)
|
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
|
||||||
}
|
}
|
||||||
@ -706,7 +737,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = 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 {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val constVal = constValue(program ,false)
|
val constVal = constValue(program ,false)
|
||||||
@ -719,7 +750,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
||||||
return InferredTypes.void() // these have no return value
|
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 -> {
|
is Subroutine -> {
|
||||||
if(stmt.returntypes.isEmpty())
|
if(stmt.returntypes.isEmpty())
|
||||||
|
@ -46,7 +46,6 @@ object InferredTypes {
|
|||||||
DataType.WORD to InferredType.known(DataType.WORD),
|
DataType.WORD to InferredType.known(DataType.WORD),
|
||||||
DataType.FLOAT to InferredType.known(DataType.FLOAT),
|
DataType.FLOAT to InferredType.known(DataType.FLOAT),
|
||||||
DataType.STR to InferredType.known(DataType.STR),
|
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_UB to InferredType.known(DataType.ARRAY_UB),
|
||||||
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),
|
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),
|
||||||
DataType.ARRAY_UW to InferredType.known(DataType.ARRAY_UW),
|
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.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.base.NameError
|
import prog8.ast.base.NameError
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.statements.AnonymousScope
|
import prog8.ast.statements.AnonymousScope
|
||||||
import prog8.ast.statements.Statement
|
import prog8.ast.statements.Statement
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
@ -1,5 +1,3 @@
|
|||||||
@file:Suppress("DuplicatedCode")
|
|
||||||
|
|
||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
@ -10,8 +8,7 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -126,7 +123,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
if (forLoop.loopRegister != null) {
|
if (forLoop.loopRegister != null) {
|
||||||
// loop register
|
// 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))
|
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
|
||||||
if(forLoop.loopRegister!=Register.A)
|
if(forLoop.loopRegister!=Register.A)
|
||||||
checkResult.add(ExpressionError("it's only possible to use A as a loop register", forLoop.position))
|
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 {
|
} else {
|
||||||
when (loopvar.datatype) {
|
when (loopvar.datatype) {
|
||||||
DataType.UBYTE -> {
|
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))
|
checkResult.add(ExpressionError("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position))
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
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)
|
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))
|
checkResult.add(ExpressionError("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position))
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
// TODO fix this, it should allow: for bb in [1,2,3]
|
|
||||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
||||||
checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position))
|
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)) {
|
else if(param.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||||
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
|
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")
|
err("parameter '${param.first.name}' should be (u)word/address")
|
||||||
}
|
}
|
||||||
else if(param.second.statusflag!=null) {
|
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)) {
|
else if(ret.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||||
if (ret.first.value != DataType.UWORD && ret.first.value != DataType.WORD
|
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")
|
err("return value #${ret.first.index + 1} should be (u)word/address")
|
||||||
}
|
}
|
||||||
else if(ret.second.statusflag!=null) {
|
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)
|
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))
|
checkResult.add(SyntaxError("cannot assign to a string or array type", assignTarget.position))
|
||||||
|
|
||||||
if (assignment is Assignment) {
|
if (assignment is Assignment) {
|
||||||
@ -448,7 +444,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(variable==null)
|
if(variable==null)
|
||||||
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
|
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
|
||||||
else {
|
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))
|
checkResult.add(ExpressionError("invalid pointer-of operand type", addressOf.position))
|
||||||
}
|
}
|
||||||
super.visit(addressOf)
|
super.visit(addressOf)
|
||||||
@ -508,9 +504,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
decl.datatype in NumericDatatypes -> {
|
decl.datatype in NumericDatatypes -> {
|
||||||
// initialize numeric var with value zero by default.
|
// initialize numeric var with value zero by default.
|
||||||
val litVal =
|
val litVal =
|
||||||
when {
|
when (decl.datatype) {
|
||||||
decl.datatype in ByteDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
|
in ByteDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
|
||||||
decl.datatype in WordDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
|
in WordDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
|
||||||
else -> NumericLiteralValue(decl.datatype, 0.0, decl.position)
|
else -> NumericLiteralValue(decl.datatype, 0.0, decl.position)
|
||||||
}
|
}
|
||||||
litVal.parent = decl
|
litVal.parent = decl
|
||||||
@ -690,23 +686,20 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(array: ArrayLiteralValue) {
|
override fun visit(array: ArrayLiteralValue) {
|
||||||
if(!compilerOptions.floats && array.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
if(array.type.isKnown) {
|
||||||
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", array.position))
|
if (!compilerOptions.floats && array.type.typeOrElse(DataType.STRUCT) in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||||
|
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", array.position))
|
||||||
|
}
|
||||||
|
val arrayspec = ArrayIndex.forArray(array)
|
||||||
|
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
|
||||||
}
|
}
|
||||||
val arrayspec = ArrayIndex.forArray(array)
|
|
||||||
checkValueTypeAndRangeArray(array.type, null, arrayspec, array)
|
|
||||||
|
|
||||||
super.visit(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) {
|
override fun visit(string: StringLiteralValue) {
|
||||||
checkValueTypeAndRangeString(string.type, string)
|
checkValueTypeAndRangeString(DataType.STR, string)
|
||||||
super.visit(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) {
|
override fun visit(expr: PrefixExpression) {
|
||||||
@ -820,25 +813,33 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression)
|
val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression)
|
||||||
if(targetStatement!=null)
|
if(targetStatement!=null)
|
||||||
checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position)
|
checkFunctionCall(targetStatement, functionCall.args, functionCall.position)
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||||
if(targetStatement!=null)
|
if(targetStatement!=null)
|
||||||
checkFunctionCall(targetStatement, functionCallStatement.arglist, functionCallStatement.position)
|
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
||||||
if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) {
|
if(!functionCallStatement.void && targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) {
|
||||||
if(targetStatement.returntypes.size==1)
|
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
|
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() == "sort") {
|
||||||
|
// sort is not supported on float arrays
|
||||||
|
val idref = functionCallStatement.args.singleOrNull() as? IdentifierReference
|
||||||
|
if(idref!=null && idref.inferType(program).istype(DataType.ARRAY_F)) {
|
||||||
|
checkResult.add(ExpressionError("sorting a floating point array is not supported", functionCallStatement.args.first().position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
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
|
// 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))
|
checkResult.add(ExpressionError("can't use that as argument to a in-place modifying function", functionCallStatement.args.first().position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCallStatement)
|
super.visit(functionCallStatement)
|
||||||
@ -877,10 +878,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkResult.add(ExpressionError("swap requires args of numerical type", position))
|
checkResult.add(ExpressionError("swap requires args of numerical type", position))
|
||||||
}
|
}
|
||||||
else if(target.name=="all" || target.name=="any") {
|
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))
|
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))
|
checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -897,7 +898,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val argDt=argIDt.typeOrElse(DataType.STRUCT)
|
val argDt=argIDt.typeOrElse(DataType.STRUCT)
|
||||||
if(!(argDt isAssignableTo arg.second.type)) {
|
if(!(argDt isAssignableTo arg.second.type)) {
|
||||||
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
|
// 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))
|
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -964,7 +965,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
||||||
if(index!=null && (index<0 || index>=arraysize))
|
if(index!=null && (index<0 || index>=arraysize))
|
||||||
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
|
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) {
|
if(target.value is StringLiteralValue) {
|
||||||
// check string lengths for non-memory mapped strings
|
// check string lengths for non-memory mapped strings
|
||||||
val stringLen = (target.value as StringLiteralValue).value.length
|
val stringLen = (target.value as StringLiteralValue).value.length
|
||||||
@ -1048,19 +1049,15 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean {
|
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean {
|
||||||
fun err(msg: String): Boolean {
|
return if (targetDt == DataType.STR) {
|
||||||
checkResult.add(ExpressionError(msg, value.position))
|
if (value.value.length > 255) {
|
||||||
return false
|
checkResult.add(ExpressionError("string length must be 0-255", value.position))
|
||||||
}
|
false
|
||||||
return when (targetDt) {
|
|
||||||
in StringDatatypes -> {
|
|
||||||
return if (value.value.length > 255)
|
|
||||||
err("string length must be 0-255")
|
|
||||||
else
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
else -> false
|
else
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
else false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkValueTypeAndRangeArray(targetDt: DataType, struct: StructDecl?,
|
private fun checkValueTypeAndRangeArray(targetDt: DataType, struct: StructDecl?,
|
||||||
@ -1069,11 +1066,15 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkResult.add(ExpressionError(msg, value.position))
|
checkResult.add(ExpressionError(msg, value.position))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(value.type.isUnknown)
|
||||||
|
return err("attempt to check values of array with as yet unknown datatype")
|
||||||
|
|
||||||
when (targetDt) {
|
when (targetDt) {
|
||||||
in StringDatatypes -> return err("string value expected")
|
DataType.STR -> return err("string value expected")
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
|
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
|
||||||
if(value.type==targetDt) {
|
if(value.type.istype(targetDt)) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.size()
|
||||||
@ -1095,7 +1096,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
// value may be either a single word, or a word arraysize, or a range
|
// value may be either a single word, or a word arraysize, or a range
|
||||||
if(value.type==targetDt) {
|
if(value.type.istype(targetDt)) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.size()
|
||||||
@ -1117,7 +1118,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
// value may be either a single float, or a float arraysize
|
// value may be either a single float, or a float arraysize
|
||||||
if(value.type==targetDt) {
|
if(value.type.istype(targetDt)) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySize = value.value.size
|
val arraySize = value.value.size
|
||||||
@ -1136,14 +1137,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
// check if the floating point values are all within range
|
// check if the floating point values are all within range
|
||||||
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
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 err("floating point value overflow")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return err("invalid float array initialization value ${value.type}, expected $targetDt")
|
return err("invalid float array initialization value ${value.type}, expected $targetDt")
|
||||||
}
|
}
|
||||||
DataType.STRUCT -> {
|
DataType.STRUCT -> {
|
||||||
if(value.type in ArrayDatatypes) {
|
if(value.type.typeOrElse(DataType.STRUCT) in ArrayDatatypes) {
|
||||||
if(value.value.size != struct!!.numberOfElements)
|
if(value.value.size != struct!!.numberOfElements)
|
||||||
return err("number of values is not the same as the number of members in the struct")
|
return err("number of values is not the same as the number of members in the struct")
|
||||||
for(elt in value.value.zip(struct.statements)) {
|
for(elt in value.value.zip(struct.statements)) {
|
||||||
@ -1207,35 +1208,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkArrayValues(value: ArrayLiteralValue, type: DataType): Boolean {
|
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 {
|
val array = value.value.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is NumericLiteralValue -> it.number.toInt()
|
is NumericLiteralValue -> it.number.toInt()
|
||||||
is AddressOf -> it.identifier.heapId(program.namespace)
|
is AddressOf -> it.identifier.heapId(program.namespace)
|
||||||
|
is TypecastExpression -> {
|
||||||
|
val constVal = it.expression.constValue(program)
|
||||||
|
constVal?.cast(it.type)?.number?.toInt() ?: -9999999
|
||||||
|
}
|
||||||
else -> -9999999
|
else -> -9999999
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1277,7 +1257,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
|
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
|
||||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||||
DataType.STR -> sourceDatatype== DataType.STR
|
DataType.STR -> sourceDatatype== DataType.STR
|
||||||
DataType.STR_S -> sourceDatatype== DataType.STR_S
|
|
||||||
DataType.STRUCT -> {
|
DataType.STRUCT -> {
|
||||||
if(sourceDatatype==DataType.STRUCT) {
|
if(sourceDatatype==DataType.STRUCT) {
|
||||||
val structLv = sourceValue as StructLiteralValue
|
val structLv = sourceValue as StructLiteralValue
|
||||||
|
@ -7,7 +7,7 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.AssemblyProgram
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
override fun visit(functionCall: FunctionCall): Expression {
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
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"
|
// 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)
|
typecast.linkParents(functionCall.parent)
|
||||||
return super.visit(typecast)
|
return super.visit(typecast)
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
|
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))
|
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,
|
// 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 {
|
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))
|
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position))
|
||||||
} else if(subroutine.name in BuiltinFunctions) {
|
} else if(subroutine.name in BuiltinFunctions) {
|
||||||
// the builtin functions can't be redefined
|
// 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 {
|
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))
|
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${label.name}'", label.position))
|
||||||
|
|
||||||
if(label.name in BuiltinFunctions) {
|
if(label.name in BuiltinFunctions) {
|
||||||
@ -225,22 +225,34 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
return super.visit(returnStmt)
|
return super.visit(returnStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
||||||
val array = super.visit(arrayLiteral)
|
val array = super.visit(arrayLiteral)
|
||||||
if(array is ArrayLiteralValue) {
|
if(array is ArrayLiteralValue) {
|
||||||
val vardecl = array.parent as? VarDecl
|
val vardecl = array.parent as? VarDecl
|
||||||
return when {
|
// adjust the datatype of the array (to an educated guess)
|
||||||
vardecl!=null -> fixupArrayDatatype(array, vardecl, program)
|
if(vardecl!=null) {
|
||||||
array.heapId!=null -> {
|
val arrayDt = array.type
|
||||||
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
|
if(!arrayDt.istype(vardecl.datatype)) {
|
||||||
// (we don't know the desired datatype here exactly so we guess)
|
val cast = array.cast(vardecl.datatype)
|
||||||
val datatype = determineArrayDt(array.value)
|
if (cast != null) {
|
||||||
val litval2 = array.cast(datatype)!!
|
vardecl.value = cast
|
||||||
litval2.parent = array.parent
|
cast.linkParents(vardecl)
|
||||||
// finally, replace the literal array by a identifier reference.
|
return cast
|
||||||
makeIdentifierFromRefLv(litval2)
|
}
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val arrayDt = array.guessDatatype(program)
|
||||||
|
if(arrayDt.isKnown) {
|
||||||
|
// this array literal is part of an expression, turn it into an identifier reference
|
||||||
|
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
|
return if (litval2 != null) {
|
||||||
|
litval2.parent = array.parent
|
||||||
|
makeIdentifierFromRefLv(litval2)
|
||||||
|
} else array
|
||||||
}
|
}
|
||||||
else -> array
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
@ -253,9 +265,6 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
// intern the string; move it into the heap
|
// intern the string; move it into the heap
|
||||||
if (string.value.length !in 1..255)
|
if (string.value.length !in 1..255)
|
||||||
checkResult.add(ExpressionError("string literal length must be between 1 and 255", string.position))
|
checkResult.add(ExpressionError("string literal length must be between 1 and 255", string.position))
|
||||||
else {
|
|
||||||
string.addToHeap()
|
|
||||||
}
|
|
||||||
return if (vardecl != null)
|
return if (vardecl != null)
|
||||||
string
|
string
|
||||||
else
|
else
|
||||||
@ -264,25 +273,10 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun determineArrayDt(array: Array<Expression>): DataType {
|
|
||||||
val datatypesInArray = array.map { it.inferType(program) }
|
|
||||||
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
|
|
||||||
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
|
|
||||||
return when {
|
|
||||||
DataType.FLOAT in dts -> DataType.ARRAY_F
|
|
||||||
DataType.WORD in dts -> DataType.ARRAY_W
|
|
||||||
DataType.UWORD in dts -> DataType.ARRAY_UW
|
|
||||||
DataType.BYTE in dts -> DataType.ARRAY_B
|
|
||||||
DataType.UBYTE in dts -> DataType.ARRAY_UB
|
|
||||||
else -> throw IllegalArgumentException("can't determine type of array")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeIdentifierFromRefLv(array: ArrayLiteralValue): IdentifierReference {
|
private fun makeIdentifierFromRefLv(array: ArrayLiteralValue): IdentifierReference {
|
||||||
// a referencetype literal value that's not declared as a variable
|
// 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
|
// 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.
|
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
||||||
array.addToHeap()
|
|
||||||
val scope = array.definingScope()
|
val scope = array.definingScope()
|
||||||
val variable = VarDecl.createAuto(array)
|
val variable = VarDecl.createAuto(array)
|
||||||
return replaceWithIdentifier(variable, scope, array.parent)
|
return replaceWithIdentifier(variable, scope, array.parent)
|
||||||
@ -292,7 +286,6 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
// a referencetype literal value that's not declared as a variable
|
// 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
|
// 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.
|
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
||||||
string.addToHeap()
|
|
||||||
val scope = string.definingScope()
|
val scope = string.definingScope()
|
||||||
val variable = VarDecl.createAuto(string)
|
val variable = VarDecl.createAuto(string)
|
||||||
return replaceWithIdentifier(variable, scope, string.parent)
|
return replaceWithIdentifier(variable, scope, string.parent)
|
||||||
@ -331,16 +324,12 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
if (expr.operator == "*") {
|
if (expr.operator == "*") {
|
||||||
// repeat a string a number of times
|
// repeat a string a number of times
|
||||||
val idt = string.inferType(program)
|
return StringLiteralValue(string.value.repeat(constvalue.number.toInt()), string.altEncoding, expr.position)
|
||||||
return StringLiteralValue(idt.typeOrElse(DataType.STR),
|
|
||||||
string.value.repeat(constvalue.number.toInt()), expr.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(expr.operator == "+" && operand is StringLiteralValue) {
|
if(expr.operator == "+" && operand is StringLiteralValue) {
|
||||||
// concatenate two strings
|
// concatenate two strings
|
||||||
val idt = string.inferType(program)
|
return StringLiteralValue("${string.value}${operand.value}", string.altEncoding, expr.position)
|
||||||
return StringLiteralValue(idt.typeOrElse(DataType.STR),
|
|
||||||
"${string.value}${operand.value}", expr.position)
|
|
||||||
}
|
}
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
@ -359,69 +348,3 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun fixupArrayDatatype(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
|
|
||||||
}
|
|
||||||
val dt = when {
|
|
||||||
DataType.FLOAT in dts -> DataType.ARRAY_F
|
|
||||||
DataType.WORD in dts -> DataType.ARRAY_W
|
|
||||||
DataType.UWORD in dts -> DataType.ARRAY_UW
|
|
||||||
DataType.BYTE in dts -> DataType.ARRAY_B
|
|
||||||
else -> DataType.ARRAY_UB
|
|
||||||
}
|
|
||||||
if(dt==array.type)
|
|
||||||
return array
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
|
@ -52,7 +52,7 @@ interface IAstModifyingVisitor {
|
|||||||
functionCall.target = newtarget
|
functionCall.target = newtarget
|
||||||
else
|
else
|
||||||
throw FatalAstException("cannot change class of function call target")
|
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
|
return functionCall
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ interface IAstModifyingVisitor {
|
|||||||
functionCallStatement.target = newtarget
|
functionCallStatement.target = newtarget
|
||||||
else
|
else
|
||||||
throw FatalAstException("cannot change class of function call target")
|
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
|
return functionCallStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,12 +41,12 @@ interface IAstVisitor {
|
|||||||
|
|
||||||
fun visit(functionCall: FunctionCall) {
|
fun visit(functionCall: FunctionCall) {
|
||||||
functionCall.target.accept(this)
|
functionCall.target.accept(this)
|
||||||
functionCall.arglist.forEach { it.accept(this) }
|
functionCall.args.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(functionCallStatement: FunctionCallStatement) {
|
fun visit(functionCallStatement: FunctionCallStatement) {
|
||||||
functionCallStatement.target.accept(this)
|
functionCallStatement.target.accept(this)
|
||||||
functionCallStatement.arglist.forEach { it.accept(this) }
|
functionCallStatement.args.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(identifier: IdentifierReference) {
|
fun visit(identifier: IdentifierReference) {
|
||||||
|
@ -13,8 +13,8 @@ private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment,
|
|||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
val struct = targetVar.struct!!
|
val struct = targetVar.struct!!
|
||||||
when {
|
when (structAssignment.value) {
|
||||||
structAssignment.value is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
||||||
if (sourceVar.struct == null)
|
if (sourceVar.struct == null)
|
||||||
throw FatalAstException("can only assign arrays or structs to structs")
|
throw FatalAstException("can only assign arrays or structs to structs")
|
||||||
@ -39,7 +39,7 @@ private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment,
|
|||||||
assign
|
assign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
structAssignment.value is StructLiteralValue -> {
|
is StructLiteralValue -> {
|
||||||
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("strange struct value")
|
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
|
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||||
when(val sub = call.target.targetStatement(scope)) {
|
when(val sub = call.target.targetStatement(scope)) {
|
||||||
is Subroutine -> {
|
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)
|
val argItype = arg.second.value.inferType(program)
|
||||||
if(argItype.isKnown) {
|
if(argItype.isKnown) {
|
||||||
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
||||||
@ -87,7 +87,7 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
|
|||||||
if (argtype isAssignableTo requiredType) {
|
if (argtype isAssignableTo requiredType) {
|
||||||
val typecasted = TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position)
|
val typecasted = TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position)
|
||||||
typecasted.linkParents(arg.second.value.parent)
|
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
|
// 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)
|
val func = BuiltinFunctions.getValue(sub.name)
|
||||||
if(func.pure) {
|
if(func.pure) {
|
||||||
// non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters
|
// 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)
|
val argItype = arg.second.value.inferType(program)
|
||||||
if (argItype.isKnown) {
|
if (argItype.isKnown) {
|
||||||
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
||||||
@ -108,7 +108,7 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
|
|||||||
if (argtype isAssignableTo possibleType) {
|
if (argtype isAssignableTo possibleType) {
|
||||||
val typecasted = TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position)
|
val typecasted = TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position)
|
||||||
typecasted.linkParents(arg.second.value.parent)
|
typecasted.linkParents(arg.second.value.parent)
|
||||||
call.arglist[arg.second.index] = typecasted
|
call.args[arg.second.index] = typecasted
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,9 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
|
|||||||
if(decl.isArray && decl.value==null) {
|
if(decl.isArray && decl.value==null) {
|
||||||
// array datatype without initialization value, add list of zeros
|
// array datatype without initialization value, add list of zeros
|
||||||
val arraysize = decl.arraysize!!.size()!!
|
val arraysize = decl.arraysize!!.size()!!
|
||||||
val array = ArrayLiteralValue(decl.datatype,
|
val array = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
|
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
|
||||||
null, decl.position)
|
decl.position)
|
||||||
array.addToHeap()
|
|
||||||
decl.value = array
|
decl.value = array
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,11 +78,11 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
|
|||||||
parentStatement = parentStatement.parent
|
parentStatement = parentStatement.parent
|
||||||
val targetStatement = functionCall.target.targetSubroutine(program.namespace)
|
val targetStatement = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if(targetStatement!=null) {
|
if(targetStatement!=null) {
|
||||||
addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, parentStatement)
|
addAddressOfExprIfNeeded(targetStatement, functionCall.args, parentStatement)
|
||||||
} else {
|
} else {
|
||||||
val builtinFunc = BuiltinFunctions[functionCall.target.nameInSource.joinToString (".")]
|
val builtinFunc = BuiltinFunctions[functionCall.target.nameInSource.joinToString (".")]
|
||||||
if(builtinFunc!=null)
|
if(builtinFunc!=null)
|
||||||
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCall.arglist, parentStatement)
|
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCall.args, parentStatement)
|
||||||
}
|
}
|
||||||
return functionCall
|
return functionCall
|
||||||
}
|
}
|
||||||
@ -91,11 +90,11 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
|
|||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
val targetStatement = functionCallStatement.target.targetSubroutine(program.namespace)
|
val targetStatement = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
if(targetStatement!=null) {
|
if(targetStatement!=null) {
|
||||||
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
|
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.args, functionCallStatement)
|
||||||
} else {
|
} else {
|
||||||
val builtinFunc = BuiltinFunctions[functionCallStatement.target.nameInSource.joinToString (".")]
|
val builtinFunc = BuiltinFunctions[functionCallStatement.target.nameInSource.joinToString (".")]
|
||||||
if(builtinFunc!=null)
|
if(builtinFunc!=null)
|
||||||
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCallStatement.arglist, functionCallStatement)
|
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCallStatement.args, functionCallStatement)
|
||||||
}
|
}
|
||||||
return 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) {
|
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.
|
// 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)) {
|
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)
|
if(argparam.second is AddressOf)
|
||||||
continue
|
continue
|
||||||
val idref = argparam.second as? IdentifierReference
|
val idref = argparam.second as? IdentifierReference
|
||||||
val strvalue = argparam.second as? StringLiteralValue
|
val strvalue = argparam.second as? StringLiteralValue
|
||||||
if(idref!=null) {
|
if(idref!=null) {
|
||||||
val variable = idref.targetVarDecl(program.namespace)
|
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)
|
val pointerExpr = AddressOf(idref, idref.position)
|
||||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||||
arglist[argparam.first.index] = pointerExpr
|
arglist[argparam.first.index] = pointerExpr
|
||||||
|
@ -192,19 +192,14 @@ class VarDecl(val type: VarDeclType,
|
|||||||
private var autoHeapValueSequenceNumber = 0
|
private var autoHeapValueSequenceNumber = 0
|
||||||
|
|
||||||
fun createAuto(string: StringLiteralValue): VarDecl {
|
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}"
|
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)
|
isArray = false, autogeneratedDontRemove = true, position = string.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createAuto(array: ArrayLiteralValue): VarDecl {
|
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 autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||||
val declaredType = ArrayElementTypes.getValue(array.type)
|
val declaredType = ArrayElementTypes.getValue(array.type.typeOrElse(DataType.STRUCT))
|
||||||
val arraysize = ArrayIndex.forArray(array)
|
val arraysize = ArrayIndex.forArray(array)
|
||||||
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
|
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
|
||||||
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
||||||
@ -477,16 +472,17 @@ class Jump(val address: Int?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCallStatement(override var target: IdentifierReference,
|
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 val position: Position) : Statement(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
get() = arglist.any { it !is NumericLiteralValue }
|
get() = args.any { it !is NumericLiteralValue }
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
target.linkParents(this)
|
target.linkParents(this)
|
||||||
arglist.forEach { it.linkParents(this) }
|
args.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
@ -598,6 +594,7 @@ class Subroutine(override val name: String,
|
|||||||
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
|
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open class SubroutineParameter(val name: String,
|
open class SubroutineParameter(val name: String,
|
||||||
val type: DataType,
|
val type: DataType,
|
||||||
override val position: Position) : Node {
|
override val position: Position) : Node {
|
||||||
@ -666,6 +663,12 @@ class ForLoop(val loopRegister: Register?,
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loopVarDt(program: Program): InferredTypes.InferredType {
|
||||||
|
val lv = loopVar
|
||||||
|
return if(loopRegister!=null) InferredTypes.InferredType.known(DataType.UBYTE)
|
||||||
|
else lv?.inferType(program) ?: InferredTypes.InferredType.unknown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhileLoop(var condition: Expression,
|
class WhileLoop(var condition: Expression,
|
||||||
|
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.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
|
||||||
import prog8.optimizer.constantFold
|
import prog8.optimizer.constantFold
|
||||||
import prog8.optimizer.optimizeStatements
|
import prog8.optimizer.optimizeStatements
|
||||||
import prog8.optimizer.simplifyExpressions
|
import prog8.optimizer.simplifyExpressions
|
||||||
@ -101,9 +100,9 @@ fun compileProgram(filepath: Path,
|
|||||||
|
|
||||||
if(writeAssembly) {
|
if(writeAssembly) {
|
||||||
// asm generation directly from the Ast, no need for intermediate code
|
// asm generation directly from the Ast, no need for intermediate code
|
||||||
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
|
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
||||||
programAst.anonscopeVarsCleanup()
|
programAst.anonscopeVarsCleanup()
|
||||||
val assembly = AsmGen(programAst, zeropage, compilerOptions, outputDir).compileToAssembly(optimize)
|
val assembly = CompilationTarget.asmGenerator(programAst, zeropage, compilerOptions, outputDir).compileToAssembly(optimize)
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions)
|
||||||
programName = assembly.name
|
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, altEncoding: Boolean) -> List<Short>
|
||||||
|
lateinit var decodeString: (bytes: List<Short>, altEncoding: Boolean) -> 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.CompilationOptions
|
||||||
import prog8.compiler.OutputType
|
import prog8.compiler.OutputType
|
||||||
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
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 assemblyFile = outputDir.resolve("$name.asm")
|
||||||
private val prgFile = outputDir.resolve("$name.prg")
|
private val prgFile = outputDir.resolve("$name.prg")
|
||||||
private val binFile = outputDir.resolve("$name.bin")
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
||||||
|
|
||||||
companion object {
|
override fun assemble(options: CompilationOptions) {
|
||||||
// 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) {
|
|
||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
|
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=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.CompilerException
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
import prog8.compiler.ZeropageType
|
import prog8.compiler.ZeropageType
|
||||||
|
import prog8.compiler.target.IMachineDefinition
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
object MachineDefinition {
|
object C64MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
// 5-byte cbm MFLPT format limitations:
|
// 5-byte cbm MFLPT format limitations:
|
||||||
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
override 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_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||||
|
override val FLOAT_MEM_SIZE = 5
|
||||||
const val BASIC_LOAD_ADDRESS = 0x0801
|
const val BASIC_LOAD_ADDRESS = 0x0801
|
||||||
const val RAW_LOAD_ADDRESS = 0xc000
|
const val RAW_LOAD_ADDRESS = 0xc000
|
||||||
|
|
||||||
@ -30,6 +31,19 @@ object MachineDefinition {
|
|||||||
const val ESTACK_HI_PLUS1_HEX = "\$cf01"
|
const val ESTACK_HI_PLUS1_HEX = "\$cf01"
|
||||||
const val ESTACK_HI_PLUS2_HEX = "\$cf02"
|
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) {
|
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) {
|
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MemorySize = 5
|
|
||||||
|
|
||||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||||
fun fromNumber(num: Number): Mflpt5 {
|
fun fromNumber(num: Number): Mflpt5 {
|
||||||
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
||||||
@ -232,7 +244,6 @@ object MachineDefinition {
|
|||||||
return bcopy
|
return bcopy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||||
Color(0x000000), // 0 = black
|
Color(0x000000), // 0 = black
|
||||||
Color(0xFFFFFF), // 1 = white
|
Color(0xFFFFFF), // 1 = white
|
@ -1058,7 +1058,7 @@ object Petscii {
|
|||||||
0.toShort()
|
0.toShort()
|
||||||
else {
|
else {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
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()
|
0.toShort()
|
||||||
else {
|
else {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
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.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
|
import prog8.compiler.target.IAssemblyGenerator
|
||||||
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
import prog8.compiler.target.c64.AssemblyProgram
|
import prog8.compiler.target.c64.AssemblyProgram
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.functions.FunctionSignature
|
import prog8.functions.FunctionSignature
|
||||||
@ -22,13 +24,10 @@ import java.util.ArrayDeque
|
|||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
internal class AssemblyError(msg: String) : RuntimeException(msg)
|
|
||||||
|
|
||||||
|
|
||||||
internal class AsmGen(private val program: Program,
|
internal class AsmGen(private val program: Program,
|
||||||
private val zeropage: Zeropage,
|
private val zeropage: Zeropage,
|
||||||
private val options: CompilationOptions,
|
private val options: CompilationOptions,
|
||||||
private val outputDir: Path) {
|
private val outputDir: Path): IAssemblyGenerator {
|
||||||
|
|
||||||
private val assemblyLines = mutableListOf<String>()
|
private val assemblyLines = mutableListOf<String>()
|
||||||
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
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 loopEndLabels = ArrayDeque<String>()
|
||||||
internal val loopContinueLabels = ArrayDeque<String>()
|
internal val loopContinueLabels = ArrayDeque<String>()
|
||||||
|
|
||||||
internal fun compileToAssembly(optimize: Boolean): AssemblyProgram {
|
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
|
||||||
assemblyLines.clear()
|
assemblyLines.clear()
|
||||||
loopEndLabels.clear()
|
loopEndLabels.clear()
|
||||||
loopContinueLabels.clear()
|
loopContinueLabels.clear()
|
||||||
@ -84,7 +83,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
program.actualLoadAddress = program.definedLoadAddress
|
program.actualLoadAddress = program.definedLoadAddress
|
||||||
if (program.actualLoadAddress == 0) // fix load address
|
if (program.actualLoadAddress == 0) // fix load address
|
||||||
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC)
|
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 {
|
when {
|
||||||
options.launcher == LauncherType.BASIC -> {
|
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
|
// the global list of all floating point constants for the whole program
|
||||||
out("; global float constants")
|
out("; global float constants")
|
||||||
for (flt in globalFloatConsts) {
|
for (flt in globalFloatConsts) {
|
||||||
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(flt.key)
|
val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(flt.key)
|
||||||
val floatFill = makeFloatFill(mflpt5)
|
val floatFill = makeFloatFill(mflpt5)
|
||||||
val floatvalue = flt.key
|
val floatvalue = flt.key
|
||||||
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
@ -198,7 +197,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
} else assemblyLines.add(fragment)
|
} 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 b0 = "$" + flt.b0.toString(16).padStart(2, '0')
|
||||||
val b1 = "$" + flt.b1.toString(16).padStart(2, '0')
|
val b1 = "$" + flt.b1.toString(16).padStart(2, '0')
|
||||||
val b2 = "$" + flt.b2.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"
|
return "$b0, $b1, $b2, $b3, $b4"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun encodeStr(str: String, dt: DataType): List<Short> {
|
private fun encode(str: String, altEncoding: Boolean): List<Short> {
|
||||||
return when(dt) {
|
val bytes = if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
DataType.STR -> {
|
return bytes.plus(0)
|
||||||
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 zeropagevars2asm(statements: List<Statement>) {
|
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.WORD -> out("${decl.name}\t.sint 0")
|
||||||
DataType.FLOAT -> out("${decl.name}\t.byte 0,0,0,0,0 ; float")
|
DataType.FLOAT -> out("${decl.name}\t.byte 0,0,0,0,0 ; float")
|
||||||
DataType.STRUCT -> {} // is flattened
|
DataType.STRUCT -> {} // is flattened
|
||||||
DataType.STR, DataType.STR_S -> {
|
DataType.STR -> {
|
||||||
val string = (decl.value as StringLiteralValue).value
|
val str = decl.value as StringLiteralValue
|
||||||
val encoded = encodeStr(string, decl.datatype)
|
outputStringvar(decl, encode(str.value, str.altEncoding))
|
||||||
outputStringvar(decl, encoded)
|
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
val data = makeArrayFillDataUnsigned(decl)
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
@ -304,7 +293,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
val array = (decl.value as ArrayLiteralValue).value
|
val array = (decl.value as ArrayLiteralValue).value
|
||||||
val floatFills = array.map {
|
val floatFills = array.map {
|
||||||
val number = (it as NumericLiteralValue).number
|
val number = (it as NumericLiteralValue).number
|
||||||
makeFloatFill(MachineDefinition.Mflpt5.fromNumber(number))
|
makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number))
|
||||||
}
|
}
|
||||||
out(decl.name)
|
out(decl.name)
|
||||||
for (f in array.zip(floatFills))
|
for (f in array.zip(floatFills))
|
||||||
@ -341,8 +330,11 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
// special treatment for string types: merge strings that are identical
|
// special treatment for string types: merge strings that are identical
|
||||||
val encodedstringVars = normalVars
|
val encodedstringVars = normalVars
|
||||||
.filter {it.datatype in StringDatatypes }
|
.filter {it.datatype == DataType.STR }
|
||||||
.map { it to encodeStr((it.value as StringLiteralValue).value, it.datatype) }
|
.map {
|
||||||
|
val str = it.value as StringLiteralValue
|
||||||
|
it to encode(str.value, str.altEncoding)
|
||||||
|
}
|
||||||
.groupBy({it.second}, {it.first})
|
.groupBy({it.second}, {it.first})
|
||||||
for((encoded, variables) in encodedstringVars) {
|
for((encoded, variables) in encodedstringVars) {
|
||||||
variables.dropLast(1).forEach { out(it.name) }
|
variables.dropLast(1).forEach { out(it.name) }
|
||||||
@ -351,7 +343,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// non-string variables
|
// 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)
|
if(it.scopedname !in allocatedZeropageVariables)
|
||||||
vardecl2asm(it)
|
vardecl2asm(it)
|
||||||
}
|
}
|
||||||
@ -367,14 +359,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
|
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
|
||||||
val array = (decl.value as ArrayLiteralValue).value
|
val array = (decl.value as ArrayLiteralValue).value
|
||||||
return when {
|
return when (decl.datatype) {
|
||||||
decl.datatype == DataType.ARRAY_UB ->
|
DataType.ARRAY_UB ->
|
||||||
// byte array can never contain pointer-to types, so treat values as all integers
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
array.map {
|
array.map {
|
||||||
val number = (it as NumericLiteralValue).number.toInt()
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
"$"+number.toString(16).padStart(2, '0')
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
}
|
}
|
||||||
decl.datatype== DataType.ARRAY_UW -> array.map {
|
DataType.ARRAY_UW -> array.map {
|
||||||
if(it is NumericLiteralValue) {
|
if(it is NumericLiteralValue) {
|
||||||
"$" + it.number.toInt().toString(16).padStart(4, '0')
|
"$" + it.number.toInt().toString(16).padStart(4, '0')
|
||||||
} else {
|
} else {
|
||||||
@ -425,7 +417,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
internal fun getFloatConst(number: Double): String {
|
internal fun getFloatConst(number: Double): String {
|
||||||
// try to match the ROM float constants to save memory
|
// 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)
|
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
|
||||||
when {
|
when {
|
||||||
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_ZERO"
|
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_ZERO"
|
||||||
@ -511,7 +503,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun readAndPushArrayvalueWithIndexA(arrayDt: DataType, variable: IdentifierReference) {
|
internal fun readAndPushArrayvalueWithIndexA(arrayDt: DataType, variable: IdentifierReference) {
|
||||||
val variablename = asmIdentifierName(variable)
|
val variablename = asmIdentifierName(variable)
|
||||||
when (arrayDt) {
|
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")
|
out(" tay | lda $variablename,y | sta $ESTACK_LO_HEX,x | dex")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
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")
|
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
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
// 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.Assignment
|
||||||
import prog8.ast.statements.DirectMemoryWrite
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
|
||||||
import prog8.compiler.toHex
|
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) {
|
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 arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||||
val indexValue = index.number.toInt() * ArrayElementTypes.getValue(arrayDt).memorySize()
|
val indexValue = index.number.toInt() * ArrayElementTypes.getValue(arrayDt).memorySize()
|
||||||
when (arrayDt) {
|
when (arrayDt) {
|
||||||
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
||||||
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | dex")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
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 ->
|
DataType.ARRAY_F ->
|
||||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
||||||
else ->
|
else ->
|
||||||
@ -124,21 +129,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
target.register!=null -> {
|
target.register!=null -> {
|
||||||
if(target.register== Register.X)
|
if(target.register== Register.X)
|
||||||
throw AssemblyError("can't pop into X register - use variable instead")
|
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 -> {
|
targetIdent!=null -> {
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)
|
val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
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 -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta $targetName
|
sta $targetName
|
||||||
lda ${MachineDefinition.ESTACK_HI_HEX},x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta $targetName+1
|
sta $targetName+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -153,14 +158,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
target.memoryAddress!=null -> {
|
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)
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
}
|
}
|
||||||
target.arrayindexed!=null -> {
|
target.arrayindexed!=null -> {
|
||||||
val arrayDt = target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!.datatype
|
val arrayDt = target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||||
val arrayVarName = asmgen.asmIdentifierName(target.arrayindexed!!.identifier)
|
val arrayVarName = asmgen.asmIdentifierName(target.arrayindexed!!.identifier)
|
||||||
asmgen.translateExpression(target.arrayindexed!!.arrayspec.index)
|
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)
|
popAndWriteArrayvalueWithIndexA(arrayDt, arrayVarName)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird assignment target $target")
|
else -> throw AssemblyError("weird assignment target $target")
|
||||||
@ -225,9 +230,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
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.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)
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||||
}
|
}
|
||||||
@ -285,9 +290,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
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.translateExpression(index)
|
||||||
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||||
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||||
}
|
}
|
||||||
target.memoryAddress != null -> {
|
target.memoryAddress != null -> {
|
||||||
@ -303,8 +308,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.translateExpression(addressExpr)
|
asmgen.translateExpression(addressExpr)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
lda $ESTACK_LO_HEX,x
|
||||||
ldy ${MachineDefinition.ESTACK_HI_HEX},x
|
ldy $ESTACK_HI_HEX,x
|
||||||
sta (+) +1
|
sta (+) +1
|
||||||
sty (+) +2
|
sty (+) +2
|
||||||
lda $sourceName
|
lda $sourceName
|
||||||
@ -361,9 +366,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
is RegisterExpr -> {
|
||||||
when(register) {
|
when(register) {
|
||||||
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
|
||||||
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
|
||||||
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
|
||||||
}
|
}
|
||||||
when(index.register) {
|
when(index.register) {
|
||||||
Register.A -> {}
|
Register.A -> {}
|
||||||
@ -372,20 +377,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
tay
|
tay
|
||||||
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
lda ${C64Zeropage.SCRATCH_B1}
|
||||||
sta $targetName,y
|
sta $targetName,y
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
when(register) {
|
when(register) {
|
||||||
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
|
||||||
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
|
||||||
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
|
||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${asmgen.asmIdentifierName(index)}
|
lda ${asmgen.asmIdentifierName(index)}
|
||||||
tay
|
tay
|
||||||
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
lda ${C64Zeropage.SCRATCH_B1}
|
||||||
sta $targetName,y
|
sta $targetName,y
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -394,15 +399,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.translateExpression(index)
|
asmgen.translateExpression(index)
|
||||||
asmgen.restoreRegister(register)
|
asmgen.restoreRegister(register)
|
||||||
when(register) {
|
when(register) {
|
||||||
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
|
||||||
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
|
||||||
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
|
||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
lda $ESTACK_LO_HEX,x
|
||||||
tay
|
tay
|
||||||
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
lda ${C64Zeropage.SCRATCH_B1}
|
||||||
sta $targetName,y
|
sta $targetName,y
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -423,29 +428,29 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
when(register) {
|
when(register) {
|
||||||
Register.A -> asmgen.out("""
|
Register.A -> asmgen.out("""
|
||||||
ldy $targetName
|
ldy $targetName
|
||||||
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
sty ${C64Zeropage.SCRATCH_W1}
|
||||||
ldy $targetName+1
|
ldy $targetName+1
|
||||||
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
sty ${C64Zeropage.SCRATCH_W1+1}
|
||||||
ldy #0
|
ldy #0
|
||||||
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
sta (${C64Zeropage.SCRATCH_W1}),y
|
||||||
""")
|
""")
|
||||||
Register.X -> asmgen.out("""
|
Register.X -> asmgen.out("""
|
||||||
txa
|
txa
|
||||||
ldy $targetName
|
ldy $targetName
|
||||||
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
sty ${C64Zeropage.SCRATCH_W1}
|
||||||
ldy $targetName+1
|
ldy $targetName+1
|
||||||
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
sty ${C64Zeropage.SCRATCH_W1+1}
|
||||||
ldy #0
|
ldy #0
|
||||||
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
sta (${C64Zeropage.SCRATCH_W1}),y
|
||||||
""")
|
""")
|
||||||
Register.Y -> asmgen.out("""
|
Register.Y -> asmgen.out("""
|
||||||
tya
|
tya
|
||||||
ldy $targetName
|
ldy $targetName
|
||||||
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
sty ${C64Zeropage.SCRATCH_W1}
|
||||||
ldy $targetName+1
|
ldy $targetName+1
|
||||||
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
sty ${C64Zeropage.SCRATCH_W1+1}
|
||||||
ldy #0
|
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("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta (+) +1
|
sta (+) +1
|
||||||
lda ${MachineDefinition.ESTACK_HI_HEX},x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) +2
|
sta (+) +2
|
||||||
+ sty ${65535.toHex()} ; modified
|
+ sty ${65535.toHex()} ; modified
|
||||||
""")
|
""")
|
||||||
@ -502,7 +507,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.translateExpression(index)
|
asmgen.translateExpression(index)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
lda $ESTACK_LO_HEX,x
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda #<${word.toHex()}
|
lda #<${word.toHex()}
|
||||||
@ -537,7 +542,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.translateExpression(index)
|
asmgen.translateExpression(index)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
ldy ${MachineDefinition.ESTACK_LO_HEX},x
|
ldy $ESTACK_LO_HEX,x
|
||||||
lda #${byte.toHex()}
|
lda #${byte.toHex()}
|
||||||
sta $targetName,y
|
sta $targetName,y
|
||||||
""")
|
""")
|
||||||
@ -567,7 +572,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
if(index is NumericLiteralValue) {
|
if(index is NumericLiteralValue) {
|
||||||
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
|
val indexValue = index.number.toInt() * C64MachineDefinition.FLOAT_MEM_SIZE
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #0
|
lda #0
|
||||||
sta $targetName+$indexValue
|
sta $targetName+$indexValue
|
||||||
@ -580,11 +585,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.translateExpression(index)
|
asmgen.translateExpression(index)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
lda $ESTACK_LO_HEX,x
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc ${MachineDefinition.ESTACK_LO_HEX},x
|
adc $ESTACK_LO_HEX,x
|
||||||
tay
|
tay
|
||||||
lda #0
|
lda #0
|
||||||
sta $targetName,y
|
sta $targetName,y
|
||||||
@ -620,7 +625,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
if(index is NumericLiteralValue) {
|
if(index is NumericLiteralValue) {
|
||||||
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
|
val indexValue = index.number.toInt() * C64MachineDefinition.FLOAT_MEM_SIZE
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $constFloat
|
lda $constFloat
|
||||||
sta $arrayVarName+$indexValue
|
sta $arrayVarName+$indexValue
|
||||||
@ -636,11 +641,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
} else {
|
} else {
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta ${MachineDefinition.C64Zeropage.SCRATCH_REG}
|
sta ${C64Zeropage.SCRATCH_REG}
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc ${MachineDefinition.C64Zeropage.SCRATCH_REG}
|
adc ${C64Zeropage.SCRATCH_REG}
|
||||||
tay
|
tay
|
||||||
lda $constFloat
|
lda $constFloat
|
||||||
sta $arrayVarName,y
|
sta $arrayVarName,y
|
||||||
@ -725,14 +730,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
private fun popAndWriteArrayvalueWithIndexA(arrayDt: DataType, variablename: String) {
|
private fun popAndWriteArrayvalueWithIndexA(arrayDt: DataType, variablename: String) {
|
||||||
when (arrayDt) {
|
when (arrayDt) {
|
||||||
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
||||||
asmgen.out(" tay | inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $variablename,y")
|
asmgen.out(" tay | inx | lda $ESTACK_LO_HEX,x | sta $variablename,y")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
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 ->
|
DataType.ARRAY_F ->
|
||||||
// index * 5 is done in the subroutine that's called
|
// index * 5 is done in the subroutine that's called
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta ${MachineDefinition.ESTACK_LO_HEX},x
|
sta $ESTACK_LO_HEX,x
|
||||||
dex
|
dex
|
||||||
lda #<$variablename
|
lda #<$variablename
|
||||||
ldy #>$variablename
|
ldy #>$variablename
|
||||||
|
@ -9,12 +9,13 @@ import prog8.ast.base.WordDatatypes
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.functions.FunctionSignature
|
import prog8.functions.FunctionSignature
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
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) {
|
when (functionName) {
|
||||||
"msb" -> {
|
"msb" -> {
|
||||||
val arg = fcall.arglist.single()
|
val arg = fcall.args.single()
|
||||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||||
throw AssemblyError("msb required word argument")
|
throw AssemblyError("msb required word argument")
|
||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
@ -52,12 +53,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"mkword" -> {
|
"mkword" -> {
|
||||||
translateFunctionArguments(fcall.arglist, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
||||||
}
|
}
|
||||||
"abs" -> {
|
"abs" -> {
|
||||||
translateFunctionArguments(fcall.arglist, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
val dt = fcall.arglist.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
||||||
@ -66,8 +67,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"swap" -> {
|
"swap" -> {
|
||||||
val first = fcall.arglist[0]
|
val first = fcall.args[0]
|
||||||
val second = fcall.arglist[1]
|
val second = fcall.args[1]
|
||||||
asmgen.translateExpression(first)
|
asmgen.translateExpression(first)
|
||||||
asmgen.translateExpression(second)
|
asmgen.translateExpression(second)
|
||||||
// pop in reverse order
|
// pop in reverse order
|
||||||
@ -77,14 +78,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.assignFromEvalResult(secondTarget)
|
asmgen.assignFromEvalResult(secondTarget)
|
||||||
}
|
}
|
||||||
"strlen" -> {
|
"strlen" -> {
|
||||||
outputPushAddressOfIdentifier(fcall.arglist[0])
|
outputPushAddressOfIdentifier(fcall.args[0])
|
||||||
asmgen.out(" jsr prog8_lib.func_strlen")
|
asmgen.out(" jsr prog8_lib.func_strlen")
|
||||||
}
|
}
|
||||||
"min", "max", "sum" -> {
|
"min", "max", "sum" -> {
|
||||||
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
outputPushAddressAndLenghtOfArray(fcall.args[0])
|
||||||
val dt = fcall.arglist.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
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_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
||||||
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
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" -> {
|
"any", "all" -> {
|
||||||
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
outputPushAddressAndLenghtOfArray(fcall.args[0])
|
||||||
val dt = fcall.arglist.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
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_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"sgn" -> {
|
"sgn" -> {
|
||||||
translateFunctionArguments(fcall.arglist, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
val dt = fcall.arglist.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when(dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr math.sign_ub")
|
DataType.UBYTE -> asmgen.out(" jsr math.sign_ub")
|
||||||
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
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",
|
"ln", "log2", "sqrt", "rad",
|
||||||
"deg", "round", "floor", "ceil",
|
"deg", "round", "floor", "ceil",
|
||||||
"rdnf" -> {
|
"rdnf" -> {
|
||||||
translateFunctionArguments(fcall.arglist, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
asmgen.out(" jsr c64flt.func_$functionName")
|
asmgen.out(" jsr c64flt.func_$functionName")
|
||||||
}
|
}
|
||||||
"lsl" -> {
|
"lsl" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
@ -179,7 +180,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
"lsr" -> {
|
"lsr" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
@ -263,7 +264,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
"rol" -> {
|
"rol" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
@ -322,7 +323,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
"rol2" -> {
|
"rol2" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
@ -374,7 +375,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
"ror" -> {
|
"ror" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
@ -432,7 +433,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
"ror2" -> {
|
"ror2" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
@ -483,7 +484,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"sort" -> {
|
"sort" -> {
|
||||||
val variable = fcall.arglist.single()
|
val variable = fcall.args.single()
|
||||||
if(variable is IdentifierReference) {
|
if(variable is IdentifierReference) {
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
val varName = asmgen.asmIdentifierName(variable)
|
||||||
@ -511,7 +512,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
""")
|
""")
|
||||||
asmgen.out(if(decl.datatype==DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
asmgen.out(if(decl.datatype==DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> TODO("sort floats (consider another solution if possible - this will be very slow, if ever implemented)")
|
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -519,7 +520,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
throw AssemblyError("weird type")
|
throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
"reverse" -> {
|
"reverse" -> {
|
||||||
val variable = fcall.arglist.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is IdentifierReference) {
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
val varName = asmgen.asmIdentifierName(variable)
|
||||||
@ -545,7 +546,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
jsr prog8_lib.reverse_w
|
jsr prog8_lib.reverse_w
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> TODO("reverse floats (consider another solution if possible - this will be quite slow, if ever implemented)")
|
DataType.ARRAY_F -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
|
lda #$numElements
|
||||||
|
jsr prog8_lib.reverse_f
|
||||||
|
""")
|
||||||
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,7 +570,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" pla | tay | pla | tax | pla | plp")
|
asmgen.out(" pla | tay | pla | tax | pla | plp")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
translateFunctionArguments(fcall.arglist, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
asmgen.out(" jsr prog8_lib.func_$functionName")
|
asmgen.out(" jsr prog8_lib.func_$functionName")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,13 @@ package prog8.compiler.target.c64.codegen
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
|
||||||
import prog8.compiler.toHex
|
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 prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.absoluteValue
|
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
|
// result value in cpu or status registers, put it on the stack
|
||||||
if (reg.registerOrPair != null) {
|
if (reg.registerOrPair != null) {
|
||||||
when (reg.registerOrPair) {
|
when (reg.registerOrPair) {
|
||||||
RegisterOrPair.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
RegisterOrPair.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
|
||||||
RegisterOrPair.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
RegisterOrPair.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
||||||
RegisterOrPair.AY -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | tya | sta ${MachineDefinition.ESTACK_HI_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")
|
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 -> {
|
DataType.UBYTE -> {
|
||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
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")
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -69,7 +74,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
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")
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_b2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -111,35 +116,35 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
|
|
||||||
private fun translateExpression(expr: AddressOf) {
|
private fun translateExpression(expr: AddressOf) {
|
||||||
val name = asmgen.asmIdentifierName(expr.identifier)
|
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) {
|
private fun translateExpression(expr: DirectMemoryRead) {
|
||||||
when(expr.addressExpression) {
|
when(expr.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
|
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 -> {
|
is IdentifierReference -> {
|
||||||
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as 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 -> {
|
else -> {
|
||||||
translateExpression(expr.addressExpression)
|
translateExpression(expr.addressExpression)
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address")
|
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) {
|
private fun translateExpression(expr: NumericLiteralValue) {
|
||||||
when(expr.type) {
|
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("""
|
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||||
lda #<${expr.number.toHex()}
|
lda #<${expr.number.toHex()}
|
||||||
sta ${MachineDefinition.ESTACK_LO_HEX},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda #>${expr.number.toHex()}
|
lda #>${expr.number.toHex()}
|
||||||
sta ${MachineDefinition.ESTACK_HI_HEX},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
""")
|
""")
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
@ -152,9 +157,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
|
|
||||||
private fun translateExpression(expr: RegisterExpr) {
|
private fun translateExpression(expr: RegisterExpr) {
|
||||||
when(expr.register) {
|
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.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)
|
val varname = asmgen.asmIdentifierName(expr)
|
||||||
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
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 -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $varname+1 | 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")
|
||||||
}
|
|
||||||
in ArrayDatatypes, in StringDatatypes -> {
|
|
||||||
asmgen.out(" lda #<$varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda #>$varname | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_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")
|
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)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> repeat(amount) { asmgen.out(" lsr ${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 ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | asl a | ror ${MachineDefinition.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 ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.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 ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | asl a | ror ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.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")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -209,9 +214,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
if (leftDt in ByteDatatypes)
|
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
|
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
|
return
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
@ -297,9 +302,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(type) {
|
when(type) {
|
||||||
in ByteDatatypes ->
|
in ByteDatatypes ->
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
lda $ESTACK_LO_PLUS1_HEX,x
|
||||||
eor #255
|
eor #255
|
||||||
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
""")
|
""")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||||
else -> throw AssemblyError("weird type")
|
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()
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
in ByteDatatypes -> {
|
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 -> {
|
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 -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_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(" jsr prog8_lib.remainder_ub")
|
||||||
}
|
}
|
||||||
"+" -> asmgen.out("""
|
"+" -> asmgen.out("""
|
||||||
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
|
lda $ESTACK_LO_PLUS2_HEX,x
|
||||||
clc
|
clc
|
||||||
adc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
adc $ESTACK_LO_PLUS1_HEX,x
|
||||||
inx
|
inx
|
||||||
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
""")
|
""")
|
||||||
"-" -> asmgen.out("""
|
"-" -> asmgen.out("""
|
||||||
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
|
lda $ESTACK_LO_PLUS2_HEX,x
|
||||||
sec
|
sec
|
||||||
sbc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
sbc $ESTACK_LO_PLUS1_HEX,x
|
||||||
inx
|
inx
|
||||||
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
""")
|
""")
|
||||||
"<<", ">>" -> throw AssemblyError("bit-shifts not via stack")
|
"<<", ">>" -> throw AssemblyError("bit-shifts not via stack")
|
||||||
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
"<" -> 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.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
// todo choose more efficient comparisons to avoid needless lda's
|
// todo choose more efficient comparisons to avoid needless lda's
|
||||||
@ -317,7 +318,7 @@ $endLabel inx""")
|
|||||||
val iterableName = asmgen.asmIdentifierName(ident)
|
val iterableName = asmgen.asmIdentifierName(ident)
|
||||||
val decl = ident.targetVarDecl(program.namespace)!!
|
val decl = ident.targetVarDecl(program.namespace)!!
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.STR, DataType.STR_S -> {
|
DataType.STR -> {
|
||||||
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
||||||
throw AssemblyError("can only use A")
|
throw AssemblyError("can only use A")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
|
@ -7,8 +7,11 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.statements.SubroutineParameter
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
|
||||||
import prog8.compiler.toHex
|
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) {
|
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...
|
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)
|
val subName = asmgen.asmIdentifierName(stmt.target)
|
||||||
if(stmt.arglist.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
for(arg in sub.parameters.withIndex().zip(stmt.arglist)) {
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
translateFuncArguments(arg.first, arg.second, sub)
|
translateFuncArguments(arg.first, arg.second, sub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,7 +142,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
lda $ESTACK_LO_HEX,x
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
@ -166,9 +169,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
when(register) {
|
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.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")
|
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)
|
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
|
||||||
throw AssemblyError("can't use X register here - use a variable")
|
throw AssemblyError("can't use X register here - use a variable")
|
||||||
else if (register == RegisterOrPair.AY)
|
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.
|
// we have a special rule for some types.
|
||||||
// strings are assignable to UWORD, for example, and vice versa
|
// 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
|
return true
|
||||||
if(argType==DataType.UWORD && paramType in StringDatatypes)
|
if(argType==DataType.UWORD && paramType == DataType.STR)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -6,8 +6,10 @@ import prog8.ast.expressions.IdentifierReference
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.RegisterExpr
|
import prog8.ast.expressions.RegisterExpr
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
|
||||||
import prog8.compiler.toHex
|
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 class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
internal fun translate(stmt: PostIncrDecr) {
|
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) {
|
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) {
|
when(arrayDt) {
|
||||||
DataType.STR, DataType.STR_S,
|
DataType.STR,
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
asmgen.out(if(incr) " inc $arrayVarName,x" else " dec $arrayVarName,x")
|
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")
|
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("address", IterableDatatypes + DataType.UWORD),
|
||||||
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
|
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
|
||||||
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
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() }!!
|
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)
|
if(!idt.isKnown)
|
||||||
throw FatalAstException("couldn't determine type of iterable $arglist")
|
throw FatalAstException("couldn't determine type of iterable $arglist")
|
||||||
return when(val dt = idt.typeOrElse(DataType.STRUCT)) {
|
return when(val dt = idt.typeOrElse(DataType.STRUCT)) {
|
||||||
in NumericDatatypes -> dt
|
DataType.STR, in NumericDatatypes -> dt
|
||||||
in StringDatatypes -> dt
|
|
||||||
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
||||||
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
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" -> {
|
"max", "min" -> {
|
||||||
when(val dt = datatypeFromIterableArg(args.single())) {
|
when(val dt = datatypeFromIterableArg(args.single())) {
|
||||||
|
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in NumericDatatypes -> InferredTypes.knownFor(dt)
|
in NumericDatatypes -> InferredTypes.knownFor(dt)
|
||||||
in StringDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
|
||||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(dt))
|
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(dt))
|
||||||
else -> InferredTypes.unknown()
|
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_UB, DataType.ARRAY_UW -> InferredTypes.knownFor(DataType.UWORD)
|
||||||
DataType.ARRAY_B, DataType.ARRAY_W -> InferredTypes.knownFor(DataType.WORD)
|
DataType.ARRAY_B, DataType.ARRAY_W -> InferredTypes.knownFor(DataType.WORD)
|
||||||
DataType.ARRAY_F -> InferredTypes.knownFor(DataType.FLOAT)
|
DataType.ARRAY_F -> InferredTypes.knownFor(DataType.FLOAT)
|
||||||
in StringDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
DataType.STR -> InferredTypes.knownFor(DataType.UWORD)
|
||||||
else -> InferredTypes.unknown()
|
else -> InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,7 +231,7 @@ private fun builtinStrlen(args: List<Expression>, position: Position, program: P
|
|||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("strlen requires one argument", position)
|
throw SyntaxError("strlen requires one argument", position)
|
||||||
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
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 SyntaxError("strlen must have string argument", position)
|
||||||
|
|
||||||
throw NotConstArgumentException() // this function is not considering the string argument a constant
|
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}")
|
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
}
|
}
|
||||||
in StringDatatypes -> {
|
DataType.STR -> {
|
||||||
val refLv = target.value as StringLiteralValue
|
val refLv = target.value as StringLiteralValue
|
||||||
if(refLv.value.length>255)
|
if(refLv.value.length>255)
|
||||||
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
||||||
|
@ -14,13 +14,23 @@ import prog8.ast.processing.IAstVisitor
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.loadAsmIncludeFile
|
import prog8.compiler.loadAsmIncludeFile
|
||||||
|
|
||||||
|
private val alwaysKeepSubroutines = setOf(
|
||||||
|
Pair("main", "start"),
|
||||||
|
Pair("irq", "irq"),
|
||||||
|
Pair("prog8_lib", "init_system")
|
||||||
|
)
|
||||||
|
|
||||||
class CallGraph(private val program: Program): IAstVisitor {
|
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||||
|
private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
|
|
||||||
|
class CallGraph(private val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||||
|
|
||||||
// TODO add dataflow graph: what statements use what variables
|
// TODO add dataflow graph: what statements use what variables
|
||||||
val usedSymbols = mutableSetOf<Statement>()
|
val usedSymbols = mutableSetOf<Statement>()
|
||||||
|
|
||||||
@ -31,9 +41,9 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
|
fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
|
||||||
fun findSubs(scope: INameScope) {
|
fun findSubs(scope: INameScope) {
|
||||||
scope.statements.forEach {
|
scope.statements.forEach {
|
||||||
if(it is Subroutine)
|
if (it is Subroutine)
|
||||||
sub(it)
|
sub(it)
|
||||||
if(it is INameScope)
|
if (it is INameScope)
|
||||||
findSubs(it)
|
findSubs(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +75,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block) {
|
override fun visit(block: Block) {
|
||||||
if(block.definingModule().isLibraryModule) {
|
if (block.definingModule().isLibraryModule) {
|
||||||
// make sure the block is not removed
|
// make sure the block is not removed
|
||||||
addNodeAndParentScopes(block)
|
addNodeAndParentScopes(block)
|
||||||
}
|
}
|
||||||
@ -75,11 +85,11 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(directive: Directive) {
|
override fun visit(directive: Directive) {
|
||||||
val thisModule = directive.definingModule()
|
val thisModule = directive.definingModule()
|
||||||
if(directive.directive=="%import") {
|
if (directive.directive == "%import") {
|
||||||
val importedModule: Module = program.modules.single { it.name==directive.args[0].name }
|
val importedModule: Module = program.modules.single { it.name == directive.args[0].name }
|
||||||
modulesImporting[thisModule] = modulesImporting.getValue(thisModule).plus(importedModule)
|
modulesImporting[thisModule] = modulesImporting.getValue(thisModule).plus(importedModule)
|
||||||
modulesImportedBy[importedModule] = modulesImportedBy.getValue(importedModule).plus(thisModule)
|
modulesImportedBy[importedModule] = modulesImportedBy.getValue(importedModule).plus(thisModule)
|
||||||
} else if (directive.directive=="%asminclude") {
|
} else if (directive.directive == "%asminclude") {
|
||||||
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
||||||
val scope = directive.definingScope()
|
val scope = directive.definingScope()
|
||||||
scanAssemblyCode(asm, directive, scope)
|
scanAssemblyCode(asm, directive, scope)
|
||||||
@ -91,7 +101,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
override fun visit(identifier: IdentifierReference) {
|
override fun visit(identifier: IdentifierReference) {
|
||||||
// track symbol usage
|
// track symbol usage
|
||||||
val target = identifier.targetStatement(this.program.namespace)
|
val target = identifier.targetStatement(this.program.namespace)
|
||||||
if(target!=null) {
|
if (target != null) {
|
||||||
addNodeAndParentScopes(target)
|
addNodeAndParentScopes(target)
|
||||||
}
|
}
|
||||||
super.visit(identifier)
|
super.visit(identifier)
|
||||||
@ -99,24 +109,18 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
|
|
||||||
private fun addNodeAndParentScopes(stmt: Statement) {
|
private fun addNodeAndParentScopes(stmt: Statement) {
|
||||||
usedSymbols.add(stmt)
|
usedSymbols.add(stmt)
|
||||||
var node: Node=stmt
|
var node: Node = stmt
|
||||||
do {
|
do {
|
||||||
if(node is INameScope && node is Statement) {
|
if (node is INameScope && node is Statement) {
|
||||||
usedSymbols.add(node)
|
usedSymbols.add(node)
|
||||||
}
|
}
|
||||||
node=node.parent
|
node = node.parent
|
||||||
} while (node !is Module && node !is ParentSentinel)
|
} while (node !is Module && node !is ParentSentinel)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
override fun visit(subroutine: Subroutine) {
|
||||||
val alwaysKeepSubroutines = setOf(
|
if (Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|
||||||
Pair("main", "start"),
|
|| subroutine.name == initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
||||||
Pair("irq", "irq"),
|
|
||||||
Pair("prog8_lib", "init_system")
|
|
||||||
)
|
|
||||||
|
|
||||||
if(Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|
|
||||||
|| subroutine.name== initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
|
||||||
// make sure the entrypoint is mentioned in the used symbols
|
// make sure the entrypoint is mentioned in the used symbols
|
||||||
addNodeAndParentScopes(subroutine)
|
addNodeAndParentScopes(subroutine)
|
||||||
}
|
}
|
||||||
@ -124,12 +128,12 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
if(decl.autogeneratedDontRemove || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) {
|
if (decl.autogeneratedDontRemove || (decl.definingModule().isLibraryModule && decl.type != VarDeclType.VAR)) {
|
||||||
// make sure autogenerated vardecls are in the used symbols
|
// make sure autogenerated vardecls are in the used symbols
|
||||||
addNodeAndParentScopes(decl)
|
addNodeAndParentScopes(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.datatype==DataType.STRUCT)
|
if (decl.datatype == DataType.STRUCT)
|
||||||
addNodeAndParentScopes(decl)
|
addNodeAndParentScopes(decl)
|
||||||
|
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
@ -137,7 +141,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(functionCall: FunctionCall) {
|
override fun visit(functionCall: FunctionCall) {
|
||||||
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if(otherSub!=null) {
|
if (otherSub != null) {
|
||||||
functionCall.definingSubroutine()?.let { thisSub ->
|
functionCall.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall)
|
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall)
|
||||||
@ -148,7 +152,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||||
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
if(otherSub!=null) {
|
if (otherSub != null) {
|
||||||
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement)
|
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement)
|
||||||
@ -159,7 +163,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(jump: Jump) {
|
override fun visit(jump: Jump) {
|
||||||
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
||||||
if(otherSub!=null) {
|
if (otherSub != null) {
|
||||||
jump.definingSubroutine()?.let { thisSub ->
|
jump.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump)
|
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump)
|
||||||
@ -181,8 +185,6 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun scanAssemblyCode(asm: String, context: Statement, scope: INameScope) {
|
private fun scanAssemblyCode(asm: String, context: Statement, scope: INameScope) {
|
||||||
val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
|
||||||
val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
|
||||||
asm.lines().forEach { line ->
|
asm.lines().forEach { line ->
|
||||||
val matches = asmJumpRx.matchEntire(line)
|
val matches = asmJumpRx.matchEntire(line)
|
||||||
if (matches != null) {
|
if (matches != null) {
|
||||||
@ -192,7 +194,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
||||||
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
||||||
} else if(jumptarget.contains('.')) {
|
} else if (jumptarget.contains('.')) {
|
||||||
// maybe only the first part already refers to a subroutine
|
// maybe only the first part already refers to a subroutine
|
||||||
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
||||||
if (node2 is Subroutine) {
|
if (node2 is Subroutine) {
|
||||||
@ -204,9 +206,9 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
} else {
|
} else {
|
||||||
val matches2 = asmRefRx.matchEntire(line)
|
val matches2 = asmRefRx.matchEntire(line)
|
||||||
if (matches2 != null) {
|
if (matches2 != null) {
|
||||||
val target= matches2.groups[2]?.value
|
val target = matches2.groups[2]?.value
|
||||||
if (target != null && (target[0].isLetter() || target[0] == '_')) {
|
if (target != null && (target[0].isLetter() || target[0] == '_')) {
|
||||||
if(target.contains('.')) {
|
if (target.contains('.')) {
|
||||||
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
||||||
|
@ -54,15 +54,15 @@ class ConstExprEvaluator {
|
|||||||
|
|
||||||
private fun logicalxor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
private fun logicalxor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute $left locical-bitxor $right"
|
val error = "cannot compute $left locical-bitxor $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
|
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)
|
DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toDouble() != 0.0), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toInt() != 0), left.position)
|
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 -> 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)
|
||||||
}
|
}
|
||||||
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 {
|
private fun logicalor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute $left locical-or $right"
|
val error = "cannot compute $left locical-or $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
|
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)
|
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toDouble() != 0.0, left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toInt() != 0, left.position)
|
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 -> 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)
|
||||||
}
|
}
|
||||||
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 {
|
private fun logicaland(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute $left locical-and $right"
|
val error = "cannot compute $left locical-and $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
|
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)
|
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toDouble() != 0.0, left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toInt() != 0, left.position)
|
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 -> 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)
|
||||||
}
|
}
|
||||||
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 {
|
private fun power(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot calculate $left ** $right"
|
val error = "cannot calculate $left ** $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
|
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)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number.toDouble()), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toInt()), left.position)
|
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 -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toDouble()), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, 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 {
|
private fun plus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot add $left and $right"
|
val error = "cannot add $left and $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
|
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)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toInt(), left.position)
|
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 -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, 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 {
|
private fun minus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot subtract $left and $right"
|
val error = "cannot subtract $left and $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
|
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)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toInt(), left.position)
|
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 -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, 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 {
|
private fun multiply(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot multiply ${left.type} and ${right.type}"
|
val error = "cannot multiply ${left.type} and ${right.type}"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
|
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)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toInt(), left.position)
|
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 -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, 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 {
|
private fun divide(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot divide $left by $right"
|
val error = "cannot divide $left by $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
val result: Int = left.number.toInt() / right.number.toInt()
|
val result: Int = left.number.toInt() / right.number.toInt()
|
||||||
NumericLiteralValue.optimalNumeric(result, left.position)
|
NumericLiteralValue.optimalNumeric(result, left.position)
|
||||||
}
|
}
|
||||||
right.type == DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
NumericLiteralValue(DataType.FLOAT, left.number.toInt() / right.number.toDouble(), left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toInt() / right.number.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toInt(), left.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)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toDouble(), left.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 {
|
private fun remainder(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute remainder of $left by $right"
|
val error = "cannot compute remainder of $left by $right"
|
||||||
return when {
|
return when (left.type) {
|
||||||
left.type in IntegerDatatypes -> when {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.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)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
NumericLiteralValue(DataType.FLOAT, left.number.toInt() % right.number.toDouble(), left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toInt() % right.number.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.type == DataType.FLOAT -> when {
|
DataType.FLOAT -> when (right.type) {
|
||||||
right.type in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toInt(), left.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)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toDouble(), left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,8 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.fixupArrayDatatype
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
|
||||||
import prog8.compiler.target.c64.codegen.AssemblyError
|
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
@ -65,12 +62,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
return super.visit(decl)
|
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 -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
val numericLv = decl.value as? NumericLiteralValue
|
val numericLv = decl.value as? NumericLiteralValue
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
@ -83,11 +74,11 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
||||||
if(eltType in ByteDatatypes) {
|
if(eltType in ByteDatatypes) {
|
||||||
decl.value = ArrayLiteralValue(decl.datatype,
|
decl.value = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
||||||
position = decl.value!!.position)
|
position = decl.value!!.position)
|
||||||
} else {
|
} else {
|
||||||
decl.value = ArrayLiteralValue(decl.datatype,
|
decl.value = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
||||||
position = decl.value!!.position)
|
position = decl.value!!.position)
|
||||||
}
|
}
|
||||||
@ -123,8 +114,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
|
||||||
val refValue = ArrayLiteralValue(decl.datatype, array, position = numericLv.position)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
refValue.addToHeap()
|
|
||||||
decl.value = refValue
|
decl.value = refValue
|
||||||
refValue.parent=decl
|
refValue.parent=decl
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -140,13 +130,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
} else {
|
} else {
|
||||||
// arraysize initializer is a single int, and we know the size.
|
// arraysize initializer is a single int, and we know the size.
|
||||||
val fillvalue = litval.number.toDouble()
|
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))
|
errors.add(ExpressionError("float value overflow", litval.position))
|
||||||
else {
|
else {
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
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)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
||||||
refValue.addToHeap()
|
|
||||||
decl.value = refValue
|
decl.value = refValue
|
||||||
refValue.parent=decl
|
refValue.parent=decl
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -156,6 +145,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// nothing to do for this type
|
// nothing to do for this type
|
||||||
|
// this includes strings and structs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,13 +168,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
|
|
||||||
return try {
|
return try {
|
||||||
val cval = identifier.constValue(program) ?: return identifier
|
val cval = identifier.constValue(program) ?: return identifier
|
||||||
return when {
|
return when (cval.type) {
|
||||||
cval.type in NumericDatatypes -> {
|
in NumericDatatypes -> {
|
||||||
val copy = NumericLiteralValue(cval.type, cval.number, identifier.position)
|
val copy = NumericLiteralValue(cval.type, cval.number, identifier.position)
|
||||||
copy.parent = identifier.parent
|
copy.parent = identifier.parent
|
||||||
copy
|
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
|
else -> identifier
|
||||||
}
|
}
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
@ -215,12 +205,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
val builtinFunction = BuiltinFunctions[functionCall.target.nameInSource.single()]
|
val builtinFunction = BuiltinFunctions[functionCall.target.nameInSource.single()]
|
||||||
if(builtinFunction!=null) {
|
if(builtinFunction!=null) {
|
||||||
// match the arguments of a builtin function signature.
|
// 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 possibleDts = arg.second.possibleDatatypes
|
||||||
val argConst = arg.first.value.constValue(program)
|
val argConst = arg.first.value.constValue(program)
|
||||||
if(argConst!=null && argConst.type !in possibleDts) {
|
if(argConst!=null && argConst.type !in possibleDts) {
|
||||||
val convertedValue = argConst.cast(possibleDts.first())
|
val convertedValue = argConst.cast(possibleDts.first())
|
||||||
functionCall.arglist[arg.first.index] = convertedValue
|
functionCall.args[arg.first.index] = convertedValue
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,12 +221,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
|
// 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 expectedDt = arg.second.type
|
||||||
val argConst = arg.first.value.constValue(program)
|
val argConst = arg.first.value.constValue(program)
|
||||||
if(argConst!=null && argConst.type!=expectedDt) {
|
if(argConst!=null && argConst.type!=expectedDt) {
|
||||||
val convertedValue = argConst.cast(expectedDt)
|
val convertedValue = argConst.cast(expectedDt)
|
||||||
functionCall.arglist[arg.first.index] = convertedValue
|
functionCall.args[arg.first.index] = convertedValue
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,27 +255,27 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
val subexpr = prefixExpr.expression
|
val subexpr = prefixExpr.expression
|
||||||
if (subexpr is NumericLiteralValue) {
|
if (subexpr is NumericLiteralValue) {
|
||||||
// accept prefixed literal values (such as -3, not true)
|
// accept prefixed literal values (such as -3, not true)
|
||||||
return when {
|
return when (prefixExpr.operator) {
|
||||||
prefixExpr.operator == "+" -> subexpr
|
"+" -> subexpr
|
||||||
prefixExpr.operator == "-" -> when {
|
"-" -> when (subexpr.type) {
|
||||||
subexpr.type in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
|
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
|
||||||
}
|
}
|
||||||
subexpr.type == DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position)
|
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
||||||
}
|
}
|
||||||
prefixExpr.operator == "~" -> when {
|
"~" -> when (subexpr.type) {
|
||||||
subexpr.type in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
|
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||||
}
|
}
|
||||||
prefixExpr.operator == "not" -> {
|
"not" -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
|
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
|
||||||
}
|
}
|
||||||
@ -593,7 +583,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
if(array2!=null && array2!==array) {
|
if(array2!=null && array2!==array) {
|
||||||
forLoop2.iterable = array2
|
forLoop2.iterable = array2
|
||||||
array2.linkParents(forLoop2)
|
array2.linkParents(forLoop2)
|
||||||
array2.addToHeap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,15 +627,18 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
||||||
|
// because constant folding can result in arrays that are now suddenly capable
|
||||||
|
// of telling the type of all their elements (for instance, when they contained -2 which
|
||||||
|
// was a prefix expression earlier), we recalculate the array's datatype.
|
||||||
val array = super.visit(arrayLiteral)
|
val array = super.visit(arrayLiteral)
|
||||||
if(array is ArrayLiteralValue) {
|
if(array is ArrayLiteralValue) {
|
||||||
array.addToHeap()
|
if(array.type.isKnown)
|
||||||
val vardecl = array.parent as? VarDecl
|
return array
|
||||||
return if (vardecl!=null) {
|
val arrayDt = array.guessDatatype(program)
|
||||||
fixupArrayDatatype(array, vardecl, program)
|
if(arrayDt.isKnown) {
|
||||||
} else {
|
val newArray = arrayLiteral.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
// it's not an array associated with a vardecl, attempt to guess the data type from the array values
|
if(newArray!=null)
|
||||||
fixupArrayDatatype(array, program)
|
return newArray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
|
@ -13,7 +13,7 @@ import kotlin.math.pow
|
|||||||
/*
|
/*
|
||||||
todo add more expression optimizations
|
todo add more expression optimizations
|
||||||
|
|
||||||
Also see https://egorbo.com/peephole-optimizations.html
|
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
if (assignment.aug_op != null)
|
if (assignment.aug_op != null)
|
||||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||||
return super.visit(assignment)
|
return super.visit(assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,15 +9,14 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.codegen.AssemblyError
|
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: remove unreachable code?
|
TODO: remove unreachable code?
|
||||||
TODO: proper inlining of tiny subroutines (correctly renaming/relocating all variables in them and refs to those as well, or restrict to subs without variables?)
|
TODO: proper inlining of tiny subroutines (at first, restrict to subs without parameters and variables in them, and build it up from there: correctly renaming/relocating all variables in them and refs to those as well)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -170,7 +169,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
|
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
|
||||||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
|
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
|
||||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
// 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?
|
val stringVar: IdentifierReference?
|
||||||
stringVar = if(arg is AddressOf) {
|
stringVar = if(arg is AddressOf) {
|
||||||
arg.identifier
|
arg.identifier
|
||||||
@ -180,20 +179,23 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if(stringVar!=null) {
|
if(stringVar!=null) {
|
||||||
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
||||||
val string = vardecl.value!! as StringLiteralValue
|
val string = vardecl.value!! as StringLiteralValue
|
||||||
val encodedString = Petscii.encodePetscii(string.value, true)
|
|
||||||
if(string.value.length==1) {
|
if(string.value.length==1) {
|
||||||
functionCallStatement.arglist.clear()
|
val firstCharEncoded = CompilationTarget.encodeString(string.value, string.altEncoding)[0]
|
||||||
functionCallStatement.arglist.add(NumericLiteralValue.optimalInteger(encodedString[0].toInt(), functionCallStatement.position))
|
functionCallStatement.args.clear()
|
||||||
|
functionCallStatement.args.add(NumericLiteralValue.optimalInteger(firstCharEncoded.toInt(), functionCallStatement.position))
|
||||||
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
|
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
|
||||||
vardeclsToRemove.add(vardecl)
|
vardeclsToRemove.add(vardecl)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return functionCallStatement
|
return functionCallStatement
|
||||||
} else if(string.value.length==2) {
|
} else if(string.value.length==2) {
|
||||||
|
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||||
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
|
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.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),
|
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)
|
vardeclsToRemove.add(vardecl)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return scope
|
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()
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Jump && first.identifier!=null) {
|
if(first is Jump && first.identifier!=null) {
|
||||||
optimizationsDone++
|
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) {
|
if(first is ReturnFromIrq || first is Return) {
|
||||||
optimizationsDone++
|
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()
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Jump && first.identifier!=null) {
|
if(first is Jump && first.identifier!=null) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
|
return FunctionCall(first.identifier, functionCall.args, functionCall.position)
|
||||||
}
|
}
|
||||||
if(first is Return && first.value!=null) {
|
if(first is Return && first.value!=null) {
|
||||||
val constval = first.value?.constValue(program)
|
val constval = first.value?.constValue(program)
|
||||||
@ -310,19 +312,20 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> print a warning, and optimize into body + jump (if there are no continue and break statements)
|
// 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))
|
if(hasContinueOrBreak(whileLoop.body))
|
||||||
return whileLoop
|
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(0, label)
|
||||||
whileLoop.body.statements.add(Jump(null,
|
whileLoop.body.statements.add(Jump(null,
|
||||||
IdentifierReference(listOf("_prog8_back"), whileLoop.condition.position),
|
IdentifierReference(listOf(backLabelName), whileLoop.condition.position),
|
||||||
null, whileLoop.condition.position))
|
null, whileLoop.condition.position))
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return whileLoop.body
|
return whileLoop.body
|
||||||
} else {
|
} else {
|
||||||
// always false -> ditch whole statement
|
// always false -> ditch whole statement
|
||||||
printWarning("condition is always false", whileLoop.position)
|
printWarning("condition is always false", whileLoop.condition.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
NopStatement.insteadOf(whileLoop)
|
NopStatement.insteadOf(whileLoop)
|
||||||
}
|
}
|
||||||
@ -336,7 +339,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only the statement block (if there are no continue and break statements)
|
// 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))
|
if(hasContinueOrBreak(repeatLoop.body))
|
||||||
repeatLoop
|
repeatLoop
|
||||||
else {
|
else {
|
||||||
@ -345,13 +348,14 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// always false -> print a warning, and optimize into body + jump (if there are no continue and break statements)
|
// 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))
|
if(hasContinueOrBreak(repeatLoop.body))
|
||||||
return repeatLoop
|
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(0, label)
|
||||||
repeatLoop.body.statements.add(Jump(null,
|
repeatLoop.body.statements.add(Jump(null,
|
||||||
IdentifierReference(listOf("__back"), repeatLoop.untilCondition.position),
|
IdentifierReference(listOf(backLabelName), repeatLoop.untilCondition.position),
|
||||||
null, repeatLoop.untilCondition.position))
|
null, repeatLoop.untilCondition.position))
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return repeatLoop.body
|
return repeatLoop.body
|
||||||
@ -420,7 +424,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
if(assignment.aug_op!=null)
|
if(assignment.aug_op!=null)
|
||||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||||
|
|
||||||
if(assignment.target isSameAs assignment.value) {
|
if(assignment.target isSameAs assignment.value) {
|
||||||
if(assignment.target.isNotMemory(program.namespace)) {
|
if(assignment.target.isNotMemory(program.namespace)) {
|
||||||
@ -430,7 +434,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
}
|
}
|
||||||
val targetIDt = assignment.target.inferType(program, assignment)
|
val targetIDt = assignment.target.inferType(program, assignment)
|
||||||
if(!targetIDt.isKnown)
|
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 targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
||||||
val bexpr=assignment.value as? BinaryExpression
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
if(bexpr!=null) {
|
if(bexpr!=null) {
|
||||||
@ -516,7 +520,8 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
var numshifts = cv.toInt()
|
var numshifts = cv.toInt()
|
||||||
while (numshifts > 0) {
|
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--
|
numshifts--
|
||||||
}
|
}
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -537,7 +542,8 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
var numshifts = cv.toInt()
|
var numshifts = cv.toInt()
|
||||||
while (numshifts > 0) {
|
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--
|
numshifts--
|
||||||
}
|
}
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
|
@ -6,7 +6,6 @@ import prog8.ast.base.WordDatatypes
|
|||||||
import prog8.ast.expressions.ArrayLiteralValue
|
import prog8.ast.expressions.ArrayLiteralValue
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.compiler.target.c64.Petscii
|
|
||||||
import prog8.vm.astvm.VmExecutionException
|
import prog8.vm.astvm.VmExecutionException
|
||||||
import java.util.Objects
|
import java.util.Objects
|
||||||
import kotlin.math.abs
|
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 altEncoding: Boolean, val heapId: Int?): RuntimeValueBase(DataType.STR) {
|
||||||
companion object {
|
companion object {
|
||||||
fun fromLv(string: StringLiteralValue): RuntimeValueString {
|
fun fromLv(string: StringLiteralValue): RuntimeValueString {
|
||||||
return RuntimeValueString(string.type, string.value, string.heapId!!)
|
return RuntimeValueString(string.value, string.altEncoding, string.heapId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String = if(type==DataType.STR) "str:$str" else "???"
|
||||||
return when (type) {
|
|
||||||
DataType.STR -> "str:$str"
|
|
||||||
DataType.STR_S -> "str_s:$str"
|
|
||||||
else -> "???"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(type, str)
|
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
|
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 {
|
override fun numericValue(): Number {
|
||||||
throw VmExecutionException("string is not a number")
|
throw VmExecutionException("string is not a number")
|
||||||
@ -610,19 +603,19 @@ open class RuntimeValueArray(type: DataType, val array: Array<Number>, val heapI
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromLv(array: ArrayLiteralValue): RuntimeValueArray {
|
fun fromLv(array: ArrayLiteralValue): RuntimeValueArray {
|
||||||
return if (array.type == DataType.ARRAY_F) {
|
return if (array.type.istype(DataType.ARRAY_F)) {
|
||||||
val doubleArray = array.value.map { (it as NumericLiteralValue).number }.toTypedArray()
|
val doubleArray = array.value.map { (it as NumericLiteralValue).number }.toTypedArray()
|
||||||
RuntimeValueArray(array.type, doubleArray, array.heapId!!)
|
RuntimeValueArray(DataType.ARRAY_F, doubleArray, array.heapId)
|
||||||
} else {
|
} else {
|
||||||
val resultArray = mutableListOf<Number>()
|
val resultArray = mutableListOf<Number>()
|
||||||
for (elt in array.value.withIndex()) {
|
for (elt in array.value.withIndex()) {
|
||||||
if (elt.value is NumericLiteralValue)
|
if (elt.value is NumericLiteralValue)
|
||||||
resultArray.add((elt.value as NumericLiteralValue).number.toInt())
|
resultArray.add((elt.value as NumericLiteralValue).number.toInt())
|
||||||
else {
|
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.typeOrElse(DataType.STRUCT), resultArray.toTypedArray(), array.heapId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,9 @@ import prog8.ast.expressions.Expression
|
|||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.Petscii
|
|
||||||
import prog8.vm.*
|
import prog8.vm.*
|
||||||
import java.awt.EventQueue
|
import java.awt.EventQueue
|
||||||
import java.io.CharConversionException
|
|
||||||
import java.util.ArrayDeque
|
import java.util.ArrayDeque
|
||||||
import kotlin.NoSuchElementException
|
import kotlin.NoSuchElementException
|
||||||
import kotlin.concurrent.fixedRateTimer
|
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 mem = Memory(::memread, ::memwrite)
|
||||||
val statusflags = StatusFlags()
|
val statusflags = StatusFlags()
|
||||||
@ -147,6 +145,8 @@ class AstVm(val program: Program) {
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
require(compilationTarget == "c64") {"using the AstVm only works for the C64 compiler target"}
|
||||||
|
|
||||||
// observe the jiffyclock and screen matrix
|
// observe the jiffyclock and screen matrix
|
||||||
mem.observe(0xa0, 0xa1, 0xa2)
|
mem.observe(0xa0, 0xa1, 0xa2)
|
||||||
for(i in 1024..2023)
|
for(i in 1024..2023)
|
||||||
@ -182,7 +182,7 @@ class AstVm(val program: Program) {
|
|||||||
if(address in 1024..2023) {
|
if(address in 1024..2023) {
|
||||||
// write to the screen matrix
|
// write to the screen matrix
|
||||||
val scraddr = address-1024
|
val scraddr = address-1024
|
||||||
dialog.canvas.setChar(scraddr % 40, scraddr / 40, value, 1)
|
dialog.canvas.setScreenChar(scraddr % 40, scraddr / 40, value, 1)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@ -222,7 +222,7 @@ class AstVm(val program: Program) {
|
|||||||
when {
|
when {
|
||||||
jx.generatedLabel != null -> {
|
jx.generatedLabel != null -> {
|
||||||
val label = entrypoint.getLabelOrVariable(jx.generatedLabel) as Label
|
val label = entrypoint.getLabelOrVariable(jx.generatedLabel) as Label
|
||||||
TODO("generatedlabel $label")
|
TODO("astvm handle generatedlabel $label")
|
||||||
}
|
}
|
||||||
jx.identifier != null -> {
|
jx.identifier != null -> {
|
||||||
when (val jumptarget = entrypoint.lookup(jx.identifier.nameInSource, jx.identifier.parent)) {
|
when (val jumptarget = entrypoint.lookup(jx.identifier.nameInSource, jx.identifier.parent)) {
|
||||||
@ -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!")
|
println("PROGRAM EXITED!")
|
||||||
dialog.title = "PROGRAM EXITED"
|
dialog.title = "PROGRAM EXITED"
|
||||||
} catch (tx: VmTerminationException) {
|
} catch (tx: VmTerminationException) {
|
||||||
@ -339,10 +339,9 @@ class AstVm(val program: Program) {
|
|||||||
// should have been defined already when the program started
|
// should have been defined already when the program started
|
||||||
}
|
}
|
||||||
is FunctionCallStatement -> {
|
is FunctionCallStatement -> {
|
||||||
val target = stmt.target.targetStatement(program.namespace)
|
when (val target = stmt.target.targetStatement(program.namespace)) {
|
||||||
when (target) {
|
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
val args = evaluate(stmt.arglist).map { it as RuntimeValueNumeric }
|
val args = evaluate(stmt.args).map { it as RuntimeValueNumeric }
|
||||||
if (target.isAsmSubroutine) {
|
if (target.isAsmSubroutine) {
|
||||||
performSyscall(target, args)
|
performSyscall(target, args)
|
||||||
} else {
|
} else {
|
||||||
@ -355,12 +354,12 @@ class AstVm(val program: Program) {
|
|||||||
// swap cannot be implemented as a function, so inline it here
|
// swap cannot be implemented as a function, so inline it here
|
||||||
executeSwap(stmt)
|
executeSwap(stmt)
|
||||||
} else {
|
} else {
|
||||||
val args = evaluate(stmt.arglist).map { it as RuntimeValueNumeric }
|
val args = evaluate(stmt.args)
|
||||||
performBuiltinFunction(target.name, args, statusflags)
|
performBuiltinFunction(target.name, args, statusflags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
TODO("weird call $target")
|
throw VmExecutionException("weird call target $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,18 +387,18 @@ class AstVm(val program: Program) {
|
|||||||
when(ident.type){
|
when(ident.type){
|
||||||
VarDeclType.VAR -> {
|
VarDeclType.VAR -> {
|
||||||
var value = runtimeVariables.get(identScope, ident.name) as RuntimeValueNumeric
|
var value = runtimeVariables.get(identScope, ident.name) as RuntimeValueNumeric
|
||||||
value = when {
|
value = when (stmt.operator) {
|
||||||
stmt.operator == "++" -> value.add(RuntimeValueNumeric(value.type, 1))
|
"++" -> value.add(RuntimeValueNumeric(value.type, 1))
|
||||||
stmt.operator == "--" -> value.sub(RuntimeValueNumeric(value.type, 1))
|
"--" -> value.sub(RuntimeValueNumeric(value.type, 1))
|
||||||
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
}
|
}
|
||||||
runtimeVariables.set(identScope, ident.name, value)
|
runtimeVariables.set(identScope, ident.name, value)
|
||||||
}
|
}
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
val addr=ident.value!!.constValue(program)!!.number.toInt()
|
val addr=ident.value!!.constValue(program)!!.number.toInt()
|
||||||
val newval = when {
|
val newval = when (stmt.operator) {
|
||||||
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
|
"++" -> mem.getUByte(addr)+1 and 255
|
||||||
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
|
"--" -> mem.getUByte(addr)-1 and 255
|
||||||
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
}
|
}
|
||||||
mem.setUByte(addr,newval.toShort())
|
mem.setUByte(addr,newval.toShort())
|
||||||
@ -409,9 +408,9 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
stmt.target.memoryAddress != null -> {
|
stmt.target.memoryAddress != null -> {
|
||||||
val addr = (evaluate(stmt.target.memoryAddress!!.addressExpression, evalCtx) as RuntimeValueNumeric).integerValue()
|
val addr = (evaluate(stmt.target.memoryAddress!!.addressExpression, evalCtx) as RuntimeValueNumeric).integerValue()
|
||||||
val newval = when {
|
val newval = when (stmt.operator) {
|
||||||
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
|
"++" -> mem.getUByte(addr)+1 and 255
|
||||||
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
|
"--" -> mem.getUByte(addr)-1 and 255
|
||||||
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
}
|
}
|
||||||
mem.setUByte(addr,newval.toShort())
|
mem.setUByte(addr,newval.toShort())
|
||||||
@ -424,18 +423,18 @@ class AstVm(val program: Program) {
|
|||||||
if(!elementType.isKnown)
|
if(!elementType.isKnown)
|
||||||
throw VmExecutionException("unknown/void elt type")
|
throw VmExecutionException("unknown/void elt type")
|
||||||
var value = RuntimeValueNumeric(elementType.typeOrElse(DataType.BYTE), arrayvalue.array[index].toInt())
|
var value = RuntimeValueNumeric(elementType.typeOrElse(DataType.BYTE), arrayvalue.array[index].toInt())
|
||||||
value = when {
|
value = when (stmt.operator) {
|
||||||
stmt.operator == "++" -> value.inc()
|
"++" -> value.inc()
|
||||||
stmt.operator == "--" -> value.dec()
|
"--" -> value.dec()
|
||||||
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
}
|
}
|
||||||
arrayvalue.array[index] = value.numericValue()
|
arrayvalue.array[index] = value.numericValue()
|
||||||
}
|
}
|
||||||
stmt.target.register != null -> {
|
stmt.target.register != null -> {
|
||||||
var value = runtimeVariables.get(program.namespace, stmt.target.register!!.name) as RuntimeValueNumeric
|
var value = runtimeVariables.get(program.namespace, stmt.target.register!!.name) as RuntimeValueNumeric
|
||||||
value = when {
|
value = when (stmt.operator) {
|
||||||
stmt.operator == "++" -> value.add(RuntimeValueNumeric(value.type, 1))
|
"++" -> value.add(RuntimeValueNumeric(value.type, 1))
|
||||||
stmt.operator == "--" -> value.sub(RuntimeValueNumeric(value.type, 1))
|
"--" -> value.sub(RuntimeValueNumeric(value.type, 1))
|
||||||
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
}
|
}
|
||||||
runtimeVariables.set(program.namespace, stmt.target.register!!.name, value)
|
runtimeVariables.set(program.namespace, stmt.target.register!!.name, value)
|
||||||
@ -468,7 +467,7 @@ class AstVm(val program: Program) {
|
|||||||
BranchCondition.NE, BranchCondition.NZ -> if(statusflags.zero) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart)
|
BranchCondition.NE, BranchCondition.NZ -> if(statusflags.zero) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart)
|
||||||
BranchCondition.MI, BranchCondition.NEG -> if(statusflags.negative) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart)
|
BranchCondition.MI, BranchCondition.NEG -> if(statusflags.negative) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart)
|
||||||
BranchCondition.PL, BranchCondition.POS -> if(statusflags.negative) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart)
|
BranchCondition.PL, BranchCondition.POS -> if(statusflags.negative) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart)
|
||||||
BranchCondition.VS, BranchCondition.VC -> TODO("overflow status")
|
BranchCondition.VS, BranchCondition.VC -> TODO("astvm branch on overflow status")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ForLoop -> {
|
is ForLoop -> {
|
||||||
@ -545,14 +544,14 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
TODO("implement $stmt")
|
TODO("astvm implement statement $stmt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeSwap(swap: FunctionCallStatement) {
|
private fun executeSwap(swap: FunctionCallStatement) {
|
||||||
val v1 = swap.arglist[0]
|
val v1 = swap.args[0]
|
||||||
val v2 = swap.arglist[1]
|
val v2 = swap.args[1]
|
||||||
val value1 = evaluate(v1, evalCtx)
|
val value1 = evaluate(v1, evalCtx)
|
||||||
val value2 = evaluate(v2, evalCtx)
|
val value2 = evaluate(v2, evalCtx)
|
||||||
val target1 = AssignTarget.fromExpr(v1)
|
val target1 = AssignTarget.fromExpr(v1)
|
||||||
@ -576,8 +575,7 @@ class AstVm(val program: Program) {
|
|||||||
DataType.UWORD -> mem.setUWord(address, (value as RuntimeValueNumeric).wordval!!)
|
DataType.UWORD -> mem.setUWord(address, (value as RuntimeValueNumeric).wordval!!)
|
||||||
DataType.WORD -> mem.setSWord(address, (value as RuntimeValueNumeric).wordval!!)
|
DataType.WORD -> mem.setSWord(address, (value as RuntimeValueNumeric).wordval!!)
|
||||||
DataType.FLOAT -> mem.setFloat(address, (value as RuntimeValueNumeric).floatval!!)
|
DataType.FLOAT -> mem.setFloat(address, (value as RuntimeValueNumeric).floatval!!)
|
||||||
DataType.STR -> mem.setString(address, (value as RuntimeValueString).str)
|
DataType.STR -> mem.setString(address, (value as RuntimeValueString).str, value.altEncoding)
|
||||||
DataType.STR_S -> mem.setScreencodeString(address, (value as RuntimeValueString).str)
|
|
||||||
else -> throw VmExecutionException("weird memaddress type $decl")
|
else -> throw VmExecutionException("weird memaddress type $decl")
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -613,7 +611,7 @@ class AstVm(val program: Program) {
|
|||||||
if (value.type != DataType.FLOAT)
|
if (value.type != DataType.FLOAT)
|
||||||
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
|
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
|
||||||
}
|
}
|
||||||
DataType.STR, DataType.STR_S -> {
|
DataType.STR -> {
|
||||||
if (value.type !in ByteDatatypes)
|
if (value.type !in ByteDatatypes)
|
||||||
throw VmExecutionException("new value is of different datatype ${value.type} for $array")
|
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)
|
if (array.type in ArrayDatatypes)
|
||||||
(array as RuntimeValueArray).array[index.integerValue()] = value.numericValue()
|
(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 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 newstr = (array as RuntimeValueString).str.replaceRange(indexInt, indexInt + 1, newchr)
|
||||||
val ident = contextStmt.definingScope().lookup(targetArrayIndexed.identifier.nameInSource, contextStmt) as? VarDecl
|
val ident = contextStmt.definingScope().lookup(targetArrayIndexed.identifier.nameInSource, contextStmt) as? VarDecl
|
||||||
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
|
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
|
||||||
val identScope = ident.definingScope()
|
val identScope = ident.definingScope()
|
||||||
runtimeVariables.set(identScope, ident.name, RuntimeValueString(array.type, newstr, array.heapId))
|
runtimeVariables.set(identScope, ident.name, RuntimeValueString(newstr, false, array.heapId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -643,7 +641,7 @@ class AstVm(val program: Program) {
|
|||||||
DataType.BYTE -> mem.setSByte(address+index, value.byteval!!)
|
DataType.BYTE -> mem.setSByte(address+index, value.byteval!!)
|
||||||
DataType.UWORD -> mem.setUWord(address+index*2, value.wordval!!)
|
DataType.UWORD -> mem.setUWord(address+index*2, value.wordval!!)
|
||||||
DataType.WORD -> mem.setSWord(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")
|
else -> throw VmExecutionException("strange array elt type $elementType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,7 +649,7 @@ class AstVm(val program: Program) {
|
|||||||
target.register != null -> {
|
target.register != null -> {
|
||||||
runtimeVariables.set(program.namespace, target.register.name, value)
|
runtimeVariables.set(program.namespace, target.register.name, value)
|
||||||
}
|
}
|
||||||
else -> TODO("assign $target")
|
else -> TODO("assign weird target $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,48 +668,48 @@ class AstVm(val program: Program) {
|
|||||||
"c64scr.print" -> {
|
"c64scr.print" -> {
|
||||||
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId)
|
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId)
|
||||||
if (args[0].wordval != null) {
|
if (args[0].wordval != null) {
|
||||||
val encodedStr = getEncodedStringFromRuntimeVars(args[0].wordval!!)
|
val string = getAsciiStringFromRuntimeVars(args[0].wordval!!)
|
||||||
dialog.canvas.printText(encodedStr)
|
dialog.canvas.printAsciiText(string)
|
||||||
} else
|
} else
|
||||||
throw VmExecutionException("print non-heap string")
|
throw VmExecutionException("print non-heap string")
|
||||||
}
|
}
|
||||||
"c64scr.print_ub" -> {
|
"c64scr.print_ub" -> {
|
||||||
dialog.canvas.printText(args[0].byteval!!.toString(), true)
|
dialog.canvas.printAsciiText(args[0].byteval!!.toString())
|
||||||
}
|
}
|
||||||
"c64scr.print_ub0" -> {
|
"c64scr.print_ub0" -> {
|
||||||
dialog.canvas.printText("%03d".format(args[0].byteval!!), true)
|
dialog.canvas.printAsciiText("%03d".format(args[0].byteval!!))
|
||||||
}
|
}
|
||||||
"c64scr.print_b" -> {
|
"c64scr.print_b" -> {
|
||||||
dialog.canvas.printText(args[0].byteval!!.toString(), true)
|
dialog.canvas.printAsciiText(args[0].byteval!!.toString())
|
||||||
}
|
}
|
||||||
"c64scr.print_uw" -> {
|
"c64scr.print_uw" -> {
|
||||||
dialog.canvas.printText(args[0].wordval!!.toString(), true)
|
dialog.canvas.printAsciiText(args[0].wordval!!.toString())
|
||||||
}
|
}
|
||||||
"c64scr.print_uw0" -> {
|
"c64scr.print_uw0" -> {
|
||||||
dialog.canvas.printText("%05d".format(args[0].wordval!!), true)
|
dialog.canvas.printAsciiText("%05d".format(args[0].wordval!!))
|
||||||
}
|
}
|
||||||
"c64scr.print_w" -> {
|
"c64scr.print_w" -> {
|
||||||
dialog.canvas.printText(args[0].wordval!!.toString(), true)
|
dialog.canvas.printAsciiText(args[0].wordval!!.toString())
|
||||||
}
|
}
|
||||||
"c64scr.print_ubhex" -> {
|
"c64scr.print_ubhex" -> {
|
||||||
val number = args[0].byteval!!
|
val number = args[0].byteval!!
|
||||||
val prefix = if (args[1].asBoolean) "$" else ""
|
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" -> {
|
"c64scr.print_uwhex" -> {
|
||||||
val number = args[0].wordval!!
|
val number = args[0].wordval!!
|
||||||
val prefix = if (args[1].asBoolean) "$" else ""
|
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" -> {
|
"c64scr.print_uwbin" -> {
|
||||||
val number = args[0].wordval!!
|
val number = args[0].wordval!!
|
||||||
val prefix = if (args[1].asBoolean) "%" else ""
|
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" -> {
|
"c64scr.print_ubbin" -> {
|
||||||
val number = args[0].byteval!!
|
val number = args[0].byteval!!
|
||||||
val prefix = if (args[1].asBoolean) "%" else ""
|
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" -> {
|
"c64scr.clear_screenchars" -> {
|
||||||
dialog.canvas.clearScreen(6)
|
dialog.canvas.clearScreen(6)
|
||||||
@ -720,7 +718,7 @@ class AstVm(val program: Program) {
|
|||||||
dialog.canvas.clearScreen(args[0].integerValue().toShort())
|
dialog.canvas.clearScreen(args[0].integerValue().toShort())
|
||||||
}
|
}
|
||||||
"c64scr.setcc" -> {
|
"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" -> {
|
"c64scr.plot" -> {
|
||||||
dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue())
|
dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue())
|
||||||
@ -736,27 +734,23 @@ class AstVm(val program: Program) {
|
|||||||
break
|
break
|
||||||
else {
|
else {
|
||||||
input.add(char)
|
input.add(char)
|
||||||
val printChar = try {
|
dialog.canvas.printAsciiText(char.toString())
|
||||||
Petscii.encodePetscii("" + char, true).first()
|
|
||||||
} catch (cv: CharConversionException) {
|
|
||||||
0x3f.toShort()
|
|
||||||
}
|
|
||||||
dialog.canvas.printPetscii(printChar)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val inputStr = input.joinToString("")
|
var inputStr = input.joinToString("")
|
||||||
|
val inputLength = inputStr.length
|
||||||
val heapId = args[0].wordval!!
|
val heapId = args[0].wordval!!
|
||||||
val origStrLength = getEncodedStringFromRuntimeVars(heapId).size
|
val origStrLength = getAsciiStringFromRuntimeVars(heapId).length
|
||||||
val encodedStr = Petscii.encodePetscii(inputStr, true).take(origStrLength).toMutableList()
|
while(inputStr.length < origStrLength) {
|
||||||
while(encodedStr.size < origStrLength)
|
inputStr += '\u0000'
|
||||||
encodedStr.add(0)
|
}
|
||||||
result = RuntimeValueNumeric(DataType.UBYTE, encodedStr.indexOf(0))
|
result = RuntimeValueNumeric(DataType.UBYTE, inputLength)
|
||||||
}
|
}
|
||||||
"c64flt.print_f" -> {
|
"c64flt.print_f" -> {
|
||||||
dialog.canvas.printText(args[0].floatval.toString(), false)
|
dialog.canvas.printAsciiText(args[0].floatval.toString())
|
||||||
}
|
}
|
||||||
"c64.CHROUT" -> {
|
"c64.CHROUT" -> {
|
||||||
dialog.canvas.printPetscii(args[0].byteval!!)
|
dialog.canvas.printPetsciiChar(args[0].byteval!!)
|
||||||
}
|
}
|
||||||
"c64.CLEARSCR" -> {
|
"c64.CLEARSCR" -> {
|
||||||
dialog.canvas.clearScreen(6)
|
dialog.canvas.clearScreen(6)
|
||||||
@ -768,26 +762,27 @@ class AstVm(val program: Program) {
|
|||||||
val char=dialog.keyboardBuffer.pop()
|
val char=dialog.keyboardBuffer.pop()
|
||||||
result = RuntimeValueNumeric(DataType.UBYTE, char.toShort())
|
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" -> {
|
"c64utils.str2uword" -> {
|
||||||
val heapId = args[0].wordval!!
|
val heapId = args[0].wordval!!
|
||||||
val argString = getEncodedStringFromRuntimeVars(heapId)
|
val argString = getAsciiStringFromRuntimeVars(heapId)
|
||||||
val numericpart = argString.takeWhile { it.toChar().isDigit() }.toString()
|
val numericpart = argString.takeWhile { it.isDigit() }
|
||||||
result = RuntimeValueNumeric(DataType.UWORD, numericpart.toInt() and 65535)
|
result = RuntimeValueNumeric(DataType.UWORD, numericpart.toInt() and 65535)
|
||||||
}
|
}
|
||||||
else -> TODO("syscall ${sub.scopedname} $sub")
|
else -> TODO("astvm implement syscall ${sub.scopedname} $sub")
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getEncodedStringFromRuntimeVars(heapId: Int): List<Short> {
|
private fun getAsciiStringFromRuntimeVars(heapId: Int): String =
|
||||||
val stringvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueString
|
(runtimeVariables.getByHeapId(heapId) as RuntimeValueString).str
|
||||||
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 getArrayFromRuntimeVars(heapId: Int): IntArray {
|
private fun getArrayFromRuntimeVars(heapId: Int): IntArray {
|
||||||
val arrayvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueArray
|
val arrayvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueArray
|
||||||
@ -1001,7 +996,7 @@ class AstVm(val program: Program) {
|
|||||||
else -> RuntimeValueNumeric(DataType.BYTE, 1)
|
else -> RuntimeValueNumeric(DataType.BYTE, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> TODO("builtin function $name")
|
else -> TODO("astvm implement builtin function $name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,8 +124,7 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValueBase {
|
|||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, ctx.mem.getUWord(address))
|
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, ctx.mem.getUWord(address))
|
||||||
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, ctx.mem.getSWord(address))
|
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, ctx.mem.getSWord(address))
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, ctx.mem.getFloat(address))
|
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, ctx.mem.getFloat(address))
|
||||||
DataType.STR -> RuntimeValueString(DataType.STR, ctx.mem.getString(address), null)
|
DataType.STR -> RuntimeValueString(ctx.mem.getString(address, false), false, null)
|
||||||
DataType.STR_S -> RuntimeValueString(DataType.STR_S, ctx.mem.getScreencodeString(address)!!, null)
|
|
||||||
else -> throw VmExecutionException("unexpected datatype $variable")
|
else -> throw VmExecutionException("unexpected datatype $variable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,7 +134,7 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValueBase {
|
|||||||
}
|
}
|
||||||
is FunctionCall -> {
|
is FunctionCall -> {
|
||||||
val sub = expr.target.targetStatement(ctx.program.namespace)
|
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) {
|
return when(sub) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
val result = ctx.executeSubroutine(sub, args, null)
|
val result = ctx.executeSubroutine(sub, args, null)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package prog8.vm.astvm
|
package prog8.vm.astvm
|
||||||
|
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
class Memory(private val readObserver: (address: Int, value: Short) -> Short,
|
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) {
|
fun setFloat(address: Int, value: Double) {
|
||||||
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(value)
|
val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(value)
|
||||||
setUByte(address, mflpt5.b0)
|
setUByte(address, mflpt5.b0)
|
||||||
setUByte(address+1, mflpt5.b1)
|
setUByte(address+1, mflpt5.b1)
|
||||||
setUByte(address+2, mflpt5.b2)
|
setUByte(address+2, mflpt5.b2)
|
||||||
@ -89,28 +89,26 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getFloat(address: Int): Double {
|
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()
|
getUByte(address + 3), getUByte(address + 4)).toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setString(address: Int, str: String) {
|
fun setString(address: Int, str: String, altEncoding: Boolean) {
|
||||||
// lowercase PETSCII
|
val encoded = CompilationTarget.encodeString(str, altEncoding)
|
||||||
val petscii = Petscii.encodePetscii(str, true)
|
|
||||||
var addr = address
|
var addr = address
|
||||||
for (c in petscii) setUByte(addr++, c)
|
for (c in encoded) setUByte(addr++, c)
|
||||||
setUByte(addr, 0)
|
setUByte(addr, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getString(strAddress: Int): String {
|
fun getString(strAddress: Int, altEncoding: Boolean): String {
|
||||||
// lowercase PETSCII
|
val encoded = mutableListOf<Short>()
|
||||||
val petscii = mutableListOf<Short>()
|
|
||||||
var addr = strAddress
|
var addr = strAddress
|
||||||
while(true) {
|
while(true) {
|
||||||
val byte = getUByte(addr++)
|
val byte = getUByte(addr++)
|
||||||
if(byte==0.toShort()) break
|
if(byte==0.toShort()) break
|
||||||
petscii.add(byte)
|
encoded.add(byte)
|
||||||
}
|
}
|
||||||
return Petscii.decodePetscii(petscii, true)
|
return CompilationTarget.decodeString(encoded, altEncoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
@ -121,24 +119,4 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
|
|||||||
for(i in 0 until numbytes)
|
for(i in 0 until numbytes)
|
||||||
setUByte(to+i, getUByte(from+i))
|
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
|
package prog8.vm.astvm
|
||||||
|
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
@ -50,41 +50,33 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clearScreen(color: Short) {
|
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)
|
g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT)
|
||||||
cursorX = 0
|
cursorX = 0
|
||||||
cursorY = 0
|
cursorY = 0
|
||||||
}
|
}
|
||||||
fun setPixel(x: Int, y: Int, color: Short) {
|
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) {
|
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)
|
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 t2 = text.substringBefore(0.toChar())
|
||||||
val lines = t2.split('\n')
|
val petscii = Petscii.encodePetscii(t2, true)
|
||||||
for(line in lines.withIndex()) {
|
petscii.forEach { printPetsciiChar(it) }
|
||||||
val petscii = Petscii.encodePetscii(line.value, lowercase)
|
|
||||||
petscii.forEach { printPetscii(it, inverseVideo) }
|
|
||||||
if(line.index<lines.size-1) {
|
|
||||||
printPetscii(13) // newline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printText(text: Iterable<Short>) {
|
fun printPetsciiChar(petscii: Short) {
|
||||||
text.forEach { printPetscii(it, false) }
|
if(petscii in listOf(0x0d.toShort(), 0x8d.toShort())) {
|
||||||
}
|
// Return and shift-Return
|
||||||
|
|
||||||
fun printPetscii(char: Short, inverseVideo: Boolean=false) {
|
|
||||||
if(char==13.toShort() || char==141.toShort()) {
|
|
||||||
cursorX=0
|
cursorX=0
|
||||||
cursorY++
|
cursorY++
|
||||||
} else {
|
} else {
|
||||||
setPetscii(cursorX, cursorY, char, 1, inverseVideo)
|
val scr = Petscii.petscii2scr(petscii, false)
|
||||||
|
setScreenChar(cursorX, cursorY, scr, 1)
|
||||||
cursorX++
|
cursorX++
|
||||||
if (cursorX >= (SCREENWIDTH / 8)) {
|
if (cursorX >= (SCREENWIDTH / 8)) {
|
||||||
cursorY++
|
cursorY++
|
||||||
@ -98,38 +90,17 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
|||||||
val graphics = image.graphics as Graphics2D
|
val graphics = image.graphics as Graphics2D
|
||||||
graphics.drawImage(screen, 0, -8, null)
|
graphics.drawImage(screen, 0, -8, null)
|
||||||
val color = graphics.color
|
val color = graphics.color
|
||||||
graphics.color = MachineDefinition.colorPalette[6]
|
graphics.color = C64MachineDefinition.colorPalette[6]
|
||||||
graphics.fillRect(0, 24*8, SCREENWIDTH, 25*8)
|
graphics.fillRect(0, 24*8, SCREENWIDTH, 25*8)
|
||||||
graphics.color=color
|
graphics.color=color
|
||||||
cursorY--
|
cursorY--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeTextAt(x: Int, y: Int, text: String, color: Short, lowercase: Boolean, inverseVideo: Boolean=false) {
|
fun setScreenChar(x: Int, y: Int, screencode: Short, color: Short) {
|
||||||
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) {
|
|
||||||
g2d.clearRect(8*x, 8*y, 8, 8)
|
g2d.clearRect(8*x, 8*y, 8, 8)
|
||||||
val colorIdx = (color % MachineDefinition.colorPalette.size).toShort()
|
val colorIdx = (color % C64MachineDefinition.colorPalette.size).toShort()
|
||||||
val screencode = Petscii.petscii2scr(petscii, inverseVideo)
|
val coloredImage = C64MachineDefinition.Charset.getColoredChar(screencode, colorIdx)
|
||||||
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)
|
|
||||||
g2d.drawImage(coloredImage, 8*x, 8*y , null)
|
g2d.drawImage(coloredImage, 8*x, 8*y , null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,19 +134,19 @@ class ScreenDialog(title: String) : JFrame(title) {
|
|||||||
// the borders (top, left, right, bottom)
|
// the borders (top, left, right, bottom)
|
||||||
val borderTop = JPanel().apply {
|
val borderTop = JPanel().apply {
|
||||||
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||||
background = MachineDefinition.colorPalette[14]
|
background = C64MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
val borderBottom = JPanel().apply {
|
val borderBottom = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||||
background = MachineDefinition.colorPalette[14]
|
background = C64MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
val borderLeft = JPanel().apply {
|
val borderLeft = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||||
background = MachineDefinition.colorPalette[14]
|
background = C64MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
val borderRight = JPanel().apply {
|
val borderRight = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||||
background = MachineDefinition.colorPalette[14]
|
background = C64MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
var c = GridBagConstraints()
|
var c = GridBagConstraints()
|
||||||
c.gridx=0; c.gridy=1; c.gridwidth=3
|
c.gridx=0; c.gridy=1; c.gridwidth=3
|
||||||
|
@ -5,6 +5,7 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.ArrayLiteralValue
|
import prog8.ast.expressions.ArrayLiteralValue
|
||||||
|
import prog8.ast.expressions.InferredTypes
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -83,8 +84,11 @@ class TestParserNumericLiteralValue {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEqualsRef() {
|
fun testEqualsRef() {
|
||||||
assertTrue(StringLiteralValue(DataType.STR, "hello", dummyPos) == StringLiteralValue(DataType.STR, "hello", dummyPos))
|
assertEquals(StringLiteralValue("hello", false, dummyPos), StringLiteralValue("hello", false, dummyPos))
|
||||||
assertFalse(StringLiteralValue(DataType.STR, "hello", dummyPos) == StringLiteralValue(DataType.STR, "bye", dummyPos))
|
assertNotEquals(StringLiteralValue("hello", false, dummyPos), StringLiteralValue("bye", false, dummyPos))
|
||||||
|
assertEquals(StringLiteralValue("hello", true, dummyPos), StringLiteralValue("hello", true, dummyPos))
|
||||||
|
assertNotEquals(StringLiteralValue("hello", true, dummyPos), StringLiteralValue("bye", true, dummyPos))
|
||||||
|
assertNotEquals(StringLiteralValue("hello", true, dummyPos), StringLiteralValue("hello", false, dummyPos))
|
||||||
|
|
||||||
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
|
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
|
||||||
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
||||||
@ -93,9 +97,9 @@ class TestParserNumericLiteralValue {
|
|||||||
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
||||||
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
|
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
|
||||||
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
|
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
|
||||||
val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), null, dummyPos)
|
val lv1 = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOne, lvTwo, lvThree), dummyPos)
|
||||||
val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), null, dummyPos)
|
val lv2 = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOneR, lvTwoR, lvThreeR), dummyPos)
|
||||||
val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), null, dummyPos)
|
val lv3 = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOneR, lvTwoR, lvFour), dummyPos)
|
||||||
assertEquals(lv1, lv2)
|
assertEquals(lv1, lv2)
|
||||||
assertNotEquals(lv1, lv3)
|
assertNotEquals(lv1, lv3)
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ import prog8.ast.base.Position
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import prog8.compiler.target.c64.MachineDefinition.Mflpt5
|
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.vm.RuntimeValueNumeric
|
import prog8.vm.RuntimeValueNumeric
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
@ -95,29 +95,29 @@ class TestCompiler {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMflpt5ToFloat() {
|
fun testMflpt5ToFloat() {
|
||||||
val PRECISION=0.000000001
|
val epsilon=0.000000001
|
||||||
assertThat(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(0.0))
|
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, 0xA1).toDouble(), closeTo(3.141592653, epsilon))
|
||||||
assertThat(Mflpt5(0x82, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(3.141592653589793, PRECISION))
|
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, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(32768.0))
|
||||||
assertThat(Mflpt5(0x90, 0x80, 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(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, 0x34).toDouble(), closeTo(0.7071067812, epsilon))
|
||||||
assertThat(Mflpt5(0x80, 0x35, 0x04, 0xF3, 0x33).toDouble(), closeTo(0.7071067811865476, PRECISION))
|
assertThat(Mflpt5(0x80, 0x35, 0x04, 0xF3, 0x33).toDouble(), closeTo(0.7071067811865476, epsilon))
|
||||||
assertThat(Mflpt5(0x81, 0x35, 0x04, 0xF3, 0x34).toDouble(), closeTo(1.4142135624, PRECISION))
|
assertThat(Mflpt5(0x81, 0x35, 0x04, 0xF3, 0x34).toDouble(), closeTo(1.4142135624, epsilon))
|
||||||
assertThat(Mflpt5(0x81, 0x35, 0x04, 0xF3, 0x33).toDouble(), closeTo(1.4142135623730951, PRECISION))
|
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, 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, 0xF8).toDouble(), closeTo(0.69314718061, epsilon))
|
||||||
assertThat(Mflpt5(0x80, 0x31, 0x72, 0x17, 0xF7).toDouble(), closeTo(0.6931471805599453, PRECISION))
|
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(0x84, 0x20, 0x00, 0x00, 0x00).toDouble(), equalTo(10.0))
|
||||||
assertThat(Mflpt5(0x9E, 0x6E, 0x6B, 0x28, 0x00).toDouble(), equalTo(1000000000.0))
|
assertThat(Mflpt5(0x9E, 0x6E, 0x6B, 0x28, 0x00).toDouble(), equalTo(1000000000.0))
|
||||||
assertThat(Mflpt5(0x80, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(.5))
|
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, 0x38, 0xAA, 0x3B, 0x29).toDouble(), closeTo(1.4426950408889634, epsilon))
|
||||||
assertThat(Mflpt5(0x81, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(1.5707963267948966, PRECISION))
|
assertThat(Mflpt5(0x81, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(1.5707963267948966, epsilon))
|
||||||
assertThat(Mflpt5(0x83, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(6.283185307179586, PRECISION))
|
assertThat(Mflpt5(0x83, 0x49, 0x0F, 0xDA, 0xA2).toDouble(), closeTo(6.283185307179586, epsilon))
|
||||||
assertThat(Mflpt5(0x7F, 0x00, 0x00, 0x00, 0x00).toDouble(), equalTo(.25))
|
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(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)
|
assertTrue(ten <= ten)
|
||||||
assertFalse(ten < ten)
|
assertFalse(ten < ten)
|
||||||
|
|
||||||
val abc = StringLiteralValue(DataType.STR, "abc", Position("", 0, 0, 0))
|
val abc = StringLiteralValue("abc", false, Position("", 0, 0, 0))
|
||||||
val abd = StringLiteralValue(DataType.STR, "abd", Position("", 0, 0, 0))
|
val abd = StringLiteralValue("abd", false, Position("", 0, 0, 0))
|
||||||
assertEquals(abc, abc)
|
assertEquals(abc, abc)
|
||||||
assertTrue(abc!=abd)
|
assertTrue(abc!=abd)
|
||||||
assertFalse(abc!=abc)
|
assertFalse(abc!=abc)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</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" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -188,9 +188,11 @@ Values will usually be part of an expression or assignment statement::
|
|||||||
12345 ; integer number
|
12345 ; integer number
|
||||||
$aa43 ; hex integer number
|
$aa43 ; hex integer number
|
||||||
%100101 ; binary integer number (% is also remainder operator so be careful)
|
%100101 ; binary integer number (% is also remainder operator so be careful)
|
||||||
"Hi, I am a string" ; text string
|
|
||||||
'a' ; petscii value (byte) for the letter a
|
|
||||||
-33.456e52 ; floating point number
|
-33.456e52 ; floating point number
|
||||||
|
"Hi, I am a string" ; text string, encoded with compiler target default encoding
|
||||||
|
'a' ; byte value (ubyte) for the letter a
|
||||||
|
@"Alternate" ; text string, encoded with alternate encoding
|
||||||
|
@'a' ; byte value of the letter a, using alternate encoding
|
||||||
|
|
||||||
byte counter = 42 ; variable of size 8 bits, with initial value 42
|
byte counter = 42 ; variable of size 8 bits, with initial value 42
|
||||||
|
|
||||||
@ -271,9 +273,12 @@ Strings
|
|||||||
Strings are a sequence of characters enclosed in ``"`` quotes. The length is limited to 255 characters.
|
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,
|
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*.
|
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.
|
Strings in your source code files will be encoded (translated from ASCII/UTF-8) into bytes via the
|
||||||
PETSCII is the default choice. If you need screencodes (also called 'poke' codes) instead,
|
default encoding that is used on the target platform. For the C-64, this is CBM PETSCII.
|
||||||
you have to use the ``str_s`` variants of the string type identifier.
|
Alternate-encoding strings (prefixed with ``@``) will be encoded via the alternate encoding for the
|
||||||
|
platform (if defined). For the C-64, that is SCREEN CODES (also known as POKE codes).
|
||||||
|
This @-prefix can also be used for character byte values.
|
||||||
|
|
||||||
|
|
||||||
You can concatenate two string literals using '+' (not very useful though) or repeat
|
You can concatenate two string literals using '+' (not very useful though) or repeat
|
||||||
a string literal a given number of times using '*'::
|
a string literal a given number of times using '*'::
|
||||||
@ -283,10 +288,10 @@ a string literal a given number of times using '*'::
|
|||||||
|
|
||||||
|
|
||||||
.. caution::
|
.. 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),
|
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.
|
it will then start working with the changed strings instead of the original ones.
|
||||||
The same is true for arrays by the way.
|
The same is true for arrays.
|
||||||
|
|
||||||
|
|
||||||
Structs
|
Structs
|
||||||
@ -608,8 +613,8 @@ Calling a subroutine
|
|||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The arguments in parentheses after the function name, should match the parameters in the subroutine definition.
|
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
|
If you want to ignore a return value of a subroutine, you should prefix the call with the ``void`` keyword.
|
||||||
will issue a warning then telling you the result values of a subroutine call are discarded.
|
Otherwise the compiler will issue a warning about discarding a result value.
|
||||||
|
|
||||||
.. caution::
|
.. caution::
|
||||||
Note that due to the way parameters are processed by the compiler,
|
Note that due to the way parameters are processed by the compiler,
|
||||||
@ -708,9 +713,13 @@ sum(x)
|
|||||||
|
|
||||||
sort(array)
|
sort(array)
|
||||||
Sort the array in ascending order (in-place)
|
Sort the array in ascending order (in-place)
|
||||||
|
Note: sorting a floating-point array is not supported right now, as a general sorting routine for this will
|
||||||
|
be extremely slow. Either build one yourself or find another solution that doesn't require sorting
|
||||||
|
floating point values.
|
||||||
|
|
||||||
reverse(array)
|
reverse(array)
|
||||||
Reverse the values in the array (in-place). Can be used after sort() to sort an array in descending order.
|
Reverse the values in the array (in-place). Supports all data types including floats.
|
||||||
|
Can be used after sort() to sort an array in descending order.
|
||||||
|
|
||||||
len(x)
|
len(x)
|
||||||
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
||||||
|
@ -230,6 +230,7 @@ Various examples::
|
|||||||
byte age = 2018 - 1974
|
byte age = 2018 - 1974
|
||||||
float wallet = 55.25
|
float wallet = 55.25
|
||||||
str name = "my name is Irmen"
|
str name = "my name is Irmen"
|
||||||
|
str name = @"my name is Irmen" ; string with alternative byte encoding
|
||||||
uword address = &counter
|
uword address = &counter
|
||||||
byte[] values = [11, 22, 33, 44, 55]
|
byte[] values = [11, 22, 33, 44, 55]
|
||||||
byte[5] values ; array of 5 bytes, initially set to zero
|
byte[5] values ; array of 5 bytes, initially set to zero
|
||||||
@ -248,7 +249,7 @@ Prog8 supports the following data types:
|
|||||||
type identifier type storage size example var declaration and literal value
|
type identifier type storage size example var declaration and literal value
|
||||||
=============== ======================= ================= =========================================
|
=============== ======================= ================= =========================================
|
||||||
``byte`` signed byte 1 byte = 8 bits ``byte myvar = -22``
|
``byte`` signed byte 1 byte = 8 bits ``byte myvar = -22``
|
||||||
``ubyte`` unsigned byte 1 byte = 8 bits ``ubyte myvar = $8f``
|
``ubyte`` unsigned byte 1 byte = 8 bits ``ubyte myvar = $8f``, ``ubyte c = 'a'``, ``ubyte c2 = @'a'``
|
||||||
-- boolean 1 byte = 8 bits ``byte myvar = true`` or ``byte myvar == false``
|
-- boolean 1 byte = 8 bits ``byte myvar = true`` or ``byte myvar == false``
|
||||||
The true and false are actually just aliases
|
The true and false are actually just aliases
|
||||||
for the byte values 1 and 0.
|
for the byte values 1 and 0.
|
||||||
@ -268,8 +269,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]``
|
``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]``
|
||||||
``str`` string (petscii) varies ``str myvar = "hello."``
|
``str`` string (petscii) varies ``str myvar = "hello."``
|
||||||
implicitly terminated by a 0-byte
|
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
|
**arrays:** you can split an array initializer list over several lines if you want. When an initialization
|
||||||
@ -342,9 +341,16 @@ The following names are reserved, they have a special meaning::
|
|||||||
Range expression
|
Range expression
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
A special value is the *range expression* ( ``<startvalue> to <endvalue>`` )
|
A special value is the *range expression* which represents a range of numbers or characters,
|
||||||
which represents a range of numbers or characters,
|
from the starting value to (and including) the ending value::
|
||||||
from the starting value to (and including) the ending value.
|
|
||||||
|
<start> to <end> [ step <step> ]
|
||||||
|
<start> downto <end> [ step <step> ]
|
||||||
|
|
||||||
|
You an provide a step value if you need something else than the default increment which is one (or,
|
||||||
|
in case of downto, a decrement of one). Because a step of minus one is so common you can just use
|
||||||
|
the downto variant to avoid having to specify the step as well.
|
||||||
|
|
||||||
If used in the place of a literal value, it expands into the actual array of values::
|
If used in the place of a literal value, it expands into the actual array of values::
|
||||||
|
|
||||||
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
||||||
@ -453,19 +459,22 @@ Subroutine / function calls
|
|||||||
|
|
||||||
You call a subroutine like this::
|
You call a subroutine like this::
|
||||||
|
|
||||||
[ result = ] subroutinename_or_address ( [argument...] )
|
[ void / result = ] subroutinename_or_address ( [argument...] )
|
||||||
|
|
||||||
; example:
|
; example:
|
||||||
resultvariable = subroutine(arg1, arg2, arg3)
|
resultvariable = subroutine(arg1, arg2, arg3)
|
||||||
|
void noresultvaluesub(arg)
|
||||||
|
|
||||||
|
|
||||||
Arguments are separated by commas. The argument list can also be empty if the subroutine
|
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
|
takes no parameters. If the subroutine returns a value, usually you assign it to a variable.
|
||||||
a result variable (but the compiler will warn you about discarding the result of the call).
|
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.
|
Normal subroutines can only return zero or one return values.
|
||||||
However, the special ``asmsub`` routines (implemented in assembly code or referencing
|
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
||||||
a routine in kernel ROM) can return more than one return values, for instance a status
|
(referencing a routine in kernel ROM) can return more than one return value.
|
||||||
in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
||||||
It is not possible to process the results of a call to these kind of routines
|
It is not possible to process the results of a call to these kind of routines
|
||||||
directly from the language, because only single value assignments are possible.
|
directly from the language, because only single value assignments are possible.
|
||||||
You can still call the subroutine and not store the results.
|
You can still call the subroutine and not store the results.
|
||||||
@ -493,9 +502,34 @@ and can have nothing following it. The close curly brace must be on its own line
|
|||||||
The parameters is a (possibly empty) comma separated list of "<datatype> <parametername>" pairs specifying the input parameters.
|
The parameters is a (possibly empty) comma separated list of "<datatype> <parametername>" pairs specifying the input parameters.
|
||||||
The return type has to be specified if the subroutine returns a value.
|
The return type has to be specified if the subroutine returns a value.
|
||||||
|
|
||||||
.. todo::
|
|
||||||
asmsub with assigning memory address to refer to predefined ROM subroutines
|
Assembly / ROM subroutines
|
||||||
asmsub with a regular body to precisely control what registers are used to call the subroutine
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Subroutines implemented in ROM are usually defined by compiler library files, with the following syntax::
|
||||||
|
|
||||||
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> clobbers() -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y
|
||||||
|
|
||||||
|
This defines the ``LOAD`` subroutine at ROM memory address $FFD5, taking arguments in all three registers A, X and Y,
|
||||||
|
and returning stuff in several registers as well. The ``clobbers`` clause is used to signify to the compiler
|
||||||
|
what CPU registers are clobbered by the call instead of being unchanged or returning a meaningful result value.
|
||||||
|
|
||||||
|
|
||||||
|
Subroutines that are implemented purely in assembly code and which have an assembly calling convention (i.e.
|
||||||
|
the parameters are strictly passed via cpu registers), are defined like this::
|
||||||
|
|
||||||
|
asmsub FREADS32() clobbers(A,X,Y) {
|
||||||
|
%asm {{
|
||||||
|
lda $62
|
||||||
|
eor #$ff
|
||||||
|
asl a
|
||||||
|
lda #0
|
||||||
|
ldx #$a0
|
||||||
|
jmp $bc4f
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
the statement body of such a subroutine should consist of just an inline assembly block.
|
||||||
|
|
||||||
|
|
||||||
Expressions
|
Expressions
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
|
- option to load library files from a directory instead of the embedded ones
|
||||||
|
|
||||||
|
|
||||||
Memory Block Operations integrated in language?
|
Memory Block Operations integrated in language?
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ sub start() {
|
|||||||
c64.MVOL = 15
|
c64.MVOL = 15
|
||||||
|
|
||||||
c64scr.print("will play the music from boulderdash,\nmade in 1984 by peter liepa.\npress enter to start: ")
|
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()
|
c64.CLEARSCR()
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
c1541 -format examples,01 d64 examples.d64
|
c1541 -format examples,01 d64 examples.d64
|
||||||
for filename in *.prg; do
|
for filename in *.prg; do
|
||||||
c1541 examples.d64 -write $filename ${filename%.*}
|
c1541 examples.d64 -write $filename ${filename%.*}
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
These .prg files are the compiled versions of most of the examples.
|
These .prg files are the compiled versions of most of the examples.
|
||||||
They can be loaded and ran on a C64 or in an emulator, for example in Vice:
|
They can be loaded and ran on a C64 or in an emulator, for example in Vice:
|
||||||
|
|
||||||
x64 hello.prg
|
x64sc hello.prg
|
||||||
|
|
||||||
will load and run the hello example.
|
will load and run the hello example.
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -138,7 +138,7 @@ main {
|
|||||||
; (simple bubble sort as it's only 8 items to sort)
|
; (simple bubble sort as it's only 8 items to sort)
|
||||||
ubyte i
|
ubyte i
|
||||||
ubyte i1
|
ubyte i1
|
||||||
for i in 6 to 0 step -1 {
|
for i in 6 downto 0 {
|
||||||
for i1 in 0 to i {
|
for i1 in 0 to i {
|
||||||
ubyte i2 = i1+1
|
ubyte i2 = i1+1
|
||||||
if(rotatedz[i1] > rotatedz[i2]) {
|
if(rotatedz[i1] > rotatedz[i2]) {
|
||||||
|
@ -64,9 +64,9 @@ main {
|
|||||||
ubyte i
|
ubyte i
|
||||||
for i in 0 to len(xcoor)-1 {
|
for i in 0 to len(xcoor)-1 {
|
||||||
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
|
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
|
||||||
rotatedx[i] = (Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i])
|
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
|
||||||
rotatedy[i] = (Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i])
|
rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]
|
||||||
rotatedz[i] = (Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i])
|
rotatedz[i] = Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ main {
|
|||||||
|
|
||||||
; use indexed loop to write characters
|
; use indexed loop to write characters
|
||||||
str bye = "Goodbye!\n"
|
str bye = "Goodbye!\n"
|
||||||
for char in 0 to len(bye)
|
for char in 0 to len(bye)-1
|
||||||
c64.CHROUT(bye[char])
|
c64.CHROUT(bye[char])
|
||||||
|
|
||||||
|
|
||||||
@ -40,6 +40,8 @@ main {
|
|||||||
c64.CHROUT(':')
|
c64.CHROUT(':')
|
||||||
c64flt.print_f(clock_seconds)
|
c64flt.print_f(clock_seconds)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
c64scr.print("bye!\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,12 @@ main {
|
|||||||
|
|
||||||
c64.VMCSB |= 2 ; switch lowercase chars
|
c64.VMCSB |= 2 ; switch lowercase chars
|
||||||
c64scr.print("Please introduce yourself: ")
|
c64scr.print("Please introduce yourself: ")
|
||||||
c64scr.input_chars(name)
|
void c64scr.input_chars(name)
|
||||||
c64scr.print("\n\nHello, ")
|
c64scr.print("\n\nHello, ")
|
||||||
c64scr.print(name)
|
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")
|
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")
|
||||||
|
|
||||||
for attempts_left in 10 to 1 step -1 {
|
for attempts_left in 10 downto 1 {
|
||||||
|
|
||||||
c64scr.print("\nYou have ")
|
c64scr.print("\nYou have ")
|
||||||
c64scr.print_ub(attempts_left)
|
c64scr.print_ub(attempts_left)
|
||||||
@ -27,7 +27,7 @@ main {
|
|||||||
if attempts_left>1
|
if attempts_left>1
|
||||||
c64scr.print("es")
|
c64scr.print("es")
|
||||||
c64scr.print(" left.\nWhat is your next guess? ")
|
c64scr.print(" left.\nWhat is your next guess? ")
|
||||||
c64scr.input_chars(input)
|
void c64scr.input_chars(input)
|
||||||
ubyte guess = lsb(c64utils.str2uword(input))
|
ubyte guess = lsb(c64utils.str2uword(input))
|
||||||
|
|
||||||
if guess==secretnumber {
|
if guess==secretnumber {
|
||||||
|
37
examples/screencodes.p8
Normal file
37
examples/screencodes.p8
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
%import c64lib
|
||||||
|
%import c64utils
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
|
||||||
|
c64.VMCSB |= 2 ; switch to lowercase charset
|
||||||
|
|
||||||
|
str s1 = "HELLO hello 1234 @[/]" ; regular strings have default encoding (petscii on c64)
|
||||||
|
str s2 = @"HELLO hello 1234 @[/]" ; alternative encoding (screencodes on c64)
|
||||||
|
|
||||||
|
c64scr.print("\n\n\n\nString output via print:\n")
|
||||||
|
c64scr.print("petscii-str: ")
|
||||||
|
c64scr.print(s1)
|
||||||
|
c64scr.print("\nscrcode-str: ")
|
||||||
|
c64scr.print(s2)
|
||||||
|
|
||||||
|
c64scr.print("\n\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]
|
||||||
|
|
||||||
|
ubyte c1 = 'z'
|
||||||
|
ubyte c2 = @'z'
|
||||||
|
|
||||||
|
c64scr.print("\npetscii z=")
|
||||||
|
c64scr.print_ub(c1)
|
||||||
|
c64scr.print("\nscreencode z=")
|
||||||
|
c64scr.print_ub(c2)
|
||||||
|
}
|
||||||
|
}
|
@ -319,19 +319,19 @@ waitkey:
|
|||||||
c64scr.setcc(boardOffsetX-1, boardOffsetY+boardHeight, 124, 12)
|
c64scr.setcc(boardOffsetX-1, boardOffsetY+boardHeight, 124, 12)
|
||||||
c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY+boardHeight, 126, 12)
|
c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY+boardHeight, 126, 12)
|
||||||
ubyte i
|
ubyte i
|
||||||
for i in boardOffsetX+boardWidth-1 to boardOffsetX step -1 {
|
for i in boardOffsetX+boardWidth-1 downto boardOffsetX {
|
||||||
c64scr.setcc(i, boardOffsetY-3, 255, 0) ; invisible barrier
|
c64scr.setcc(i, boardOffsetY-3, 255, 0) ; invisible barrier
|
||||||
c64scr.setcc(i, boardOffsetY+boardHeight, 69, 11)
|
c64scr.setcc(i, boardOffsetY+boardHeight, 69, 11)
|
||||||
}
|
}
|
||||||
for i in boardOffsetY+boardHeight-1 to boardOffsetY step -1 {
|
for i in boardOffsetY+boardHeight-1 downto boardOffsetY {
|
||||||
c64scr.setcc(boardOffsetX-1, i, 89, 11)
|
c64scr.setcc(boardOffsetX-1, i, 89, 11)
|
||||||
c64scr.setcc(boardOffsetX+boardWidth, i, 84, 11)
|
c64scr.setcc(boardOffsetX+boardWidth, i, 84, 11)
|
||||||
}
|
}
|
||||||
|
|
||||||
ubyte[] colors = [6,8,7,5,4]
|
ubyte[] colors = [6,8,7,5,4]
|
||||||
for i in len(colors)-1 to 0 step -1 {
|
for i in len(colors)-1 downto 0 {
|
||||||
ubyte x
|
ubyte x
|
||||||
for x in 5 to 0 step -1 {
|
for x in 5 downto 0 {
|
||||||
c64scr.setcc(6+x-i, 11+2*i, 102, colors[i])
|
c64scr.setcc(6+x-i, 11+2*i, 102, colors[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -352,7 +352,7 @@ waitkey:
|
|||||||
const ubyte nextBlockXpos = 29
|
const ubyte nextBlockXpos = 29
|
||||||
const ubyte nextBlockYpos = 5
|
const ubyte nextBlockYpos = 5
|
||||||
ubyte x
|
ubyte x
|
||||||
for x in nextBlockXpos+3 to nextBlockXpos step -1 {
|
for x in nextBlockXpos+3 downto nextBlockXpos {
|
||||||
c64scr.setcc(x, nextBlockYpos, ' ', 0)
|
c64scr.setcc(x, nextBlockYpos, ' ', 0)
|
||||||
c64scr.setcc(x, nextBlockYpos+1, ' ', 0)
|
c64scr.setcc(x, nextBlockYpos+1, ' ', 0)
|
||||||
}
|
}
|
||||||
@ -368,7 +368,7 @@ waitkey:
|
|||||||
const ubyte holdBlockXpos = 7
|
const ubyte holdBlockXpos = 7
|
||||||
const ubyte holdBlockYpos = 6
|
const ubyte holdBlockYpos = 6
|
||||||
ubyte x
|
ubyte x
|
||||||
for x in holdBlockXpos+3 to holdBlockXpos step -1 {
|
for x in holdBlockXpos+3 downto holdBlockXpos {
|
||||||
c64scr.setcc(x, holdBlockYpos, '@', 0)
|
c64scr.setcc(x, holdBlockYpos, '@', 0)
|
||||||
c64scr.setcc(x, holdBlockYpos+1, '@', 0)
|
c64scr.setcc(x, holdBlockYpos+1, '@', 0)
|
||||||
}
|
}
|
||||||
@ -383,7 +383,7 @@ waitkey:
|
|||||||
|
|
||||||
sub drawBlock(ubyte x, ubyte y, ubyte character) {
|
sub drawBlock(ubyte x, ubyte y, ubyte character) {
|
||||||
ubyte i
|
ubyte i
|
||||||
for i in 15 to 0 step -1 {
|
for i in 15 downto 0 {
|
||||||
ubyte c=blocklogic.currentBlock[i]
|
ubyte c=blocklogic.currentBlock[i]
|
||||||
if c
|
if c
|
||||||
c64scr.setcc((i&3)+x, (i/4)+y, character, c)
|
c64scr.setcc((i&3)+x, (i/4)+y, character, c)
|
||||||
@ -532,7 +532,7 @@ blocklogic {
|
|||||||
|
|
||||||
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
||||||
ubyte i
|
ubyte i
|
||||||
for i in 15 to 0 step -1 {
|
for i in 15 downto 0 {
|
||||||
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -558,7 +558,7 @@ blocklogic {
|
|||||||
sub collapse(ubyte ypos) {
|
sub collapse(ubyte ypos) {
|
||||||
while(ypos>main.startYpos+1) {
|
while(ypos>main.startYpos+1) {
|
||||||
ubyte x
|
ubyte x
|
||||||
for x in main.boardOffsetX+main.boardWidth-1 to main.boardOffsetX step -1 {
|
for x in main.boardOffsetX+main.boardWidth-1 downto main.boardOffsetX {
|
||||||
ubyte char = c64scr.getchr(x, ypos-1)
|
ubyte char = c64scr.getchr(x, ypos-1)
|
||||||
ubyte color = c64scr.getclr(x, ypos-1)
|
ubyte color = c64scr.getclr(x, ypos-1)
|
||||||
c64scr.setcc(x, ypos, char, color)
|
c64scr.setcc(x, ypos, char, color)
|
||||||
|
@ -1,52 +1,11 @@
|
|||||||
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte[] ubarr = [22,33,44,55,66]
|
|
||||||
byte[] barr = [22,-33,-44,55,66]
|
byte[] barr = [22,-33,-44,55,66]
|
||||||
ubyte endub1
|
|
||||||
byte endb1
|
|
||||||
ubyte aa
|
|
||||||
ubyte ub
|
|
||||||
byte bb
|
|
||||||
uword uw
|
|
||||||
word total
|
|
||||||
uword count
|
|
||||||
|
|
||||||
|
|
||||||
; ---------- BYTE var ---------
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
total = 0
|
|
||||||
c64scr.print("byte var in arrayliteral: ")
|
|
||||||
for bb in [1,3,5,99] {
|
|
||||||
count++
|
|
||||||
total += bb
|
|
||||||
}
|
|
||||||
if count==4 and total==108
|
|
||||||
c64scr.print("ok\n")
|
|
||||||
else
|
|
||||||
c64scr.print("fail!!!\n")
|
|
||||||
|
|
||||||
|
|
||||||
; ---------- WORD var ---------
|
|
||||||
|
|
||||||
word[] warr = [-111,222,-333,444]
|
|
||||||
word endw1
|
|
||||||
word ww
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
total = 0
|
|
||||||
c64scr.print("word var in arrayliteral: ")
|
|
||||||
for ww in [1111,3333,555,999] {
|
|
||||||
count++
|
|
||||||
total += ww
|
|
||||||
}
|
|
||||||
if count==4 and total==5998
|
|
||||||
c64scr.print("ok\n")
|
|
||||||
else
|
|
||||||
c64scr.print("fail!!!\n")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
|
%import c64lib
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
main {
|
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"
|
||||||
|
|
||||||
str s1 = "irmen"
|
|
||||||
str_s s2 = "hello"
|
|
||||||
&str ms1 = $c000
|
&str ms1 = $c000
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +38,6 @@ main {
|
|||||||
; read array
|
; read array
|
||||||
A=s1[2]
|
A=s1[2]
|
||||||
ub=s1[2]
|
ub=s1[2]
|
||||||
ub=s2[2]
|
|
||||||
bb=barray[2]
|
bb=barray[2]
|
||||||
ub=ubarray[2]
|
ub=ubarray[2]
|
||||||
ww=warray[2]
|
ww=warray[2]
|
||||||
@ -47,8 +52,7 @@ main {
|
|||||||
fl=mflarray[2]
|
fl=mflarray[2]
|
||||||
|
|
||||||
A=s1[A]
|
A=s1[A]
|
||||||
ub=s2[A]
|
ub=s1[A]
|
||||||
ub=s2[A]
|
|
||||||
bb=barray[A]
|
bb=barray[A]
|
||||||
ub=ubarray[A]
|
ub=ubarray[A]
|
||||||
ww=warray[A]
|
ww=warray[A]
|
||||||
@ -64,7 +68,6 @@ main {
|
|||||||
|
|
||||||
A=s1[bb]
|
A=s1[bb]
|
||||||
ub=s1[bb]
|
ub=s1[bb]
|
||||||
ub=s2[bb]
|
|
||||||
bb=barray[bb]
|
bb=barray[bb]
|
||||||
ub=ubarray[bb]
|
ub=ubarray[bb]
|
||||||
ww=warray[bb]
|
ww=warray[bb]
|
||||||
@ -80,7 +83,6 @@ main {
|
|||||||
|
|
||||||
A=s1[bb*3]
|
A=s1[bb*3]
|
||||||
ub=s1[bb*3]
|
ub=s1[bb*3]
|
||||||
ub=s2[bb*3]
|
|
||||||
bb=barray[bb*3]
|
bb=barray[bb*3]
|
||||||
ub=ubarray[bb*3]
|
ub=ubarray[bb*3]
|
||||||
ww=warray[bb*3]
|
ww=warray[bb*3]
|
||||||
@ -99,7 +101,6 @@ main {
|
|||||||
barray[2]--
|
barray[2]--
|
||||||
s1[2] = A
|
s1[2] = A
|
||||||
s1[2] = ub
|
s1[2] = ub
|
||||||
s2[2] = ub
|
|
||||||
barray[2] = bb
|
barray[2] = bb
|
||||||
ubarray[2] = ub
|
ubarray[2] = ub
|
||||||
warray[2] = ww
|
warray[2] = ww
|
||||||
@ -116,7 +117,6 @@ main {
|
|||||||
mflarray[2] = fl
|
mflarray[2] = fl
|
||||||
|
|
||||||
s1[A] = ub
|
s1[A] = ub
|
||||||
s2[A] = ub
|
|
||||||
barray[A] = bb
|
barray[A] = bb
|
||||||
ubarray[A] = ub
|
ubarray[A] = ub
|
||||||
warray[A] = ww
|
warray[A] = ww
|
||||||
@ -124,7 +124,6 @@ main {
|
|||||||
flarray[A] = fl
|
flarray[A] = fl
|
||||||
|
|
||||||
s1[bb] = ub
|
s1[bb] = ub
|
||||||
s2[bb] = ub
|
|
||||||
barray[bb] = bb
|
barray[bb] = bb
|
||||||
ubarray[bb] = ub
|
ubarray[bb] = ub
|
||||||
warray[bb] = ww
|
warray[bb] = ww
|
||||||
@ -132,7 +131,6 @@ main {
|
|||||||
flarray[bb] = fl
|
flarray[bb] = fl
|
||||||
|
|
||||||
s1[bb*3] = ub
|
s1[bb*3] = ub
|
||||||
s2[bb*3] = ub
|
|
||||||
barray[bb*3] = bb
|
barray[bb*3] = bb
|
||||||
ubarray[bb*3] = ub
|
ubarray[bb*3] = ub
|
||||||
warray[bb*3] = ww
|
warray[bb*3] = ww
|
||||||
|
@ -71,7 +71,7 @@ main {
|
|||||||
count = 0
|
count = 0
|
||||||
total = 0
|
total = 0
|
||||||
c64scr.print("a in range step -1: ")
|
c64scr.print("a in range step -1: ")
|
||||||
for A in 20 to 10 step -1 {
|
for A in 20 downto 10 {
|
||||||
aa=A
|
aa=A
|
||||||
count++
|
count++
|
||||||
total += aa
|
total += aa
|
||||||
@ -151,7 +151,7 @@ main {
|
|||||||
total = 0
|
total = 0
|
||||||
endub1=101
|
endub1=101
|
||||||
c64scr.print("a in ncrange step -1: ")
|
c64scr.print("a in ncrange step -1: ")
|
||||||
for A in endub1 to 95 step -1 {
|
for A in endub1 downto 95 {
|
||||||
aa=A
|
aa=A
|
||||||
count++
|
count++
|
||||||
total += aa
|
total += aa
|
||||||
@ -272,7 +272,7 @@ main {
|
|||||||
count = 0
|
count = 0
|
||||||
total = 0
|
total = 0
|
||||||
c64scr.print("ubyte var in range step -1: ")
|
c64scr.print("ubyte var in range step -1: ")
|
||||||
for ub in 20 to 10 step -1 {
|
for ub in 20 downto 10 step -1 {
|
||||||
count++
|
count++
|
||||||
total += ub
|
total += ub
|
||||||
}
|
}
|
||||||
@ -346,7 +346,7 @@ main {
|
|||||||
total = 0
|
total = 0
|
||||||
endub1=101
|
endub1=101
|
||||||
c64scr.print("ubyte var in ncrange step -1: ")
|
c64scr.print("ubyte var in ncrange step -1: ")
|
||||||
for ub in endub1 to 95 step -1 {
|
for ub in endub1 downto 95 {
|
||||||
count++
|
count++
|
||||||
total += ub
|
total += ub
|
||||||
}
|
}
|
||||||
@ -450,7 +450,7 @@ main {
|
|||||||
count = 0
|
count = 0
|
||||||
total = 0
|
total = 0
|
||||||
c64scr.print("byte var in range step -1: ")
|
c64scr.print("byte var in range step -1: ")
|
||||||
for bb in 20 to 10 step -1 {
|
for bb in 20 downto 10 {
|
||||||
count++
|
count++
|
||||||
total += bb
|
total += bb
|
||||||
}
|
}
|
||||||
@ -524,7 +524,7 @@ main {
|
|||||||
total = 0
|
total = 0
|
||||||
endb1=101
|
endb1=101
|
||||||
c64scr.print("byte var in ncrange step -1: ")
|
c64scr.print("byte var in ncrange step -1: ")
|
||||||
for bb in endb1 to 95 step -1 {
|
for bb in endb1 downto 95 {
|
||||||
count++
|
count++
|
||||||
total += bb
|
total += bb
|
||||||
}
|
}
|
||||||
@ -645,7 +645,7 @@ main {
|
|||||||
count = 0
|
count = 0
|
||||||
totaluw = 0
|
totaluw = 0
|
||||||
c64scr.print("uword var in range step -1: ")
|
c64scr.print("uword var in range step -1: ")
|
||||||
for uw in 2000 to 1500 step -1 {
|
for uw in 2000 downto 1500 {
|
||||||
count++
|
count++
|
||||||
totaluw += uw
|
totaluw += uw
|
||||||
}
|
}
|
||||||
@ -718,7 +718,7 @@ main {
|
|||||||
count = 0
|
count = 0
|
||||||
totaluw = 0
|
totaluw = 0
|
||||||
c64scr.print("uword var in ncrange step -1: ")
|
c64scr.print("uword var in ncrange step -1: ")
|
||||||
for uw in enduw1 to 16500 step -1 {
|
for uw in enduw1 downto 16500 {
|
||||||
count++
|
count++
|
||||||
totaluw += uw
|
totaluw += uw
|
||||||
}
|
}
|
||||||
@ -826,7 +826,7 @@ main {
|
|||||||
count = 0
|
count = 0
|
||||||
total = 0
|
total = 0
|
||||||
c64scr.print("word var in range step -1: ")
|
c64scr.print("word var in range step -1: ")
|
||||||
for ww in 1000 to -500 step -1 {
|
for ww in 1000 downto -500 {
|
||||||
count++
|
count++
|
||||||
total += ww
|
total += ww
|
||||||
}
|
}
|
||||||
@ -899,7 +899,7 @@ main {
|
|||||||
count = 0
|
count = 0
|
||||||
total = 0
|
total = 0
|
||||||
c64scr.print("word var in ncrange step -1: ")
|
c64scr.print("word var in ncrange step -1: ")
|
||||||
for ww in endw1 to 16500 step -1 {
|
for ww in endw1 downto 16500 {
|
||||||
count++
|
count++
|
||||||
total += ww
|
total += ww
|
||||||
}
|
}
|
||||||
@ -967,7 +967,7 @@ main {
|
|||||||
sub wait_input() {
|
sub wait_input() {
|
||||||
c64scr.print("enter to continue:")
|
c64scr.print("enter to continue:")
|
||||||
str input = " "
|
str input = " "
|
||||||
c64scr.input_chars(input)
|
void c64scr.input_chars(input)
|
||||||
c64scr.print("\n\n")
|
c64scr.print("\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ irq {
|
|||||||
c64.MSIGX=0
|
c64.MSIGX=0
|
||||||
|
|
||||||
|
|
||||||
for spri in 7 to 0 step -1 {
|
for spri in 7 downto 0 {
|
||||||
uword @zp x = sin8u(angle*2-spri*16) as uword + 50
|
uword @zp x = sin8u(angle*2-spri*16) as uword + 50
|
||||||
ubyte @zp y = cos8u(angle*3-spri*16) / 2 + 70
|
ubyte @zp y = cos8u(angle*3-spri*16) / 2 + 70
|
||||||
c64.SPXYW[spri] = mkword(lsb(x), y)
|
c64.SPXYW[spri] = mkword(lsb(x), y)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user