mirror of
https://github.com/irmen/prog8.git
synced 2024-09-10 09:54:32 +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
|
val origTarget = assign.target.origAstTarget
|
||||||
if(origTarget!=null) {
|
if(origTarget!=null) {
|
||||||
assignConstantByte(assign.target, 0)
|
assignConstantByte(assign.target, 0)
|
||||||
@ -381,13 +390,19 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
if(expr.type !in IntegerDatatypes)
|
if(expr.type !in IntegerDatatypes)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
fun simpleLogicalBytesExpr() {
|
if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) {
|
||||||
// both left and right expression operands are simple.
|
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
||||||
if (expr.right is PtNumber || expr.right is PtIdentifier)
|
if (expr.right.isSimple()) {
|
||||||
assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right)
|
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||||
else if (expr.left is PtNumber || expr.left is PtIdentifier)
|
assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right)
|
||||||
assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left)
|
return true
|
||||||
else {
|
}
|
||||||
|
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)
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
@ -399,42 +414,28 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
else -> throw AssemblyError("invalid operator")
|
else -> throw AssemblyError("invalid operator")
|
||||||
}
|
}
|
||||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||||
|
if (expr.right.isSimple()) {
|
||||||
fun simpleLogicalWordsExpr() {
|
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||||
// both left and right expression operands are simple.
|
assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right)
|
||||||
if (expr.right is PtNumber || expr.right is PtIdentifier)
|
return true
|
||||||
assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right)
|
}
|
||||||
else if (expr.left is PtNumber || expr.left is PtIdentifier)
|
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||||
assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left)
|
assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left)
|
||||||
else {
|
return true
|
||||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
}
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
}
|
||||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
|
||||||
when (expr.operator) {
|
when (expr.operator) {
|
||||||
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
|
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | pha | tya | and P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||||
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
|
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | pha | tya | ora P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||||
"^", "xor" -> asmgen.out(" pla | eor P8ZP_SCRATCH_W1+1 | tay | pla | eor P8ZP_SCRATCH_W1")
|
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | pha | tya | eor P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||||
else -> throw AssemblyError("invalid operator")
|
else -> throw AssemblyError("invalid operator")
|
||||||
}
|
}
|
||||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
}
|
return true
|
||||||
}
|
|
||||||
|
|
||||||
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 false
|
return false
|
||||||
}
|
}
|
||||||
@ -468,12 +469,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
return true
|
return true
|
||||||
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
|
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
|
||||||
expr.left.isSimple() && expr.right.isSimple()) {
|
expr.left.isSimple() && expr.right.isSimple()) {
|
||||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
|
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||||
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)
|
|
||||||
if(expr.operator=="==") {
|
if(expr.operator=="==") {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cmp P8ZP_SCRATCH_W1
|
cmp P8ZP_SCRATCH_W1
|
||||||
@ -525,9 +521,44 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes)
|
assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes)
|
||||||
return true
|
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) {
|
} 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) {
|
when (right) {
|
||||||
is PtAddressOf -> {
|
is PtAddressOf -> {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
@ -603,30 +634,37 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
is PtTypeCast -> {
|
is PtTypeCast -> {
|
||||||
val castedValue = right.value
|
val castedValue = right.value
|
||||||
if(right.type in WordDatatypes && castedValue.type in ByteDatatypes) {
|
if(right.type in WordDatatypes && castedValue.type in ByteDatatypes && castedValue is PtIdentifier) {
|
||||||
if(castedValue is PtIdentifier) {
|
val castedSymname = asmgen.asmVariableName(castedValue)
|
||||||
val castedSymname = asmgen.asmVariableName(castedValue)
|
assignExpressionToRegister(left, RegisterOrPair.AY, dt == DataType.WORD)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
if (expr.operator == "+")
|
||||||
if(expr.operator=="+")
|
asmgen.out(
|
||||||
asmgen.out("""
|
"""
|
||||||
clc
|
clc
|
||||||
adc $castedSymname
|
adc $castedSymname
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+""")
|
+"""
|
||||||
else
|
)
|
||||||
asmgen.out("""
|
else
|
||||||
sec
|
asmgen.out(
|
||||||
sbc $castedSymname
|
"""
|
||||||
bcs +
|
sec
|
||||||
dey
|
sbc $castedSymname
|
||||||
+""")
|
bcs +
|
||||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
dey
|
||||||
return true
|
+"""
|
||||||
}
|
)
|
||||||
|
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
|
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) {
|
private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||||
val operand = when(right) {
|
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) {
|
private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
|
||||||
when(statusflag) {
|
when(statusflag) {
|
||||||
Statusflag.Pc -> {
|
Statusflag.Pc -> {
|
||||||
@ -2058,14 +2511,24 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister, signed: Boolean) {
|
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
|
val assignAsWord = target.datatype in WordDatatypes
|
||||||
// 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}"}
|
|
||||||
|
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out(" st${register.name.lowercase()} ${target.asmVarname}")
|
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 -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
when(register) {
|
when(register) {
|
||||||
@ -2076,6 +2539,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
storeRegisterAInMemoryAddress(target.memory!!)
|
storeRegisterAInMemoryAddress(target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
|
if(assignAsWord)
|
||||||
|
TODO("assign register as word into Array not yet supported")
|
||||||
if (target.constArrayIndexValue!=null) {
|
if (target.constArrayIndexValue!=null) {
|
||||||
when (register) {
|
when (register) {
|
||||||
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||||
@ -2237,6 +2702,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
|
if(assignAsWord)
|
||||||
|
TODO("assign register as word onto Stack not yet supported")
|
||||||
when(register) {
|
when(register) {
|
||||||
CpuRegister.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
CpuRegister.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
CpuRegister.X -> throw AssemblyError("can't use X here")
|
CpuRegister.X -> throw AssemblyError("can't use X here")
|
||||||
|
@ -772,13 +772,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
"<" -> {
|
"<" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
|
||||||
cmp $otherName
|
|
||||||
bcc +
|
|
||||||
lda #0
|
lda #0
|
||||||
beq ++
|
ldy $name
|
||||||
+ lda #1
|
cpy $otherName
|
||||||
+ sta $name""")
|
rol a
|
||||||
|
eor #1
|
||||||
|
sta $name""")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
@ -798,13 +797,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
"<=" -> {
|
"<=" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $otherName
|
|
||||||
cmp $name
|
|
||||||
bcs +
|
|
||||||
lda #0
|
lda #0
|
||||||
beq ++
|
ldy $otherName
|
||||||
+ lda #1
|
cpy $name
|
||||||
+ sta $name""")
|
rol a
|
||||||
|
sta $name""")
|
||||||
} else {
|
} else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -823,13 +820,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
">" -> {
|
">" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda #0
|
||||||
cmp $otherName
|
ldy $name
|
||||||
|
cpy $otherName
|
||||||
beq +
|
beq +
|
||||||
bcs ++
|
rol a
|
||||||
+ lda #0
|
|
||||||
beq ++
|
|
||||||
+ lda #1
|
|
||||||
+ sta $name""")
|
+ sta $name""")
|
||||||
} else {
|
} else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
@ -849,13 +844,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
">=" -> {
|
">=" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
|
||||||
cmp $otherName
|
|
||||||
bcs +
|
|
||||||
lda #0
|
lda #0
|
||||||
beq ++
|
ldy $name
|
||||||
+ lda #1
|
cpy $otherName
|
||||||
+ sta $name""")
|
rol a
|
||||||
|
sta $name""")
|
||||||
} else {
|
} else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -963,13 +956,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
"<" -> {
|
"<" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
|
||||||
cmp #$value
|
|
||||||
bcc +
|
|
||||||
lda #0
|
lda #0
|
||||||
beq ++
|
ldy $name
|
||||||
+ lda #1
|
cpy #$value
|
||||||
+ sta $name""")
|
rol a
|
||||||
|
eor #1
|
||||||
|
sta $name""")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
@ -989,13 +981,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
"<=" -> {
|
"<=" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #$value
|
|
||||||
cmp $name
|
|
||||||
bcs +
|
|
||||||
lda #0
|
lda #0
|
||||||
beq ++
|
ldy #$value
|
||||||
+ lda #1
|
cpy $name
|
||||||
+ sta $name""")
|
rol a
|
||||||
|
sta $name""")
|
||||||
} else {
|
} else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1014,13 +1004,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
">" -> {
|
">" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda #0
|
||||||
cmp #$value
|
ldy $name
|
||||||
|
cpy #$value
|
||||||
beq +
|
beq +
|
||||||
bcs ++
|
rol a
|
||||||
+ lda #0
|
|
||||||
beq ++
|
|
||||||
+ lda #1
|
|
||||||
+ sta $name""")
|
+ sta $name""")
|
||||||
} else {
|
} else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
@ -1040,13 +1028,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
">=" -> {
|
">=" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
|
||||||
cmp #$value
|
|
||||||
bcs +
|
|
||||||
lda #0
|
lda #0
|
||||||
beq ++
|
ldy $name
|
||||||
+ lda #1
|
cpy #$value
|
||||||
+ sta $name""")
|
rol a
|
||||||
|
sta $name""")
|
||||||
} else {
|
} else {
|
||||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
|
@ -161,7 +161,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val targetIdent = assignment.target.identifier
|
val targetIdent = assignment.target.identifier
|
||||||
val targetMemory = assignment.target.memory
|
val targetMemory = assignment.target.memory
|
||||||
val targetArray = assignment.target.array
|
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>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
|
||||||
var valueRegister = -1
|
var valueRegister = -1
|
||||||
@ -169,30 +170,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val zero = codeGen.isZero(assignment.value)
|
val zero = codeGen.isZero(assignment.value)
|
||||||
if(!zero) {
|
if(!zero) {
|
||||||
// calculate the assignment value
|
// calculate the assignment value
|
||||||
if (vmDt == IRDataType.FLOAT) {
|
if (valueDt == IRDataType.FLOAT) {
|
||||||
val tr = expressionEval.translateExpression(assignment.value)
|
val tr = expressionEval.translateExpression(assignment.value)
|
||||||
valueFpRegister = tr.resultFpReg
|
valueFpRegister = tr.resultFpReg
|
||||||
addToResult(result, tr, -1, valueFpRegister)
|
addToResult(result, tr, -1, valueFpRegister)
|
||||||
} else {
|
} 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) {
|
if (assignment.value is PtMachineRegister) {
|
||||||
valueRegister = (assignment.value as PtMachineRegister).register
|
valueRegister = (assignment.value as PtMachineRegister).register
|
||||||
|
if(extendByteToWord)
|
||||||
|
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
|
||||||
} else {
|
} else {
|
||||||
val tr = expressionEval.translateExpression(assignment.value)
|
val tr = expressionEval.translateExpression(assignment.value)
|
||||||
valueRegister = tr.resultReg
|
valueRegister = tr.resultReg
|
||||||
addToResult(result, tr, valueRegister, -1)
|
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) {
|
if(targetIdent!=null) {
|
||||||
val instruction = if(zero) {
|
val instruction = if(zero) {
|
||||||
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||||
} else {
|
} else {
|
||||||
if (vmDt == IRDataType.FLOAT) {
|
if (targetDt == IRDataType.FLOAT)
|
||||||
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||||
}
|
|
||||||
else
|
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 }
|
result += IRCodeChunk(null, null).also { it += instruction }
|
||||||
return result
|
return result
|
||||||
@ -214,9 +227,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if(zero) {
|
if(zero) {
|
||||||
// there's no STOREZIX instruction
|
// there's no STOREZIX instruction
|
||||||
valueRegister = codeGen.registers.nextFree()
|
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
|
result += code
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -225,59 +238,59 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
if(zero) {
|
if(zero) {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
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
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += code
|
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 {
|
} else {
|
||||||
if(vmDt== IRDataType.FLOAT) {
|
if(targetDt== IRDataType.FLOAT) {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
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
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += code
|
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 {
|
} else {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
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
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += code
|
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
|
return result
|
||||||
}
|
}
|
||||||
else if(targetMemory!=null) {
|
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(zero) {
|
||||||
if(targetMemory.address is PtNumber) {
|
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
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||||
val addressReg = tr.resultReg
|
val addressReg = tr.resultReg
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
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 {
|
} else {
|
||||||
if(targetMemory.address is PtNumber) {
|
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
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||||
val addressReg = tr.resultReg
|
val addressReg = tr.resultReg
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
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() {
|
private fun optimizeOnlyJoinChunks() {
|
||||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||||
|
joinChunks(sub)
|
||||||
removeEmptyChunks(sub)
|
removeEmptyChunks(sub)
|
||||||
joinChunks(sub)
|
joinChunks(sub)
|
||||||
}
|
}
|
||||||
@ -32,6 +33,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
|
|
||||||
private fun peepholeOptimize() {
|
private fun peepholeOptimize() {
|
||||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||||
|
joinChunks(sub)
|
||||||
removeEmptyChunks(sub)
|
removeEmptyChunks(sub)
|
||||||
joinChunks(sub)
|
joinChunks(sub)
|
||||||
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
||||||
@ -112,7 +114,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
if(sub.chunks.isEmpty())
|
if(sub.chunks.isEmpty())
|
||||||
return
|
return
|
||||||
|
|
||||||
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
||||||
if(chunk.label!=null)
|
if(chunk.label!=null)
|
||||||
return false
|
return false
|
||||||
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
||||||
@ -129,12 +131,39 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
chunks += sub.chunks[0]
|
chunks += sub.chunks[0]
|
||||||
for(ix in 1 until sub.chunks.size) {
|
for(ix in 1 until sub.chunks.size) {
|
||||||
val lastChunk = chunks.last()
|
val lastChunk = chunks.last()
|
||||||
if(mayJoin(lastChunk, sub.chunks[ix])) {
|
val candidate = sub.chunks[ix]
|
||||||
lastChunk.instructions += sub.chunks[ix].instructions
|
when(candidate) {
|
||||||
lastChunk.next = sub.chunks[ix].next
|
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.clear()
|
||||||
sub.chunks += chunks
|
sub.chunks += chunks
|
||||||
|
@ -400,7 +400,7 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
|||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta _use_kernal
|
sta _use_kernal
|
||||||
sei
|
sei
|
||||||
lda #<_irq_handler
|
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) {
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta set_irq._use_kernal
|
sta set_irq._use_kernal
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
ldy cx16.r0+1
|
ldy cx16.r0+1
|
||||||
sei
|
sei
|
||||||
|
@ -365,7 +365,7 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
|||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta _use_kernal
|
sta _use_kernal
|
||||||
sei
|
sei
|
||||||
lda #<_irq_handler
|
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) {
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta set_irq._use_kernal
|
sta set_irq._use_kernal
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
ldy cx16.r0+1
|
ldy cx16.r0+1
|
||||||
sei
|
sei
|
||||||
|
@ -22,7 +22,10 @@ psg {
|
|||||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||||
envelope_states[voice_num] = 255
|
envelope_states[voice_num] = 255
|
||||||
sys.set_irqd()
|
%asm {{
|
||||||
|
php
|
||||||
|
sei
|
||||||
|
}}
|
||||||
cx16.r0 = $f9c2 + voice_num * 4
|
cx16.r0 = $f9c2 + voice_num * 4
|
||||||
cx16.VERA_CTRL = 0
|
cx16.VERA_CTRL = 0
|
||||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||||
@ -33,7 +36,9 @@ psg {
|
|||||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||||
envelope_volumes[voice_num] = mkword(volume, 0)
|
envelope_volumes[voice_num] = mkword(volume, 0)
|
||||||
envelope_maxvolumes[voice_num] = volume
|
envelope_maxvolumes[voice_num] = volume
|
||||||
sys.clear_irqd()
|
%asm {{
|
||||||
|
plp
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
; sub freq_hz(ubyte voice_num, float hertz) {
|
; 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.
|
; 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)
|
; (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
|
; 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.r0 = $f9c1 + voice_num * 4
|
||||||
cx16.VERA_CTRL = 0
|
cx16.VERA_CTRL = 0
|
||||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||||
@ -57,7 +65,9 @@ psg {
|
|||||||
cx16.VERA_DATA0 = msb(vera_freq)
|
cx16.VERA_DATA0 = msb(vera_freq)
|
||||||
cx16.VERA_ADDR_L--
|
cx16.VERA_ADDR_L--
|
||||||
cx16.VERA_DATA0 = lsb(vera_freq)
|
cx16.VERA_DATA0 = lsb(vera_freq)
|
||||||
sys.clear_irqd()
|
%asm {{
|
||||||
|
plp
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub volume(ubyte voice_num, ubyte vol) {
|
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,
|
; 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.
|
; 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)
|
; 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.r0 = the volume word (volume scaled by 256)
|
||||||
; cx16.r1L = the voice number
|
; cx16.r1L = the voice number
|
||||||
; cx16.r2L = attack value
|
; cx16.r2L = attack value
|
||||||
|
|
||||||
pushw(cx16.r0)
|
pushw(cx16.r0)
|
||||||
push(cx16.r1L)
|
push(cx16.r1L)
|
||||||
push(cx16.r2L)
|
push(cx16.r2L)
|
||||||
|
@ -637,6 +637,7 @@ asmsub save_vera_context() clobbers(A) {
|
|||||||
lda cx16.VERA_CTRL
|
lda cx16.VERA_CTRL
|
||||||
sta _vera_storage+3
|
sta _vera_storage+3
|
||||||
eor #1
|
eor #1
|
||||||
|
sta _vera_storage+7
|
||||||
sta cx16.VERA_CTRL
|
sta cx16.VERA_CTRL
|
||||||
lda cx16.VERA_ADDR_L
|
lda cx16.VERA_ADDR_L
|
||||||
sta _vera_storage+4
|
sta _vera_storage+4
|
||||||
@ -644,8 +645,6 @@ asmsub save_vera_context() clobbers(A) {
|
|||||||
sta _vera_storage+5
|
sta _vera_storage+5
|
||||||
lda cx16.VERA_ADDR_H
|
lda cx16.VERA_ADDR_H
|
||||||
sta _vera_storage+6
|
sta _vera_storage+6
|
||||||
lda cx16.VERA_CTRL
|
|
||||||
sta _vera_storage+7
|
|
||||||
rts
|
rts
|
||||||
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
_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) {
|
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta _use_kernal
|
sta _use_kernal
|
||||||
sei
|
sei
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
sta cx16.CINV
|
sta cx16.CINV
|
||||||
lda #>_irq_handler
|
lda #>_irq_handler
|
||||||
sta cx16.CINV+1
|
sta cx16.CINV+1
|
||||||
lda cx16.VERA_IEN
|
lda cx16.VERA_IEN
|
||||||
ora #%00000001 ; enable the vsync irq
|
ora #%00000001 ; enable the vsync irq
|
||||||
sta cx16.VERA_IEN
|
sta cx16.VERA_IEN
|
||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_irq_handler jsr _irq_handler_init
|
_irq_handler jsr _irq_handler_init
|
||||||
_modified jsr $ffff ; modified
|
_modified jsr $ffff ; modified
|
||||||
|
@ -292,7 +292,7 @@ str = P8ZP_SCRATCH_W1
|
|||||||
sta modify_pattern2+2
|
sta modify_pattern2+2
|
||||||
jsr _match
|
jsr _match
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
@ -140,7 +140,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
|||||||
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
|
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
|
||||||
xml.writeStartElement("BYTES")
|
xml.writeStartElement("BYTES")
|
||||||
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
|
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
|
||||||
xml.writeCharacters("\n")
|
|
||||||
chunk.data.withIndex().forEach {(index, byte) ->
|
chunk.data.withIndex().forEach {(index, byte) ->
|
||||||
xml.writeCharacters(byte.toString(16).padStart(2,'0'))
|
xml.writeCharacters(byte.toString(16).padStart(2,'0'))
|
||||||
if(index and 63 == 63 && index < chunk.data.size-1)
|
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)
|
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)
|
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)
|
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.)
|
( 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
|
sz reg1, reg2 - set reg1=1.b if reg2==0, otherwise set reg1=0.b
|
||||||
snz reg1, reg2 - set reg1=1 if reg2!=0, otherwise set reg1=0
|
snz reg1, reg2 - set reg1=1.b if reg2!=0, otherwise set reg1=0.b
|
||||||
seq reg1, reg2 - set reg1=1 if reg1 == reg2, otherwise set reg1=0
|
seq reg1, reg2 - set reg1=1.b if reg1 == reg2, otherwise set reg1=0.b
|
||||||
sne reg1, reg2 - set reg1=1 if reg1 != reg2, otherwise set reg1=0
|
sne reg1, reg2 - set reg1=1.b if reg1 != reg2, otherwise set reg1=0.b
|
||||||
slt reg1, reg2 - set reg1=1 if reg1 < reg2 (unsigned), otherwise set reg1=0
|
slt reg1, reg2 - set reg1=1.b if reg1 < reg2 (unsigned), otherwise set reg1=0.b
|
||||||
slts reg1, reg2 - set reg1=1 if reg1 < reg2 (signed), otherwise set reg1=0
|
slts reg1, reg2 - set reg1=1.b if reg1 < reg2 (signed), otherwise set reg1=0.b
|
||||||
sle reg1, reg2 - set reg1=1 if reg1 <= reg2 (unsigned), otherwise set reg1=0
|
sle reg1, reg2 - set reg1=1.b if reg1 <= reg2 (unsigned), otherwise set reg1=0.b
|
||||||
sles reg1, reg2 - set reg1=1 if reg1 <= reg2 (signed), otherwise set reg1=0
|
sles reg1, reg2 - set reg1=1.b if reg1 <= reg2 (signed), otherwise set reg1=0.b
|
||||||
sgt reg1, reg2 - set reg1=1 if reg1 > reg2 (unsigned), otherwise set reg1=0
|
sgt reg1, reg2 - set reg1=1.b if reg1 > reg2 (unsigned), otherwise set reg1=0.b
|
||||||
sgts reg1, reg2 - set reg1=1 if reg1 > reg2 (signed), otherwise set reg1=0
|
sgts reg1, reg2 - set reg1=1.b if reg1 > reg2 (signed), otherwise set reg1=0.b
|
||||||
sge reg1, reg2 - set reg1=1 if reg1 >= reg2 (unsigned), otherwise set reg1=0
|
sge reg1, reg2 - set reg1=1.b if reg1 >= reg2 (unsigned), otherwise set reg1=0.b
|
||||||
sges reg1, reg2 - set reg1=1 if reg1 >= reg2 (signed), otherwise set reg1=0
|
sges reg1, reg2 - set reg1=1.b if reg1 >= reg2 (signed), otherwise set reg1=0.b
|
||||||
|
|
||||||
|
|
||||||
ARITHMETIC
|
ARITHMETIC
|
||||||
----------
|
----------
|
||||||
All have type b or w or f. Note: result types are the same as operand types! E.g. byte*byte->byte.
|
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)
|
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
|
inc reg1 - reg1 = reg1+1
|
||||||
incm address - memory at address += 1
|
incm address - memory at address += 1
|
||||||
dec reg1 - reg1 = reg1-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)
|
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
|
push [b, w, f] reg1 - push value in reg1 on the stack
|
||||||
pop [b, w, f] reg1 - pop value from stack into reg1
|
pop [b, w, f] reg1 - pop value from stack into reg1
|
||||||
binarydata - 'instruction' to hold inlined binary data bytes
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum class Opcode {
|
enum class Opcode {
|
||||||
@ -374,8 +373,7 @@ enum class Opcode {
|
|||||||
POP,
|
POP,
|
||||||
MSIG,
|
MSIG,
|
||||||
CONCAT,
|
CONCAT,
|
||||||
BREAKPOINT,
|
BREAKPOINT
|
||||||
BINARYDATA
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val OpcodesThatJump = setOf(
|
val OpcodesThatJump = setOf(
|
||||||
@ -653,7 +651,6 @@ val instructionFormats = mutableMapOf(
|
|||||||
Opcode.CLC to InstructionFormat.from("N"),
|
Opcode.CLC to InstructionFormat.from("N"),
|
||||||
Opcode.SEC to InstructionFormat.from("N"),
|
Opcode.SEC to InstructionFormat.from("N"),
|
||||||
Opcode.BREAKPOINT 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 immediate: Int?=null, // 0-$ff or $ffff if word
|
||||||
val immediateFp: Float?=null,
|
val immediateFp: Float?=null,
|
||||||
val address: Int?=null, // 0-$ffff
|
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 labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!)
|
||||||
val binaryData: Collection<UByte>?=null,
|
var branchTarget: IRCodeChunkBase? = null // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.
|
||||||
var branchTarget: IRCodeChunkBase? = null // will be linked after loading
|
|
||||||
) {
|
) {
|
||||||
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
|
// 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.
|
// 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"}
|
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
|
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)
|
val formats = instructionFormats.getValue(opcode)
|
||||||
require (type != null || formats.containsKey(null)) { "missing type" }
|
require (type != null || formats.containsKey(null)) { "missing type" }
|
||||||
|
|
||||||
|
@ -284,7 +284,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
|||||||
Opcode.BREAKPOINT -> InsBREAKPOINT()
|
Opcode.BREAKPOINT -> InsBREAKPOINT()
|
||||||
Opcode.CLC -> { statusCarry = false; nextPc() }
|
Opcode.CLC -> { statusCarry = false; nextPc() }
|
||||||
Opcode.SEC -> { statusCarry = true; nextPc() }
|
Opcode.SEC -> { statusCarry = true; nextPc() }
|
||||||
Opcode.BINARYDATA -> TODO("BINARYDATA not yet supported in VM")
|
|
||||||
Opcode.LOADCPU -> InsLOADCPU(ins)
|
Opcode.LOADCPU -> InsLOADCPU(ins)
|
||||||
Opcode.STORECPU -> InsSTORECPU(ins)
|
Opcode.STORECPU -> InsSTORECPU(ins)
|
||||||
Opcode.STOREZCPU -> InsSTOREZCPU(ins)
|
Opcode.STOREZCPU -> InsSTOREZCPU(ins)
|
||||||
|
@ -46,7 +46,7 @@ class VmProgramLoader {
|
|||||||
is IRCodeChunk -> programChunks += child
|
is IRCodeChunk -> programChunks += child
|
||||||
is IRInlineAsmChunk -> {
|
is IRInlineAsmChunk -> {
|
||||||
val replacement = addAssemblyToProgram(child, programChunks, variableAddresses)
|
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 IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
|
||||||
is IRSubroutine -> {
|
is IRSubroutine -> {
|
||||||
@ -55,7 +55,7 @@ class VmProgramLoader {
|
|||||||
when (chunk) {
|
when (chunk) {
|
||||||
is IRInlineAsmChunk -> {
|
is IRInlineAsmChunk -> {
|
||||||
val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses)
|
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 IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
|
||||||
is IRCodeChunk -> programChunks += chunk
|
is IRCodeChunk -> programChunks += chunk
|
||||||
@ -339,7 +339,7 @@ class VmProgramLoader {
|
|||||||
asmChunk: IRInlineAsmChunk,
|
asmChunk: IRInlineAsmChunk,
|
||||||
chunks: MutableList<IRCodeChunk>,
|
chunks: MutableList<IRCodeChunk>,
|
||||||
symbolAddresses: MutableMap<String, Int>,
|
symbolAddresses: MutableMap<String, Int>,
|
||||||
): Pair<IRCodeChunkBase, IRCodeChunk> {
|
): IRCodeChunk {
|
||||||
if(asmChunk.isIR) {
|
if(asmChunk.isIR) {
|
||||||
val chunk = IRCodeChunk(asmChunk.label, asmChunk.next)
|
val chunk = IRCodeChunk(asmChunk.label, asmChunk.next)
|
||||||
asmChunk.assembly.lineSequence().forEach {
|
asmChunk.assembly.lineSequence().forEach {
|
||||||
@ -350,7 +350,7 @@ class VmProgramLoader {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
chunks += chunk
|
chunks += chunk
|
||||||
return Pair(asmChunk, chunk)
|
return chunk
|
||||||
} else {
|
} else {
|
||||||
throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.label}")
|
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
|
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") {
|
test("asmsub not supported in vm even with IR") {
|
||||||
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
||||||
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||||
|
Loading…
Reference in New Issue
Block a user