diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index c9cc9f0d0..aee9dc96a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -357,6 +357,15 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } } + + if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) { + if(assignOptimizedComparisonBytes(expr, assign)) + return true + } else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { + if(assignOptimizedComparisonWords(expr, assign)) + return true + } + val origTarget = assign.target.origAstTarget if(origTarget!=null) { assignConstantByte(assign.target, 0) @@ -381,13 +390,19 @@ internal class AssignmentAsmGen(private val program: PtProgram, if(expr.type !in IntegerDatatypes) return false - fun simpleLogicalBytesExpr() { - // both left and right expression operands are simple. - if (expr.right is PtNumber || expr.right is PtIdentifier) - assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right) - else if (expr.left is PtNumber || expr.left is PtIdentifier) - assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left) - else { + if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) { + if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) { + if (expr.right.isSimple()) { + if (expr.right is PtNumber || expr.right is PtIdentifier) { + assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right) + return true + } + else if (expr.left is PtNumber || expr.left is PtIdentifier) { + assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left) + return true + } + } + assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.saveRegisterStack(CpuRegister.A, false) assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE) @@ -399,42 +414,28 @@ internal class AssignmentAsmGen(private val program: PtProgram, else -> throw AssemblyError("invalid operator") } assignRegisterByte(assign.target, CpuRegister.A, false) + return true } - } - - fun simpleLogicalWordsExpr() { - // both left and right expression operands are simple. - if (expr.right is PtNumber || expr.right is PtIdentifier) - assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right) - else if (expr.left is PtNumber || expr.left is PtIdentifier) - assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left) - else { - assignExpressionToRegister(expr.left, RegisterOrPair.AY, false) - asmgen.saveRegisterStack(CpuRegister.A, false) - asmgen.saveRegisterStack(CpuRegister.Y, false) - assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD) + else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { + if (expr.right.isSimple()) { + if (expr.right is PtNumber || expr.right is PtIdentifier) { + assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right) + return true + } + else if (expr.left is PtNumber || expr.left is PtIdentifier) { + assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left) + return true + } + } + assignExpressionWordOperandsLeftAYRightScratchW1(expr) when (expr.operator) { - "&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1") - "|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1") - "^", "xor" -> asmgen.out(" pla | eor P8ZP_SCRATCH_W1+1 | tay | pla | eor P8ZP_SCRATCH_W1") + "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | pha | tya | and P8ZP_SCRATCH_W1+1 | tay | pla") + "|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | pha | tya | ora P8ZP_SCRATCH_W1+1 | tay | pla") + "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | pha | tya | eor P8ZP_SCRATCH_W1+1 | tay | pla") else -> throw AssemblyError("invalid operator") } assignRegisterpairWord(assign.target, RegisterOrPair.AY) - } - } - - if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) { - if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) { - if (expr.right.isSimple()) { - simpleLogicalBytesExpr() - return true - } - } - if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { - if (expr.right.isSimple()) { - simpleLogicalWordsExpr() - return true - } + return true } return false } @@ -468,12 +469,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, return true } else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes && expr.left.isSimple() && expr.right.isSimple()) { - assignExpressionToRegister(expr.left, RegisterOrPair.AY, false) - asmgen.saveRegisterStack(CpuRegister.A, false) - asmgen.saveRegisterStack(CpuRegister.Y, false) - assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD) - asmgen.restoreRegisterStack(CpuRegister.Y, false) - asmgen.restoreRegisterStack(CpuRegister.A, false) + assignExpressionWordOperandsLeftAYRightScratchW1(expr) if(expr.operator=="==") { asmgen.out(""" cmp P8ZP_SCRATCH_W1 @@ -525,9 +521,44 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes) return true } - else -> return false + else -> { + assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE) + asmgen.out(" pha") + assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type) + asmgen.out(" pla") + if(expr.operator=="+") + asmgen.out(" clc | adc P8ZP_SCRATCH_B1") + else + asmgen.out(" sec | sbc P8ZP_SCRATCH_B1") + assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes) + return true + } } } else if(dt in WordDatatypes) { + + fun doAddOrSubWordExpr() { + assignExpressionWordOperandsLeftAYRightScratchW1(expr) + if(expr.operator=="+") + asmgen.out(""" + clc + adc P8ZP_SCRATCH_W1 + pha + tya + adc P8ZP_SCRATCH_W1+1 + tay + pla""") + else + asmgen.out(""" + sec + sbc P8ZP_SCRATCH_W1 + pha + tya + sbc P8ZP_SCRATCH_W1+1 + tay + pla""") + assignRegisterpairWord(assign.target, RegisterOrPair.AY) + } + when (right) { is PtAddressOf -> { assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) @@ -603,30 +634,37 @@ internal class AssignmentAsmGen(private val program: PtProgram, } is PtTypeCast -> { val castedValue = right.value - if(right.type in WordDatatypes && castedValue.type in ByteDatatypes) { - if(castedValue is PtIdentifier) { - val castedSymname = asmgen.asmVariableName(castedValue) - assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD) - if(expr.operator=="+") - asmgen.out(""" - clc - adc $castedSymname - bcc + - iny - +""") - else - asmgen.out(""" - sec - sbc $castedSymname - bcs + - dey - +""") - assignRegisterpairWord(assign.target, RegisterOrPair.AY) - return true - } + if(right.type in WordDatatypes && castedValue.type in ByteDatatypes && castedValue is PtIdentifier) { + val castedSymname = asmgen.asmVariableName(castedValue) + assignExpressionToRegister(left, RegisterOrPair.AY, dt == DataType.WORD) + if (expr.operator == "+") + asmgen.out( + """ + clc + adc $castedSymname + bcc + + iny ++""" + ) + else + asmgen.out( + """ + sec + sbc $castedSymname + bcs + + dey ++""" + ) + assignRegisterpairWord(assign.target, RegisterOrPair.AY) + return true } + doAddOrSubWordExpr() + return true + } + else -> { + doAddOrSubWordExpr() + return true } - else -> return false } } } @@ -682,9 +720,396 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } } + else if(expr.operator=="*") { + val value = expr.right.asConstInteger() + if(value==null) { + when(expr.type) { + in ByteDatatypes -> { + assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes) + asmgen.out(" pha") + assignExpressionToRegister(expr.right, RegisterOrPair.Y, expr.type in SignedDatatypes) + asmgen.out(" pla | jsr math.multiply_bytes") + assignRegisterByte(assign.target, CpuRegister.A, false) + return true + } + in WordDatatypes -> { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(""" + jsr math.multiply_words + lda math.multiply_words.result + ldy math.multiply_words.result+1""") + assignRegisterpairWord(assign.target, RegisterOrPair.AY) + return true + } + else -> return false + } + } else { + when (expr.type) { + in ByteDatatypes -> { + assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes) + if (value in asmgen.optimizedByteMultiplications) + asmgen.out(" jsr math.mul_byte_${value}") + else + asmgen.out(" ldy #$value | jsr math.multiply_bytes") + assignRegisterByte(assign.target, CpuRegister.A, false) + return true + } + in WordDatatypes -> { + assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type in SignedDatatypes) + if (value in asmgen.optimizedWordMultiplications) + asmgen.out(" jsr math.mul_word_${value}") + else + asmgen.out(""" + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #<$value + ldy #>$value + jsr math.multiply_words + lda math.multiply_words.result + ldy math.multiply_words.result+1""") + assignRegisterpairWord(assign.target, RegisterOrPair.AY) + return true + } + else -> return false + } + } + } + else if(expr.operator=="/") { + when(expr.type) { + DataType.UBYTE -> { + assignExpressionToRegister(expr.left, RegisterOrPair.A, false) + asmgen.out(" pha") + assignExpressionToRegister(expr.right, RegisterOrPair.Y, false) + asmgen.out(" pla | jsr math.divmod_ub_asm") + assignRegisterByte(assign.target, CpuRegister.Y, false) + return true + } + DataType.BYTE -> { + assignExpressionToRegister(expr.left, RegisterOrPair.A, true) + asmgen.out(" pha") + assignExpressionToRegister(expr.right, RegisterOrPair.Y, true) + asmgen.out(" pla | jsr math.divmod_b_asm") + assignRegisterByte(assign.target, CpuRegister.Y, true) + return true + } + DataType.UWORD -> { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(" jsr math.divmod_uw_asm") + assignRegisterpairWord(assign.target, RegisterOrPair.AY) + return true + } + DataType.WORD -> { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(" jsr math.divmod_w_asm") + assignRegisterpairWord(assign.target, RegisterOrPair.AY) + return true + } + else -> return false + } + } + else if(expr.operator=="%") { + when(expr.type) { + DataType.UBYTE -> { + assignExpressionToRegister(expr.left, RegisterOrPair.A, false) + asmgen.out(" pha") + assignExpressionToRegister(expr.right, RegisterOrPair.Y, false) + asmgen.out(" pla | jsr math.divmod_ub_asm") + if(assign.target.register==RegisterOrPair.A) + asmgen.out(" cmp #0") // fix the status register + else + assignRegisterByte(assign.target, CpuRegister.A, false) + return true + } + DataType.UWORD -> { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(" jsr math.divmod_uw_asm") + assignVariableWord(assign.target, "P8ZP_SCRATCH_W2") + return true + } + else -> return false + } + } + return false } + private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { + val signed = expr.left.type == DataType.BYTE || expr.right.type == DataType.BYTE + + fun assignExpressionOperandsLeftScratchRightA() { + if(expr.right.isSimple()) { + assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_B1", expr.left.type) + assignExpressionToRegister(expr.right, RegisterOrPair.A, signed) + } else { + assignExpressionToRegister(expr.right, RegisterOrPair.A, signed) + asmgen.saveRegisterStack(CpuRegister.A, false) + assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_B1", expr.left.type) + asmgen.restoreRegisterStack(CpuRegister.A, false) + } + } + + when(expr.operator) { + "==" -> { + assignExpressionOperandsLeftScratchRightA() + asmgen.out(""" + cmp P8ZP_SCRATCH_B1 + beq + + lda #0 + beq ++ ++ lda #1 ++""") + } + "!=" -> { + assignExpressionOperandsLeftScratchRightA() + asmgen.out(""" + cmp P8ZP_SCRATCH_B1 + bne + + lda #0 + beq ++ ++ lda #1 ++""") + } + "<" -> { + assignExpressionOperandsLeftScratchRightA() + if(signed) + asmgen.out(""" + clc + sbc P8ZP_SCRATCH_B1 + bvc + + eor #$80 ++ bpl + + lda #0 + beq ++ ++ lda #1 ++""") + else + asmgen.out(""" + tay + lda #0 + cpy P8ZP_SCRATCH_B1 + beq + + rol a ++""") + } + "<=" -> { + assignExpressionOperandsLeftScratchRightA() + if(signed) + asmgen.out(""" + sec + sbc P8ZP_SCRATCH_B1 + bvc + + eor #$80 ++ bpl + + lda #0 + beq ++ ++ lda #1 ++""") + else + asmgen.out(""" + cmp P8ZP_SCRATCH_B1 + lda #0 + rol a""") + } + ">" -> { + assignExpressionOperandsLeftScratchRightA() + if(signed) + asmgen.out(""" + sec + sbc P8ZP_SCRATCH_B1 + bvc + + eor #$80 ++ bmi + + lda #0 + beq ++ ++ lda #1 ++""") + else + asmgen.out(""" + cmp P8ZP_SCRATCH_B1 + lda #0 + rol a + eor #1""") + } + ">=" -> { + assignExpressionOperandsLeftScratchRightA() + if(signed) + asmgen.out(""" + clc + sbc P8ZP_SCRATCH_B1 + bvc + + eor #$80 ++ bmi + + lda #0 + beq ++ ++ lda #1 ++""") + else + asmgen.out(""" + cmp P8ZP_SCRATCH_B1 + beq + + bcc + + lda #0 + beq ++ ++ lda #1 ++""") + } + else -> return false + } + + assignRegisterByte(assign.target, CpuRegister.A, signed) + return true + } + + private fun assignOptimizedComparisonWords(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { + val signed = expr.left.type == DataType.WORD || expr.right.type == DataType.WORD + when(expr.operator) { + "==" -> { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(""" + cmp P8ZP_SCRATCH_W1 + bne + + cpy P8ZP_SCRATCH_W1+1 + bne + + lda #1 + bne ++ ++ lda #0 ++""") + } + "!=" -> { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(""" + cmp P8ZP_SCRATCH_W1 + bne + + cpy P8ZP_SCRATCH_W1+1 + bne + + lda #0 + beq ++ ++ lda #1 ++""") + } + "<" -> { + if(signed) { + assignExpressionWordOperandsLeftAYRightScratchW1(expr) + asmgen.out(""" + cmp P8ZP_SCRATCH_W1 + tya + sbc P8ZP_SCRATCH_W1+1 + bvc + + eor #${'$'}80 ++ bpl ++ ++ lda #1 + bne ++ ++ lda #0 ++""") + } + else { + assignExpressionWordOperandsLeftAYRightScratchW1(expr) + asmgen.out(""" + cpy P8ZP_SCRATCH_W1+1 + bcc + + bne ++ + cmp P8ZP_SCRATCH_W1 + bcs ++ ++ lda #1 + bne ++ ++ lda #0 ++""") + } + } + "<=" -> { + if(signed) { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(""" + cmp P8ZP_SCRATCH_W1 + tya + sbc P8ZP_SCRATCH_W1+1 + bvc + + eor #${'$'}80 ++ bmi + + lda #1 + bne ++ ++ lda #0 ++""") + } + else { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(""" + cpy P8ZP_SCRATCH_W1+1 + bcc ++ + bne + + cmp P8ZP_SCRATCH_W1 + bcc ++ ++ lda #1 + bne ++ ++ lda #0 ++""") + } + } + ">" -> { + if(signed) { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(""" + cmp P8ZP_SCRATCH_W1 + tya + sbc P8ZP_SCRATCH_W1+1 + bvc + + eor #${'$'}80 ++ bpl ++ ++ lda #1 + bne ++ ++ lda #0 ++""") + } + else { + assignExpressionWordOperandsLeftScratchW1RightAY(expr) + asmgen.out(""" + cpy P8ZP_SCRATCH_W1+1 + bcc + + bne ++ + cmp P8ZP_SCRATCH_W1 + bcs ++ ++ lda #1 + bne ++ ++ lda #0 ++""") + } + } + ">=" -> { + if(signed) { + assignExpressionWordOperandsLeftAYRightScratchW1(expr) + asmgen.out(""" + cmp P8ZP_SCRATCH_W1 + tya + sbc P8ZP_SCRATCH_W1+1 + bvc + + eor #${'$'}80 ++ bmi + + lda #1 + bne ++ ++ lda #0 ++""") + } + else { + assignExpressionWordOperandsLeftAYRightScratchW1(expr) + asmgen.out(""" + cpy P8ZP_SCRATCH_W1+1 + bcc ++ + bne + + cmp P8ZP_SCRATCH_W1 + bcc ++ ++ lda #1 + bne ++ ++ lda #0 ++""") + } + } + else -> return false + } + + assignRegisterByte(assign.target, CpuRegister.A, signed) + return true + } + private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { assignExpressionToRegister(left, RegisterOrPair.A, false) val operand = when(right) { @@ -853,6 +1278,34 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } + private fun assignExpressionWordOperandsLeftScratchW1RightAY(expr: PtBinaryExpression) { + if(expr.right.isSimple()) { + assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_W1", expr.left.type) + assignExpressionToRegister(expr.right, RegisterOrPair.AY, expr.right.type in SignedDatatypes) + } else { + assignExpressionToRegister(expr.right, RegisterOrPair.AY, expr.right.type in SignedDatatypes) + asmgen.saveRegisterStack(CpuRegister.A, false) + asmgen.saveRegisterStack(CpuRegister.Y, false) + assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_W1", expr.left.type) + asmgen.restoreRegisterStack(CpuRegister.Y, false) + asmgen.restoreRegisterStack(CpuRegister.A, false) + } + } + + private fun assignExpressionWordOperandsLeftAYRightScratchW1(expr: PtBinaryExpression) { + if(expr.left.isSimple()) { + assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", expr.left.type) + assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes) + } else { + assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes) + asmgen.saveRegisterStack(CpuRegister.A, false) + asmgen.saveRegisterStack(CpuRegister.Y, false) + assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", expr.left.type) + asmgen.restoreRegisterStack(CpuRegister.Y, false) + asmgen.restoreRegisterStack(CpuRegister.A, false) + } + } + private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) { when(statusflag) { Statusflag.Pc -> { @@ -2058,14 +2511,24 @@ internal class AssignmentAsmGen(private val program: PtProgram, } internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister, signed: Boolean) { - // we make an exception in the type check for assigning something to a register pair AX, AY or XY - // these will be correctly typecasted from a byte to a word value here - if(target.register !in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) - require(target.datatype in ByteDatatypes) { "assign target must be byte type ${target.position}"} + val assignAsWord = target.datatype in WordDatatypes when(target.kind) { TargetStorageKind.VARIABLE -> { asmgen.out(" st${register.name.lowercase()} ${target.asmVarname}") + if(assignAsWord) { + if(target.datatype in SignedDatatypes) { + if(register!=CpuRegister.A) + asmgen.out(" t${register.name.lowercase()}a") + asmgen.signExtendAYlsb(if(target.datatype in SignedDatatypes) DataType.BYTE else DataType.UBYTE) + asmgen.out(" sty ${target.asmVarname}+1") + } else { + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz ${target.asmVarname}+1") + else + asmgen.out(" lda #0 | sta ${target.asmVarname}+1") + } + } } TargetStorageKind.MEMORY -> { when(register) { @@ -2076,6 +2539,8 @@ internal class AssignmentAsmGen(private val program: PtProgram, storeRegisterAInMemoryAddress(target.memory!!) } TargetStorageKind.ARRAY -> { + if(assignAsWord) + TODO("assign register as word into Array not yet supported") if (target.constArrayIndexValue!=null) { when (register) { CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}") @@ -2237,6 +2702,8 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } TargetStorageKind.STACK -> { + if(assignAsWord) + TODO("assign register as word onto Stack not yet supported") when(register) { CpuRegister.A -> asmgen.out(" sta P8ESTACK_LO,x | dex") CpuRegister.X -> throw AssemblyError("can't use X here") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index e7284e706..006237f2c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -772,13 +772,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, "<" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda $name - cmp $otherName - bcc + lda #0 - beq ++ -+ lda #1 -+ sta $name""") + ldy $name + cpy $otherName + rol a + eor #1 + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html @@ -798,13 +797,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, "<=" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda $otherName - cmp $name - bcs + lda #0 - beq ++ -+ lda #1 -+ sta $name""") + ldy $otherName + cpy $name + rol a + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html asmgen.out(""" @@ -823,13 +820,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, ">" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda $name - cmp $otherName + lda #0 + ldy $name + cpy $otherName beq + - bcs ++ -+ lda #0 - beq ++ -+ lda #1 + rol a + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html @@ -849,13 +844,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, ">=" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda $name - cmp $otherName - bcs + lda #0 - beq ++ -+ lda #1 -+ sta $name""") + ldy $name + cpy $otherName + rol a + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html asmgen.out(""" @@ -963,13 +956,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, "<" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda $name - cmp #$value - bcc + lda #0 - beq ++ -+ lda #1 -+ sta $name""") + ldy $name + cpy #$value + rol a + eor #1 + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html @@ -989,13 +981,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, "<=" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda #$value - cmp $name - bcs + lda #0 - beq ++ -+ lda #1 -+ sta $name""") + ldy #$value + cpy $name + rol a + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html asmgen.out(""" @@ -1014,13 +1004,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, ">" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda $name - cmp #$value + lda #0 + ldy $name + cpy #$value beq + - bcs ++ -+ lda #0 - beq ++ -+ lda #1 + rol a + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html @@ -1040,13 +1028,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, ">=" -> { if(dt==DataType.UBYTE) { asmgen.out(""" - lda $name - cmp #$value - bcs + lda #0 - beq ++ -+ lda #1 -+ sta $name""") + ldy $name + cpy #$value + rol a + sta $name""") } else { // see http://www.6502.org/tutorials/compare_beyond.html asmgen.out(""" diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index 8ce38d3b9..29ad7f341 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -161,7 +161,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express val targetIdent = assignment.target.identifier val targetMemory = assignment.target.memory val targetArray = assignment.target.array - val vmDt = irType(assignment.value.type) + val valueDt = irType(assignment.value.type) + val targetDt = irType(assignment.target.type) val result = mutableListOf() var valueRegister = -1 @@ -169,30 +170,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express val zero = codeGen.isZero(assignment.value) if(!zero) { // calculate the assignment value - if (vmDt == IRDataType.FLOAT) { + if (valueDt == IRDataType.FLOAT) { val tr = expressionEval.translateExpression(assignment.value) valueFpRegister = tr.resultFpReg addToResult(result, tr, -1, valueFpRegister) } else { + val extendByteToWord = if(targetDt != valueDt) { + // usually an error EXCEPT when a byte is assigned to a word. + if(targetDt==IRDataType.WORD && valueDt==IRDataType.BYTE) + true + else + throw AssemblyError("assignment value and target dt mismatch") + } else false if (assignment.value is PtMachineRegister) { valueRegister = (assignment.value as PtMachineRegister).register + if(extendByteToWord) + addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null) } else { val tr = expressionEval.translateExpression(assignment.value) valueRegister = tr.resultReg addToResult(result, tr, valueRegister, -1) + if(extendByteToWord) { + val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT + addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1 = valueRegister), null) + } } } } if(targetIdent!=null) { val instruction = if(zero) { - IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name) + IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name) } else { - if (vmDt == IRDataType.FLOAT) { - IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name) - } + if (targetDt == IRDataType.FLOAT) + IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name) else - IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name) + IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name) } result += IRCodeChunk(null, null).also { it += instruction } return result @@ -214,9 +227,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express if(zero) { // there's no STOREZIX instruction valueRegister = codeGen.registers.nextFree() - code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, immediate = 0) + code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0) } - code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable) + code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable) result += code return result } @@ -225,59 +238,59 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express if(zero) { if(fixedIndex!=null) { val offset = fixedIndex*itemsize - val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") } + val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+$offset") } result += chunk } else { val (code, indexReg) = loadIndexReg(targetArray, itemsize) result += code - result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) } + result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable) } } } else { - if(vmDt== IRDataType.FLOAT) { + if(targetDt== IRDataType.FLOAT) { if(fixedIndex!=null) { val offset = fixedIndex*itemsize - val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, 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, vmDt, 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, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") } + val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") } result += chunk } else { val (code, indexReg) = loadIndexReg(targetArray, itemsize) result += code - result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) } + result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) } } } } return result } else if(targetMemory!=null) { - require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"} + require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"} if(zero) { if(targetMemory.address is PtNumber) { - val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, address = (targetMemory.address as PtNumber).number.toInt()) } + val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) } result += chunk } else { val tr = expressionEval.translateExpression(targetMemory.address) val addressReg = tr.resultReg addToResult(result, tr, tr.resultReg, -1) - result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) } + result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) } } } else { if(targetMemory.address is PtNumber) { - val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) } + val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) } result += chunk } else { val tr = expressionEval.translateExpression(targetMemory.address) val addressReg = tr.resultReg addToResult(result, tr, tr.resultReg, -1) - result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) } + result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) } } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 6c88761e3..b81b34abb 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -24,6 +24,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { private fun optimizeOnlyJoinChunks() { irprog.blocks.asSequence().flatMap { it.children.filterIsInstance() }.forEach { sub -> + joinChunks(sub) removeEmptyChunks(sub) joinChunks(sub) } @@ -32,6 +33,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { private fun peepholeOptimize() { irprog.blocks.asSequence().flatMap { it.children.filterIsInstance() }.forEach { sub -> + joinChunks(sub) removeEmptyChunks(sub) joinChunks(sub) sub.chunks.withIndex().forEach { (index, chunk1) -> @@ -112,7 +114,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { if(sub.chunks.isEmpty()) return - fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { + fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { if(chunk.label!=null) return false if(previous is IRCodeChunk && chunk is IRCodeChunk) { @@ -129,12 +131,39 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { chunks += sub.chunks[0] for(ix in 1 until sub.chunks.size) { val lastChunk = chunks.last() - if(mayJoin(lastChunk, sub.chunks[ix])) { - lastChunk.instructions += sub.chunks[ix].instructions - lastChunk.next = sub.chunks[ix].next + val candidate = sub.chunks[ix] + when(candidate) { + is IRCodeChunk -> { + if(mayJoinCodeChunks(lastChunk, candidate)) { + lastChunk.instructions += candidate.instructions + lastChunk.next = candidate.next + } + else + chunks += candidate + } + is IRInlineAsmChunk -> { + if(candidate.label!=null) + chunks += candidate + else if(lastChunk.isEmpty()) { + val label = lastChunk.label + if(label!=null) + chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next) + else + chunks += candidate + } + } + is IRInlineBinaryChunk -> { + if(candidate.label!=null) + chunks += candidate + else if(lastChunk.isEmpty()) { + val label = lastChunk.label + if(label!=null) + chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next) + else + chunks += candidate + } + } } - else - chunks += sub.chunks[ix] } sub.chunks.clear() sub.chunks += chunks diff --git a/compiler/res/prog8lib/c128/syslib.p8 b/compiler/res/prog8lib/c128/syslib.p8 index 03d0380d3..949c73c36 100644 --- a/compiler/res/prog8lib/c128/syslib.p8 +++ b/compiler/res/prog8lib/c128/syslib.p8 @@ -400,7 +400,7 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) { sta _modified+1 sty _modified+2 lda #0 - adc #0 + rol a sta _use_kernal sei lda #<_irq_handler @@ -491,11 +491,11 @@ asmsub restore_irq() clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) { %asm {{ - sta _modified+1 - sty _modified+2 - lda #0 - adc #0 - sta set_irq._use_kernal + sta _modified+1 + sty _modified+2 + lda #0 + rol a + sta set_irq._use_kernal lda cx16.r0 ldy cx16.r0+1 sei diff --git a/compiler/res/prog8lib/c64/syslib.p8 b/compiler/res/prog8lib/c64/syslib.p8 index cdaa0a958..6796fa634 100644 --- a/compiler/res/prog8lib/c64/syslib.p8 +++ b/compiler/res/prog8lib/c64/syslib.p8 @@ -365,7 +365,7 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) { sta _modified+1 sty _modified+2 lda #0 - adc #0 + rol a sta _use_kernal sei lda #<_irq_handler @@ -456,11 +456,11 @@ asmsub restore_irq() clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) { %asm {{ - sta _modified+1 - sty _modified+2 - lda #0 - adc #0 - sta set_irq._use_kernal + sta _modified+1 + sty _modified+2 + lda #0 + rol a + sta set_irq._use_kernal lda cx16.r0 ldy cx16.r0+1 sei diff --git a/compiler/res/prog8lib/cx16/psg.p8 b/compiler/res/prog8lib/cx16/psg.p8 index 27a55947f..0d5a0c850 100644 --- a/compiler/res/prog8lib/cx16/psg.p8 +++ b/compiler/res/prog8lib/cx16/psg.p8 @@ -22,7 +22,10 @@ psg { ; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE. ; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE. envelope_states[voice_num] = 255 - sys.set_irqd() + %asm {{ + php + sei + }} cx16.r0 = $f9c2 + voice_num * 4 cx16.VERA_CTRL = 0 cx16.VERA_ADDR_L = lsb(cx16.r0) @@ -33,7 +36,9 @@ psg { cx16.VERA_DATA0 = waveform | pulsewidth envelope_volumes[voice_num] = mkword(volume, 0) envelope_maxvolumes[voice_num] = volume - sys.clear_irqd() + %asm {{ + plp + }} } ; sub freq_hz(ubyte voice_num, float hertz) { @@ -48,7 +53,10 @@ psg { ; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation. ; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md) ; Write freq MSB first and then LSB to reduce the chance on clicks - sys.set_irqd() + %asm {{ + php + sei + }} cx16.r0 = $f9c1 + voice_num * 4 cx16.VERA_CTRL = 0 cx16.VERA_ADDR_L = lsb(cx16.r0) @@ -57,7 +65,9 @@ psg { cx16.VERA_DATA0 = msb(vera_freq) cx16.VERA_ADDR_L-- cx16.VERA_DATA0 = lsb(vera_freq) - sys.clear_irqd() + %asm {{ + plp + }} } sub volume(ubyte voice_num, ubyte vol) { @@ -106,12 +116,11 @@ psg { ; you have to call this routine every 1/60th second, for example from your vsync irq handler, ; or just install this routine as the only irq handler if you don't have to do other things there. ; Example: cx16.set_irq(&psg.envelopes_irq, true) - ; NOTE: this routine calls save/restore_vera_context() for you, don't nest this! + ; NOTE: this routine calls save/restore_vera_context() for you, don't nest this or call it yourself! ; cx16.r0 = the volume word (volume scaled by 256) ; cx16.r1L = the voice number ; cx16.r2L = attack value - pushw(cx16.r0) push(cx16.r1L) push(cx16.r2L) diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 51a758b62..2e67e6f51 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -637,6 +637,7 @@ asmsub save_vera_context() clobbers(A) { lda cx16.VERA_CTRL sta _vera_storage+3 eor #1 + sta _vera_storage+7 sta cx16.VERA_CTRL lda cx16.VERA_ADDR_L sta _vera_storage+4 @@ -644,8 +645,6 @@ asmsub save_vera_context() clobbers(A) { sta _vera_storage+5 lda cx16.VERA_ADDR_H sta _vera_storage+6 - lda cx16.VERA_CTRL - sta _vera_storage+7 rts _vera_storage: .byte 0,0,0,0,0,0,0,0 }} @@ -750,21 +749,21 @@ asmsub cleanup_at_exit() { asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) { %asm {{ - sta _modified+1 - sty _modified+2 - lda #0 - adc #0 - sta _use_kernal - sei - lda #<_irq_handler - sta cx16.CINV - lda #>_irq_handler - sta cx16.CINV+1 - lda cx16.VERA_IEN - ora #%00000001 ; enable the vsync irq - sta cx16.VERA_IEN - cli - rts + sta _modified+1 + sty _modified+2 + lda #0 + rol a + sta _use_kernal + sei + lda #<_irq_handler + sta cx16.CINV + lda #>_irq_handler + sta cx16.CINV+1 + lda cx16.VERA_IEN + ora #%00000001 ; enable the vsync irq + sta cx16.VERA_IEN + cli + rts _irq_handler jsr _irq_handler_init _modified jsr $ffff ; modified diff --git a/compiler/res/prog8lib/string.p8 b/compiler/res/prog8lib/string.p8 index 49e170a21..cae6bc6e4 100644 --- a/compiler/res/prog8lib/string.p8 +++ b/compiler/res/prog8lib/string.p8 @@ -292,7 +292,7 @@ str = P8ZP_SCRATCH_W1 sta modify_pattern2+2 jsr _match lda #0 - adc #0 + rol a ldx P8ZP_SCRATCH_REG rts diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 5521f04bb..1cb02ad92 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -140,7 +140,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { private fun writeInlineBytes(chunk: IRInlineBinaryChunk) { xml.writeStartElement("BYTES") chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) } - xml.writeCharacters("\n") chunk.data.withIndex().forEach {(index, byte) -> xml.writeCharacters(byte.toString(16).padStart(2,'0')) if(index and 63 == 63 && index < chunk.data.size-1) diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index dcfe1aff5..c830135d3 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -91,27 +91,27 @@ bger reg1, reg2, address - jump to location in program given by l bgesr reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed) ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned) bles reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (signed) - ( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.) -sz reg1, reg2 - set reg1=1 if reg2==0, otherwise set reg1=0 -snz reg1, reg2 - set reg1=1 if reg2!=0, otherwise set reg1=0 -seq reg1, reg2 - set reg1=1 if reg1 == reg2, otherwise set reg1=0 -sne reg1, reg2 - set reg1=1 if reg1 != reg2, otherwise set reg1=0 -slt reg1, reg2 - set reg1=1 if reg1 < reg2 (unsigned), otherwise set reg1=0 -slts reg1, reg2 - set reg1=1 if reg1 < reg2 (signed), otherwise set reg1=0 -sle reg1, reg2 - set reg1=1 if reg1 <= reg2 (unsigned), otherwise set reg1=0 -sles reg1, reg2 - set reg1=1 if reg1 <= reg2 (signed), otherwise set reg1=0 -sgt reg1, reg2 - set reg1=1 if reg1 > reg2 (unsigned), otherwise set reg1=0 -sgts reg1, reg2 - set reg1=1 if reg1 > reg2 (signed), otherwise set reg1=0 -sge reg1, reg2 - set reg1=1 if reg1 >= reg2 (unsigned), otherwise set reg1=0 -sges reg1, reg2 - set reg1=1 if reg1 >= reg2 (signed), otherwise set reg1=0 +( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.) +sz reg1, reg2 - set reg1=1.b if reg2==0, otherwise set reg1=0.b +snz reg1, reg2 - set reg1=1.b if reg2!=0, otherwise set reg1=0.b +seq reg1, reg2 - set reg1=1.b if reg1 == reg2, otherwise set reg1=0.b +sne reg1, reg2 - set reg1=1.b if reg1 != reg2, otherwise set reg1=0.b +slt reg1, reg2 - set reg1=1.b if reg1 < reg2 (unsigned), otherwise set reg1=0.b +slts reg1, reg2 - set reg1=1.b if reg1 < reg2 (signed), otherwise set reg1=0.b +sle reg1, reg2 - set reg1=1.b if reg1 <= reg2 (unsigned), otherwise set reg1=0.b +sles reg1, reg2 - set reg1=1.b if reg1 <= reg2 (signed), otherwise set reg1=0.b +sgt reg1, reg2 - set reg1=1.b if reg1 > reg2 (unsigned), otherwise set reg1=0.b +sgts reg1, reg2 - set reg1=1.b if reg1 > reg2 (signed), otherwise set reg1=0.b +sge reg1, reg2 - set reg1=1.b if reg1 >= reg2 (unsigned), otherwise set reg1=0.b +sges reg1, reg2 - set reg1=1.b if reg1 >= reg2 (signed), otherwise set reg1=0.b ARITHMETIC ---------- All have type b or w or f. Note: result types are the same as operand types! E.g. byte*byte->byte. -ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (ext.w not yet implemented as we don't have longs yet) exts reg1 - reg1 = signed extension of reg1 (byte to word, or word to long) (note: ext.w is not yet implemented as we don't have longs yet) +ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (ext.w not yet implemented as we don't have longs yet) inc reg1 - reg1 = reg1+1 incm address - memory at address += 1 dec reg1 - reg1 = reg1-1 @@ -216,7 +216,6 @@ msig [b, w] reg1, reg2 - reg1 becomes the most significant by concat [b, w] reg1, reg2 - reg1 = concatenated lsb/lsw of reg1 (as lsb) and lsb/lsw of reg2 (as msb) into word or int (int not yet implemented; requires 32bits regs) push [b, w, f] reg1 - push value in reg1 on the stack pop [b, w, f] reg1 - pop value from stack into reg1 -binarydata - 'instruction' to hold inlined binary data bytes */ enum class Opcode { @@ -374,8 +373,7 @@ enum class Opcode { POP, MSIG, CONCAT, - BREAKPOINT, - BINARYDATA + BREAKPOINT } val OpcodesThatJump = setOf( @@ -653,7 +651,6 @@ val instructionFormats = mutableMapOf( Opcode.CLC to InstructionFormat.from("N"), Opcode.SEC to InstructionFormat.from("N"), Opcode.BREAKPOINT to InstructionFormat.from("N"), - Opcode.BINARYDATA to InstructionFormat.from("N"), ) @@ -667,9 +664,8 @@ data class IRInstruction( val immediate: Int?=null, // 0-$ff or $ffff if word val immediateFp: Float?=null, val address: Int?=null, // 0-$ffff - val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!) - val binaryData: Collection?=null, - var branchTarget: IRCodeChunkBase? = null // will be linked after loading + val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!) + var branchTarget: IRCodeChunkBase? = null // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to. ) { // reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT) // This knowledge is useful in IL assembly optimizers to see how registers are used. @@ -686,10 +682,6 @@ data class IRInstruction( require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"} if(reg1!=null && reg2!=null) require(reg1!=reg2) {"reg1 must not be same as reg2"} // note: this is ok for fpRegs as these are always the same type - require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) { - "binarydata inconsistency" - } - val formats = instructionFormats.getValue(opcode) require (type != null || formats.containsKey(null)) { "missing type" } diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index ee4537f67..f90ad90f0 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -284,7 +284,6 @@ class VirtualMachine(irProgram: IRProgram) { Opcode.BREAKPOINT -> InsBREAKPOINT() Opcode.CLC -> { statusCarry = false; nextPc() } Opcode.SEC -> { statusCarry = true; nextPc() } - Opcode.BINARYDATA -> TODO("BINARYDATA not yet supported in VM") Opcode.LOADCPU -> InsLOADCPU(ins) Opcode.STORECPU -> InsSTORECPU(ins) Opcode.STOREZCPU -> InsSTOREZCPU(ins) diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 14ac964e1..aa9546439 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -46,7 +46,7 @@ class VmProgramLoader { is IRCodeChunk -> programChunks += child is IRInlineAsmChunk -> { val replacement = addAssemblyToProgram(child, programChunks, variableAddresses) - chunkReplacements += replacement + chunkReplacements += Pair(child, replacement) } is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO is IRSubroutine -> { @@ -55,7 +55,7 @@ class VmProgramLoader { when (chunk) { is IRInlineAsmChunk -> { val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses) - chunkReplacements += replacement + chunkReplacements += Pair(chunk, replacement) } is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO is IRCodeChunk -> programChunks += chunk @@ -339,7 +339,7 @@ class VmProgramLoader { asmChunk: IRInlineAsmChunk, chunks: MutableList, symbolAddresses: MutableMap, - ): Pair { + ): IRCodeChunk { if(asmChunk.isIR) { val chunk = IRCodeChunk(asmChunk.label, asmChunk.next) asmChunk.assembly.lineSequence().forEach { @@ -350,7 +350,7 @@ class VmProgramLoader { ) } chunks += chunk - return Pair(asmChunk, chunk) + return chunk } else { throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.label}") } diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index 24ab28a57..ec8956d86 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -70,22 +70,6 @@ class TestVm: FunSpec( { vm.stepCount shouldBe code.instructions.size } - test("vm asmbinary not supported") { - val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) - val block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY) - val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) - val code = IRCodeChunk(startSub.label, null) - code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u)) - code += IRInstruction(Opcode.RETURN) - startSub += code - block += startSub - program.addBlock(block) - val vm = VirtualMachine(program) - shouldThrowWithMessage("An operation is not implemented: BINARYDATA not yet supported in VM") { - vm.run() - } - } - test("asmsub not supported in vm even with IR") { val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)