mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
Merge branch 'master' into version_9
# Conflicts: # codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt # compiler/res/prog8lib/c128/syslib.p8 # compiler/res/prog8lib/c64/syslib.p8 # compiler/res/prog8lib/cx16/syslib.p8 # docs/source/todo.rst # examples/test.p8 # intermediate/src/prog8/intermediate/IRInstructions.kt
This commit is contained in:
commit
bd2bcb6994
@ -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")
|
||||
|
@ -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("""
|
||||
|
@ -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<IRCodeChunkBase>()
|
||||
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|
||||
private fun optimizeOnlyJoinChunks() {
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.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<IRSubroutine>() }.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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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<UByte>?=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" }
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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<IRCodeChunk>,
|
||||
symbolAddresses: MutableMap<String, Int>,
|
||||
): Pair<IRCodeChunkBase, IRCodeChunk> {
|
||||
): 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}")
|
||||
}
|
||||
|
@ -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<NotImplementedError>("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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user