diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 26154c310..77f0cb2d2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -1940,7 +1940,10 @@ internal class AssignmentAsmGen(private val program: PtProgram, asmgen.out(" jsr prog8_lib.containment_bytearray") } DataType.ARRAY_F -> { - TODO("containment check of floats") + assignExpressionToRegister(containment.element, RegisterOrPair.FAC1, true) + assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null) + asmgen.out(" ldy #$numElements") + asmgen.out(" jsr floats.containment_floatarray") } DataType.ARRAY_B, DataType.ARRAY_UB -> { assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 8fc13ac0e..78847942e 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -565,7 +565,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { return exprGen.translateExpression(call.args.single()) // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. - // TODO to be more strict, maybe we *should* introduce a new result register that is of type .b? + // ....To be more strict, maybe we should introduce a new result register that is of type .b? } private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 5ec7d57b5..c0e59860d 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -154,7 +154,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1) } - DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported") + DataType.ARRAY_F -> { + addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) + val elementTr = translateExpression(check.element) + addToResult(result, elementTr, -1, elementTr.resultFpReg) + val iterableTr = translateExpression(check.iterable) + addToResult(result, iterableTr, iterableTr.resultReg, -1) + val lengthReg = codeGen.registers.nextFree() + val resultReg = codeGen.registers.nextFree() + val iterableLength = codeGen.symbolTable.getLength(check.iterable.name) + addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null) + result += codeGen.makeSyscall(IMSyscall.FLOATARRAY_CONTAINS, listOf(IRDataType.FLOAT to elementTr.resultFpReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg) + return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) + } else -> throw AssemblyError("weird iterable dt ${check.iterable.type} for ${check.iterable.name}") } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index a61c553cb..38d4ac7ca 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1709,7 +1709,7 @@ class IRCodeGen( is PtLabel -> { irBlock += IRCodeChunk(child.name, null) } - else -> TODO("weird child node $child") + else -> TODO("weird block child node $child") } } return irBlock diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 907afa8c5..c34de2332 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -419,7 +419,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { // TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory) // TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory) // TODO: detect multiple float ffrom/fto to the same target, only keep first - // TODO: detect multiple sequential rnd with same reg1, only keep one // TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out // TODO: detect multiple same ands, ors; only keep first // TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt index 8d97d03b9..252871291 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt @@ -221,7 +221,7 @@ class IRUnusedCodeRemover( } // make sure that chunks that are only used as a prefix of a label, are also marked as linked - linkedChunks.forEach { chunk -> + linkedChunks.toList().forEach { chunk -> chunk.instructions.forEach { if(it.labelSymbol!=null) { val chunkName = it.labelSymbol!!.substringBeforeLast('.') diff --git a/compiler/res/prog8lib/c64/floats_funcs.asm b/compiler/res/prog8lib/c64/floats_funcs.asm index a06747e77..00ca7bcf3 100644 --- a/compiler/res/prog8lib/c64/floats_funcs.asm +++ b/compiler/res/prog8lib/c64/floats_funcs.asm @@ -147,3 +147,54 @@ func_sqrt_into_FAC1 .proc jsr MOVFM jmp SQR .pend + + + +containment_floatarray .proc + ; -- check if a value exists in a float array. + ; parameters: FAC1: value to check, P8ZP_SCRATCH_W1: address of the word array, Y = length of array (>=1). + ; returns boolean 0/1 in A. + sty P8ZP_SCRATCH_REG + ldx #floats.floats_temp_var + jsr floats.MOVMF + ldx P8ZP_SCRATCH_REG + ldy #0 +- lda floats.floats_temp_var + cmp (P8ZP_SCRATCH_W1),y + bne _firstmiss + iny + lda floats.floats_temp_var+1 + cmp (P8ZP_SCRATCH_W1),y + bne _secondmiss + iny + lda floats.floats_temp_var+2 + cmp (P8ZP_SCRATCH_W1),y + bne _thirdmiss + iny + lda floats.floats_temp_var+3 + cmp (P8ZP_SCRATCH_W1),y + bne _fourthmiss + iny + lda floats.floats_temp_var+4 + cmp (P8ZP_SCRATCH_W1),y + bne _fifthmiss + lda #1 + rts + +_firstmiss + iny +_secondmiss + iny +_thirdmiss + iny +_fourthmiss + iny +_fifthmiss + iny + dex + bne - + lda #0 + rts + + .pend diff --git a/compiler/res/prog8lib/cx16/gfx2.p8 b/compiler/res/prog8lib/cx16/gfx2.p8 index ae7bfab30..f275e11ac 100644 --- a/compiler/res/prog8lib/cx16/gfx2.p8 +++ b/compiler/res/prog8lib/cx16/gfx2.p8 @@ -708,7 +708,7 @@ gfx2 { while cx16.r12L { pop_stack() xx = x1 - ; TODO: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it? + ; possible speed optimization: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it? while xx >= 0 { if pget(xx as uword, yy as uword) != cx16.r11L break @@ -726,7 +726,7 @@ gfx2 { do { cx16.r9 = xx - ; TODO: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it? + ; possible speed optimization: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it? while xx <= width-1 { if pget(xx as uword, yy as uword) != cx16.r11L break diff --git a/compiler/res/prog8lib/virtual/monogfx.p8 b/compiler/res/prog8lib/virtual/monogfx.p8 index 2a6d12e03..c1faa75fc 100644 --- a/compiler/res/prog8lib/virtual/monogfx.p8 +++ b/compiler/res/prog8lib/virtual/monogfx.p8 @@ -454,12 +454,12 @@ skip: ; -- select the text charset to use with the text() routine ; the charset number is the same as for the cx16.screen_set_charset() ROM function. ; 1 = ISO charset, 2 = PETSCII uppercase+graphs, 3= PETSCII uppercase+lowercase. - ; TODO vm has no bitmap charset + ; TODO vm bitmap charset } sub text(uword @zp xx, uword yy, bool draw, uword sctextptr) { ; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!). ; You must also have called text_charset() first to select and prepare the character set to use. - ; TODO vm has no bitmap charset + ; TODO vm bitmap charset } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index b93540988..8869348ee 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,10 @@ TODO ==== +split words sort and reverse +split words rol and ror and rol2/ror2 +split words any and all + Mark had a compiler crash FatalAstException: invalid dt ... @@ -33,6 +37,7 @@ Compiler: - ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype) global initialization values are simply a list of LOAD instructions. Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings. +- ir: fix call() return value handling - ir: add more optimizations in IRPeepholeOptimizer - ir: the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line. maybe another representation is needed once actual codegeneration is done from the IR...? @@ -59,7 +64,7 @@ Optimizations: for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest - various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code, those checks should probably be removed, or be made permanent - +- optimizeCommonSubExpressions: currently only looks in expressions on a single line, could search across multiple expressions STRUCTS again? -------------- diff --git a/examples/test.p8 b/examples/test.p8 index d793f54b7..85f90a601 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,12 +1,13 @@ %import textio -%import sprites +%import floats %zeropage basicsafe %option no_sysinit main { sub start() { + float[] fa = [1.1, 2.2, 3.3] + float @shared fl = 2.2 - cx16.mouse_config2(1) - sprites.set_mousepointer_hand() + txt.print_ub(fl in fa) } } diff --git a/intermediate/src/prog8/intermediate/IMSyscall.kt b/intermediate/src/prog8/intermediate/IMSyscall.kt index b738f90c8..fa3da2640 100644 --- a/intermediate/src/prog8/intermediate/IMSyscall.kt +++ b/intermediate/src/prog8/intermediate/IMSyscall.kt @@ -22,10 +22,11 @@ enum class IMSyscall(val number: Int) { STRING_CONTAINS(0x100e), BYTEARRAY_CONTAINS(0x100f), WORDARRAY_CONTAINS(0x1010), - CLAMP_UBYTE(0x1011), - CLAMP_BYTE(0x1012), - CLAMP_UWORD(0x1013), - CLAMP_WORD(0x1014), - CLAMP_FLOAT(0x1015), - CALLFAR(0x1016), + FLOATARRAY_CONTAINS(0x1011), + CLAMP_UBYTE(0x1012), + CLAMP_BYTE(0x1013), + CLAMP_UWORD(0x1014), + CLAMP_WORD(0x1015), + CLAMP_FLOAT(0x1016), + CALLFAR(0x1017), } diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index e71afa77c..447dfa058 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -55,6 +55,7 @@ SYSCALLS: 45 = str to float 46 = MUL16_LAST_UPPER 47 = float to str +48 = FLOATARRAY_CONTAINS */ enum class Syscall { @@ -105,7 +106,8 @@ enum class Syscall { ATAN, STR_TO_FLOAT, MUL16_LAST_UPPER, - FLOAT_TO_STR + FLOAT_TO_STR, + FLOATARRAY_CONTAINS, ; companion object { @@ -456,6 +458,18 @@ object SysCalls { } returnValue(callspec.returns!!, 0u, vm) } + Syscall.FLOATARRAY_CONTAINS -> { + val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm) + var length = lengthV as UByte + var array = (arrayV as UShort).toInt() + while(length>0u) { + if(vm.memory.getFloat(array)==value) + return returnValue(callspec.returns!!, 1u, vm) + array += vm.machinedef.FLOAT_MEM_SIZE + length-- + } + returnValue(callspec.returns!!, 0u, vm) + } Syscall.CLAMP_BYTE -> { val (valueU, minimumU, maximumU) = getArgValues(callspec.arguments, vm) val value = (valueU as UByte).toByte().toInt() diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 5aa275aed..ff973b8ce 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -2,6 +2,7 @@ package prog8.vm import prog8.code.core.toHex import prog8.code.target.virtual.IVirtualMachineRunner +import prog8.code.target.virtual.VirtualMachineDefinition import prog8.intermediate.* import java.awt.Color import java.awt.Toolkit @@ -34,6 +35,7 @@ class BreakpointException(val pcChunk: IRCodeChunk, val pcIndex: Int): Exception class VirtualMachine(irProgram: IRProgram) { class CallSiteContext(val returnChunk: IRCodeChunk, val returnIndex: Int, val fcallSpec: FunctionCallArgs) val memory = Memory() + val machinedef = VirtualMachineDefinition() val program: List val registers = Registers() val callStack = Stack() diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 9f9feeec3..f2a3f053c 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -119,6 +119,7 @@ class VmProgramLoader { IMSyscall.STRING_CONTAINS.number -> Syscall.STRING_CONTAINS IMSyscall.BYTEARRAY_CONTAINS.number -> Syscall.BYTEARRAY_CONTAINS IMSyscall.WORDARRAY_CONTAINS.number -> Syscall.WORDARRAY_CONTAINS + IMSyscall.FLOATARRAY_CONTAINS.number -> Syscall.FLOATARRAY_CONTAINS IMSyscall.CLAMP_BYTE.number -> Syscall.CLAMP_BYTE IMSyscall.CLAMP_UBYTE.number -> Syscall.CLAMP_UBYTE IMSyscall.CLAMP_WORD.number -> Syscall.CLAMP_WORD