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:
Irmen de Jong 2023-05-08 23:15:30 +02:00
commit bd2bcb6994
14 changed files with 706 additions and 229 deletions

View File

@ -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")

View File

@ -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("""

View File

@ -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) }
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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" }

View File

@ -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)

View File

@ -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}")
} }

View File

@ -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)