mirror of
https://github.com/irmen/prog8.git
synced 2025-11-03 19:16:13 +00:00
allow array syntax on pointers
This commit is contained in:
@@ -56,9 +56,9 @@ val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataT
|
||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
|
||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER, BaseDataType.POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
|
||||
|
||||
|
||||
interface ISubType {
|
||||
|
||||
@@ -382,6 +382,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arrayVarSymbol = arrayIx.variable.name
|
||||
var resultRegister = -1
|
||||
val isPointer = arrayIx.variable.type.isPointer
|
||||
|
||||
if(arrayIx.splitWords) {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
@@ -409,8 +410,27 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
var resultFpRegister = -1
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
||||
|
||||
fun getByNumber(index: Int) {
|
||||
val memOffset = index * eltSize
|
||||
if(isPointer) {
|
||||
// indexing a pointer
|
||||
val pointerTr = translateExpression(arrayIx.variable)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
if(memOffset>0)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = memOffset), null)
|
||||
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, vmDt, reg1=resultRegister, reg2=pointerReg), null)
|
||||
}
|
||||
} else {
|
||||
// indexing an array or string type
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
@@ -419,23 +439,54 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
}
|
||||
} else {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
fun getByExpression(index: PtExpression) {
|
||||
val indexByteTr = translateExpression(index)
|
||||
addToResult(result, indexByteTr, indexByteTr.resultReg, -1)
|
||||
if(isPointer) {
|
||||
// indexing on a pointer
|
||||
val indexWordReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexByteTr.resultReg), null)
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
result += codeGen.multiplyByConst(IRDataType.WORD, indexWordReg, eltSize)
|
||||
val pointerTr = translateExpression(arrayIx.variable)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexWordReg), null)
|
||||
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, vmDt, reg1=resultRegister, reg2=pointerReg), null)
|
||||
}
|
||||
} else {
|
||||
// indexing an array or string type
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, indexByteTr.resultReg, eltSize)
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteTr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteTr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayIx.index is PtNumber)
|
||||
getByNumber((arrayIx.index as PtNumber).number.toInt())
|
||||
else
|
||||
getByExpression(arrayIx.index)
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister)
|
||||
}
|
||||
|
||||
|
||||
private fun translate(expr: PtPrefix): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = translateExpression(expr.value)
|
||||
|
||||
@@ -423,6 +423,17 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(arrayIndexedExpression.indexer.constIndex()==0) {
|
||||
if(arrayIndexedExpression.arrayvar.inferType(program).isPointer) {
|
||||
// pointer[0] --> pointer^^
|
||||
val deref = PtrDereference(arrayIndexedExpression.arrayvar, emptyList(), null, arrayIndexedExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
|
||||
// NOTE: only when the terms are not function calls!!!
|
||||
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
|
||||
|
||||
@@ -119,6 +119,7 @@ verafx {
|
||||
; Returns the 16 bits unsigned result of R0*R1 in AY.
|
||||
; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed)
|
||||
; Verafx doesn't support unsigned values like this for full 32 bit result.
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -136,6 +137,7 @@ verafx {
|
||||
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 {
|
||||
; Returns the 32 bits signed result in AY and R0 (lower word, upper word).
|
||||
; Vera Fx multiplication support only works on signed values!
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda #(2 << 1)
|
||||
sta cx16.VERA_CTRL ; $9F25
|
||||
|
||||
@@ -1341,7 +1341,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
checkLongType(typecast)
|
||||
if(typecast.type.isIterable)
|
||||
if(typecast.type.isPassByRef)
|
||||
errors.err("cannot type cast to string or array type", typecast.position)
|
||||
|
||||
if(!typecast.expression.inferType(program).isKnown)
|
||||
@@ -1796,6 +1796,9 @@ internal class AstChecker(private val program: Program,
|
||||
val memsize = struct.memsize(program.memsizer)
|
||||
if(memsize>256)
|
||||
errors.err("struct contains too many fields, max struct size is 256 bytes (actual: $memsize)", struct.position)
|
||||
|
||||
if(uniqueFields.isEmpty())
|
||||
errors.err("struct must contain at least one field", struct.position)
|
||||
}
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
|
||||
@@ -644,8 +644,8 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
|
||||
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
|
||||
val dt = srcArr.arrayvar.targetVarDecl()!!.datatype
|
||||
if(!dt.isArray && !dt.isString)
|
||||
throw FatalAstException("array indexing can only be used on array or string variables ${srcArr.position}")
|
||||
if(!dt.isArray && !dt.isString && !dt.isPointer)
|
||||
throw FatalAstException("array indexing can only be used on array, string or pointer variables ${srcArr.position}")
|
||||
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val array = PtArrayIndexer(eltType, srcArr.position)
|
||||
array.add(transform(srcArr.arrayvar))
|
||||
|
||||
@@ -356,6 +356,12 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
||||
return when {
|
||||
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
|
||||
target.datatype.isArray -> InferredTypes.knownFor(target.datatype.elementType())
|
||||
target.datatype.isPointer -> {
|
||||
if(target.datatype.subType!=null)
|
||||
TODO("indexing on pointer to struct would yield the struct type itself, this is not yet supported (only pointers) at $position")
|
||||
else
|
||||
InferredTypes.knownFor(target.datatype.sub!!)
|
||||
}
|
||||
else -> InferredTypes.knownFor(target.datatype)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,17 +25,18 @@ STRUCTS and TYPED POINTERS
|
||||
- DONE: implicit cast of pointer to bool, also in loop conditions (while ptr {...})
|
||||
- DONE: implicit cast of pointer to uword in conditional expressions
|
||||
- DONE: subroutine parameters and return values should be able to accept pointers as well now
|
||||
- DONE (for basic types only): allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
|
||||
- allow array syntax on pointers to structs too, but what type will ptr[2] have? And it will require ptr[2].field to work as well now. Actually that will be the only thing to work for now.
|
||||
- pointer arithmetic should follow C: ptr=ptr+10 adds 10*sizeof() instead of just 10.
|
||||
- add unit tests for all changes
|
||||
- arrays of structs? No -> Just an array of uword pointers to said structs. Can even be @split as the only representation form because that's the default for word arrays.
|
||||
- static initialization of structs may be allowed only at block scope and then behaves like arrays; it won't reset to the original value when program is restarted, so beware. Syntax = TBD
|
||||
- allow memory-mapped structs? Something like &Sprite sprite0 = $9000 basically behaves identically to a typed pointer, but the address is immutable as usual
|
||||
- existing STR and ARRAY remain unchanged (don't become typed pointers) so we can keep doing register-indexed addressing directly on them
|
||||
- rather than str or uword parameter types for routines with a string argument, use ^^str (or ^^ubyte maybe? these are more or less identical..?)
|
||||
- same for arrays? pointer-to-array syntax = TBD
|
||||
- pointer-to-array syntax = TBD
|
||||
- what about pointers to subroutines? should these be typed as well now?
|
||||
- asm symbol name prefixing should work for dereferences too.
|
||||
- pointer arithmetic is a pain, but need to follow C? ptr=ptr+10 adds 10*sizeof() instead of just 10.
|
||||
- allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
struct Node {
|
||||
bool flag
|
||||
}
|
||||
|
||||
^^Node ptr = 2000
|
||||
|
||||
txt.print_uw(ptr)
|
||||
txt.nl()
|
||||
|
||||
bool derp = ptr[10].flag
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
main {
|
||||
|
||||
struct Enemy {
|
||||
@@ -109,3 +125,4 @@ main {
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user