more split array stuff

This commit is contained in:
Irmen de Jong 2023-05-26 22:56:12 +02:00
parent 91d87c2d9b
commit c94e292176
20 changed files with 278 additions and 91 deletions

View File

@ -1,9 +1,6 @@
package prog8.code.ast
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.NumericDatatypes
import prog8.code.core.Position
import prog8.code.core.*
import java.util.*
import kotlin.math.abs
import kotlin.math.round
@ -28,7 +25,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
infix fun isSameAs(other: PtExpression): Boolean {
return when(this) {
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
@ -51,7 +48,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
}
@ -118,6 +115,9 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
val index: PtExpression
get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
init {
require(elementType in NumericDatatypes)
}

View File

@ -17,7 +17,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> "&"
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}"
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
is PtBuiltinFunctionCall -> {
val str = if(node.void) "void " else ""

View File

@ -123,6 +123,7 @@ val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWO
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,

View File

@ -377,6 +377,10 @@ class AsmGen6502Internal (
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}")
val indexName = asmVariableName(indexVar)
if(expr.splitWords)
TODO("split word access ${expr.position}")
if (addOneExtra) {
// add 1 to the result
when (elementDt) {

View File

@ -432,6 +432,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("splitwords ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw")
}
@ -491,6 +493,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("splitwords ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw")
}
@ -533,6 +537,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("splitwords ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw")
}
@ -592,6 +598,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("splitwords ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw")
}
@ -607,6 +615,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
if(indexer.splitWords)
TODO("split word access ${indexer.position}")
if(arrayvar.type==DataType.UWORD) {
if(dt!='b')
throw AssemblyError("non-array var indexing requires bytes dt")

View File

@ -237,6 +237,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
}
in SplitWordArrayTypes -> {
TODO("split $varname")
}
in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
}
@ -744,6 +747,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
val elementDt = arrayExpr.type
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
if(arrayExpr.splitWords)
TODO("split word access ${arrayExpr.position}")
if(arrayExpr.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)

View File

@ -730,7 +730,7 @@ internal class ProgramAndVarsGen(
val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0')
}
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
in SplitWordArrayTypes -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0)

View File

@ -115,7 +115,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
left is PtIdentifier && left.name==scopedName
}
TargetStorageKind.ARRAY -> {
left is PtArrayIndexer && left isSameAs array!!
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
}
TargetStorageKind.MEMORY -> {
left isSameAs memory!!
@ -172,6 +172,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
}
is PtArrayIndexer -> {
if(value.splitWords)
TODO("splitwords ${value.position}")
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
}
is PtBuiltinFunctionCall -> {

View File

@ -1,5 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
@ -235,36 +236,60 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
val fixedIndex = constIntValue(targetArray.index)
val iterable = codeGen.symbolTable.flat.getValue(targetArray.variable.name) as StStaticVariable
val arrayLength = iterable.length!!
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also {
it += if(targetArray.splitWords)
IRInstruction(Opcode.STOREZMSPLIT, targetDt, immediate = arrayLength, labelSymbol = "$variable+$fixedIndex")
else
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also {
it += if(targetArray.splitWords)
IRInstruction(Opcode.STOREZXSPLIT, targetDt, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = variable)
else
IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
}
} else {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
}
} else {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also {
it += if(targetArray.splitWords)
IRInstruction(Opcode.STOREMSPLIT, targetDt, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "$variable+$fixedIndex")
else
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also {
it += if(targetArray.splitWords)
IRInstruction(Opcode.STOREXSPLIT, targetDt, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = variable)
else
IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
}
}
}
@ -301,10 +326,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also return\ed.
// returns the code to load the Index into the register, which is also returned.
val result = mutableListOf<IRCodeChunkBase>()
if(itemsize==1) {
if(itemsize==1 || array.splitWords) {
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)

View File

@ -160,6 +160,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
var resultRegister = -1
if(arrayIx.splitWords) {
require(vmDt==IRDataType.WORD)
val iterable = codeGen.symbolTable.flat.getValue(arrayIx.variable.name) as StStaticVariable
val arrayLength = iterable.length!!
resultRegister = codeGen.registers.nextFree()
if(arrayIx.index is PtNumber) {
val memOffset = (arrayIx.index as PtNumber).number.toInt()
addInstr(result, IRInstruction(Opcode.LOADMSPLIT, IRDataType.WORD, reg1=resultRegister, immediate = arrayLength, labelSymbol = "$arrayVarSymbol+$memOffset"), null)
} else {
val tr = translateExpression(arrayIx.index)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOADXSPLIT, IRDataType.WORD, reg1=resultRegister, reg2=tr.resultReg, immediate = arrayLength, labelSymbol = arrayVarSymbol), null)
}
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
}
var resultFpRegister = -1
if(arrayIx.index is PtNumber) {
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()

View File

@ -456,46 +456,59 @@ class IRCodeGen(
val endLabel = createLabelName()
if(iterableVar.dt==DataType.STR) {
// iterate over a zero-terminated string
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
val chunk = IRCodeChunk(loopLabel, null)
chunk += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = iterable.name)
chunk += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=tmpReg, immediate = 0, labelSymbol = endLabel)
chunk += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
chunk += IRInstruction(
Opcode.LOADX,
IRDataType.BYTE,
reg1 = tmpReg,
reg2 = indexReg,
labelSymbol = iterable.name
)
chunk += IRInstruction(
Opcode.BEQ,
IRDataType.BYTE,
reg1 = tmpReg,
immediate = 0,
labelSymbol = endLabel
)
chunk += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
result += chunk
result += translateNode(forLoop.statements)
val jumpChunk = IRCodeChunk(null, null)
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1 = indexReg)
jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
result += jumpChunk
result += IRCodeChunk(endLabel, null)
} else if(iterable.type in SplitWordArrayTypes) {
// iterate over lsb/msb split word array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
if(elementDt !in WordDatatypes)
throw AssemblyError("weird dt")
val length = iterableVar.length!!
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADXSPLIT, irType(elementDt), reg1=tmpReg, reg2=indexReg, immediate = length, labelSymbol=iterable.name)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
it += IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(length==256) 0 else length, labelSymbol = loopLabel)
}
} else {
// iterate over array
// iterate over regular array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
val lengthBytes = iterableVar.length!! * elementSize
if(lengthBytes<256) {
val chunk = IRCodeChunk(null, null)
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0)
result += chunk
val chunk2 = IRCodeChunk(loopLabel, null)
chunk2 += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
chunk2 += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk2
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = lengthBytes, labelSymbol = loopLabel), null)
} else if(lengthBytes==256) {
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
val chunk = IRCodeChunk(loopLabel, null)
chunk += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
chunk += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = 0, labelSymbol = loopLabel), null)
} else {
throw AssemblyError("iterator length should never exceed 256")
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(lengthBytes==256) 0 else lengthBytes, labelSymbol = loopLabel), null)
}
}
else -> throw AssemblyError("weird for iterable")

View File

@ -275,7 +275,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
}
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> {
val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) {
// convert the initializer range expression to an actual array

View File

@ -628,12 +628,12 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B, DataType.ARRAY_UB ->
if(arraySize > 256)
err("byte array length must be 1-256")
in SplitWordArrayTypes ->
if(arraySize > 256)
err("split word array length must be 1-256")
DataType.ARRAY_W, DataType.ARRAY_UW ->
if(arraySize > 128)
err("word array length must be 1-128")
DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT ->
if(arraySize > 256)
err("split word array length must be 1-256")
DataType.ARRAY_F ->
if(arraySize > 51)
err("float array length must be 1-51")
@ -679,6 +679,10 @@ internal class AstChecker(private val program: Program,
if (length == 0 || length > 256)
err("string and byte array length must be 1-256")
}
in SplitWordArrayTypes -> {
if (length == 0 || length > 256)
err("split word array length must be 1-256")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if (length == 0 || length > 128)
err("word array length must be 1-128")
@ -1279,7 +1283,7 @@ internal class AstChecker(private val program: Program,
// check index value 0..255
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
if(dtxNum.isKnown && dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
super.visit(arrayIndexedExpression)
@ -1429,29 +1433,26 @@ internal class AstChecker(private val program: Program,
}
return err("invalid byte array initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT-> {
// value may be either a single word, or a word arraysize, or a range
if(value.type istype targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.constIndex()
val arraySize = value.value.size
val maxLength = if(targetDt in SplitWordArrayTypes) 256 else 128
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize>128)
return err("word array length must be 1-128")
if(arraySpecSize>maxLength)
return err("array length must be 1-$maxLength")
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true
}
return err("invalid word array size, must be 1-128")
return err("invalid array size, must be 1-$maxLength")
}
return err("invalid word array initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
return true
TODO("check split array type")
}
DataType.ARRAY_F -> {
// value may be either a single float, or a float arraysize
if(value.type istype targetDt) {
@ -1555,10 +1556,10 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B -> {
correct = array.all { it in -128..127 }
}
DataType.ARRAY_UW -> {
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> {
correct = array.all { (it in 0..65535) }
}
DataType.ARRAY_W -> {
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> {
correct = array.all { it in -32768..32767 }
}
DataType.ARRAY_F -> correct = true

View File

@ -112,7 +112,7 @@ class TestMemory: FunSpec({
}
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -150,7 +150,7 @@ class TestMemory: FunSpec({
}
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -162,7 +162,7 @@ class TestMemory: FunSpec({
test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -174,7 +174,7 @@ class TestMemory: FunSpec({
test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -185,7 +185,7 @@ class TestMemory: FunSpec({
}
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -198,7 +198,7 @@ class TestMemory: FunSpec({
test("memory mapped array not in mapped IO ram") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -211,7 +211,7 @@ class TestMemory: FunSpec({
test("memory mapped array in mapped IO ram") {
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)

View File

@ -46,8 +46,8 @@ class TestAsmGenSymbols: StringSpec({
}
*/
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, Position.DUMMY)
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, false, Position.DUMMY)
val labelInSub = Label("locallabel", Position.DUMMY)
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, Position.DUMMY)
@ -63,7 +63,7 @@ class TestAsmGenSymbols: StringSpec({
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
val labelInBlock = Label("label_outside", Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY)
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))

View File

@ -20,6 +20,7 @@ For 9.0 major changes
- [much work:] add special (u)word array type (or modifier such as @fast or @split? ) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction....
So maybe only allow the bare essentials? (store, get, ++/--/+/-, bitwise operations?)
- TODO: splitarrays unit tests
- [much work:] more support for (64tass) SEGMENTS ?
- (What, how, isn't current BSS support enough?)
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)

View File

@ -9,11 +9,40 @@ main {
word[] @split split_words = [-12345, -2222, 22222]
uword[256] @split @shared split2
word[256] @split @shared split3
ubyte[200] @shared dummy
uword[256] @split split_large = 1000 to 1255
print_arrays()
txt.nl()
uword ww
for ww in split_large {
txt.print_uw(ww)
txt.spc()
}
txt.nl()
txt.nl()
ubyte xx=1
split_uwords[1] = 0
txt.print_uw(split_uwords[1])
txt.spc()
txt.print_w(split_words[1])
txt.spc()
split_words[1] = -9999
txt.print_w(split_words[1])
txt.spc()
txt.print_uw(split_uwords[xx])
txt.spc()
txt.print_w(split_words[xx])
txt.spc()
split_words[1]=-1111
txt.print_w(split_words[xx])
txt.nl()
txt.nl()
sub print_arrays() {
ubyte[200] @shared dummy2 = 22
for cx16.r0 in split_uwords {
txt.print_uw(cx16.r0)
txt.spc()
@ -28,7 +57,7 @@ main {
}
ubyte idx
for idx in 0 to 2 {
for idx in 0 to len(split_uwords)-1 {
cx16.r0 = split_uwords[idx]
cx16.r1s = split_words[idx]
txt.print_uw(cx16.r0)
@ -36,16 +65,19 @@ main {
txt.print_w(cx16.r1s)
txt.nl()
}
txt.nl()
split_uwords[1] = 9999
split_words[1] = -9999
print_arrays()
txt.nl()
split_uwords[1]++
split_words[1]--
print_arrays()
txt.nl()
}
}

View File

@ -36,11 +36,15 @@ load reg1, value - load immediate value into register. If y
loadm reg1, address - load reg1 with value at memory address
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2
loadxsplit reg1, reg2, arraylength, arrayaddress - load reg1 word with value from "split lsb/msb array" indexed by reg2
loadmsplit reg1, arraylength, indexedarrayaddress - load reg1 word with value from "split lsb/msb array" element at indexedarrayaddress
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2
loadr reg1, reg2 - load reg1 with value in register reg2
storem reg1, address - store reg1 at memory address
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2
storexsplit reg1, reg2, arraylength, arrayaddress - store reg1 word in "split lsb/msb array", indexed by value in reg2
storemsplit reg1, arraylength, indexedarrayaddress - store reg1 word in "split lsb/msb array" element at indexedarrayaddress
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2
storezm address - store zero at memory address
storezi reg1 - store zero at memory pointed to by reg1
@ -227,15 +231,21 @@ enum class Opcode {
LOADM,
LOADI,
LOADX,
LOADXSPLIT,
LOADMSPLIT,
LOADIX,
LOADR,
STOREM,
STOREI,
STOREX,
STOREXSPLIT,
STOREMSPLIT,
STOREIX,
STOREZM,
STOREZI,
STOREZX,
STOREZXSPLIT,
STOREZMSPLIT,
JUMP,
JUMPA,
@ -502,15 +512,21 @@ val instructionFormats = mutableMapOf(
Opcode.LOADM to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
Opcode.LOADI to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<r1"),
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
Opcode.LOADXSPLIT to InstructionFormat.from("W,>r1,<r2,<i,<a"),
Opcode.LOADMSPLIT to InstructionFormat.from("W,>r1,<i,<a"),
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
Opcode.LOADR to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.STOREM to InstructionFormat.from("BW,<r1,>a | F,<fr1,>a"),
Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"),
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREXSPLIT to InstructionFormat.from("W,<r1,<r2,<i,>a"),
Opcode.STOREMSPLIT to InstructionFormat.from("W,<r1,<i,>a"),
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREZM to InstructionFormat.from("BW,>a | F,>a"),
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
Opcode.STOREZMSPLIT to InstructionFormat.from("W,<i,>a"),
Opcode.STOREZXSPLIT to InstructionFormat.from("W,<r1,<i,>a"),
Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPA to InstructionFormat.from("N,<a"),
Opcode.CALL to InstructionFormat.from("N,call"),

View File

@ -164,11 +164,17 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.LOAD -> InsLOAD(ins)
Opcode.LOADM -> InsLOADM(ins)
Opcode.LOADX -> InsLOADX(ins)
Opcode.LOADXSPLIT -> InsLOADXSPLIT(ins)
Opcode.LOADMSPLIT -> InsLOADMSPLIT(ins)
Opcode.LOADI -> InsLOADI(ins)
Opcode.LOADIX -> InsLOADIX(ins)
Opcode.LOADR -> InsLOADR(ins)
Opcode.STOREM -> InsSTOREM(ins)
Opcode.STOREX -> InsSTOREX(ins)
Opcode.STOREXSPLIT -> InsSTOREXSPLIT(ins)
Opcode.STOREZXSPLIT -> InsSTOREZXSPLIT(ins)
Opcode.STOREMSPLIT -> InsSTOREMSPLIT(ins)
Opcode.STOREZMSPLIT -> InsSTOREZMSPLIT(ins)
Opcode.STOREIX -> InsSTOREIX(ins)
Opcode.STOREI -> InsSTOREI(ins)
Opcode.STOREZM -> InsSTOREZM(ins)
@ -413,6 +419,60 @@ class VirtualMachine(irProgram: IRProgram) {
nextPc()
}
private fun InsLOADXSPLIT(i: IRInstruction) {
require(i.type==IRDataType.WORD)
val address = i.address!! + registers.getUW(i.reg2!!).toInt()
val lsb = memory.getUB(address)
val msb = memory.getUB(address+i.immediate!!)
registers.setUW(i.reg1!!, ((msb.toInt() shl 8) or lsb.toInt()).toUShort())
nextPc()
}
private fun InsLOADMSPLIT(i: IRInstruction) {
require(i.type==IRDataType.WORD)
val address = i.address!!
val lsb = memory.getUB(address)
val msb = memory.getUB(address+i.immediate!!)
registers.setUW(i.reg1!!, ((msb.toInt() shl 8) or lsb.toInt()).toUShort())
nextPc()
}
private fun InsSTOREXSPLIT(i: IRInstruction) {
require(i.type==IRDataType.WORD)
val address = i.address!! + registers.getUW(i.reg2!!).toInt()
val lsb = registers.getUW(i.reg1!!).toUByte()
val msb = (registers.getUW(i.reg1!!).toInt() shr 8).toUByte()
memory.setUB(address, lsb)
memory.setUB(address+i.immediate!!, msb)
nextPc()
}
private fun InsSTOREMSPLIT(i: IRInstruction) {
require(i.type==IRDataType.WORD)
val address = i.address!!
val lsb = registers.getUW(i.reg1!!).toUByte()
val msb = (registers.getUW(i.reg1!!).toInt() shr 8).toUByte()
memory.setUB(address, lsb)
memory.setUB(address+i.immediate!!, msb)
nextPc()
}
private fun InsSTOREZXSPLIT(i: IRInstruction) {
require(i.type==IRDataType.WORD)
val address = i.address!! + registers.getUW(i.reg2!!).toInt()
memory.setUB(address, 0u)
memory.setUB(address+i.immediate!!, 0u)
nextPc()
}
private fun InsSTOREZMSPLIT(i: IRInstruction) {
require(i.type==IRDataType.WORD)
val address = i.address!!
memory.setUB(address, 0u)
memory.setUB(address+i.immediate!!, 0u)
nextPc()
}
private fun InsLOADIX(i: IRInstruction) {
when (i.type!!) {
IRDataType.BYTE -> {

View File

@ -3,6 +3,7 @@ package prog8.vm
import prog8.code.core.ArrayDatatypes
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.SplitWordArrayTypes
import prog8.intermediate.*
class VmProgramLoader {
@ -190,12 +191,6 @@ class VmProgramLoader {
}
private val functionCallOpcodes = setOf(Opcode.CALL, Opcode.SYSCALL, Opcode.JUMP, Opcode.JUMPA)
private fun findCall(it: IRCodeChunk, startIndex: Int): IRInstruction {
var idx = startIndex
while(it.instructions[idx].opcode !in functionCallOpcodes)
idx++
return it.instructions[idx]
}
private fun varsToMemory(
program: IRProgram,
@ -223,7 +218,7 @@ class VmProgramLoader {
memory.setFloat(addr, 0.0f)
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
in SplitWordArrayTypes -> {
// lo bytes come after the hi bytes
memory.setUB(addr, 0u)
memory.setUB(addr+variable.length!!, 0u)
@ -280,11 +275,13 @@ class VmProgramLoader {
addr+=2
}
}
DataType.ARRAY_UW_SPLIT -> {
TODO("$it")
}
DataType.ARRAY_W_SPLIT -> {
TODO("$it")
in SplitWordArrayTypes -> {
val number = value.toUInt()
for(elt in it) {
memory.setUB(addr, (number and 255u).toUByte())
memory.setUB(addr+variable.length!!, (number shr 8).toUByte())
addr++
}
}
DataType.ARRAY_F -> {
repeat(variable.length!!) {
@ -327,11 +324,13 @@ class VmProgramLoader {
addr+=2
}
}
DataType.ARRAY_UW_SPLIT -> {
TODO("$it")
}
DataType.ARRAY_W_SPLIT -> {
TODO("$it")
in SplitWordArrayTypes -> {
for(elt in it) {
val number = elt.number!!.toInt().toUInt()
memory.setUB(addr, (number and 255u).toUByte())
memory.setUB(addr+variable.length!!, (number shr 8).toUByte())
addr++
}
}
DataType.ARRAY_F -> {
for(elt in it) {