mirror of
https://github.com/irmen/prog8.git
synced 2025-07-24 05:24:17 +00:00
more ast-codegen v2
This commit is contained in:
@@ -52,7 +52,7 @@ asmsub MOVMF (uword mflpt @ XY) clobbers(A,Y) = $bbd4 ; store fac1 to memory
|
|||||||
|
|
||||||
; 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
|
asmsub FTOSWORDYA () clobbers(X) -> ubyte @ Y, ubyte @ A = $b1aa ; 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)
|
||||||
@@ -196,8 +196,8 @@ sub print_f (float value) {
|
|||||||
; ---- prints the floating point value (without a newline) using basic rom routines.
|
; ---- prints the floating point value (without a newline) using basic rom routines.
|
||||||
%asm {{
|
%asm {{
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx c64.SCRATCH_ZPREGX
|
||||||
lda #<print_f_value
|
lda #<value
|
||||||
ldy #>print_f_value
|
ldy #>value
|
||||||
jsr MOVFM ; load float into fac1
|
jsr MOVFM ; load float into fac1
|
||||||
jsr FOUT ; fac1 to string in A/Y
|
jsr FOUT ; fac1 to string in A/Y
|
||||||
jsr c64.STROUT ; print string in A/Y
|
jsr c64.STROUT ; print string in A/Y
|
||||||
@@ -310,7 +310,7 @@ stack_uw2float .proc
|
|||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_float2w .proc
|
stack_float2w .proc ; also used for float2b
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx c64.SCRATCH_ZPREGX
|
||||||
jsr AYINT
|
jsr AYINT
|
||||||
@@ -323,7 +323,7 @@ stack_float2w .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_float2uw .proc
|
stack_float2uw .proc ; also used for float2ub
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx c64.SCRATCH_ZPREGX
|
||||||
jsr GETADR
|
jsr GETADR
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package prog8.ast.base
|
package prog8.ast.base
|
||||||
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
|
||||||
/**************************** AST Data classes ****************************/
|
/**************************** AST Data classes ****************************/
|
||||||
|
|
||||||
@@ -25,10 +26,10 @@ enum class DataType {
|
|||||||
infix fun isAssignableTo(targetType: DataType) =
|
infix fun isAssignableTo(targetType: DataType) =
|
||||||
// what types are assignable to others without loss of precision?
|
// what types are assignable to others without loss of precision?
|
||||||
when(this) {
|
when(this) {
|
||||||
UBYTE -> targetType in setOf(UBYTE, UWORD, WORD, FLOAT)
|
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
|
||||||
BYTE -> targetType in setOf(BYTE, UBYTE, UWORD, WORD, FLOAT)
|
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
|
||||||
UWORD -> targetType in setOf(UWORD, FLOAT)
|
UWORD -> targetType in setOf(UWORD, FLOAT)
|
||||||
WORD -> targetType in setOf(WORD, UWORD, FLOAT)
|
WORD -> targetType in setOf(WORD, FLOAT)
|
||||||
FLOAT -> targetType == FLOAT
|
FLOAT -> targetType == FLOAT
|
||||||
STR -> targetType == STR || targetType==STR_S
|
STR -> targetType == STR || targetType==STR_S
|
||||||
STR_S -> targetType == STR || targetType==STR_S
|
STR_S -> targetType == STR || targetType==STR_S
|
||||||
@@ -52,6 +53,16 @@ enum class DataType {
|
|||||||
in WordDatatypes -> other in WordDatatypes
|
in WordDatatypes -> other in WordDatatypes
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun memorySize(): Int {
|
||||||
|
return when(this) {
|
||||||
|
in ByteDatatypes -> 1
|
||||||
|
in WordDatatypes -> 2
|
||||||
|
FLOAT -> MachineDefinition.Mflpt5.MemorySize
|
||||||
|
in PassByReferenceDatatypes -> 2
|
||||||
|
else -> -9999999
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Register {
|
enum class Register {
|
||||||
|
@@ -133,7 +133,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
subroutine.parameters
|
subroutine.parameters
|
||||||
.filter { it.name !in namesInSub }
|
.filter { it.name !in namesInSub }
|
||||||
.forEach {
|
.forEach {
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.DONTCARE, null, it.name, null, null,
|
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.NOT_IN_ZEROPAGE, null, it.name, null, null,
|
||||||
isArray = false, autogeneratedDontRemove = true, position = subroutine.position)
|
isArray = false, autogeneratedDontRemove = true, position = subroutine.position)
|
||||||
vardecl.linkParents(subroutine)
|
vardecl.linkParents(subroutine)
|
||||||
subroutine.statements.add(0, vardecl)
|
subroutine.statements.add(0, vardecl)
|
||||||
|
@@ -64,7 +64,7 @@ fun compileProgram(filepath: Path,
|
|||||||
programAst.removeNopsFlattenAnonScopes()
|
programAst.removeNopsFlattenAnonScopes()
|
||||||
|
|
||||||
// if you want to print the AST, do it before shuffling the statements around below
|
// if you want to print the AST, do it before shuffling the statements around below
|
||||||
//printAst(programAst)
|
printAst(programAst)
|
||||||
|
|
||||||
|
|
||||||
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
|
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
|
||||||
|
@@ -459,8 +459,8 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.CAST_B_TO_F -> " jsr c64flt.stack_b2float"
|
Opcode.CAST_B_TO_F -> " jsr c64flt.stack_b2float"
|
||||||
Opcode.CAST_UW_TO_F -> " jsr c64flt.stack_uw2float"
|
Opcode.CAST_UW_TO_F -> " jsr c64flt.stack_uw2float"
|
||||||
Opcode.CAST_W_TO_F -> " jsr c64flt.stack_w2float"
|
Opcode.CAST_W_TO_F -> " jsr c64flt.stack_w2float"
|
||||||
Opcode.CAST_F_TO_UB -> " jsr c64flt.stack_float2uw"
|
Opcode.CAST_F_TO_UB -> " jsr c64flt.stack_float2ub"
|
||||||
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2w"
|
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2b"
|
||||||
Opcode.CAST_F_TO_UW -> " jsr c64flt.stack_float2uw"
|
Opcode.CAST_F_TO_UW -> " jsr c64flt.stack_float2uw"
|
||||||
Opcode.CAST_F_TO_W -> " jsr c64flt.stack_float2w"
|
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_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta $ESTACK_HI_PLUS1_HEX,x" // clear the msb
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package prog8.compiler.target.c64.codegen2
|
package prog8.compiler.target.c64.codegen2
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.initvarsSubName
|
import prog8.ast.base.initvarsSubName
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
@@ -8,9 +11,16 @@ import prog8.ast.statements.*
|
|||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.c64.AssemblyProgram
|
import prog8.compiler.target.c64.AssemblyProgram
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.compiler.target.c64.codegen.optimizeAssembly
|
import prog8.compiler.target.c64.codegen.optimizeAssembly
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
internal class AssemblyError(msg: String) : RuntimeException(msg)
|
internal class AssemblyError(msg: String) : RuntimeException(msg)
|
||||||
@@ -24,6 +34,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
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)
|
||||||
private val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
|
private val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
|
||||||
private val breakpointLabels = mutableListOf<String>()
|
private val breakpointLabels = mutableListOf<String>()
|
||||||
|
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, options, zeropage, this)
|
||||||
|
|
||||||
internal fun compileToAssembly(optimize: Boolean): AssemblyProgram {
|
internal fun compileToAssembly(optimize: Boolean): AssemblyProgram {
|
||||||
assemblyLines.clear()
|
assemblyLines.clear()
|
||||||
@@ -123,8 +134,10 @@ internal class AsmGen2(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 floatFill = makeFloatFill(MachineDefinition.Mflpt5.fromNumber(flt.key))
|
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(flt.key)
|
||||||
out("${flt.value}\t.byte $floatFill ; float ${flt.key}")
|
val floatFill = makeFloatFill(mflpt5)
|
||||||
|
val floatvalue = flt.key
|
||||||
|
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,10 +149,11 @@ internal class AsmGen2(val program: Program,
|
|||||||
out("* = ${block.address.toHex()}")
|
out("* = ${block.address.toHex()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputSourceLine(block)
|
||||||
zeropagevars2asm(block.statements)
|
zeropagevars2asm(block.statements)
|
||||||
memdefs2asm(block.statements)
|
memdefs2asm(block.statements)
|
||||||
vardecls2asm(block.statements)
|
vardecls2asm(block.statements)
|
||||||
out("")
|
out("\n; subroutines in this block")
|
||||||
|
|
||||||
// first translate regular statements, and then put the subroutines at the end.
|
// first translate regular statements, and then put the subroutines at the end.
|
||||||
val (subroutine, stmts) = block.statements.partition { it is Subroutine }
|
val (subroutine, stmts) = block.statements.partition { it is Subroutine }
|
||||||
@@ -149,14 +163,20 @@ internal class AsmGen2(val program: Program,
|
|||||||
out("\n\t.pend\n") // TODO not if force_output?
|
out("\n\t.pend\n") // TODO not if force_output?
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun out(str: String, splitlines: Boolean = true) {
|
private fun outputSourceLine(node: Node) {
|
||||||
|
out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun out(str: String, splitlines: Boolean = true) {
|
||||||
|
val fragment = (if(" | " in str) str.replace("|", "\n") else str).trim('\n')
|
||||||
|
|
||||||
if (splitlines) {
|
if (splitlines) {
|
||||||
for (line in str.split('\n')) {
|
for (line in fragment.split('\n')) {
|
||||||
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim()
|
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim()
|
||||||
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
|
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
|
||||||
assemblyLines.add(trimmed)
|
assemblyLines.add(trimmed)
|
||||||
}
|
}
|
||||||
} else assemblyLines.add(str)
|
} else assemblyLines.add(fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFloatFill(flt: MachineDefinition.Mflpt5): String {
|
private fun makeFloatFill(flt: MachineDefinition.Mflpt5): String {
|
||||||
@@ -168,10 +188,25 @@ internal class AsmGen2(val program: Program,
|
|||||||
return "$b0, $b1, $b2, $b3, $b4"
|
return "$b0, $b1, $b2, $b3, $b4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun encodeStr(str: String, dt: DataType): List<Short> {
|
||||||
|
return when(dt) {
|
||||||
|
DataType.STR -> {
|
||||||
|
val bytes = Petscii.encodePetscii(str, true)
|
||||||
|
bytes.plus(0)
|
||||||
|
}
|
||||||
|
DataType.STR_S -> {
|
||||||
|
val bytes = Petscii.encodeScreencode(str, true)
|
||||||
|
bytes.plus(0)
|
||||||
|
}
|
||||||
|
else -> throw prog8.compiler.target.c64.codegen.AssemblyError("invalid str type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun zeropagevars2asm(statements: List<Statement>) {
|
private fun zeropagevars2asm(statements: List<Statement>) {
|
||||||
out("; vars allocated on zeropage")
|
out("; vars allocated on zeropage")
|
||||||
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
for(variable in variables) {
|
for(variable in variables) {
|
||||||
|
// should NOT allocate subroutine parameters on the zero page
|
||||||
val fullName = variable.scopedname
|
val fullName = variable.scopedname
|
||||||
val zpVar = allocatedZeropageVariables[fullName]
|
val zpVar = allocatedZeropageVariables[fullName]
|
||||||
if(zpVar==null) {
|
if(zpVar==null) {
|
||||||
@@ -205,10 +240,14 @@ internal class AsmGen2(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 -> TODO()
|
DataType.STR, DataType.STR_S -> {
|
||||||
DataType.STR_S -> TODO()
|
val string = (decl.value as ReferenceLiteralValue).str!!
|
||||||
|
val bytes = encodeStr(string, decl.datatype).map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
|
out("${decl.name}\t; ${decl.datatype} \"${escape(string).replace("\u0000", "<NULL>")}\"")
|
||||||
|
for (chunk in bytes.chunked(16))
|
||||||
|
out(" .byte " + chunk.joinToString())
|
||||||
|
}
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
// unsigned integer byte arraysize
|
|
||||||
val data = makeArrayFillDataUnsigned(decl)
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${decl.name}\t.byte ${data.joinToString()}")
|
out("${decl.name}\t.byte ${data.joinToString()}")
|
||||||
@@ -218,10 +257,46 @@ internal class AsmGen2(val program: Program,
|
|||||||
out(" .byte " + chunk.joinToString())
|
out(" .byte " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> TODO()
|
DataType.ARRAY_B -> {
|
||||||
DataType.ARRAY_UW -> TODO()
|
val data = makeArrayFillDataSigned(decl)
|
||||||
DataType.ARRAY_W -> TODO()
|
if (data.size <= 16)
|
||||||
DataType.ARRAY_F -> TODO()
|
out("${decl.name}\t.char ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
out(decl.name)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
out(" .char " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW -> {
|
||||||
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
|
if (data.size <= 16)
|
||||||
|
out("${decl.name}\t.word ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
out(decl.name)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
out(" .word " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W -> {
|
||||||
|
val data = makeArrayFillDataSigned(decl)
|
||||||
|
if (data.size <= 16)
|
||||||
|
out("${decl.name}\t.sint ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
out(decl.name)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
out(" .sint " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
val array = (decl.value as ReferenceLiteralValue).array!!
|
||||||
|
val floatFills = array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number
|
||||||
|
makeFloatFill(MachineDefinition.Mflpt5.fromNumber(number))
|
||||||
|
}
|
||||||
|
out(decl.name)
|
||||||
|
for (f in array.zip(floatFills))
|
||||||
|
out(" .byte ${f.second} ; float ${f.first}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +334,45 @@ internal class AsmGen2(val program: Program,
|
|||||||
|
|
||||||
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
|
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
|
||||||
val array = (decl.value as ReferenceLiteralValue).array!!
|
val array = (decl.value as ReferenceLiteralValue).array!!
|
||||||
return array.map { (it as NumericLiteralValue).number.toString() }
|
return when {
|
||||||
|
decl.datatype == DataType.ARRAY_UB ->
|
||||||
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
|
array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
decl.datatype== DataType.ARRAY_UW -> array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
// TODO word array with address-references
|
||||||
|
"$"+number.toString(16).padStart(4, '0')
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid arraysize type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeArrayFillDataSigned(decl: VarDecl): List<String> {
|
||||||
|
val array = (decl.value as ReferenceLiteralValue).array!!
|
||||||
|
return when {
|
||||||
|
decl.datatype == DataType.ARRAY_UB ->
|
||||||
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
|
array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
|
||||||
|
if(number>=0)
|
||||||
|
"$$hexnum"
|
||||||
|
else
|
||||||
|
"-$$hexnum"
|
||||||
|
}
|
||||||
|
decl.datatype== DataType.ARRAY_UW -> array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||||
|
if(number>=0)
|
||||||
|
"$$hexnum"
|
||||||
|
else
|
||||||
|
"-$$hexnum"
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid arraysize type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFloatConst(number: Double): String {
|
private fun getFloatConst(number: Double): String {
|
||||||
@@ -271,6 +384,14 @@ internal class AsmGen2(val program: Program,
|
|||||||
return newName
|
return newName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun signExtendAtoMsb(destination: String) =
|
||||||
|
"""
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
+ sta $destination
|
||||||
|
"""
|
||||||
|
|
||||||
private fun asmIdentifierName(identifier: IdentifierReference): String {
|
private fun asmIdentifierName(identifier: IdentifierReference): String {
|
||||||
val name = if(identifier.memberOfStruct(program.namespace)!=null) {
|
val name = if(identifier.memberOfStruct(program.namespace)!=null) {
|
||||||
identifier.targetVarDecl(program.namespace)!!.name
|
identifier.targetVarDecl(program.namespace)!!.name
|
||||||
@@ -308,13 +429,28 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: Statement) {
|
private fun translate(stmt: Statement) {
|
||||||
|
outputSourceLine(stmt)
|
||||||
when(stmt) {
|
when(stmt) {
|
||||||
is VarDecl, is StructDecl, is NopStatement -> {}
|
is VarDecl, is StructDecl, is NopStatement -> {}
|
||||||
is Directive -> translate(stmt)
|
is Directive -> translate(stmt)
|
||||||
is Return -> translate(stmt)
|
is Return -> translate(stmt)
|
||||||
is Subroutine -> translate(stmt)
|
is Subroutine -> translate(stmt)
|
||||||
is InlineAssembly -> translate(stmt)
|
is InlineAssembly -> translate(stmt)
|
||||||
is FunctionCallStatement -> translate(stmt)
|
is FunctionCallStatement -> {
|
||||||
|
val functionName = stmt.target.nameInSource.last()
|
||||||
|
val builtinFunc = BuiltinFunctions[functionName]
|
||||||
|
if(builtinFunc!=null) {
|
||||||
|
builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc)
|
||||||
|
} else {
|
||||||
|
translateSubroutineCall(stmt)
|
||||||
|
// discard any results from the stack:
|
||||||
|
val returns = stmt.target.targetSubroutine(program.namespace)!!.returntypes
|
||||||
|
for(t in returns) {
|
||||||
|
if (t in IntegerDatatypes || t in PassByReferenceDatatypes) out(" inx")
|
||||||
|
else if (t == DataType.FLOAT) out(" inx | inx | inx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
is Assignment -> translate(stmt)
|
is Assignment -> translate(stmt)
|
||||||
is Jump -> translate(stmt)
|
is Jump -> translate(stmt)
|
||||||
is PostIncrDecr -> translate(stmt)
|
is PostIncrDecr -> translate(stmt)
|
||||||
@@ -333,6 +469,80 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translateSubroutineCall(stmt: IFunctionCall) {
|
||||||
|
val subName = stmt.target.nameInSource.joinToString(".")
|
||||||
|
if(stmt.arglist.isNotEmpty()) {
|
||||||
|
val sub = stmt.target.targetSubroutine(program.namespace)!!
|
||||||
|
for(arg in sub.parameters.withIndex().zip(stmt.arglist)) {
|
||||||
|
if(arg.first.value.type!=arg.second.inferType(program))
|
||||||
|
throw AssemblyError("argument type mismatch")
|
||||||
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
|
// pass arg via a variable
|
||||||
|
val paramVar = arg.first.value
|
||||||
|
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||||
|
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||||
|
target.linkParents(stmt as Node)
|
||||||
|
val literal = arg.second as? NumericLiteralValue
|
||||||
|
when {
|
||||||
|
literal!=null -> {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
when(arg.first.value.type) {
|
||||||
|
in ByteDatatypes -> assignByteConstant(target, literal.number.toShort())
|
||||||
|
in WordDatatypes -> assignWordConstant(target, literal.number.toInt())
|
||||||
|
DataType.FLOAT -> assignFloatConstant(target, literal.number.toDouble())
|
||||||
|
in PassByReferenceDatatypes-> TODO( "str/array/struct sub arg")
|
||||||
|
else -> throw AssemblyError("weird arg datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg.second is IdentifierReference -> {
|
||||||
|
// optimize when the argument is a variable
|
||||||
|
when (arg.first.value.type) {
|
||||||
|
in ByteDatatypes -> assignByteVariable(target, arg.second as IdentifierReference)
|
||||||
|
in WordDatatypes -> assignWordVariable(target, arg.second as IdentifierReference)
|
||||||
|
DataType.FLOAT -> assignFloatVariable(target, arg.second as IdentifierReference)
|
||||||
|
in PassByReferenceDatatypes -> TODO("str/array/struct sub arg")
|
||||||
|
else -> throw AssemblyError("weird arg datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("non-constant sub arg $arg")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// pass arg via a register parameter
|
||||||
|
val paramRegister = sub.asmParameterRegisters[arg.first.index]
|
||||||
|
val statusflag = paramRegister.statusflag
|
||||||
|
val register = paramRegister.registerOrPair
|
||||||
|
val stack = paramRegister.stack
|
||||||
|
when {
|
||||||
|
stack==true -> TODO("stack param")
|
||||||
|
statusflag!=null -> {
|
||||||
|
if (statusflag == Statusflag.Pc) TODO("carry flag param")
|
||||||
|
else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
|
}
|
||||||
|
register!=null -> {
|
||||||
|
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
||||||
|
target.linkParents(stmt as Node)
|
||||||
|
val literal = arg.second as? NumericLiteralValue
|
||||||
|
if(literal!=null) {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
when(register) {
|
||||||
|
RegisterOrPair.A,
|
||||||
|
RegisterOrPair.X,
|
||||||
|
RegisterOrPair.Y -> assignByteConstant(target, literal.number.toShort())
|
||||||
|
RegisterOrPair.AX -> TODO("register A+X param $literal")
|
||||||
|
RegisterOrPair.AY -> TODO("register A+Y param $literal")
|
||||||
|
RegisterOrPair.XY -> TODO("register X+Y param $literal")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TODO("register param")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out(" jsr $subName")
|
||||||
|
}
|
||||||
|
|
||||||
private fun translate(stmt: Label) {
|
private fun translate(stmt: Label) {
|
||||||
out("${stmt.name}")
|
out("${stmt.name}")
|
||||||
}
|
}
|
||||||
@@ -347,7 +557,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
val instruction = branchInstruction(stmt.condition, false)
|
val instruction = branchInstruction(stmt.condition, false)
|
||||||
out(" $instruction ${getJumpTarget(jump)}")
|
out(" $instruction ${getJumpTarget(jump)}")
|
||||||
} else {
|
} else {
|
||||||
TODO("$stmt")
|
TODO("branch $stmt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,26 +586,50 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: ForLoop) {
|
private fun translate(stmt: ForLoop) {
|
||||||
out("; for $stmt")
|
out("; TODO for $stmt") // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: PostIncrDecr) {
|
private fun translate(stmt: PostIncrDecr) {
|
||||||
|
val incr = stmt.operator=="++"
|
||||||
when {
|
when {
|
||||||
stmt.target.register!=null -> {
|
stmt.target.register!=null -> {
|
||||||
when(stmt.target.register!!) {
|
when(stmt.target.register!!) {
|
||||||
Register.A -> out("""
|
Register.A -> {
|
||||||
clc
|
if(incr)
|
||||||
adc #1
|
out(" clc | adc #1 ")
|
||||||
""")
|
else
|
||||||
Register.X -> out(" inx")
|
out(" sec | sbc #1 ")
|
||||||
Register.Y -> out(" iny")
|
}
|
||||||
|
Register.X -> {
|
||||||
|
if(incr) out(" inx") else out(" dex")
|
||||||
|
}
|
||||||
|
Register.Y -> {
|
||||||
|
if(incr) out(" iny") else out(" dey")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stmt.target.identifier!=null -> {
|
stmt.target.identifier!=null -> {
|
||||||
val targetName = asmIdentifierName(stmt.target.identifier!!)
|
val what = asmIdentifierName(stmt.target.identifier!!)
|
||||||
out(" inc $targetName")
|
out(if(incr) " inc $what" else " dec $what")
|
||||||
}
|
}
|
||||||
else -> TODO("postincrdecr $stmt")
|
stmt.target.memoryAddress!=null -> {
|
||||||
|
val target = stmt.target.memoryAddress!!.addressExpression
|
||||||
|
when (target) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val what = target.number.toHex()
|
||||||
|
out(if(incr) " inc $what" else " dec $what")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val what = target.nameInSource.joinToString(".")
|
||||||
|
out(if(incr) " inc $what" else " dec $what")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target type $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stmt.target.arrayindexed!=null -> {
|
||||||
|
TODO("postincrdecr array element ${stmt.target.arrayindexed}")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target type ${stmt.target}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,13 +647,14 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(ret: Return) {
|
private fun translate(ret: Return) {
|
||||||
if(ret.value!=null) {
|
ret.value?.let { translateExpression(it) }
|
||||||
TODO("$ret value")
|
|
||||||
}
|
|
||||||
out(" rts")
|
out(" rts")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(sub: Subroutine) {
|
private fun translate(sub: Subroutine) {
|
||||||
|
out("")
|
||||||
|
outputSourceLine(sub)
|
||||||
|
|
||||||
if(sub.isAsmSubroutine) {
|
if(sub.isAsmSubroutine) {
|
||||||
if(sub.asmAddress!=null)
|
if(sub.asmAddress!=null)
|
||||||
return // already done at the memvars section
|
return // already done at the memvars section
|
||||||
@@ -433,7 +668,9 @@ internal class AsmGen2(val program: Program,
|
|||||||
out("${sub.name}\t.proc")
|
out("${sub.name}\t.proc")
|
||||||
zeropagevars2asm(sub.statements)
|
zeropagevars2asm(sub.statements)
|
||||||
memdefs2asm(sub.statements)
|
memdefs2asm(sub.statements)
|
||||||
|
out("; statements")
|
||||||
sub.statements.forEach{ translate(it) }
|
sub.statements.forEach{ translate(it) }
|
||||||
|
out("; variables")
|
||||||
vardecls2asm(sub.statements)
|
vardecls2asm(sub.statements)
|
||||||
out(" .pend\n")
|
out(" .pend\n")
|
||||||
}
|
}
|
||||||
@@ -444,14 +681,6 @@ internal class AsmGen2(val program: Program,
|
|||||||
assemblyLines.add(assembly)
|
assemblyLines.add(assembly)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(call: FunctionCallStatement) {
|
|
||||||
if(call.arglist.isEmpty()) {
|
|
||||||
out(" jsr ${call.target.nameInSource.joinToString(".")}")
|
|
||||||
} else {
|
|
||||||
TODO("call $call")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(assign: Assignment) {
|
private fun translate(assign: Assignment) {
|
||||||
if(assign.aug_op!=null)
|
if(assign.aug_op!=null)
|
||||||
throw AssemblyError("aug-op assignments should have been transformed to normal ones")
|
throw AssemblyError("aug-op assignments should have been transformed to normal ones")
|
||||||
@@ -460,7 +689,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
val numVal = assign.value as NumericLiteralValue
|
val numVal = assign.value as NumericLiteralValue
|
||||||
when(numVal.type) {
|
when(numVal.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> assignByteConstant(assign.target, numVal.number.toInt())
|
DataType.UBYTE, DataType.BYTE -> assignByteConstant(assign.target, numVal.number.toShort())
|
||||||
DataType.UWORD, DataType.WORD -> assignWordConstant(assign.target, numVal.number.toInt())
|
DataType.UWORD, DataType.WORD -> assignWordConstant(assign.target, numVal.number.toInt())
|
||||||
DataType.FLOAT -> assignFloatConstant(assign.target, numVal.number.toDouble())
|
DataType.FLOAT -> assignFloatConstant(assign.target, numVal.number.toDouble())
|
||||||
DataType.STR -> TODO()
|
DataType.STR -> TODO()
|
||||||
@@ -481,7 +710,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
when(type) {
|
when(type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> assignByteVariable(assign.target, assign.value as IdentifierReference)
|
DataType.UBYTE, DataType.BYTE -> assignByteVariable(assign.target, assign.value as IdentifierReference)
|
||||||
DataType.UWORD, DataType.WORD -> assignWordVariable(assign.target, assign.value as IdentifierReference)
|
DataType.UWORD, DataType.WORD -> assignWordVariable(assign.target, assign.value as IdentifierReference)
|
||||||
DataType.FLOAT -> TODO()
|
DataType.FLOAT -> assignFloatVariable(assign.target, assign.value as IdentifierReference)
|
||||||
DataType.STR -> TODO()
|
DataType.STR -> TODO()
|
||||||
DataType.STR_S -> TODO()
|
DataType.STR_S -> TODO()
|
||||||
DataType.ARRAY_UB -> TODO()
|
DataType.ARRAY_UB -> TODO()
|
||||||
@@ -509,7 +738,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
translateExpression(read.addressExpression)
|
translateExpression(read.addressExpression)
|
||||||
out("; read memory byte from result and put that in ${assign.target}") // TODO
|
out("; TODO read memory byte from result and put that in ${assign.target}") // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -550,19 +779,65 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: ArrayIndexedExpression) {
|
private fun translateExpression(expr: ArrayIndexedExpression) {
|
||||||
out("; evaluate arrayindexed ${expr}")
|
out("; TODO evaluate arrayindexed ${expr}") // TODO
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateExpression(expr: FunctionCall) {
|
|
||||||
out("; evaluate funccall ${expr}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: TypecastExpression) {
|
private fun translateExpression(expr: TypecastExpression) {
|
||||||
translateExpression(expr.expression)
|
translateExpression(expr.expression)
|
||||||
out("; typecast to ${expr.type}")
|
when(expr.expression.inferType(program)!!) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> out(" lda #0 | sta $ESTACK_HI_PLUS1_HEX,x")
|
||||||
|
DataType.FLOAT -> out(" jsr c64flt.stack_ub2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> out(" lda $ESTACK_HI_PLUS1_HEX,x | ${signExtendAtoMsb("$ESTACK_HI_PLUS1_HEX,x")}")
|
||||||
|
DataType.FLOAT -> out(" jsr c64flt.stack_b2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> out(" jsr c64flt.stack_uw2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> out(" jsr c64flt.stack_w2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE -> out(" jsr c64flt.stack_float2uw")
|
||||||
|
DataType.BYTE -> out(" jsr c64flt.stack_float2w")
|
||||||
|
DataType.UWORD -> out(" jsr c64flt.stack_float2uw")
|
||||||
|
DataType.WORD -> out(" jsr c64flt.stack_float2w")
|
||||||
|
DataType.FLOAT -> {}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot case a pass-by-reference datatypes into something else")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expression: Expression) {
|
internal fun translateExpression(expression: Expression) {
|
||||||
when(expression) {
|
when(expression) {
|
||||||
is PrefixExpression -> translateExpression(expression)
|
is PrefixExpression -> translateExpression(expression)
|
||||||
is BinaryExpression -> translateExpression(expression)
|
is BinaryExpression -> translateExpression(expression)
|
||||||
@@ -573,7 +848,15 @@ internal class AsmGen2(val program: Program,
|
|||||||
is NumericLiteralValue -> translateExpression(expression)
|
is NumericLiteralValue -> translateExpression(expression)
|
||||||
is RegisterExpr -> translateExpression(expression)
|
is RegisterExpr -> translateExpression(expression)
|
||||||
is IdentifierReference -> translateExpression(expression)
|
is IdentifierReference -> translateExpression(expression)
|
||||||
is FunctionCall -> translateExpression(expression)
|
is FunctionCall -> {
|
||||||
|
val functionName = expression.target.nameInSource.last()
|
||||||
|
val builtinFunc = BuiltinFunctions[functionName]
|
||||||
|
if(builtinFunc!=null) {
|
||||||
|
builtinFunctionsAsmGen.translateFunctioncallExpression(expression, builtinFunc)
|
||||||
|
} else {
|
||||||
|
translateSubroutineCall(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
is ReferenceLiteralValue -> TODO("string/array/struct assignment?")
|
is ReferenceLiteralValue -> TODO("string/array/struct assignment?")
|
||||||
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
||||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
@@ -581,38 +864,105 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: AddressOf) {
|
private fun translateExpression(expr: AddressOf) {
|
||||||
out("; take address of ${expr}")
|
out("; TODO take address of ${expr}") // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: DirectMemoryRead) {
|
private fun translateExpression(expr: DirectMemoryRead) {
|
||||||
out("; memread ${expr}")
|
out("; TODO memread ${expr}") // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: NumericLiteralValue) {
|
private fun translateExpression(expr: NumericLiteralValue) {
|
||||||
out("; literalvalue ${expr}")
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> out(" lda #${expr.number.toHex()} | sta $ESTACK_LO_HEX,x | dex")
|
||||||
|
DataType.UWORD, DataType.WORD -> out("""
|
||||||
|
lda #<${expr.number.toHex()}
|
||||||
|
sta $ESTACK_LO_HEX,x
|
||||||
|
lda #>${expr.number.toHex()}
|
||||||
|
sta $ESTACK_HI_HEX,x
|
||||||
|
dex
|
||||||
|
""")
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
val floatConst = getFloatConst(expr.number.toDouble())
|
||||||
|
out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: RegisterExpr) {
|
private fun translateExpression(expr: RegisterExpr) {
|
||||||
out("; register value ${expr}")
|
when(expr.register) {
|
||||||
|
Register.A -> out(" sta $ESTACK_LO_HEX,x | dex")
|
||||||
|
Register.X -> throw AssemblyError("cannot push X")
|
||||||
|
Register.Y -> out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: IdentifierReference) {
|
private fun translateExpression(expr: IdentifierReference) {
|
||||||
out("; identifier value ${expr}")
|
val varname = expr.nameInSource.joinToString(".")
|
||||||
|
when(expr.inferType(program)!!) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
out(" lda $varname | sta $ESTACK_LO_HEX,x | dex")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
out(" lda $varname | sta $ESTACK_LO_HEX,x | lda $varname+1 | sta $ESTACK_HI_HEX,x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: BinaryExpression) {
|
private fun translateExpression(expr: BinaryExpression) {
|
||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
translateExpression(expr.right)
|
translateExpression(expr.right)
|
||||||
out("; evaluate binary ${expr.operator}")
|
out("; TODO evaluate binary ${expr.operator}") // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: PrefixExpression) {
|
private fun translateExpression(expr: PrefixExpression) {
|
||||||
translateExpression(expr.expression)
|
translateExpression(expr.expression)
|
||||||
out("; evaluate prefix ${expr.operator}")
|
out("; TODO evaluate prefix ${expr.operator}") // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignEvalResult(target: AssignTarget) {
|
private fun assignEvalResult(target: AssignTarget) {
|
||||||
out("; put result in $target")
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
out(" inx | ld${target.register.name.toLowerCase()} $ESTACK_LO_HEX,x ")
|
||||||
|
}
|
||||||
|
target.identifier!=null -> {
|
||||||
|
val targetName = target.identifier.nameInSource.joinToString(".")
|
||||||
|
val targetDt = target.identifier.inferType(program)!!
|
||||||
|
when(targetDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
out(" inx | lda $ESTACK_LO_HEX,x | sta $targetName")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
out("""
|
||||||
|
inx
|
||||||
|
lda $ESTACK_LO_HEX,x
|
||||||
|
sta $targetName
|
||||||
|
lda $ESTACK_HI_HEX,x
|
||||||
|
sta $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
out("""
|
||||||
|
lda #<$targetName
|
||||||
|
ldy #>$targetName
|
||||||
|
jsr c64flt.pop_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type $targetDt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
out("; TODO put result in $target") // TODO
|
||||||
|
}
|
||||||
|
target.arrayindexed!=null -> {
|
||||||
|
out("; TODO put result in $target") // TODO
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird assignment target $target")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignAddressOf(target: AssignTarget, name: IdentifierReference, scopedname: String) {
|
private fun assignAddressOf(target: AssignTarget, name: IdentifierReference, scopedname: String) {
|
||||||
@@ -664,6 +1014,28 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignFloatVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = variable.nameInSource.joinToString(".")
|
||||||
|
when {
|
||||||
|
target.identifier!=null -> {
|
||||||
|
val targetName = asmIdentifierName(target.identifier)
|
||||||
|
out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta $targetName
|
||||||
|
lda $sourceName+1
|
||||||
|
sta $targetName+1
|
||||||
|
lda $sourceName+2
|
||||||
|
sta $targetName+2
|
||||||
|
lda $sourceName+3
|
||||||
|
sta $targetName+3
|
||||||
|
lda $sourceName+4
|
||||||
|
sta $targetName+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> TODO("assign float to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun assignByteVariable(target: AssignTarget, variable: IdentifierReference) {
|
private fun assignByteVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
val sourceName = variable.nameInSource.joinToString(".")
|
val sourceName = variable.nameInSource.joinToString(".")
|
||||||
when {
|
when {
|
||||||
@@ -706,38 +1078,43 @@ internal class AsmGen2(val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> out("; assign register $register to $target")
|
else -> out("; TODO assign register $register to $target") // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignWordConstant(target: AssignTarget, word: Int) {
|
private fun assignWordConstant(target: AssignTarget, word: Int) {
|
||||||
if(target.identifier!=null) {
|
if(target.identifier!=null) {
|
||||||
val targetName = asmIdentifierName(target.identifier)
|
val targetName = asmIdentifierName(target.identifier)
|
||||||
// TODO optimize case where lsb = msb
|
if(word ushr 8 == word and 255) {
|
||||||
out("""
|
// lsb=msb
|
||||||
lda #<${word.toHex()}
|
out("""
|
||||||
ldy #>${word.toHex()}
|
lda #${(word and 255).toHex()}
|
||||||
sta $targetName
|
sta $targetName
|
||||||
sty $targetName+1
|
sta $targetName+1
|
||||||
""")
|
""")
|
||||||
|
} else {
|
||||||
|
out("""
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
ldy #>${word.toHex()}
|
||||||
|
sta $targetName
|
||||||
|
sty $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
out("; assign byte $word to $target")
|
out("; TODO assign byte $word to $target") // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignByteConstant(target: AssignTarget, byte: Int) {
|
private fun assignByteConstant(target: AssignTarget, byte: Short) {
|
||||||
when {
|
when {
|
||||||
target.register!=null -> {
|
target.register!=null -> {
|
||||||
out(" ld${target.register.name.toLowerCase()} #${byte.toHex()}")
|
out(" ld${target.register.name.toLowerCase()} #${byte.toHex()}")
|
||||||
}
|
}
|
||||||
target.identifier!=null -> {
|
target.identifier!=null -> {
|
||||||
val targetName = asmIdentifierName(target.identifier)
|
val targetName = asmIdentifierName(target.identifier)
|
||||||
out("""
|
out(" lda #${byte.toHex()} | sta $targetName ")
|
||||||
lda #${byte.toHex()}
|
|
||||||
sta $targetName
|
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
else -> out("; assign byte $byte to $target")
|
else -> out("; TODO assign byte $byte to $target") // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -755,7 +1132,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
sta $targetName+4
|
sta $targetName+4
|
||||||
""")
|
""")
|
||||||
} else {
|
} else {
|
||||||
out("; assign float 0.0 to $target")
|
out("; TODO assign float 0.0 to $target") // TODO
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// non-zero value
|
// non-zero value
|
||||||
@@ -775,7 +1152,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
sta $targetName+4
|
sta $targetName+4
|
||||||
""")
|
""")
|
||||||
} else {
|
} else {
|
||||||
out("; assign float $float ($constFloat) to $target")
|
out("; TODO assign float $float ($constFloat) to $target") // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -793,7 +1170,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
sta $targetName
|
sta $targetName
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> TODO()
|
else -> TODO("assign memory byte $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(identifier!=null) {
|
else if(identifier!=null) {
|
||||||
@@ -818,7 +1195,7 @@ internal class AsmGen2(val program: Program,
|
|||||||
sta $targetName
|
sta $targetName
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> TODO()
|
else -> TODO("assign memory byte $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,68 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen2
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.WordDatatypes
|
||||||
|
import prog8.ast.expressions.FunctionCall
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.compiler.Zeropage
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
import prog8.functions.FunctionSignature
|
||||||
|
|
||||||
|
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||||
|
private val options: CompilationOptions,
|
||||||
|
private val zeropage: Zeropage,
|
||||||
|
private val asmgen: AsmGen2) {
|
||||||
|
|
||||||
|
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FunctionSignature) {
|
||||||
|
translateFunctioncall(fcall, func, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FunctionSignature) {
|
||||||
|
translateFunctioncall(fcall, func, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateFunctioncall(fcall: IFunctionCall, func: FunctionSignature, discardResult: Boolean) {
|
||||||
|
val functionName = fcall.target.nameInSource.last()
|
||||||
|
if(discardResult) {
|
||||||
|
if(func.pure)
|
||||||
|
return // can just ignore the whole function call altogether
|
||||||
|
else
|
||||||
|
throw AssemblyError("discarding result of non-pure function $fcall")
|
||||||
|
}
|
||||||
|
|
||||||
|
when(functionName) {
|
||||||
|
"msb" -> {
|
||||||
|
val arg = fcall.arglist.single()
|
||||||
|
if(arg.inferType(program) !in WordDatatypes)
|
||||||
|
throw AssemblyError("msb required word argument")
|
||||||
|
if(arg is NumericLiteralValue)
|
||||||
|
throw AssemblyError("should have been const-folded")
|
||||||
|
if(arg is IdentifierReference) {
|
||||||
|
val sourceName = arg.nameInSource.joinToString(".")
|
||||||
|
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(arg)
|
||||||
|
asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_LO_PLUS1_HEX,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"mkword" -> {
|
||||||
|
asmgen.translateExpression(fcall.arglist[0])
|
||||||
|
asmgen.translateExpression(fcall.arglist[1])
|
||||||
|
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
||||||
|
}
|
||||||
|
else -> TODO("builtin function $functionName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -379,10 +379,10 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
printWarning("condition is always true", whileLoop.position)
|
printWarning("condition is always true", whileLoop.position)
|
||||||
if(hasContinueOrBreak(whileLoop.body))
|
if(hasContinueOrBreak(whileLoop.body))
|
||||||
return whileLoop
|
return whileLoop
|
||||||
val label = Label("__back", whileLoop.condition.position)
|
val label = Label("_prog8_back", 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("__back"), whileLoop.condition.position),
|
IdentifierReference(listOf("_prog8_back"), whileLoop.condition.position),
|
||||||
null, whileLoop.condition.position))
|
null, whileLoop.condition.position))
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return whileLoop.body
|
return whileLoop.body
|
||||||
|
74
examples/romfloats.p8
Normal file
74
examples/romfloats.p8
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
%import c64flt
|
||||||
|
%zeropage basicsafe
|
||||||
|
%option enable_floats
|
||||||
|
|
||||||
|
~ main {
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
|
||||||
|
float f1
|
||||||
|
|
||||||
|
; these are all floating point constants defined in the ROM so no allocation required
|
||||||
|
; TODO actually read these from ROM
|
||||||
|
|
||||||
|
f1 = 3.141592653589793
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = -32768.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.7071067811865476
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.4142135623730951
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = -0.5
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.6931471805599453
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 10.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.0e9
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.5
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.4426950408889634
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.5707963267948966
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 6.283185307179586
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.25
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,3 @@
|
|||||||
%import c64utils
|
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
@@ -7,11 +6,69 @@
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
if_z goto start
|
float f1
|
||||||
if_pos goto start
|
|
||||||
if_cc goto start
|
|
||||||
if_nz goto start
|
|
||||||
|
|
||||||
|
; these are all floating point constants defined in the ROM so no allocation required
|
||||||
|
; TODO actually read these from ROM
|
||||||
|
|
||||||
|
f1 = 3.141592653589793
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = -32768.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.7071067811865476
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.4142135623730951
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = -0.5
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.6931471805599453
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 10.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.0e9
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.5
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.4426950408889634
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 1.5707963267948966
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 6.283185307179586
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.25
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
f1 = 0.0
|
||||||
|
c64flt.print_f(f1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user