mirror of
https://github.com/irmen/prog8.git
synced 2024-12-22 18:30:01 +00:00
Merge branch 'master' into remove_evalstack
# Conflicts: # codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt # codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt # compiler/src/prog8/buildversion/BuildVersion.kt # examples/test.p8
This commit is contained in:
commit
9f247901d4
@ -1025,9 +1025,27 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tay | pla | cpy #0")
|
||||
asmgen.out(" tay | cpy #0")
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" ldy #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
|
||||
asmgen.out(" ldx #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
|
||||
asmgen.out(" ldy #0 | cpx #0")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister)
|
||||
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
zero.parent=fcall
|
||||
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
|
||||
asmgen.out(" lda cx16.r0L")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
|
@ -43,12 +43,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
val variable = assign.source.asmVarname
|
||||
when (assign.target.datatype) {
|
||||
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
|
||||
DataType.WORD -> assignVariableWord(assign.target, variable)
|
||||
DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
DataType.UWORD -> {
|
||||
if(assign.source.datatype in PassByReferenceDatatypes)
|
||||
assignAddressOf(assign.target, variable)
|
||||
else
|
||||
assignVariableWord(assign.target, variable)
|
||||
assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
}
|
||||
DataType.FLOAT -> assignVariableFloat(assign.target, variable)
|
||||
DataType.STR -> assignVariableString(assign.target, variable)
|
||||
@ -277,20 +277,45 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtPrefix -> {
|
||||
if(assign.target.array==null) {
|
||||
// First assign the value to the target then apply the operator in place on the target.
|
||||
// This saves a temporary variable
|
||||
translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value.value, program, asmgen),
|
||||
assign.target, program.memsizer, assign.position
|
||||
), scope
|
||||
)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> inplaceNegate(assign, true, scope)
|
||||
"~" -> inplaceInvert(assign, scope)
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
if(assign.source.datatype==assign.target.datatype) {
|
||||
// First assign the value to the target then apply the operator in place on the target.
|
||||
// This saves a temporary variable
|
||||
translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value.value, program, asmgen),
|
||||
assign.target, program.memsizer, assign.position
|
||||
), scope
|
||||
)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> inplaceNegate(assign, true, scope)
|
||||
"~" -> inplaceInvert(assign, scope)
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
} else {
|
||||
// use a temporary variable
|
||||
val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
|
||||
assignExpressionToVariable(value.value, tempvar, value.type)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-", "~" -> {
|
||||
val assignTempvar = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar),
|
||||
program.memsizer, assign.position)
|
||||
if(value.operator=="-")
|
||||
inplaceNegate(assignTempvar, true, scope)
|
||||
else
|
||||
inplaceInvert(assignTempvar, scope)
|
||||
}
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
if(value.type in ByteDatatypes)
|
||||
assignVariableByte(assign.target, tempvar)
|
||||
else
|
||||
assignVariableWord(assign.target, tempvar, value.type)
|
||||
}
|
||||
} else {
|
||||
assignPrefixedExpressionToArrayElt(assign, scope)
|
||||
@ -325,7 +350,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.translateNormalAssignment(assignToTempvar, scope)
|
||||
when(assign.target.datatype) {
|
||||
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
|
||||
in WordDatatypes -> assignVariableWord(assign.target, tempvar)
|
||||
in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype)
|
||||
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@ -422,7 +447,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" jsr math.divmod_uw_asm")
|
||||
assignVariableWord(target, "P8ZP_SCRATCH_W2")
|
||||
assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
@ -1932,19 +1957,29 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) {
|
||||
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String, sourceDt: DataType) {
|
||||
require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE)
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
ldy $sourceName+1
|
||||
sta ${target.asmVarname}
|
||||
sty ${target.asmVarname}+1""")
|
||||
if(sourceDt==DataType.UBYTE) {
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${target.asmVarname}")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${target.asmVarname}")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
ldy $sourceName+1
|
||||
sta ${target.asmVarname}
|
||||
sty ${target.asmVarname}+1""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(sourceDt==DataType.UBYTE) TODO("assign byte to word array")
|
||||
target.array!!
|
||||
if(target.constArrayIndexValue!=null) {
|
||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
|
||||
@ -2020,20 +2055,36 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
when(target.register!!) {
|
||||
RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName")
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(
|
||||
"""
|
||||
lda $sourceName
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda $sourceName+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
if(sourceDt==DataType.UBYTE) {
|
||||
when(target.register!!) {
|
||||
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName")
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" lda $sourceName | sta cx16.${target.register.toString().lowercase()}")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta cx16.${target.register.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("can't load word in a single 8-bit register")
|
||||
}
|
||||
} else {
|
||||
when(target.register!!) {
|
||||
RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName")
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(
|
||||
"""
|
||||
lda $sourceName
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda $sourceName+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't load word in a single 8-bit register")
|
||||
}
|
||||
else -> throw AssemblyError("can't load word in a single 8-bit register")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package prog8tests.codegencpu6502
|
||||
|
||||
import io.kotest.assertions.throwables.shouldNotThrowAny
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
@ -107,9 +109,19 @@ class TestCodegen: FunSpec({
|
||||
test("64tass assembler available? - if this fails you need to install 64tass in the path") {
|
||||
val command = mutableListOf("64tass", "--version")
|
||||
shouldNotThrowAny {
|
||||
val proc = ProcessBuilder(command).inheritIO().start()
|
||||
val proc = ProcessBuilder(command).start()
|
||||
val output = String(proc.inputStream.readBytes())
|
||||
val result = proc.waitFor()
|
||||
result.shouldBe(0)
|
||||
val (_, version) = output.split('V')
|
||||
val (major, minor, _) = version.split('.')
|
||||
val majorNum = major.toInt()
|
||||
val minorNum = minor.toInt()
|
||||
withClue("64tass version should be 1.58 or newer") {
|
||||
majorNum shouldBeGreaterThanOrEqual 1
|
||||
if (majorNum == 1)
|
||||
minorNum shouldBeGreaterThanOrEqual 58
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -396,42 +396,35 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translate(whenStmt: PtWhen): IRCodeChunks {
|
||||
if(whenStmt.choices.children.isEmpty())
|
||||
return emptyList()
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueDt = irType(whenStmt.value.type)
|
||||
val valueTr = expressionEval.translateExpression(whenStmt.value)
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
|
||||
|
||||
val choices = mutableListOf<Pair<String, PtWhenChoice>>()
|
||||
val endLabel = createLabelName()
|
||||
for (choice in choices) {
|
||||
whenStmt.choices.children.forEach {
|
||||
val choice = it as PtWhenChoice
|
||||
if(choice.isElse) {
|
||||
result += translateNode(choice.statements)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
} else {
|
||||
val skipLabel = createLabelName()
|
||||
val values = choice.values.children.map {it as PtNumber}
|
||||
if(values.size==1) {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, immediate = values[0].number.toInt(), labelSymbol = skipLabel)
|
||||
result += chunk
|
||||
result += translateNode(choice.statements)
|
||||
if(choice.statements.children.last() !is PtReturn)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
} else {
|
||||
val matchLabel = createLabelName()
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
for (value in values) {
|
||||
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = matchLabel)
|
||||
}
|
||||
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
|
||||
result += chunk
|
||||
result += labelFirstChunk(translateNode(choice.statements), matchLabel)
|
||||
if(choice.statements.children.last() !is PtReturn)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
val choiceLabel = createLabelName()
|
||||
choices.add(choiceLabel to choice)
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), null)
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
choices.forEach { (label, choice) ->
|
||||
result += labelFirstChunk(translateNode(choice.statements), label)
|
||||
val lastStatement = choice.statements.children.last()
|
||||
if(lastStatement !is PtReturn && lastStatement !is PtJump)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
}
|
||||
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
return result
|
||||
}
|
||||
|
@ -285,4 +285,37 @@ derp {
|
||||
compileText(VMTarget(), true, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(Cx16Target(), true, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("prefix expressions with typecasting") {
|
||||
val src="""
|
||||
main
|
||||
{
|
||||
sub start()
|
||||
{
|
||||
uword uw = 54321
|
||||
ubyte ub = 123
|
||||
word sw = -12345
|
||||
byte sb = -123
|
||||
|
||||
func_uw(~ub as uword)
|
||||
func_ub(~uw as ubyte)
|
||||
func_uw(~sb as uword)
|
||||
func_ub(~sw as ubyte)
|
||||
func_w(-sb as word)
|
||||
func_b(-sw as byte)
|
||||
}
|
||||
|
||||
sub func_uw(uword arg) {
|
||||
}
|
||||
sub func_w(word arg) {
|
||||
}
|
||||
sub func_ub(ubyte arg) {
|
||||
}
|
||||
sub func_b(byte arg) {
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
})
|
@ -5,32 +5,22 @@ main
|
||||
{
|
||||
sub start()
|
||||
{
|
||||
uword zc = 54321
|
||||
ubyte zb = 123
|
||||
ubyte shift = 2
|
||||
uword uw = 54321
|
||||
ubyte ub = 123
|
||||
word sw = -12345
|
||||
byte sb = -123
|
||||
|
||||
txt.print_uw(zc<<shift)
|
||||
txt.print_uw(~ub as uword) ;132
|
||||
txt.nl()
|
||||
txt.print_uw(zc>>shift)
|
||||
txt.print_ub(~uw as ubyte) ;206
|
||||
txt.nl()
|
||||
txt.print_ub(zb<<shift)
|
||||
txt.print_uw(~sb as uword) ;122
|
||||
txt.nl()
|
||||
txt.print_ub(zb>>shift)
|
||||
txt.print_ub(~sw as ubyte) ;56
|
||||
txt.nl()
|
||||
|
||||
word szc = -12345
|
||||
byte szb = -123
|
||||
txt.print_w(szc<<shift)
|
||||
txt.print_w(-sb as word) ;123
|
||||
txt.nl()
|
||||
txt.print_w(szc>>shift)
|
||||
txt.print_b(-sw as byte) ;57
|
||||
txt.nl()
|
||||
txt.print_b(szb<<shift)
|
||||
txt.nl()
|
||||
txt.print_b(szb>>shift)
|
||||
txt.nl()
|
||||
|
||||
|
||||
; cx16.r1L = (zc<<shift) as ubyte
|
||||
; txt.print_ub(cx16.r1L)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user