diff --git a/compiler/src/prog8/ast/base/Base.kt b/compiler/src/prog8/ast/base/Base.kt index 564c39015..d5733d49d 100644 --- a/compiler/src/prog8/ast/base/Base.kt +++ b/compiler/src/prog8/ast/base/Base.kt @@ -35,8 +35,9 @@ enum class DataType { else -> false } - infix fun isAssignableTo(targetTypes: Set) = targetTypes.any { this isAssignableTo it } + infix fun isNotAssignableTo(targetType: DataType) = !this.isAssignableTo(targetType) + infix fun isNotAssignableTo(targetTypes: Set) = !this.isAssignableTo(targetTypes) infix fun largerThan(other: DataType) = when { diff --git a/compiler/src/prog8/ast/expressions/InferredTypes.kt b/compiler/src/prog8/ast/expressions/InferredTypes.kt index 196ddb741..953e63bb1 100644 --- a/compiler/src/prog8/ast/expressions/InferredTypes.kt +++ b/compiler/src/prog8/ast/expressions/InferredTypes.kt @@ -36,9 +36,12 @@ object InferredTypes { override fun hashCode(): Int = Objects.hash(isVoid, datatype) - infix fun isAssignableTo(targetDt: InferredType): Boolean { - return isKnown && targetDt.isKnown && (datatype!! isAssignableTo targetDt.datatype!!) - } + infix fun isAssignableTo(targetDt: InferredType): Boolean = + isKnown && targetDt.isKnown && (datatype!! isAssignableTo targetDt.datatype!!) + infix fun isAssignableTo(targetDt: DataType): Boolean = + isKnown && (datatype!! isAssignableTo targetDt) + infix fun isNotAssignableTo(targetDt: InferredType): Boolean = !this.isAssignableTo(targetDt) + infix fun isNotAssignableTo(targetDt: DataType): Boolean = !this.isAssignableTo(targetDt) } private val unknownInstance = InferredType.unknown() diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 744b7f392..b0cc39c7e 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -348,7 +348,7 @@ internal class AstChecker(private val program: Program, if(!idt.isKnown) { errors.err("return type mismatch", assignment.value.position) } - if(stmt.returntypes.size <= 1 && !(stmt.returntypes.single() isAssignableTo idt.typeOrElse(DataType.BYTE))) { + if(stmt.returntypes.size <= 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE)) { errors.err("return type mismatch", assignment.value.position) } } @@ -593,8 +593,8 @@ internal class AstChecker(private val program: Program, if(declValue!=null && decl.type==VarDeclType.VAR) { if(decl.datatype==DataType.STRUCT) { val valueIdt = declValue.inferType(program) - if(valueIdt.isUnknown) - throw AstException("invalid value type") + if(!valueIdt.isKnown) + throw AstException("unknown dt") val valueDt = valueIdt.typeOrElse(DataType.STRUCT) if(valueDt !in ArrayDatatypes) err("initialisation of struct should be with array value", declValue.position) @@ -769,7 +769,11 @@ internal class AstChecker(private val program: Program, } override fun visit(expr: PrefixExpression) { - val dt = expr.inferType(program).typeOrElse(DataType.STRUCT) + val idt = expr.inferType(program) + if(!idt.isKnown) + return // any error should be reported elsewhere + + val dt = idt.typeOrElse(DataType.STRUCT) if(expr.operator=="-") { if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) { errors.err("can only take negative of a signed number type", expr.position) @@ -1218,7 +1222,7 @@ internal class AstChecker(private val program: Program, for(elt in value.value.zip(struct.statements)) { val vardecl = elt.second as VarDecl val valuetype = elt.first.inferType(program) - if (!valuetype.isKnown || !(valuetype.typeOrElse(DataType.STRUCT) isAssignableTo vardecl.datatype)) { + if (!valuetype.isKnown || valuetype isNotAssignableTo vardecl.datatype) { errors.err("invalid struct member init value type $valuetype, expected ${vardecl.datatype}", elt.first.position) return false } diff --git a/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt index 8765e727d..1fd3d272e 100644 --- a/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt @@ -4,6 +4,7 @@ import prog8.ast.IFunctionCall import prog8.ast.INameScope import prog8.ast.Program import prog8.ast.base.DataType +import prog8.ast.base.FatalAstException import prog8.ast.expressions.Expression import prog8.ast.expressions.FunctionCall import prog8.ast.statements.* @@ -39,7 +40,10 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { } fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? { - val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) } + val argITypes = call.args.map { it.inferType(program) } + if(argITypes.any { !it.isKnown }) + throw FatalAstException("unknown dt") + val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) } val target = call.target.targetStatement(scope) if (target is Subroutine) { if(call.args.size != target.parameters.size) diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 8fae929df..15dc3a9ad 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -187,7 +187,12 @@ open class VarDecl(val type: VarDeclType, fun createAuto(array: ArrayLiteralValue): VarDecl { val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}" - val declaredType = ArrayElementTypes.getValue(array.type.typeOrElse(DataType.STRUCT)) + val arrayDt = + if(!array.type.isKnown) + throw FatalAstException("unknown dt") + else + array.type.typeOrElse(DataType.STRUCT) + val declaredType = ArrayElementTypes.getValue(arrayDt) val arraysize = ArrayIndex.forArray(array) return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array, isArray = true, autogeneratedDontRemove = true, position = array.position) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 62d4281f7..c416e07ce 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -896,8 +896,10 @@ internal class AsmGen(private val program: Program, } else -> { translateExpression(stmt.iterations!!) - val dt = stmt.iterations!!.inferType(program).typeOrElse(DataType.STRUCT) - when (dt) { + val dt = stmt.iterations!!.inferType(program) + if(!dt.isKnown) + throw AssemblyError("unknown dt") + when (dt.typeOrElse(DataType.STRUCT)) { in ByteDatatypes -> { out(" inx | lda P8ESTACK_LO,x") repeatByteCountInA(null, repeatLabel, endLabel, stmt.body) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 5c53d8b3d..f6f17dafd 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -578,7 +578,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) { val arrayVarName1 = asmgen.asmVariableName(first.arrayvar) val arrayVarName2 = asmgen.asmVariableName(second.arrayvar) - val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT) + val elementIDt = first.inferType(program) + if(!elementIDt.isKnown) + throw AssemblyError("unknown dt") + val elementDt = elementIDt.typeOrElse(DataType.STRUCT) val firstNum = first.indexer.indexNum val firstVar = first.indexer.indexVar @@ -612,7 +615,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.translateExpression(first) asmgen.translateExpression(second) - val datatype = first.inferType(program).typeOrElse(DataType.STRUCT) + val idatatype = first.inferType(program) + if(!idatatype.isKnown) + throw AssemblyError("unknown dt") + val datatype = idatatype.typeOrElse(DataType.STRUCT) val assignFirst = AsmAssignment( AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype), targetFromExpr(first, datatype), diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index b694c0704..71d37bd90 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -53,7 +53,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } } - val dt = left.inferType(program).typeOrElse(DataType.STRUCT) + val idt = left.inferType(program) + if(!idt.isKnown) + throw AssemblyError("unknown dt") + val dt = idt.typeOrElse(DataType.STRUCT) when (operator) { "==" -> { // if the left operand is an expression, and the right is 0, we can just evaluate that expression. @@ -1512,7 +1515,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge private fun translateExpression(expr: PrefixExpression) { translateExpression(expr.expression) - val type = expr.inferType(program).typeOrElse(DataType.STRUCT) + val itype = expr.inferType(program) + if(!itype.isKnown) + throw AssemblyError("unknown dt") + val type = itype.typeOrElse(DataType.STRUCT) when(expr.operator) { "+" -> {} "-" -> { @@ -1547,7 +1553,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } private fun translateExpression(arrayExpr: ArrayIndexedExpression) { - val elementDt = arrayExpr.inferType(program).typeOrElse(DataType.STRUCT) + val elementIDt = arrayExpr.inferType(program) + if(!elementIDt.isKnown) + throw AssemblyError("unknown dt") + val elementDt = elementIDt.typeOrElse(DataType.STRUCT) val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar) if(arrayExpr.indexer.indexNum!=null) { val indexValue = arrayExpr.indexer.constIndex()!! * elementDt.memorySize() diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt index 7156cce00..2c872f7bf 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt @@ -18,7 +18,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen: internal fun translate(stmt: ForLoop) { val iterableDt = stmt.iterable.inferType(program) if(!iterableDt.isKnown) - throw AssemblyError("can't determine iterable dt") + throw AssemblyError("unknown dt") when(stmt.iterable) { is RangeExpr -> { val range = (stmt.iterable as RangeExpr).toConstantIntegerRange() diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index 38eb28886..e52d838bd 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -159,7 +159,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg // pass parameter via a regular variable (not via registers) val valueIDt = value.inferType(program) if(!valueIDt.isKnown) - throw AssemblyError("arg type unknown") + throw AssemblyError("unknown dt") val valueDt = valueIDt.typeOrElse(DataType.STRUCT) if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) throw AssemblyError("argument type incompatible") @@ -175,7 +175,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg // pass argument via a register parameter val valueIDt = value.inferType(program) if(!valueIDt.isKnown) - throw AssemblyError("arg type unknown") + throw AssemblyError("unknown dt") val valueDt = valueIDt.typeOrElse(DataType.STRUCT) if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) throw AssemblyError("argument type incompatible") diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt index 9563624d0..9917b464c 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt @@ -55,7 +55,10 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, companion object { fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) { - val dt = inferType(program, assign).typeOrElse(DataType.STRUCT) + val idt = inferType(program, assign) + if(!idt.isKnown) + throw AssemblyError("unknown dt") + val dt = idt.typeOrElse(DataType.STRUCT) when { identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this) @@ -127,8 +130,10 @@ internal class AsmAssignSource(val kind: SourceStorageKind, AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) } is BuiltinFunctionStatementPlaceholder -> { - val returnType = value.inferType(program).typeOrElse(DataType.STRUCT) - AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) + val returnType = value.inferType(program) + if(!returnType.isKnown) + throw AssemblyError("unknown dt") + AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.typeOrElse(DataType.STRUCT), expression = value) } else -> { throw AssemblyError("weird call") @@ -136,8 +141,10 @@ internal class AsmAssignSource(val kind: SourceStorageKind, } } else -> { - val dt = value.inferType(program).typeOrElse(DataType.STRUCT) - AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value) + val dt = value.inferType(program) + if(!dt.isKnown) + throw AssemblyError("unknown dt") + AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.typeOrElse(DataType.STRUCT), expression = value) } } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt index c3595f58a..28ecfbe05 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -150,8 +150,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen val signature = BuiltinFunctions.getValue(sub.name) asmgen.translateBuiltinFunctionCallExpression(value, signature, false) val returntype = builtinFunctionReturnType(sub.name, value.args, program) - if(returntype.isUnknown) - throw AssemblyError("weird result type") + if(!returntype.isKnown) + throw AssemblyError("unknown dt") when(returntype.typeOrElse(DataType.STRUCT)) { in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY @@ -184,7 +184,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) { - val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT) + val valueIDt = value.inferType(program) + if(!valueIDt.isKnown) + throw AssemblyError("unknown dt") + val valueDt = valueIDt.typeOrElse(DataType.STRUCT) when(value) { is IdentifierReference -> { if(targetDt in WordDatatypes) { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt index aac13bc65..43a984718 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt @@ -22,7 +22,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, when (val value = assign.source.expression!!) { is PrefixExpression -> { // A = -A , A = +A, A = ~A, A = not A - val type = value.inferType(program).typeOrElse(DataType.STRUCT) + val itype = value.inferType(program) + if(!itype.isKnown) + throw AssemblyError("unknown dt") + val type = itype.typeOrElse(DataType.STRUCT) when (value.operator) { "+" -> {} "-" -> inplaceNegate(assign.target, type) @@ -210,7 +213,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, private fun tryRemoveRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean { if (target.datatype == value.type) { - val childDt = value.expression.inferType(program).typeOrElse(DataType.STRUCT) + val childIDt = value.expression.inferType(program) + if(!childIDt.isKnown) + throw AssemblyError("unknown dt") + val childDt = childIDt.typeOrElse(DataType.STRUCT) if (value.type.equalsSize(childDt) || value.type.largerThan(childDt)) { // this typecast is redundant here; the rest of the code knows how to deal with the uncasted value. inplaceModification(target, operator, value.expression) @@ -1096,7 +1102,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, if(asmgen.options.slowCodegenWarnings) println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO asmgen.translateExpression(value) - val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT) + + val valueiDt = value.inferType(program) + if(!valueiDt.isKnown) + throw AssemblyError("unknown dt") + val valueDt = valueiDt.typeOrElse(DataType.STRUCT) fun multiplyWord() { asmgen.out(""" diff --git a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt index 6572150e0..13ea4345e 100644 --- a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt @@ -477,7 +477,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker() when (expr.operator) { "%" -> { if (cv == 1.0) { - return NumericLiteralValue(expr.inferType(program).typeOrElse(DataType.STRUCT), 0, expr.position) + val idt = expr.inferType(program) + if(!idt.isKnown) + throw FatalAstException("unknown dt") + return NumericLiteralValue(idt.typeOrElse(DataType.STRUCT), 0, expr.position) } else if (cv == 2.0) { expr.operator = "&" expr.right = NumericLiteralValue.optimalInteger(1, expr.position) @@ -606,8 +609,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker() if (amount == 0) { return expr.left } - val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT) - when (targetDt) { + val targetIDt = expr.left.inferType(program) + if(!targetIDt.isKnown) + throw FatalAstException("unknown dt") + when (val targetDt = targetIDt.typeOrElse(DataType.STRUCT)) { DataType.UBYTE, DataType.BYTE -> { if (amount >= 8) { return NumericLiteralValue(targetDt, 0, expr.position) @@ -639,7 +644,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker() if (amount == 0) { return expr.left } - when (expr.left.inferType(program).typeOrElse(DataType.STRUCT)) { + val idt = expr.left.inferType(program) + if(!idt.isKnown) + throw FatalAstException("unknown dt") + when (idt.typeOrElse(DataType.STRUCT)) { DataType.UBYTE -> { if (amount >= 8) { return NumericLiteralValue.optimalInteger(0, expr.position) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 9a86282c7..d67a13205 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,16 +2,15 @@ TODO ==== -- get rid of all the .typeOrElse(STRUCT) 'shortcuts' and replace them with proper error handling - make memset(w) and memcopy able to work with >256 bytes -- make memset and memcopy use the ROM routines on the CX16 +- after that: make memset and memcopy use the ROM routines on the CX16 - calling convention for builtin functions no longer via stack but via statically allocated vars inside the subroutine proc (just as normal subroutines) - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_' - option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging) - see if we can group some errors together for instance the (now single) errors about unidentified symbols - use VIC banking to move up the graphics bitmap memory location. Don't move it under the ROM though as that would require IRQ disabling and memory bank swapping for every bitmap manipulation -- add some primitives/subroutines/examples for using custom char sets, copying the default charset. -- some better handling of recursive subroutines? via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters +- add a c-64 example for using custom char sets, copying (part of) the default charset. +- some support for recursive subroutines? via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters - get rid of all other TODO's in the code ;-) More optimizations @@ -21,8 +20,7 @@ Add more compiler optimizations to the existing ones. - further optimize assignment codegeneration, such as the following: - binexpr splitting (beware self-referencing expressions and asm code ballooning though) -- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise. Especially for built-in functions! -- can such parameter passing to subroutines be optimized to avoid copying? +- detect var->var argument passing to subroutines and avoid the second variable and copying of the value - more optimizations on the language AST level - more optimizations on the final assembly source level - note: subroutine inlining is abandoned because of problems referencing non-local stuff. Can't move everything around. diff --git a/examples/cx16/cobramk3-gfx.p8 b/examples/cx16/cobramk3-gfx.p8 index c8ffc3bd7..03a58108b 100644 --- a/examples/cx16/cobramk3-gfx.p8 +++ b/examples/cx16/cobramk3-gfx.p8 @@ -191,6 +191,7 @@ main { sub facing_away(ubyte edgePointsIdx) -> ubyte { ; simplistic visibility determination by checking the Z component of the surface normal + ; TODO: actually take the line of sight vector into account ubyte p1 = shipdata.facesPoints[edgePointsIdx] edgePointsIdx++ ubyte p2 = shipdata.facesPoints[edgePointsIdx]