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
if(origTarget!=null) {
assignConstantByte(assign.target, 0)
@ -381,13 +390,19 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(expr.type !in IntegerDatatypes)
return false
fun simpleLogicalBytesExpr() {
// both left and right expression operands are simple.
if (expr.right is PtNumber || expr.right is PtIdentifier)
assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right)
else if (expr.left is PtNumber || expr.left is PtIdentifier)
assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left)
else {
if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) {
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
if (expr.right.isSimple()) {
if (expr.right is PtNumber || expr.right is PtIdentifier) {
assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right)
return true
}
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left)
return true
}
}
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
@ -399,42 +414,28 @@ internal class AssignmentAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid operator")
}
assignRegisterByte(assign.target, CpuRegister.A, false)
return true
}
}
fun simpleLogicalWordsExpr() {
// both left and right expression operands are simple.
if (expr.right is PtNumber || expr.right is PtIdentifier)
assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right)
else if (expr.left is PtNumber || expr.left is PtIdentifier)
assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left)
else {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD)
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if (expr.right.isSimple()) {
if (expr.right is PtNumber || expr.right is PtIdentifier) {
assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right)
return true
}
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left)
return true
}
}
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
when (expr.operator) {
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
"^", "xor" -> asmgen.out(" pla | eor P8ZP_SCRATCH_W1+1 | tay | pla | eor P8ZP_SCRATCH_W1")
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | pha | tya | and P8ZP_SCRATCH_W1+1 | tay | pla")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | pha | tya | ora P8ZP_SCRATCH_W1+1 | tay | pla")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | pha | tya | eor P8ZP_SCRATCH_W1+1 | tay | pla")
else -> throw AssemblyError("invalid operator")
}
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
}
if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) {
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
if (expr.right.isSimple()) {
simpleLogicalBytesExpr()
return true
}
}
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if (expr.right.isSimple()) {
simpleLogicalWordsExpr()
return true
}
return true
}
return false
}
@ -468,12 +469,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
expr.left.isSimple() && expr.right.isSimple()) {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
if(expr.operator=="==") {
asmgen.out("""
cmp P8ZP_SCRATCH_W1
@ -525,9 +521,44 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes)
return true
}
else -> return false
else -> {
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
asmgen.out(" pha")
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
asmgen.out(" pla")
if(expr.operator=="+")
asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
else
asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes)
return true
}
}
} else if(dt in WordDatatypes) {
fun doAddOrSubWordExpr() {
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
if(expr.operator=="+")
asmgen.out("""
clc
adc P8ZP_SCRATCH_W1
pha
tya
adc P8ZP_SCRATCH_W1+1
tay
pla""")
else
asmgen.out("""
sec
sbc P8ZP_SCRATCH_W1
pha
tya
sbc P8ZP_SCRATCH_W1+1
tay
pla""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
when (right) {
is PtAddressOf -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
@ -603,30 +634,37 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
is PtTypeCast -> {
val castedValue = right.value
if(right.type in WordDatatypes && castedValue.type in ByteDatatypes) {
if(castedValue is PtIdentifier) {
val castedSymname = asmgen.asmVariableName(castedValue)
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
if(expr.operator=="+")
asmgen.out("""
clc
adc $castedSymname
bcc +
iny
+""")
else
asmgen.out("""
sec
sbc $castedSymname
bcs +
dey
+""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
if(right.type in WordDatatypes && castedValue.type in ByteDatatypes && castedValue is PtIdentifier) {
val castedSymname = asmgen.asmVariableName(castedValue)
assignExpressionToRegister(left, RegisterOrPair.AY, dt == DataType.WORD)
if (expr.operator == "+")
asmgen.out(
"""
clc
adc $castedSymname
bcc +
iny
+"""
)
else
asmgen.out(
"""
sec
sbc $castedSymname
bcs +
dey
+"""
)
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
doAddOrSubWordExpr()
return true
}
else -> {
doAddOrSubWordExpr()
return true
}
else -> return false
}
}
}
@ -682,9 +720,396 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
}
else if(expr.operator=="*") {
val value = expr.right.asConstInteger()
if(value==null) {
when(expr.type) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes)
asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, expr.type in SignedDatatypes)
asmgen.out(" pla | jsr math.multiply_bytes")
assignRegisterByte(assign.target, CpuRegister.A, false)
return true
}
in WordDatatypes -> {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out("""
jsr math.multiply_words
lda math.multiply_words.result
ldy math.multiply_words.result+1""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
else -> return false
}
} else {
when (expr.type) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes)
if (value in asmgen.optimizedByteMultiplications)
asmgen.out(" jsr math.mul_byte_${value}")
else
asmgen.out(" ldy #$value | jsr math.multiply_bytes")
assignRegisterByte(assign.target, CpuRegister.A, false)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type in SignedDatatypes)
if (value in asmgen.optimizedWordMultiplications)
asmgen.out(" jsr math.mul_word_${value}")
else
asmgen.out("""
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$value
ldy #>$value
jsr math.multiply_words
lda math.multiply_words.result
ldy math.multiply_words.result+1""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
else -> return false
}
}
}
else if(expr.operator=="/") {
when(expr.type) {
DataType.UBYTE -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
asmgen.out(" pla | jsr math.divmod_ub_asm")
assignRegisterByte(assign.target, CpuRegister.Y, false)
return true
}
DataType.BYTE -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, true)
asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, true)
asmgen.out(" pla | jsr math.divmod_b_asm")
assignRegisterByte(assign.target, CpuRegister.Y, true)
return true
}
DataType.UWORD -> {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out(" jsr math.divmod_uw_asm")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
DataType.WORD -> {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out(" jsr math.divmod_w_asm")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
else -> return false
}
}
else if(expr.operator=="%") {
when(expr.type) {
DataType.UBYTE -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
asmgen.out(" pla | jsr math.divmod_ub_asm")
if(assign.target.register==RegisterOrPair.A)
asmgen.out(" cmp #0") // fix the status register
else
assignRegisterByte(assign.target, CpuRegister.A, false)
return true
}
DataType.UWORD -> {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out(" jsr math.divmod_uw_asm")
assignVariableWord(assign.target, "P8ZP_SCRATCH_W2")
return true
}
else -> return false
}
}
return false
}
private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
val signed = expr.left.type == DataType.BYTE || expr.right.type == DataType.BYTE
fun assignExpressionOperandsLeftScratchRightA() {
if(expr.right.isSimple()) {
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_B1", expr.left.type)
assignExpressionToRegister(expr.right, RegisterOrPair.A, signed)
} else {
assignExpressionToRegister(expr.right, RegisterOrPair.A, signed)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_B1", expr.left.type)
asmgen.restoreRegisterStack(CpuRegister.A, false)
}
}
when(expr.operator) {
"==" -> {
assignExpressionOperandsLeftScratchRightA()
asmgen.out("""
cmp P8ZP_SCRATCH_B1
beq +
lda #0
beq ++
+ lda #1
+""")
}
"!=" -> {
assignExpressionOperandsLeftScratchRightA()
asmgen.out("""
cmp P8ZP_SCRATCH_B1
bne +
lda #0
beq ++
+ lda #1
+""")
}
"<" -> {
assignExpressionOperandsLeftScratchRightA()
if(signed)
asmgen.out("""
clc
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bpl +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
tay
lda #0
cpy P8ZP_SCRATCH_B1
beq +
rol a
+""")
}
"<=" -> {
assignExpressionOperandsLeftScratchRightA()
if(signed)
asmgen.out("""
sec
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bpl +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
lda #0
rol a""")
}
">" -> {
assignExpressionOperandsLeftScratchRightA()
if(signed)
asmgen.out("""
sec
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bmi +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
lda #0
rol a
eor #1""")
}
">=" -> {
assignExpressionOperandsLeftScratchRightA()
if(signed)
asmgen.out("""
clc
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bmi +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
beq +
bcc +
lda #0
beq ++
+ lda #1
+""")
}
else -> return false
}
assignRegisterByte(assign.target, CpuRegister.A, signed)
return true
}
private fun assignOptimizedComparisonWords(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
val signed = expr.left.type == DataType.WORD || expr.right.type == DataType.WORD
when(expr.operator) {
"==" -> {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out("""
cmp P8ZP_SCRATCH_W1
bne +
cpy P8ZP_SCRATCH_W1+1
bne +
lda #1
bne ++
+ lda #0
+""")
}
"!=" -> {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out("""
cmp P8ZP_SCRATCH_W1
bne +
cpy P8ZP_SCRATCH_W1+1
bne +
lda #0
beq ++
+ lda #1
+""")
}
"<" -> {
if(signed) {
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
asmgen.out("""
cmp P8ZP_SCRATCH_W1
tya
sbc P8ZP_SCRATCH_W1+1
bvc +
eor #${'$'}80
+ bpl ++
+ lda #1
bne ++
+ lda #0
+""")
}
else {
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bcc +
bne ++
cmp P8ZP_SCRATCH_W1
bcs ++
+ lda #1
bne ++
+ lda #0
+""")
}
}
"<=" -> {
if(signed) {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out("""
cmp P8ZP_SCRATCH_W1
tya
sbc P8ZP_SCRATCH_W1+1
bvc +
eor #${'$'}80
+ bmi +
lda #1
bne ++
+ lda #0
+""")
}
else {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bcc ++
bne +
cmp P8ZP_SCRATCH_W1
bcc ++
+ lda #1
bne ++
+ lda #0
+""")
}
}
">" -> {
if(signed) {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out("""
cmp P8ZP_SCRATCH_W1
tya
sbc P8ZP_SCRATCH_W1+1
bvc +
eor #${'$'}80
+ bpl ++
+ lda #1
bne ++
+ lda #0
+""")
}
else {
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bcc +
bne ++
cmp P8ZP_SCRATCH_W1
bcs ++
+ lda #1
bne ++
+ lda #0
+""")
}
}
">=" -> {
if(signed) {
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
asmgen.out("""
cmp P8ZP_SCRATCH_W1
tya
sbc P8ZP_SCRATCH_W1+1
bvc +
eor #${'$'}80
+ bmi +
lda #1
bne ++
+ lda #0
+""")
}
else {
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bcc ++
bne +
cmp P8ZP_SCRATCH_W1
bcc ++
+ lda #1
bne ++
+ lda #0
+""")
}
}
else -> return false
}
assignRegisterByte(assign.target, CpuRegister.A, signed)
return true
}
private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
assignExpressionToRegister(left, RegisterOrPair.A, false)
val operand = when(right) {
@ -853,6 +1278,34 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
private fun assignExpressionWordOperandsLeftScratchW1RightAY(expr: PtBinaryExpression) {
if(expr.right.isSimple()) {
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_W1", expr.left.type)
assignExpressionToRegister(expr.right, RegisterOrPair.AY, expr.right.type in SignedDatatypes)
} else {
assignExpressionToRegister(expr.right, RegisterOrPair.AY, expr.right.type in SignedDatatypes)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_W1", expr.left.type)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
}
}
private fun assignExpressionWordOperandsLeftAYRightScratchW1(expr: PtBinaryExpression) {
if(expr.left.isSimple()) {
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", expr.left.type)
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes)
} else {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", expr.left.type)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
}
}
private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
when(statusflag) {
Statusflag.Pc -> {
@ -2058,14 +2511,24 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister, signed: Boolean) {
// we make an exception in the type check for assigning something to a register pair AX, AY or XY
// these will be correctly typecasted from a byte to a word value here
if(target.register !in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY))
require(target.datatype in ByteDatatypes) { "assign target must be byte type ${target.position}"}
val assignAsWord = target.datatype in WordDatatypes
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out(" st${register.name.lowercase()} ${target.asmVarname}")
if(assignAsWord) {
if(target.datatype in SignedDatatypes) {
if(register!=CpuRegister.A)
asmgen.out(" t${register.name.lowercase()}a")
asmgen.signExtendAYlsb(if(target.datatype in SignedDatatypes) DataType.BYTE else DataType.UBYTE)
asmgen.out(" sty ${target.asmVarname}+1")
} else {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${target.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
}
}
}
TargetStorageKind.MEMORY -> {
when(register) {
@ -2076,6 +2539,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(assignAsWord)
TODO("assign register as word into Array not yet supported")
if (target.constArrayIndexValue!=null) {
when (register) {
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
@ -2237,6 +2702,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
TargetStorageKind.STACK -> {
if(assignAsWord)
TODO("assign register as word onto Stack not yet supported")
when(register) {
CpuRegister.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
CpuRegister.X -> throw AssemblyError("can't use X here")

View File

@ -772,13 +772,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
bcc +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy $otherName
rol a
eor #1
sta $name""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -798,13 +797,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $otherName
cmp $name
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $otherName
cpy $name
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -823,13 +820,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
lda #0
ldy $name
cpy $otherName
beq +
bcs ++
+ lda #0
beq ++
+ lda #1
rol a
+ sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -849,13 +844,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy $otherName
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -963,13 +956,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
bcc +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy #$value
rol a
eor #1
sta $name""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -989,13 +981,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda #$value
cmp $name
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy #$value
cpy $name
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -1014,13 +1004,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
lda #0
ldy $name
cpy #$value
beq +
bcs ++
+ lda #0
beq ++
+ lda #1
rol a
+ sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -1040,13 +1028,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy #$value
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""

View File

@ -161,7 +161,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory
val targetArray = assignment.target.array
val vmDt = irType(assignment.value.type)
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
var valueRegister = -1
@ -169,30 +170,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val zero = codeGen.isZero(assignment.value)
if(!zero) {
// calculate the assignment value
if (vmDt == IRDataType.FLOAT) {
if (valueDt == IRDataType.FLOAT) {
val tr = expressionEval.translateExpression(assignment.value)
valueFpRegister = tr.resultFpReg
addToResult(result, tr, -1, valueFpRegister)
} else {
val extendByteToWord = if(targetDt != valueDt) {
// usually an error EXCEPT when a byte is assigned to a word.
if(targetDt==IRDataType.WORD && valueDt==IRDataType.BYTE)
true
else
throw AssemblyError("assignment value and target dt mismatch")
} else false
if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
} else {
val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) {
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1 = valueRegister), null)
}
}
}
}
if(targetIdent!=null) {
val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
} else {
if (vmDt == IRDataType.FLOAT) {
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
}
if (targetDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
else
IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
}
result += IRCodeChunk(null, null).also { it += instruction }
return result
@ -214,9 +227,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) {
// there's no STOREZIX instruction
valueRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, immediate = 0)
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
}
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
result += code
return result
}
@ -225,59 +238,59 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable) }
}
} else {
if(vmDt== IRDataType.FLOAT) {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
}
} else {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
}
}
}
return result
}
else if(targetMemory!=null) {
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
if(zero) {
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, address = (targetMemory.address as PtNumber).number.toInt()) }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
}
} else {
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
}
}

View File

@ -24,6 +24,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
private fun optimizeOnlyJoinChunks() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
}
@ -32,6 +33,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
private fun peepholeOptimize() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
sub.chunks.withIndex().forEach { (index, chunk1) ->
@ -112,7 +114,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(sub.chunks.isEmpty())
return
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null)
return false
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
@ -129,12 +131,39 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[0]
for(ix in 1 until sub.chunks.size) {
val lastChunk = chunks.last()
if(mayJoin(lastChunk, sub.chunks[ix])) {
lastChunk.instructions += sub.chunks[ix].instructions
lastChunk.next = sub.chunks[ix].next
val candidate = sub.chunks[ix]
when(candidate) {
is IRCodeChunk -> {
if(mayJoinCodeChunks(lastChunk, candidate)) {
lastChunk.instructions += candidate.instructions
lastChunk.next = candidate.next
}
else
chunks += candidate
}
is IRInlineAsmChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
else
chunks += candidate
}
}
is IRInlineBinaryChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
else
chunks += candidate
}
}
}
else
chunks += sub.chunks[ix]
}
sub.chunks.clear()
sub.chunks += chunks

View File

@ -400,7 +400,7 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta _use_kernal
sei
lda #<_irq_handler
@ -491,11 +491,11 @@ asmsub restore_irq() clobbers(A) {
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
sty _modified+2
lda #0
adc #0
sta set_irq._use_kernal
sta _modified+1
sty _modified+2
lda #0
rol a
sta set_irq._use_kernal
lda cx16.r0
ldy cx16.r0+1
sei

View File

@ -365,7 +365,7 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta _use_kernal
sei
lda #<_irq_handler
@ -456,11 +456,11 @@ asmsub restore_irq() clobbers(A) {
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
sty _modified+2
lda #0
adc #0
sta set_irq._use_kernal
sta _modified+1
sty _modified+2
lda #0
rol a
sta set_irq._use_kernal
lda cx16.r0
ldy cx16.r0+1
sei

View File

@ -22,7 +22,10 @@ psg {
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
envelope_states[voice_num] = 255
sys.set_irqd()
%asm {{
php
sei
}}
cx16.r0 = $f9c2 + voice_num * 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -33,7 +36,9 @@ psg {
cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume
sys.clear_irqd()
%asm {{
plp
}}
}
; sub freq_hz(ubyte voice_num, float hertz) {
@ -48,7 +53,10 @@ psg {
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
; Write freq MSB first and then LSB to reduce the chance on clicks
sys.set_irqd()
%asm {{
php
sei
}}
cx16.r0 = $f9c1 + voice_num * 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -57,7 +65,9 @@ psg {
cx16.VERA_DATA0 = msb(vera_freq)
cx16.VERA_ADDR_L--
cx16.VERA_DATA0 = lsb(vera_freq)
sys.clear_irqd()
%asm {{
plp
}}
}
sub volume(ubyte voice_num, ubyte vol) {
@ -106,12 +116,11 @@ psg {
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
; or just install this routine as the only irq handler if you don't have to do other things there.
; Example: cx16.set_irq(&psg.envelopes_irq, true)
; NOTE: this routine calls save/restore_vera_context() for you, don't nest this!
; NOTE: this routine calls save/restore_vera_context() for you, don't nest this or call it yourself!
; cx16.r0 = the volume word (volume scaled by 256)
; cx16.r1L = the voice number
; cx16.r2L = attack value
pushw(cx16.r0)
push(cx16.r1L)
push(cx16.r2L)

View File

@ -637,6 +637,7 @@ asmsub save_vera_context() clobbers(A) {
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
@ -644,8 +645,6 @@ asmsub save_vera_context() clobbers(A) {
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
lda cx16.VERA_CTRL
sta _vera_storage+7
rts
_vera_storage: .byte 0,0,0,0,0,0,0,0
}}
@ -750,21 +749,21 @@ asmsub cleanup_at_exit() {
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
sty _modified+2
lda #0
adc #0
sta _use_kernal
sei
lda #<_irq_handler
sta cx16.CINV
lda #>_irq_handler
sta cx16.CINV+1
lda cx16.VERA_IEN
ora #%00000001 ; enable the vsync irq
sta cx16.VERA_IEN
cli
rts
sta _modified+1
sty _modified+2
lda #0
rol a
sta _use_kernal
sei
lda #<_irq_handler
sta cx16.CINV
lda #>_irq_handler
sta cx16.CINV+1
lda cx16.VERA_IEN
ora #%00000001 ; enable the vsync irq
sta cx16.VERA_IEN
cli
rts
_irq_handler jsr _irq_handler_init
_modified jsr $ffff ; modified

View File

@ -292,7 +292,7 @@ str = P8ZP_SCRATCH_W1
sta modify_pattern2+2
jsr _match
lda #0
adc #0
rol a
ldx P8ZP_SCRATCH_REG
rts

View File

@ -140,7 +140,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
xml.writeStartElement("BYTES")
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
xml.writeCharacters("\n")
chunk.data.withIndex().forEach {(index, byte) ->
xml.writeCharacters(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index < chunk.data.size-1)

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)
ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned)
bles reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (signed)
( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.)
sz reg1, reg2 - set reg1=1 if reg2==0, otherwise set reg1=0
snz reg1, reg2 - set reg1=1 if reg2!=0, otherwise set reg1=0
seq reg1, reg2 - set reg1=1 if reg1 == reg2, otherwise set reg1=0
sne reg1, reg2 - set reg1=1 if reg1 != reg2, otherwise set reg1=0
slt reg1, reg2 - set reg1=1 if reg1 < reg2 (unsigned), otherwise set reg1=0
slts reg1, reg2 - set reg1=1 if reg1 < reg2 (signed), otherwise set reg1=0
sle reg1, reg2 - set reg1=1 if reg1 <= reg2 (unsigned), otherwise set reg1=0
sles reg1, reg2 - set reg1=1 if reg1 <= reg2 (signed), otherwise set reg1=0
sgt reg1, reg2 - set reg1=1 if reg1 > reg2 (unsigned), otherwise set reg1=0
sgts reg1, reg2 - set reg1=1 if reg1 > reg2 (signed), otherwise set reg1=0
sge reg1, reg2 - set reg1=1 if reg1 >= reg2 (unsigned), otherwise set reg1=0
sges reg1, reg2 - set reg1=1 if reg1 >= reg2 (signed), otherwise set reg1=0
( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.)
sz reg1, reg2 - set reg1=1.b if reg2==0, otherwise set reg1=0.b
snz reg1, reg2 - set reg1=1.b if reg2!=0, otherwise set reg1=0.b
seq reg1, reg2 - set reg1=1.b if reg1 == reg2, otherwise set reg1=0.b
sne reg1, reg2 - set reg1=1.b if reg1 != reg2, otherwise set reg1=0.b
slt reg1, reg2 - set reg1=1.b if reg1 < reg2 (unsigned), otherwise set reg1=0.b
slts reg1, reg2 - set reg1=1.b if reg1 < reg2 (signed), otherwise set reg1=0.b
sle reg1, reg2 - set reg1=1.b if reg1 <= reg2 (unsigned), otherwise set reg1=0.b
sles reg1, reg2 - set reg1=1.b if reg1 <= reg2 (signed), otherwise set reg1=0.b
sgt reg1, reg2 - set reg1=1.b if reg1 > reg2 (unsigned), otherwise set reg1=0.b
sgts reg1, reg2 - set reg1=1.b if reg1 > reg2 (signed), otherwise set reg1=0.b
sge reg1, reg2 - set reg1=1.b if reg1 >= reg2 (unsigned), otherwise set reg1=0.b
sges reg1, reg2 - set reg1=1.b if reg1 >= reg2 (signed), otherwise set reg1=0.b
ARITHMETIC
----------
All have type b or w or f. Note: result types are the same as operand types! E.g. byte*byte->byte.
ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (ext.w not yet implemented as we don't have longs yet)
exts reg1 - reg1 = signed extension of reg1 (byte to word, or word to long) (note: ext.w is not yet implemented as we don't have longs yet)
ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (ext.w not yet implemented as we don't have longs yet)
inc reg1 - reg1 = reg1+1
incm address - memory at address += 1
dec reg1 - reg1 = reg1-1
@ -216,7 +216,6 @@ msig [b, w] reg1, reg2 - reg1 becomes the most significant by
concat [b, w] reg1, reg2 - reg1 = concatenated lsb/lsw of reg1 (as lsb) and lsb/lsw of reg2 (as msb) into word or int (int not yet implemented; requires 32bits regs)
push [b, w, f] reg1 - push value in reg1 on the stack
pop [b, w, f] reg1 - pop value from stack into reg1
binarydata - 'instruction' to hold inlined binary data bytes
*/
enum class Opcode {
@ -374,8 +373,7 @@ enum class Opcode {
POP,
MSIG,
CONCAT,
BREAKPOINT,
BINARYDATA
BREAKPOINT
}
val OpcodesThatJump = setOf(
@ -653,7 +651,6 @@ val instructionFormats = mutableMapOf(
Opcode.CLC to InstructionFormat.from("N"),
Opcode.SEC to InstructionFormat.from("N"),
Opcode.BREAKPOINT to InstructionFormat.from("N"),
Opcode.BINARYDATA to InstructionFormat.from("N"),
)
@ -667,9 +664,8 @@ data class IRInstruction(
val immediate: Int?=null, // 0-$ff or $ffff if word
val immediateFp: Float?=null,
val address: Int?=null, // 0-$ffff
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val binaryData: Collection<UByte>?=null,
var branchTarget: IRCodeChunkBase? = null // will be linked after loading
val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!)
var branchTarget: IRCodeChunkBase? = null // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.
) {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
// This knowledge is useful in IL assembly optimizers to see how registers are used.
@ -686,10 +682,6 @@ data class IRInstruction(
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
if(reg1!=null && reg2!=null) require(reg1!=reg2) {"reg1 must not be same as reg2"} // note: this is ok for fpRegs as these are always the same type
require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) {
"binarydata inconsistency"
}
val formats = instructionFormats.getValue(opcode)
require (type != null || formats.containsKey(null)) { "missing type" }

View File

@ -284,7 +284,6 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.BREAKPOINT -> InsBREAKPOINT()
Opcode.CLC -> { statusCarry = false; nextPc() }
Opcode.SEC -> { statusCarry = true; nextPc() }
Opcode.BINARYDATA -> TODO("BINARYDATA not yet supported in VM")
Opcode.LOADCPU -> InsLOADCPU(ins)
Opcode.STORECPU -> InsSTORECPU(ins)
Opcode.STOREZCPU -> InsSTOREZCPU(ins)

View File

@ -46,7 +46,7 @@ class VmProgramLoader {
is IRCodeChunk -> programChunks += child
is IRInlineAsmChunk -> {
val replacement = addAssemblyToProgram(child, programChunks, variableAddresses)
chunkReplacements += replacement
chunkReplacements += Pair(child, replacement)
}
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
is IRSubroutine -> {
@ -55,7 +55,7 @@ class VmProgramLoader {
when (chunk) {
is IRInlineAsmChunk -> {
val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses)
chunkReplacements += replacement
chunkReplacements += Pair(chunk, replacement)
}
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
is IRCodeChunk -> programChunks += chunk
@ -339,7 +339,7 @@ class VmProgramLoader {
asmChunk: IRInlineAsmChunk,
chunks: MutableList<IRCodeChunk>,
symbolAddresses: MutableMap<String, Int>,
): Pair<IRCodeChunkBase, IRCodeChunk> {
): IRCodeChunk {
if(asmChunk.isIR) {
val chunk = IRCodeChunk(asmChunk.label, asmChunk.next)
asmChunk.assembly.lineSequence().forEach {
@ -350,7 +350,7 @@ class VmProgramLoader {
)
}
chunks += chunk
return Pair(asmChunk, chunk)
return chunk
} else {
throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.label}")
}

View File

@ -70,22 +70,6 @@ class TestVm: FunSpec( {
vm.stepCount shouldBe code.instructions.size
}
test("vm asmbinary not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(startSub.label, null)
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
code += IRInstruction(Opcode.RETURN)
startSub += code
block += startSub
program.addBlock(block)
val vm = VirtualMachine(program)
shouldThrowWithMessage<NotImplementedError>("An operation is not implemented: BINARYDATA not yet supported in VM") {
vm.run()
}
}
test("asmsub not supported in vm even with IR") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)