Compare commits

..
32 Commits
Author SHA1 Message Date
Irmen de Jong 3caf9108ad finalizing 9.4.1 release 2023-09-06 21:18:01 +02:00
Irmen de Jong bde4be8231 fix VM indexed instructions to only use lsb part of the index 2023-09-06 02:44:04 +02:00
Irmen de Jong 0bbbb12ed2 fix bench8 examples 2023-09-05 23:40:54 +02:00
Irmen de Jong b570bdaed7 fix codegen for array[i] += float expression 2023-09-05 22:38:52 +02:00
Irmen de Jong 8c0843cc87 fix an invalid 6502 instruction on c64 in certain float assignment 2023-09-05 21:54:52 +02:00
Irmen de Jong 31458ffd81 examples cleanup and improving c64 graphics module (shift bitmap to higher ram area) 2023-09-05 20:39:12 +02:00
Irmen de Jong c15c10a94e fixed 'unroll CONSTANTEXPR' compiler errors 2023-09-05 01:03:35 +02:00
Irmen de Jong 9fca978725 optimized plasma examples even more 2023-09-05 00:23:50 +02:00
Irmen de Jong b125901717 added cx16 plasma example 2023-09-04 23:54:13 +02:00
Irmen de Jong eb018ae660 code optimization for bytearray[x] +/- bytearray[y]
use adc array,y or sbc array,y instead of tempvar
2023-09-04 23:01:53 +02:00
Irmen de Jong 7e5a9474fe improve plasma example 2023-09-04 20:35:43 +02:00
Irmen de Jong 525a9b5036 prepare parser to allow chained array indexing later 2023-09-03 19:06:47 +02:00
Irmen de Jong c3fbdf34ca fixed c64 float problem 2023-09-03 16:40:10 +02:00
Irmen de Jong 48bd51e1a5 c64 float problem 2023-09-03 16:29:01 +02:00
Irmen de Jong 10d0b03a90 use less tempvars 2023-09-03 01:32:47 +02:00
Irmen de Jong e1b3582f08 fix wordvar -= @(memory) 2023-09-03 01:12:26 +02:00
Irmen de Jong 95be1c9e22 fix optimized swapped in-place byte comparisons 2023-09-03 00:47:55 +02:00
Irmen de Jong 1ce8fe06d5 fix in-place <= for bytes 2023-09-03 00:01:11 +02:00
Irmen de Jong 15c649024e float problems on c64 2023-09-02 23:09:55 +02:00
Irmen de Jong e97303c226 fix word multiplication to not clobber r0 and r1 anymore
This was causing corruption in certain programs such as the cx16/amiga example.
The problem was introduced in 9.4 with the new multiply_words routine
2023-09-02 20:52:16 +02:00
Irmen de Jong 3b786c819d avoid using temp var even more 2023-09-01 23:47:01 +02:00
Irmen de Jong 04959dbd8b optimize asm: don't use temp var for some additions 2023-09-01 22:24:17 +02:00
Irmen de Jong 5cd4b874ea tweak sprites module 2023-09-01 21:25:19 +02:00
Irmen de Jong f14ea1b3de micro optimization to save 2 cycles: change some pha+pla into tax+txa 2023-09-01 20:37:24 +02:00
Irmen de Jong 9cc0cda0fb added sprites library module (cx16 only) 2023-09-01 17:35:07 +02:00
Irmen de Jong 09a7a4bbe5 optimize comparison against zero 2023-09-01 02:28:11 +02:00
Irmen de Jong cfea8b3745 save a cycle 2023-09-01 00:50:24 +02:00
Irmen de Jong 28bf0b61ce added math.log2() and math.log2w() 2023-09-01 00:42:15 +02:00
Irmen de Jong 2dc2429735 tweaks to the cx16 sprite example 2023-08-31 23:24:46 +02:00
Irmen de Jong 83d4592526 tweaks to the cx16 sprite example 2023-08-31 22:33:49 +02:00
Irmen de Jong 2d528c26ae added cx16 sprite demo 2023-08-31 16:56:52 +02:00
Irmen de Jong 66b3dce794 doc tweak 2023-08-30 13:16:39 +02:00
61 changed files with 1210 additions and 1077 deletions

No files matched your search

@@ -53,6 +53,6 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
}
override fun allocateCx16VirtualRegisters() {
TODO("Not known if C128 can put the virtual regs in ZP")
TODO("Not known if PET can put the virtual regs in ZP")
}
}
@@ -3031,7 +3031,11 @@ $repeatLabel""")
}
internal fun popFAC1() {
out(" jsr floats.popFAC1")
out(" clc | jsr floats.popFAC")
}
internal fun popFAC2() {
out(" sec | jsr floats.popFAC")
}
internal fun needAsaveForExpr(arg: PtExpression): Boolean =
@@ -43,6 +43,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++
}
mods = optimizeUnneededTempvarInAdd(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
var linesByFourteen = getLinesBy(lines, 14)
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
if(mods.isNotEmpty()) {
@@ -508,4 +515,27 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
optimize('y', lines)
}
return mods
}
}
private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
// this can be performed without the scratch variable: clc / adc something
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
val first = lines[0].value.trimStart()
val second = lines[1].value.trimStart()
val third = lines[2].value.trimStart()
val fourth = lines[3].value.trimStart()
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
if(fourth.substring(4)==first.substring(4)) {
mods.add(Modification(lines[0].index, false, " clc"))
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
return mods
}
@@ -742,11 +742,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out("""
ldy #0
lda ($varname),y
pha
tax
iny
lda ($varname),y
tay
pla""")
txa""")
}
} else fallback()
}
@@ -759,11 +759,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.out("""
lda ($varname),y
pha
tax
iny
lda ($varname),y
tay
pla""")
txa""")
} else fallback()
}
else -> fallback()
@@ -255,11 +255,11 @@ $loopLabel""")
sec
sbc #<${stepsize.absoluteValue}
sta $varname
pha
tax
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
pla
txa
$modifiedLabel2 cmp #0 ; modified
lda $varname+1
$modifiedLabel sbc #0 ; modified
@@ -142,12 +142,12 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
is PtIdentifier -> {
val sourceName = asmgen.asmVariableName(value)
asmgen.out("""
pha
tax
clc
lda $sourceName
beq +
sec
+ pla""")
+ txa""")
}
else -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
@@ -1,7 +1,10 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.ast.PtBinaryExpression
import prog8.code.ast.PtExpression
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.AsmGen6502Internal
//
@@ -180,37 +183,25 @@ internal class AnyExprAsmGen(
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
if(!expr.right.isSimple()) asmgen.popFAC1()
assignFloatOperandsToFACandARG(expr.left, expr.right)
asmgen.out(" jsr floats.FADDT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
if(!expr.left.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
if(!expr.left.isSimple()) asmgen.popFAC1()
assignFloatOperandsToFACandARG(expr.right, expr.left)
asmgen.out(" jsr floats.FSUBT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"*" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
if(!expr.right.isSimple()) asmgen.popFAC1()
assignFloatOperandsToFACandARG(expr.left, expr.right)
asmgen.out(" jsr floats.FMULTT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"/" -> {
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
if(!expr.left.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
if(!expr.left.isSimple()) asmgen.popFAC1()
assignFloatOperandsToFACandARG(expr.right, expr.left)
asmgen.out(" jsr floats.FDIVT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
@@ -255,6 +246,31 @@ internal class AnyExprAsmGen(
}
}
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
when(asmgen.options.compTarget.name) {
C64Target.NAME -> {
// c64 has a quirk: always make sure FAC2 is loaded last (done using CONUPK) otherwise the result will be corrupt on C64
// this requires some more forced copying around of float values in certain cases
if (right.isSimple()) {
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
} else {
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
asmgen.pushFAC1()
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.popFAC2()
}
}
Cx16Target.NAME -> {
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
if (!right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
if (!right.isSimple()) asmgen.popFAC1()
}
else -> TODO("don't know how to evaluate float expression for selected compilation target")
}
}
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
@@ -88,7 +88,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
} else {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.out(" lda ${arrayVarName}_lsb,y | pha | lda ${arrayVarName}_msb,y | tay | pla")
asmgen.out(" lda ${arrayVarName}_lsb,y | tax | lda ${arrayVarName}_msb,y | tay | txa")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return
@@ -122,7 +122,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | pha | lda $arrayVarName+1,y | tay | pla")
asmgen.out(" lda $arrayVarName,y | tax | lda $arrayVarName+1,y | tay | txa")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
DataType.FLOAT -> {
@@ -257,13 +257,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when (assign.target.datatype) {
DataType.STR -> {
asmgen.out("""
pha
lda #<${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1+1
pla
jsr prog8_lib.strcpy""")
tax
lda #<${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1+1
txa
jsr prog8_lib.strcpy""")
}
DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
else -> throw AssemblyError("str return value type mismatch with target")
@@ -306,14 +306,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sec
eor #255
adc #0
pha
tax
tya
eor #255
adc #0
tay
pla""")
txa""")
}
"~" -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
"~" -> asmgen.out(" tax | tya | eor #255 | tay | txa | eor #255")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
@@ -416,7 +416,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val translatedOk = when (expr.operator) {
in ComparisonOperators -> optimizedComparison(expr, assign)
in setOf("&", "|", "^", "and", "or", "xor") -> optimizedLogicalOrBitwiseExpr(expr, assign.target)
"==", "!=" -> optimizedEqualityExpr(expr, assign.target)
"+", "-" -> optimizedPlusMinExpr(expr, assign.target)
"<<", ">>" -> optimizedBitshiftExpr(expr, assign.target)
"*" -> optimizedMultiplyExpr(expr, assign.target)
@@ -544,7 +543,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true
}
in WordDatatypes -> {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r0")
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier")
asmgen.out(" jsr math.multiply_words")
assignRegisterpairWord(target, RegisterOrPair.AY)
return true
@@ -568,7 +567,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" jsr math.mul_word_${value}")
}
else {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r0")
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier")
asmgen.out(" jsr math.multiply_words")
}
assignRegisterpairWord(target, RegisterOrPair.AY)
@@ -740,15 +739,31 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true
}
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(target, CpuRegister.A, dt in SignedDatatypes)
val rightArrayIndexer = expr.right as? PtArrayIndexer
if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
// special optimization for bytevalue +/- bytearr[y] : no need to use a tempvar, just use adc array,y or sbc array,y
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
asmgen.out(" pla")
val arrayvarname = if(rightArrayIndexer.usesPointerVariable)
"(${rightArrayIndexer.variable.name})"
else
asmgen.asmSymbolName(rightArrayIndexer.variable)
if (expr.operator == "+")
asmgen.out(" clc | adc $arrayvarname,y")
else
asmgen.out(" sec | sbc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes)
} else {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
if (expr.operator == "+")
asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
else
asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes)
}
return true
}
}
@@ -760,20 +775,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
clc
adc P8ZP_SCRATCH_W1
pha
tax
tya
adc P8ZP_SCRATCH_W1+1
tay
pla""")
txa""")
else
asmgen.out("""
sec
sbc P8ZP_SCRATCH_W1
pha
tax
tya
sbc P8ZP_SCRATCH_W1+1
tay
pla""")
txa""")
assignRegisterpairWord(target, RegisterOrPair.AY)
}
@@ -785,20 +800,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
clc
adc #<$symbol
pha
tax
tya
adc #>$symbol
tay
pla""")
txa""")
else
asmgen.out("""
sec
sbc #<$symbol
pha
tax
tya
sbc #>$symbol
tay
pla""")
txa""")
assignRegisterpairWord(target, RegisterOrPair.AY)
return true
}
@@ -809,20 +824,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
clc
adc $symname
pha
tax
tya
adc $symname+1
tay
pla""")
txa""")
else
asmgen.out("""
sec
sbc $symname
pha
tax
tya
sbc $symname+1
tay
pla""")
txa""")
assignRegisterpairWord(target, RegisterOrPair.AY)
return true
}
@@ -832,20 +847,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
clc
adc #<${right.number.toHex()}
pha
tax
tya
adc #>${right.number.toHex()}
tay
pla""")
txa""")
} else if(expr.operator=="-") {
asmgen.out("""
sec
sbc #<${right.number.toHex()}
pha
tax
tya
sbc #>${right.number.toHex()}
tay
pla""")
txa""")
}
assignRegisterpairWord(target, RegisterOrPair.AY)
return true
@@ -888,63 +903,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return false
}
private fun optimizedEqualityExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
// expression datatype is BOOL (ubyte) but operands can be anything
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes &&
expr.left.isSimple() && expr.right.isSimple()) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") {
asmgen.out("""
cmp P8ZP_SCRATCH_B1
bne +
lda #1
bne ++
+ lda #0
+""")
} else {
asmgen.out("""
cmp P8ZP_SCRATCH_B1
beq +
lda #1
bne ++
+ lda #0
+""")
}
assignRegisterByte(target, CpuRegister.A, false)
return true
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
expr.left.isSimple() && expr.right.isSimple()) {
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
if(expr.operator=="==") {
asmgen.out("""
cmp P8ZP_SCRATCH_W1
bne +
cpy P8ZP_SCRATCH_W1+1
bne +
lda #1
bne ++
+ lda #0
+""")
} else {
asmgen.out("""
cmp P8ZP_SCRATCH_W1
bne +
cpy P8ZP_SCRATCH_W1+1
bne +
lda #0
beq ++
+ lda #1
+""")
}
assignRegisterByte(target, CpuRegister.A, false)
return true
}
return false
}
private fun optimizedLogicalOrBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
if (expr.right.isSimple()) {
@@ -984,9 +942,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
when (expr.operator) {
"&", "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")
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
else -> throw AssemblyError("invalid operator")
}
assignRegisterpairWord(target, RegisterOrPair.AY)
@@ -1085,7 +1043,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
// checked OK:
private fun byteLess(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteGreaterEqual
when(expr.right) {
@@ -1232,78 +1189,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
private fun byteLessEqualsWRONG(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteGreater
when(expr.right) {
is PtNumber -> {
// TODO verify if this is correct code on all corner cases
val number = (expr.right as PtNumber).number.toInt()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed)
asmgen.out("""
sec
sbc #$number
bvc +
eor #$80
+ bmi +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp #$number
lda #0
rol a
eor #1""")
}
is PtIdentifier -> {
// TODO verify if this is correct code on all corner cases
val varname = (expr.right as PtIdentifier).name
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed)
asmgen.out("""
sec
sbc $varname
bvc +
eor #$80
+ bmi +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp $varname
lda #0
rol a
eor #1""")
}
else -> {
// TODO verify if this is correct code on all corner cases
// note: left and right operands get reversed here to reduce code size
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
if(signed)
asmgen.out("""
sec
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bmi +
lda #1
bne ++
+ lda #0
+""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
lda #0
rol a""")
}
}
}
// checked OK:
private fun byteGreater(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteLessEqual
when(expr.right) {
@@ -1381,7 +1266,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
// checked OK:
private fun byteGreaterEquals(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteLess
when(expr.right) {
@@ -1621,18 +1505,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
is PtNumber -> {
val number = right.number.toHex()
when (operator) {
"&", "and" -> asmgen.out(" and #<$number | pha | tya | and #>$number | tay | pla")
"|", "or" -> asmgen.out(" ora #<$number | pha | tya | ora #>$number | tay | pla")
"^", "xor" -> asmgen.out(" eor #<$number | pha | tya | eor #>$number | tay | pla")
"&", "and" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa")
"|", "or" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa")
"^", "xor" -> asmgen.out(" eor #<$number | tax | tya | eor #>$number | tay | txa")
else -> throw AssemblyError("invalid operator")
}
}
is PtIdentifier -> {
val name = asmgen.asmSymbolName(right)
when (operator) {
"&", "and" -> asmgen.out(" and $name | pha | tya | and $name+1 | tay | pla")
"|", "or" -> asmgen.out(" ora $name | pha | tya | ora $name+1 | tay | pla")
"^", "xor" -> asmgen.out(" eor $name | pha | tya | eor $name+1 | tay | pla")
"&", "and" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa")
"|", "or" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa")
"^", "xor" -> asmgen.out(" eor $name | tax | tya | eor $name+1 | tay | txa")
else -> throw AssemblyError("invalid operator")
}
}
@@ -1642,6 +1526,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
// TODO optimized code for (word1 & word2) == 0 :
// if(expr.left.type in WordDatatypes && (expr.operator=="==" || expr.operator=="!=") && expr.left is PtBinaryExpression) {
// ...
// }
when (expr.operator) {
"==" -> {
when(val dt = expr.left.type) {
@@ -1649,10 +1538,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+""")
lda #1
+ eor #1""")
assignRegisterByte(assign.target, CpuRegister.A, false)
return true
}
@@ -1662,10 +1549,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #0
beq ++
+ lda #1
+""")
lda #1
+ eor #1""")
assignRegisterByte(assign.target, CpuRegister.A, false)
return true
}
@@ -1988,7 +1873,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return
}
// No more special optmized cases yet. Do the rest via more complex evaluation
// No more special optimized cases yet. Do the rest via more complex evaluation
// note: cannot use assignTypeCastedValue because that is ourselves :P
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
asmgen.assignExpressionTo(origTypeCastExpression, target)
@@ -2231,12 +2116,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(regs!=RegisterOrPair.AY)
throw AssemblyError("only supports AY here")
asmgen.out("""
pha
tax
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
pla
txa
jsr floats.cast_from_uw""")
}
else -> throw AssemblyError("weird type")
@@ -2259,12 +2144,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(regs!=RegisterOrPair.AY)
throw AssemblyError("only supports AY here")
asmgen.out("""
pha
tax
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
pla
txa
jsr floats.cast_from_w""")
}
else -> throw AssemblyError("weird type")
@@ -2698,12 +2583,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
pla""")
RegisterOrPair.AY -> asmgen.out("""
lda $sourceName
pha
tax
ora #$7f
bmi +
lda #0
+ tay
pla""")
txa""")
RegisterOrPair.XY -> asmgen.out("""
lda $sourceName
tax
@@ -3906,12 +3791,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sec
eor #255
adc #0
pha
tax
tya
eor #255
adc #0
tay
pla""")
txa""")
}
RegisterOrPair.XY -> {
asmgen.out("""
@@ -57,9 +57,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
// the asm-gen code can deal with situations where you want to assign a byte into a word.
// it will create the most optimized code to do this (so it type-extends for us).
// But we can't deal with writing a word into a byte - explicit typeconversion should be done
if(program.memsizer.memorySize(value.datatype) > program.memsizer.memorySize(target.datatype)) {
TODO("missing type cast: value type > target type ${target.position}")
}
if(program.memsizer.memorySize(value.datatype) > program.memsizer.memorySize(target.datatype))
throw AssemblyError("missing type cast: value type > target type ${target.position}")
fun regName(v: AsmAssignSource) = "cx16.${v.register!!.name.lowercase()}"
@@ -385,8 +384,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
// copy array value into tempvar
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
asmgen.out("""
ldy #>${target.asmVarname}
clc
@@ -395,9 +395,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
iny
+ sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
pha ; save array ptr lsb
tya
pha ; save array ptr msb
lda #<$tempvar
ldy #>$tempvar
jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y
jsr floats.copy_float""")
// calculate on tempvar
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
@@ -414,18 +419,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
}
// copy tempvar back into array
asmgen.out("""
lda P8ZP_SCRATCH_W1
pha
lda P8ZP_SCRATCH_W1+1
pha
lda #<$tempvar
ldy #>$tempvar
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ply
pla
jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y
pla ; restore array ptr msb
tay
pla ; restore array ptr lsb
jsr floats.copy_float""")
}
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
@@ -445,11 +449,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
clc
adc $variable
pha
tay
txa
adc $variable+1
tax
pla""")
tya""")
true
} else {
asmgen.out("""
@@ -469,11 +473,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
sec
sbc $variable
pha
tay
txa
sbc $variable+1
tax
pla""")
tya""")
true
} else {
asmgen.out("""
@@ -508,11 +512,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
clc
adc #<$number
pha
tay
txa
adc #>$number
tax
pla""")
tya""")
true
}
}
@@ -529,11 +533,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out("""
sec
sbc #<$number
pha
tay
txa
sbc #>$number
tax
pla""")
tya""")
true
}
}
@@ -708,10 +712,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
val tmpVar = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.assignExpressionToVariable(value, tmpVar, value.type)
asmgen.out(" lda $name")
inplacemodificationRegisterAwithVariable(operator, tmpVar, dt in SignedDatatypes)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
asmgen.out(" sta $name")
}
@@ -722,15 +724,16 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun inplacemodificationRegisterAwithVariable(operator: String, variable: String, signed: Boolean) {
// A = A <operator> variable
when (operator) {
"+" -> asmgen.out(" clc | adc $variable")
"-" -> asmgen.out(" sec | sbc $variable")
"*" -> asmgen.out(" ldy $variable | jsr math.multiply_bytes")
"/" -> {
if(signed)
asmgen.out(" ldy $variable | jsr math.divmod_b_asm | tya")
asmgen.out(" ldy $variable | jsr math.divmod_b_asm | tya")
else
asmgen.out(" ldy $variable | jsr math.divmod_ub_asm | tya")
asmgen.out(" ldy $variable | jsr math.divmod_ub_asm | tya")
}
"%" -> {
if(signed)
@@ -792,11 +795,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<" -> {
if(!signed) {
asmgen.out("""
tay
cmp $variable
bcc +
lda #0
cpy $variable
rol a
eor #1""")
beq ++
+ lda #1
+""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
@@ -815,10 +819,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<=" -> {
if(!signed) {
asmgen.out("""
tay
cmp $variable
bcc +
beq +
lda #0
ldy $variable
rol a""")
beq ++
+ lda #1
+""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@@ -881,6 +888,164 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
private fun inplacemodificationRegisterAwithVariableWithSwappedOperands(operator: String, variable: String, signed: Boolean) {
// A = variable <operator> A
if(operator in AssociativeOperators)
return inplacemodificationRegisterAwithVariable(operator, variable, signed) // just reuse existing code for associative operators
// now implement the non-assiciative operators...
when (operator) {
"-" -> {
// A = variable - A
val tmpVar = if(variable!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.out(" sta $tmpVar | lda $variable | sec | sbc $tmpVar")
}
"/" -> {
if(signed)
asmgen.out(" tay | lda $variable | jsr math.divmod_b_asm | tya")
else
asmgen.out(" tay | lda $variable | jsr math.divmod_ub_asm | tya")
}
"%" -> {
if(signed)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" tay | lda $variable | jsr math.divmod_ub_asm")
}
"<<" -> {
asmgen.out("""
tay
beq +
lda $variable
- asl a
dey
bne -
+""")
}
">>" -> {
if(!signed) {
asmgen.out("""
tay
beq +
lda $variable
- lsr a
dey
bne -
+""")
} else {
asmgen.out("""
tay
beq +
lda $variable
sta P8ZP_SCRATCH_B1
- asl a
ror P8ZP_SCRATCH_B1
lda P8ZP_SCRATCH_B1
dey
bne -
+""")
}
}
"<" -> {
// variable<A --> A>variable?
if(!signed) {
asmgen.out("""
tay
lda #0
cpy $variable
beq +
rol a
+""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
clc
sbc $variable
bvc +
eor #$80
+ bpl +
lda #0
beq ++
+ lda #1
+""")
}
}
"<=" -> {
// variable<=A --> A>=variable?
if(!signed) {
asmgen.out("""
tay
lda #0
cpy $variable
rol a""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
sec
sbc $variable
bvc +
eor #$80
+ bpl +
lda #0
beq ++
+ lda #1
+""")
}
}
">" -> {
// variable>A --> A<variable?
if(!signed) {
asmgen.out("""
cmp $variable
bcc +
lda #0
beq ++
+ lda #1
+""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
sec
sbc $variable
bvc +
eor #$80
+ bmi +
lda #0
beq ++
+ lda #1
+""")
}
}
">=" -> {
// variable>=A --> A<=variable?
if(!signed) {
asmgen.out("""
cmp $variable
bcc +
beq +
lda #0
beq ++
+ lda #1
+""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
clc
sbc $variable
bvc +
eor #$80
+ bmi +
lda #0
beq ++
+ lda #1
+""")
}
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
private fun inplacemodificationByteVariableWithLiteralval(name: String, dt: DataType, operator: String, value: Int) {
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
when (operator) {
@@ -1080,6 +1245,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
+""")
}
"-" -> {
// name -= @(memory)
asmgen.translateDirectMemReadExpressionToRegA(memread)
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.out("""
@@ -1088,7 +1254,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sec
sbc $tmpByte
sta $name
bcc +
bcs +
dec $name+1
+""")
}
@@ -1178,9 +1344,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} else {
asmgen.out("""
lda $name
sta cx16.r0
sta math.multiply_words.multiplier
lda $name+1
sta cx16.r0+1
sta math.multiply_words.multiplier+1
lda #<$value
ldy #>$value
jsr math.multiply_words
@@ -1629,15 +1795,15 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
"*" -> {
if(valueDt==DataType.UBYTE) {
asmgen.out(" lda $otherName | sta cx16.r0")
asmgen.out(" lda $otherName | sta math.multiply_words.multiplier")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.r0+1")
asmgen.out(" stz math.multiply_words.multiplier+1")
else
asmgen.out(" lda #0 | sta cx16.r0+1")
asmgen.out(" lda #0 | sta math.multiply_words.multiplier+1")
} else {
asmgen.out(" lda $otherName")
asmgen.signExtendAYlsb(valueDt)
asmgen.out(" sta cx16.r0 | sty cx16.r0+1")
asmgen.out(" sta math.multiply_words.multiplier | sty math.multiply_words.multiplier+1")
}
asmgen.out("""
lda $name
@@ -1773,31 +1939,16 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
"*" -> {
if(otherName=="cx16.r0")
asmgen.out("""
lda $name
ldy $name+1
jsr math.multiply_words
sta $name
sty $name+1""")
else if(name=="cx16.r0")
asmgen.out("""
lda $otherName
ldy $otherName+1
jsr math.multiply_words
sta $name
sty $name+1""")
else
asmgen.out("""
lda $otherName
ldy $otherName+1
sta cx16.r0
sty cx16.r0+1
lda $name
ldy $name+1
jsr math.multiply_words
sta $name
sty $name+1""")
asmgen.out("""
lda $otherName
ldy $otherName+1
sta math.multiply_words.multiplier
sty math.multiply_words.multiplier+1
lda $name
ldy $name+1
jsr math.multiply_words
sta $name
sty $name+1""")
}
"/" -> {
if(dt==DataType.WORD) {
@@ -1978,8 +2129,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
fun multiplyVarByWordInAY() {
asmgen.out("""
sta cx16.r0
sty cx16.r0+1
sta math.multiply_words.multiplier
sty math.multiply_words.multiplier+1
lda $name
ldy $name+1
jsr math.multiply_words
@@ -1990,12 +2141,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
fun divideVarByWordInAY() {
asmgen.out("""
pha
tax
lda $name
sta P8ZP_SCRATCH_W1
lda $name+1
sta P8ZP_SCRATCH_W1+1
pla""")
txa""")
if (dt == DataType.WORD)
asmgen.out(" jsr math.divmod_w_asm")
else
@@ -2007,12 +2158,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
pha
tax
lda $name
sta P8ZP_SCRATCH_W1
lda $name+1
sta P8ZP_SCRATCH_W1+1
pla
txa
jsr math.divmod_uw_asm
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
@@ -2026,20 +2177,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
// the other variable is a BYTE type so optimize for that
when (operator) {
"+" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt)
if(valueDt==DataType.UBYTE)
// name += byteexpression
if(valueDt==DataType.UBYTE) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
asmgen.out("""
lda $name
clc
adc P8ZP_SCRATCH_B1
adc $name
sta $name
bcc +
inc $name+1
+""")
else
} else {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, true)
asmgen.out("""
ldy #0
lda P8ZP_SCRATCH_B1
cmp #0
bpl +
dey ; sign extend
+ clc
@@ -2048,8 +2200,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
tya
adc $name+1
sta $name+1""")
}
}
"-" -> {
// name -= byteexpression
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt)
if(valueDt==DataType.UBYTE)
asmgen.out("""
@@ -549,6 +549,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
return exprGen.translateExpression(call.args.single())
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
// TODO to be more strict, maybe we *should* introduce a new result register that is of type .b?
}
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@@ -628,5 +628,4 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
return null
}
}
}
@@ -384,7 +384,8 @@ class StatementOptimizer(private val program: Program,
}
override fun before(unrollLoop: UnrollLoop, parent: Node): Iterable<IAstModification> {
return if(unrollLoop.iterations<1)
val iterations = unrollLoop.iterations.constValue(program)?.number?.toInt()
return if(iterations!=null && iterations<1)
listOf(IAstModification.Remove(unrollLoop, parent as IStatementContainer))
else
noModifications
+7 -3
View File
@@ -418,8 +418,9 @@ pushFAC1 .proc
rts
.pend
popFAC1 .proc
; -- pop floating point value from cpu stack into FAC1
popFAC .proc
; -- pop floating point value from cpu stack into FAC1 or FAC2 (
; carry flag clear=FAC1, carry set=FAC2
; save return address
pla
sta P8ZP_SCRATCH_W2
@@ -437,8 +438,11 @@ popFAC1 .proc
sta floats.floats_temp_var
lda #<floats.floats_temp_var
ldy #>floats.floats_temp_var
bcs +
jsr floats.MOVFM
; re-push return address
jmp ++
+ jsr floats.CONUPK
+ ; re-push return address
lda P8ZP_SCRATCH_W2+1
pha
lda P8ZP_SCRATCH_W2
+21 -14
View File
@@ -1,35 +1,42 @@
%import textio
; bitmap pixel graphics module for the C64
; only black/white monochrome 320x200 for now
; assumes bitmap screen memory is $2000-$3fff
;
; Sets character matrix and bitmap screen memory at a higher memory location $5c00-$7fff
; so that the program itself can be larger without starting to overwrite the graphics memory.
graphics {
%option no_symbol_prefixing
const uword BITMAP_ADDRESS = $2000
const uword WIDTH = 320
const ubyte HEIGHT = 200
const uword BITMAP_ADDRESS = $6000 ; MUST BE IN REGULAR RAM if you are not messing with ROM/RAM banking.
const uword CHARS_ADDRESS = $5c00 ; must be in same vic bank as the bitmap
sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white.
c64.SCROLY = %00111011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
clear_screen(1, 0)
c64.SCROLY = %00111011 ; enable bitmap graphics mode
c64.SCROLX = %00001000 ; 40 column mode, no scrolling, multicolor bitmap off
c64.VMCSB = (lsb(CHARS_ADDRESS >> 6) & $F0) | (((BITMAP_ADDRESS & $3fff) / $0800) << 1) ; set bitmap address
c64.CIA2DDRA |= %11
c64.CIA2PRA = lsb(BITMAP_ADDRESS >> 14) ^ 3 ; set VIC bank.
}
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
c64.SCROLY = %00011011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
txt.fill_screen(' ', 1)
; enables erase the text screen, text mode
sys.memset(CHARS_ADDRESS, 40*25, sc:' ')
c64.SCROLY = %00011011 ; disable bitmap graphics mode
c64.SCROLX = %00001000 ; 40 column mode, no scrolling
c64.VMCSB = %00010100 ; screen addresses back to defaults
c64.CIA2DDRA |= %11
c64.CIA2PRA = %11 ; back to VIC bank 0.
}
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
sys.memset(BITMAP_ADDRESS, 320*200/8, 0)
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
sys.memset(CHARS_ADDRESS, 40*25, pixelcolor << 4 | bgcolor)
sys.memset(cbm.Colors, 40*25, 255)
}
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
@@ -352,7 +359,7 @@ _ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
_plot_y_values := BITMAP_ADDRESS + 320*(range(200)>>3) + (range(200) & 7)
_y_lookup_lo .byte <_plot_y_values
_y_lookup_hi .byte >_plot_y_values
+1
View File
@@ -136,6 +136,7 @@ c64 {
%option no_symbol_prefixing
; the default locations of the 8 sprite pointers (store address of sprite / 64)
; (depending on the VIC bank and screen ram address selection these can be shifted around though)
&ubyte SPRPTR0 = 2040
&ubyte SPRPTR1 = 2041
&ubyte SPRPTR2 = 2042
+1 -1
View File
@@ -29,7 +29,7 @@ graphics {
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
void cx16.screen_mode(0, false)
txt.fill_screen(' ', 1) ; doesn't seem to fully clear the text screen after returning from gfx mode
txt.clear_screen()
}
+110
View File
@@ -0,0 +1,110 @@
; Simple routines to control sprites.
; They're not written for high performance, but for simplicity.
; That's why they control 1 sprite at a time. The exception is pos_batch().
; which is quite efficient to update sprite positions of multiple sprites in one go.
; note: sprites z-order will be in front of all layers.
; note: collision mask is not supported here yet.
sprites {
const ubyte SIZE_8 = 0
const ubyte SIZE_16 = 1
const ubyte SIZE_32 = 2
const ubyte SIZE_64 = 3
const ubyte COLORS_16 = 0
const ubyte COLORS_256 = 128
const uword VERA_SPRITEREGS = $fc00 ; $1fc00
uword @zp sprite_reg
sub init(ubyte spritenum,
ubyte databank, uword dataaddr,
ubyte width_flag, ubyte height_flag,
ubyte colors_flag, ubyte palette_offset_idx) {
hide(spritenum)
cx16.VERA_DC_VIDEO |= %01000000 ; enable sprites globally
dataaddr >>= 5
dataaddr |= (databank as uword)<<11
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
cx16.vpoke(1, sprite_reg, lsb(dataaddr)) ; address 12:5
cx16.vpoke(1, sprite_reg+1, colors_flag | msb(dataaddr)) ; 4 bpp + address 16:13
cx16.vpoke(1, sprite_reg+6, %00001100) ; z depth %11 = in front of both layers, no flips
cx16.vpoke(1, sprite_reg+7, height_flag<<6 | width_flag<<4 | palette_offset_idx>>4) ; 64x64 pixels, palette offset
}
sub data(ubyte spritenum, ubyte bank, uword addr) {
addr >>= 5
addr |= (bank as uword)<<11
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
cx16.vpoke(1, sprite_reg, lsb(addr)) ; address 12:5
cx16.vpoke_mask(1, sprite_reg+1, %11110000, msb(addr)) ; address 16:13
}
sub pos(ubyte spritenum, word xpos, word ypos) {
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
cx16.vpoke(1, sprite_reg, lsb(xpos))
cx16.vpoke(1, sprite_reg+1, msb(xpos))
cx16.vpoke(1, sprite_reg+2, lsb(ypos))
cx16.vpoke(1, sprite_reg+3, msb(ypos))
}
sub pos_batch(ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr) {
; -- note: the x and y positions word arrays must be regular arrays, they cannot be @split arrays!
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites {
cx16.VERA_DATA0 = @(xpositions_ptr)
xpositions_ptr ++
cx16.VERA_DATA1 = @(xpositions_ptr)
xpositions_ptr ++
}
sprite_reg += 2
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites {
cx16.VERA_DATA0 = @(ypositions_ptr)
ypositions_ptr ++
cx16.VERA_DATA1 = @(ypositions_ptr)
ypositions_ptr ++
}
}
sub setx(ubyte spritenum, word xpos) {
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
cx16.vpoke(1, sprite_reg, lsb(xpos))
cx16.vpoke(1, sprite_reg+1, msb(xpos))
}
sub sety(ubyte spritenum, word ypos) {
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
cx16.vpoke(1, sprite_reg, lsb(ypos))
cx16.vpoke(1, sprite_reg+1, msb(ypos))
}
sub getx(ubyte spritenum) -> word {
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
}
sub gety(ubyte spritenum) -> word {
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
}
sub hide(ubyte spritenum) {
pos(spritenum, -64, -64)
}
sub flipx(ubyte spritenum, bool flipped) {
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111110, flipped)
}
sub flipy(ubyte spritenum, bool flipped) {
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111101, flipped<<1)
}
sub set_palette_offset(ubyte spritenum, ubyte offset) {
sprite_reg = VERA_SPRITEREGS + 7 + spritenum*$0008
cx16.vpoke_mask(1, sprite_reg, %11110000, offset>>4)
}
}
+5 -3
View File
@@ -395,6 +395,7 @@ romsub $febd = kbdbuf_peek() -> ubyte @A, ubyte @X ; key in A, queue length
romsub $febd = kbdbuf_peek2() -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
romsub $fec0 = kbdbuf_get_modifiers() -> ubyte @A
romsub $fec3 = kbdbuf_put(ubyte key @A) clobbers(X)
romsub $fed2 = keymap(uword identifier @XY, bool read @Pc) -> bool @Pc
romsub $ff68 = mouse_config(ubyte shape @A, ubyte resX @X, ubyte resY @Y) clobbers (A, X, Y)
romsub $ff6b = mouse_get(ubyte zpdataptr @X) -> ubyte @A
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
@@ -403,10 +404,10 @@ romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
; Audio (rom bank 10)
romsub $C04B = psg_init() clobbers(A,X,Y)
romsub $C04B = psg_init() clobbers(A,X,Y) ; (re)init Vera PSG
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize PSG and YM audio chips
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize both vera PSG and YM audio chips
; TODO: add more of the audio routines?
@@ -515,7 +516,8 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
; note: inefficient when reading multiple sequential bytes!
%asm {{
pha
lda #1
lda cx16.VERA_CTRL
ora #1
sta cx16.VERA_CTRL
pla
and #1
+11 -5
View File
@@ -56,8 +56,12 @@ _multiplier = P8ZP_SCRATCH_REG
multiply_words .proc
; -- multiply two 16-bit words into a 32-bit result (signed and unsigned)
; input: A/Y = first 16-bit number, cx16.R0 = second 16-bit number
; output: multiply_words.result == cx16.R0:R1, 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY.
; input: A/Y = first 16-bit number, multiply_words.multiplier = second 16-bit number
; output: multiply_words.result, 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY.
; NOTE: the result (which includes the multiplier parameter on entry) is a 4-byte array.
; this routine could be faster if we could stick that into zeropage,
; but there currently is no way to use 4 consecutive bytes in ZP (without disabling irq and saving/restoring them)...
; mult62.a
; based on Dr Jefyll, http://forum.6502.org/viewtopic.php?f=9&t=689&start=0#p19958
@@ -72,9 +76,8 @@ multiply_words .proc
; Average cycles:
; 93 bytes
_multiplicand = P8ZP_SCRATCH_W1 ; 2 bytes
_multiplier = cx16.r0 ; 2 bytes
result = cx16.r0 ; 4 bytes (note: shares memory with multiplier) so is r0 and ALSO r1.
_multiplicand = P8ZP_SCRATCH_W2 ; 2 bytes
multiplier = result
; 16 bit x 16 bit unsigned multiply, 32 bit result
;
@@ -174,6 +177,9 @@ _inner_loop2
lda result
ldy result+1
rts
result .byte 0,0,0,0
.pend
+41
View File
@@ -98,6 +98,47 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
}}
}
asmsub log2(ubyte value @A) -> ubyte @Y {
%asm {{
sta P8ZP_SCRATCH_B1
lda #$80
ldy #7
- bit P8ZP_SCRATCH_B1
beq +
rts
+ dey
bne +
rts
+ lsr a
bne -
}}
}
asmsub log2w(uword value @AY) -> ubyte @Y {
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$8000
sta cx16.r0
lda #>$8000
sta cx16.r0+1
ldy #15
- lda P8ZP_SCRATCH_W1
and cx16.r0
sta P8ZP_SCRATCH_B1
lda P8ZP_SCRATCH_W1+1
and cx16.r0+1
ora P8ZP_SCRATCH_B1
beq +
rts
+ dey
bne +
rts
+ lsr cx16.r0+1
ror cx16.r0
jmp -
}}
}
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
+25
View File
@@ -183,6 +183,31 @@ math {
}}
}
sub log2(ubyte value) -> ubyte {
ubyte result = 7
ubyte compare = $80
repeat {
if value&compare
return result
result--
if_z
return 0
compare >>= 1
}
}
sub log2w(uword value) -> ubyte {
ubyte result = 15
uword compare = $8000
repeat {
if value&compare
return result
result--
if_z
return 0
compare >>= 1
}
}
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
@@ -76,14 +76,19 @@ internal class AstChecker(private val program: Program,
}
override fun visit(unrollLoop: UnrollLoop) {
if(unrollLoop.iterations<0 || unrollLoop.iterations>65535)
errors.err("invalid number of unrolls", unrollLoop.position)
unrollLoop.body.statements.forEach {
if(it !is InlineAssembly && it !is Assignment && it !is BuiltinFunctionCallStatement && it !is FunctionCallStatement && it !is PostIncrDecr)
errors.err("invalid statement in unroll loop", it.position)
}
if(unrollLoop.iterations * unrollLoop.body.statements.size > 256) {
errors.warn("large number of unrolls, potential code size issue", unrollLoop.position)
val iterations = unrollLoop.iterations.constValue(program)?.number?.toInt()
if(iterations==null) {
errors.err("unroll needs constant number of iterations", unrollLoop.position)
} else {
if (iterations < 0 || iterations > 65535)
errors.err("invalid number of unrolls", unrollLoop.position)
unrollLoop.body.statements.forEach {
if (it !is InlineAssembly && it !is Assignment && it !is BuiltinFunctionCallStatement && it !is FunctionCallStatement && it !is PostIncrDecr)
errors.err("invalid statement in unroll loop", it.position)
}
if (iterations * unrollLoop.body.statements.size > 256) {
errors.warn("large number of unrolls, potential code size issue", unrollLoop.position)
}
}
super.visit(unrollLoop)
}
@@ -306,7 +311,7 @@ internal class AstChecker(private val program: Program,
if(subroutine.name in BuiltinFunctions)
err("cannot redefine a built-in function")
if(subroutine.parameters.size>6 && !subroutine.isAsmSubroutine)
if(subroutine.parameters.size>6 && !subroutine.isAsmSubroutine && !subroutine.definingBlock.isInLibrary)
errors.warn("subroutine has a large number of parameters, this slows down code execution a lot", subroutine.position)
val uniqueNames = subroutine.parameters.asSequence().map { it.name }.toSet()
@@ -78,6 +78,16 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val typeCast = binExpr.left as? TypecastExpression
if(typeCast!=null && typeCast.expression isSameAs assignment.target)
return noModifications
if(binExpr.operator in "+-") {
val leftDt = binExpr.left.inferType(program)
val rightDt = binExpr.right.inferType(program)
if(leftDt==rightDt && leftDt.isInteger && rightDt.isInteger && binExpr.right is ArrayIndexedExpression) {
// don't split array[i] +/- array[i] (the codegen has an optimized path for this)
return noModifications
}
}
val sourceDt = binExpr.left.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
val (_, left) = binExpr.left.typecastTo(assignment.target.inferType(program).getOrElse { throw AssemblyError(
"unknown dt"
@@ -326,7 +326,7 @@ class IntermediateAstMaker(private val program: Program) {
private fun transform(srcUnroll: UnrollLoop): PtNodeGroup {
val result = PtNodeGroup()
repeat(srcUnroll.iterations) {
repeat(srcUnroll.iterations.constValue(program)!!.number.toInt()) {
srcUnroll.body.statements.forEach {
result.add(transformStatement(it))
}
@@ -143,8 +143,6 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
private fun simplifyConditionalExpression(expr: BinaryExpression): CondExprSimplificationResult {
// TODO: somehow figure out if the expr will result in stack-evaluation STILL after being split off,
// in that case: do *not* split it off but just keep it as it is (otherwise code size increases)
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
if(compTarget.name == VMTarget.NAME) // don't apply this optimization for Vm target
+4 -5
View File
@@ -98,6 +98,8 @@ class TestCompilerOnExamplesCx16: FunSpec({
"vtui/testvtui",
"pcmaudio/play-adpcm",
"pcmaudio/stream-wav",
"sprites/dragon",
"sprites/dragons",
"amiga",
"bdmusic",
"bobs",
@@ -107,17 +109,14 @@ class TestCompilerOnExamplesCx16: FunSpec({
"colorbars",
"cube3d",
"cxlogo",
"datetime",
"diskspeed",
"fileseek",
"highresbitmap",
"kefrenbars",
"keyboardhandler",
"mandelbrot",
"mandelbrot-gfx-colors",
"multipalette",
"plasma",
"rasterbars",
"sincos",
"snow",
"spotlight",
"tehtriz",
@@ -142,7 +141,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
listOf(
"animals",
"balls",
"bsieve",
"cube3d",
"cube3d-float",
"cube3d-gfx",
@@ -156,6 +154,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
"numbergame",
"primes",
"screencodes",
"sincos",
"sorting",
"swirl",
"swirl-float",
@@ -317,9 +317,10 @@ private fun Prog8ANTLRParser.Assign_targetContext.toAst() : AssignTarget {
is MemoryTargetContext ->
AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()), toPosition())
is ArrayindexedTargetContext -> {
val arrayvar = scoped_identifier().toAst()
val index = arrayindex().toAst()
val arrayindexed = ArrayIndexedExpression(arrayvar, index, scoped_identifier().toPosition())
val ax = arrayindexed()
val arrayvar = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst()
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
AssignTarget(null, arrayindexed, null, toPosition())
}
else -> throw FatalAstException("weird assign target node $this")
@@ -434,10 +435,11 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression {
}
}
if(arrayindex()!=null) {
val identifier = scoped_identifier().toAst()
val index = arrayindex().toAst()
return ArrayIndexedExpression(identifier, index, scoped_identifier().toPosition())
if(arrayindexed()!=null) {
val ax = arrayindexed()
val identifier = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst()
return ArrayIndexedExpression(identifier, index, ax.toPosition())
}
if(scoped_identifier()!=null)
@@ -581,7 +583,7 @@ private fun Prog8ANTLRParser.RepeatloopContext.toAst(): RepeatLoop {
}
private fun Prog8ANTLRParser.UnrollloopContext.toAst(): UnrollLoop {
val iterations = integerliteral().toAst().number.toInt()
val iterations = expression().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
@@ -931,11 +931,13 @@ class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override
iterations?.referencesIdentifier(nameInSource)==true || body.referencesIdentifier(nameInSource)
}
class UnrollLoop(val iterations: Int, var body: AnonymousScope, override val position: Position) : Statement() {
class UnrollLoop(var iterations: Expression, var body: AnonymousScope, override val position: Position) : Statement() {
// note: the iterations needs to evaluate to a constant number once parsed.
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
iterations.linkParents(this)
body.linkParents(this)
}
@@ -943,13 +945,14 @@ class UnrollLoop(val iterations: Int, var body: AnonymousScope, override val pos
override fun replaceChildNode(node: Node, replacement: Node) {
if (node===body) body = replacement as AnonymousScope
else if (node===iterations) iterations = replacement as Expression
else throw FatalAstException("invalid replace")
replacement.parent = this
}
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun referencesIdentifier(nameInSource: List<String>): Boolean = body.referencesIdentifier(nameInSource)
override fun referencesIdentifier(nameInSource: List<String>): Boolean = iterations.referencesIdentifier(nameInSource) || body.referencesIdentifier(nameInSource)
}
class UntilLoop(var body: AnonymousScope,
@@ -398,6 +398,7 @@ abstract class AstWalker {
fun visit(unrollLoop: UnrollLoop, parent: Node) {
track(before(unrollLoop, parent), unrollLoop, parent)
unrollLoop.iterations.accept(this, unrollLoop)
unrollLoop.body.accept(this, unrollLoop)
track(after(unrollLoop, parent), unrollLoop, parent)
}
+6 -7
View File
@@ -52,7 +52,7 @@ Language features
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
It generates a machine code program runnable on actual 8-bit 6502 hardware.
- Fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
- Fast execution speed due to compilation to native assembly code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects.
- Provides a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
@@ -62,19 +62,18 @@ Language features
and inline assembly to have full control when every register, cycle or byte matters
- Subroutines with parameters and return values
- Complex nested expressions are possible
- Variables are allocated statically
- Variables are all allocated statically
- Conditional branches to map directly on processor branch instructions
- ``when`` statement to avoid if-else chains
- ``in`` expression for concise and efficient multi-value/containment test
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Several powerful built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``, ``sort`` and ``reverse``
- Variable data types include signed and unsigned bytes and words, arrays, strings.
- Floating point math also supported on select compiler targets (C64, Cx16 and virtual).
- Floating point math is supported on select compiler targets.
- Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents.
- High-level code optimizations, such as const-folding (zero-allocation constants that are optimized away in expressions), expression and statement simplifications/rewriting.
- Many built-in functions, such as ``sin``, ``cos``, ``abs``, ``sqrt``, ``msb``, ``min``, ``max``, ``rol``, ``ror``, ``sort`` and ``reverse``
- Programs can be run multiple times without reloading because of automatic variable (re)initializations.
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the other machines.
- If you only use standard Kernal and core prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compilation target flag)!
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 as defined on the Commander X16, also on the other machines.
- If you only use standard Kernal and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines (just change the compilation target flag)
Code example
+20 -4
View File
@@ -326,10 +326,9 @@ Bitmap graphics routines:
- drawing individual pixels
- drawing lines, rectangles, filled rectangles, circles, discs
This library is available both on the C64 and the Cx16.
This library is available both on the C64 and the cx16.
It uses the ROM based graphics routines on the latter, and it is a very small library because of that.
That also means though that it is constrained to 320*200 resolution on the Cx16 as well.
Use the ``gfx2`` library if you want full-screen graphics or non-monochrome drawing (only on Cx16). See below for that one.
On the cx16 there's also the ``gfx2`` library if you want full-screen graphics or non-monochrome drawing. See below for that one.
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/c64/graphics.p8>`_
to see what's in there. (Note: slight variations for different compiler targets)
@@ -343,6 +342,11 @@ Various 8-bit integer trig functions that use lookup tables to quickly calculate
Usually a custom lookup table is the way to go if your application needs these,
but perhaps the provided ones can be of service too.
``log2 (ubyte v)``
Returns the 2-Log of the byte value v.
``log2w (uword v)``
Returns the 2-Log of the word value v.
``rnd ()``
Returns next random byte 0-255 from the pseudo-RNG sequence.
@@ -467,5 +471,17 @@ It includes an interrupt routine to handle simple Attack/Release envelopes as we
See the examples/cx16/bdmusic.p8 program for ideas how to use it.
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/psg.p8>`_
to see what's in there. (Note: slight variations for different compiler targets)
to see what's in there.
sprites (cx16 only)
--------------------
Available for the Cx16 target. Simple routines to manipulate sprites.
They're not written for high performance, but for simplicity.
That's why they control one sprite at a time. The exception is the ``pos_batch`` routine,
which is quite efficient to update sprite positions of multiple sprites in one go.
See the examples/cx16/sprites/dragon.p8 and dragons.p8 programs for ideas how to use it.
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/sprites.p8>`_
to see what's in there.
+5 -3
View File
@@ -1,9 +1,7 @@
TODO
====
- prefix prog8 subroutines with p8s_ instead of p8_ to not let them clash with variables in the asm?
- allow 'chained' array indexing for expressions: value = ptrarray[0][0]
- allow 'chained' array indexing for assign targets: ptrarray[0][0] = 42 this is just evaluating the lhs as a uword pointer expression
- prefix prog8 subroutines with p8s_ instead of p8_ to not let them clash with variables in the asm??
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
- IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction
- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified!
@@ -22,6 +20,9 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- allow 'chained' array indexing for expressions: value = ptrarray[0][0]
- allow 'chained' array indexing for assign targets: ptrarray[0][0] = 42 this is just evaluating the lhs as a uword pointer expression
- [much work:] more support for (64tass) SEGMENTS ?
- (What, how, isn't current BSS support enough?)
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
@@ -57,6 +58,7 @@ Libraries:
Optimizations:
- optimize assembly output for ( word1 & word2 ==0) ... (no need for stack pushes) see attemptAssignToByteCompareZero().
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
+7 -6
View File
@@ -1,11 +1,12 @@
%import textio
%import floats
main {
sub crc16(uword data, uword length) -> uword {
uword crc = 0
repeat length {
crc ^= @(data) << $0008
crc ^= mkword(@(data), 0)
repeat 8 {
if crc & $8000
crc = (crc<<1)^$1021
@@ -18,13 +19,13 @@ main {
}
sub start() {
txt.print("calculating...")
txt.print("calculating (expecting $ffd0)...")
cbm.SETTIM(0,0,0)
uword crc = crc16($e000, $2000)
txt.print_uwhex(crc, true) ; should be $ffd0
txt.print_uwhex(crc, true)
txt.nl()
txt.print_uw(cbm.RDTIM16())
txt.print(" jiffies")
sys.wait(100)
floats.print_f(cbm.RDTIM16() / 60.0)
txt.print(" seconds")
sys.wait(9999)
}
}
+6 -5
View File
@@ -1,4 +1,5 @@
%import textio
%import floats
main {
sub crc32(uword data, uword length) {
@@ -7,7 +8,7 @@ main {
cx16.r0 = 0
cx16.r1 = 0
repeat length {
cx16.r0 ^= @(data) << $0008
cx16.r0 ^= mkword(@(data), 0)
repeat 8 {
if cx16.r0 & $8000 {
sys.clear_carry()
@@ -29,14 +30,14 @@ main {
}
sub start() {
txt.print("calculating...")
txt.print("calculating (expecting $e1fa84c6)...\n")
cbm.SETTIM(0,0,0)
crc32($e000, $2000)
txt.print_uwhex(cx16.r0, true)
txt.print_uwhex(cx16.r1, false)
txt.nl()
txt.print_uw(cbm.RDTIM16())
txt.print(" jiffies")
sys.wait(100)
floats.print_f(cbm.RDTIM16() / 60.0)
txt.print(" seconds")
sys.wait(9999)
}
}
+6 -5
View File
@@ -1,4 +1,5 @@
%import textio
%import floats
main {
@@ -18,13 +19,13 @@ main {
}
sub start() {
txt.print("calculating...")
txt.print("calculating (expecting $a2)...")
cbm.SETTIM(0,0,0)
ubyte crc = crc8($e000, $2000)
txt.print_ubhex(crc, true) ; should be $a2
txt.print_ubhex(crc, true)
txt.nl()
txt.print_uw(cbm.RDTIM16())
txt.print(" jiffies")
sys.wait(100)
floats.print_f(cbm.RDTIM16() / 60.0)
txt.print(" seconds")
sys.wait(9999)
}
}
+5 -5
View File
@@ -18,10 +18,10 @@ main {
}
sub start() {
txt.print("calculating...")
txt.print("calculating (expecting 3.614007e+12)...\n")
cbm.SETTIM(0,0,0)
float res
float res=0.0
uword i
ubyte j
for i in 0 to N_ITER-1 {
@@ -35,8 +35,8 @@ main {
floats.print_f(res)
txt.nl()
txt.print_uw(cbm.RDTIM16())
txt.print(" jiffies")
sys.wait(100)
floats.print_f(cbm.RDTIM16() / 60.0)
txt.print(" seconds")
sys.wait(9999)
}
}
+64
View File
@@ -0,0 +1,64 @@
%import textio
%import floats
main {
const ubyte N_ITER = 4
const uword SIZE = 16000
uword @zp flags_ptr = memory("flags", SIZE/8+1, $100)
ubyte[] bitv = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
sub start() {
txt.print_ub(N_ITER)
txt.print(" iterations, calculating... (expecting 3431)\n")
cbm.SETTIM(0, 0, 0)
uword prime_count
repeat N_ITER {
prime_count = sieve()
}
txt.print_uw(prime_count)
txt.print(" primes\n")
float time = cbm.RDTIM16() as float / 60.0
floats.print_f(time)
txt.print(" sec total = ")
floats.print_f(time/N_ITER)
txt.print(" sec per iteration\n")
sys.wait(9999)
}
sub check_flag(uword idx) -> ubyte
{
return flags_ptr[idx/8] & bitv[lsb(idx)&7]
}
sub clear_flag(uword idx)
{
flags_ptr[idx/8] &= ~bitv[lsb(idx)&7]
}
sub sieve() -> uword {
uword prime
uword k
uword count=0
uword i
sys.memset(flags_ptr, SIZE/8+1, $ff)
for i in 0 to SIZE-1 {
if check_flag(i) {
prime = i*2 + 3
k = i + prime
while k < SIZE {
clear_flag(k)
k += prime
}
; txt.print_uw(prime)
; txt.spc()
count++
}
}
return count
}
}
@@ -1,7 +1,5 @@
%import textio
%import floats
%zeropage basicsafe
%option no_sysinit
; The "Byte Sieve" test. https://en.wikipedia.org/wiki/Byte_Sieve
; Note: this program can be compiled for multiple target systems.
@@ -9,7 +7,6 @@
main {
sub start() {
cbm.SETTIM(0, 0, 0)
const ubyte ITERS = 10
uword count
@@ -22,15 +19,17 @@ main {
txt.print_ub(ITERS)
txt.print(" iterations, calculating...\n")
cbm.SETTIM(0, 0, 0)
repeat ITERS {
sys.memset(flags_ptr, SIZEPL, 1)
count = 0
for i in 0 to SIZEPL-1 {
if @(flags_ptr+i) {
prime = i + i + 3
if flags_ptr[i] {
prime = i*2 + 3
k = i + prime
while k <= SIZEPL-1 {
@(flags_ptr + k) = false
while k < SIZEPL {
flags_ptr[k] = false
k += prime
}
; txt.print_uw(prime)
@@ -48,5 +47,6 @@ main {
txt.print(" sec total = ")
floats.print_f(time/ITERS)
txt.print(" sec per iteration\n")
sys.wait(9999)
}
}
-3
View File
@@ -13,10 +13,7 @@ main {
charset.make_custom_charset()
; activate the new charset in RAM
ubyte block = c64.CIA2PRA
const ubyte PAGE1 = ((cbm.Screen >> 6) & $F0) | ((charset.CHARSET >> 10) & $0E)
c64.CIA2PRA = (block & $FC) | (lsb(cbm.Screen >> 14) ^ $03)
c64.VMCSB = PAGE1
txt.print("\n @ @ @ @\n")
+43 -34
View File
@@ -1,5 +1,4 @@
%import syslib
%import test_stack
%import textio
%import math
@@ -7,8 +6,9 @@
; which is (w)2001 by groepaz/hitmen
;
; Cleanup and porting to C by Ullrich von Bassewitz.
; Converted to prog8 by Irmen de Jong.
; See https://github.com/cc65/cc65/tree/master/samples/cbm/plasma.c
;
; Optimized and Converted to prog8 by Irmen de Jong.
main {
@@ -22,27 +22,39 @@ main {
sub start() {
txt.color(1)
txt.clear_screen()
txt.print("creating charset...\n")
txt.print("creating charset...\n\nwhile running, press key to stop.\n\n")
makechar()
ubyte block = c64.CIA2PRA
; ubyte v = cbm.VMCSB
ubyte v = c64.VMCSB
c64.CIA2PRA = (block & $FC) | (lsb(SCREEN1 >> 14) ^ $03)
repeat {
uword frames = 0
cbm.SETTIM(0,0,0)
while cbm.GETIN()==0 {
doplasma(SCREEN1)
c64.VMCSB = PAGE1
doplasma(SCREEN2)
c64.VMCSB = PAGE2
frames += 2
}
; restore screen (if you want)
;c64.VMCSB = v
;c64.CIA2PRA = block
;txt.print("done!\n")
;test_stack.test()
;repeat {
;}
uword jiffies = cbm.RDTIM16()
; restore screen and displays stats
c64.VMCSB = v
c64.CIA2PRA = block
txt.print("time in jiffies: ")
txt.print_uw(jiffies)
txt.print("\nframes: ")
txt.print_uw(frames)
uword fps = (frames*60)/jiffies
txt.print("\nfps: ")
txt.print_uw(fps)
txt.print("\ndone!\n")
repeat {
}
}
; several variables outside of doplasma to make them retain their value
@@ -51,7 +63,7 @@ main {
ubyte c2A
ubyte c2B
sub doplasma(uword screen) {
sub doplasma(uword @zp screen) {
ubyte[40] xbuf
ubyte[25] ybuf
ubyte c1a = c1A
@@ -61,14 +73,14 @@ main {
ubyte @zp x
ubyte @zp y
for y in 24 downto 0 {
for y in 0 to 24 {
ybuf[y] = math.sin8u(c1a) + math.sin8u(c1b)
c1a += 4
c1b += 9
}
c1A += 3
c1B -= 5
for x in 39 downto 0 {
for x in 0 to 39 {
xbuf[x] = math.sin8u(c2a) + math.sin8u(c2b)
c2a += 3
c2b += 7
@@ -76,20 +88,18 @@ main {
c2A += 2
c2B -= 3
for y in 24 downto 0 {
for x in 39 downto 0 {
; using a temp var here to enable expression optimization that can't be done on a 'problematic' ROM/RAM memory location
ubyte @zp cc = xbuf[x] + ybuf[y]
@(screen+x) = cc
; this is the fastest way to do this inner part:
; %asm {{
; ldy y
; lda ybuf,y
; ldy x
; clc
; adc xbuf,y
; sta (screen),y
; }}
for y in 0 to 24 {
ubyte @zp @shared yvalue = ybuf[y]
for x in 0 to 39 {
; @(screen+x) = xbuf[x] + yvalue
; max optimized asm is this: (achieving ~21 fps on the C64):
%asm {{
lda p8_yvalue
ldy p8_x
clc
adc p8_xbuf,y
sta (p8_screen),y
}}
}
screen += 40
}
@@ -99,16 +109,15 @@ main {
ubyte[8] bittab = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
ubyte c
for c in 0 to 255 {
ubyte @zp s = math.sin8u(c)
ubyte @zp s = math.sin8u(c) ; chance
ubyte i
; for all the pixels in the 8x8 character grid, determine (with a rnd chance) if they should be on or off
for i in 0 to 7 {
ubyte b=0
ubyte @zp ii
for ii in 0 to 7 {
; use 16 bit rng for a bit more randomness instead of the 8-bit rng
if math.rnd() > s {
if math.rnd() > s
b |= bittab[ii]
}
}
@(CHARSET + i + c*$0008) = b
}
+30 -34
View File
@@ -1,6 +1,5 @@
%import floats
%import graphics
%import test_stack
%zeropage floatsafe
@@ -9,9 +8,10 @@ main {
sub start() {
graphics.enable_bitmap_mode()
turtle.init()
; turtle.pu()
; turtle.pos(150, 110)
; turtle.pd()
turtle.pu()
turtle.pos(150, 110)
turtle.pd()
ubyte i
for i in 0 to 100 {
@@ -19,8 +19,6 @@ main {
turtle.rt(94)
}
; test_stack.test()
repeat {
}
}
@@ -38,7 +36,11 @@ turtle {
angle = 0.0
pendown = true
c64.SPRPTR[0] = $0d00 / 64
const uword SPRITE_MEMORY = $5800
const uword SPRITE_ADDR_POINTERS = (graphics.CHARS_ADDRESS & $fc00) + 1016 ; no longer the default location 2040!
sys.memcopy(&turtlesprite, SPRITE_MEMORY, len(turtlesprite))
@(SPRITE_ADDR_POINTERS) = (SPRITE_MEMORY & $3fff) / 64
c64.SPENA = 1
c64.SP0COL = 5
@@ -88,32 +90,26 @@ turtle {
sub pd() {
pendown = true
}
}
spritedata $0d00 {
; this memory block contains the sprite data
; it must start on an address aligned to 64 bytes.
%option force_output ; make sure the data in this block appears in the resulting program
ubyte[] balloonsprite = [ %00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%01111110,%00000000,
%00000000,%11000011,%00000000,
%00000000,%11000011,%00000000,
%00000000,%11000011,%00000000,
%00000000,%01111110,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000 ]
ubyte[] turtlesprite = [ %00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%01111110,%00000000,
%00000000,%11000011,%00000000,
%00000000,%11000011,%00000000,
%00000000,%11000011,%00000000,
%00000000,%01111110,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000,
%00000000,%00000000,%00000000 ]
}
+4 -4
View File
@@ -5,7 +5,7 @@
; "unlimited sprites / bobs" demo effect.
; Note that everything is prog8, no inline assembly used/required.
; Note2: these aren't actual sprites, it's a bitmap backbuffer display trick.
main {
sub start() {
@@ -104,7 +104,7 @@ main {
bitshift(lsb(blit_x) & 7)
; left column of the (shifted)sprite
; left column of the (shifted) "sprite" (bob)
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
@@ -117,7 +117,7 @@ main {
for ix in 0 to len(shifted_sprite)-1 step 3
cx16.VERA_DATA1 = cx16.VERA_DATA0 & shifted_mask[ix] | shifted_sprite[ix]
; middle column of the (shifted)sprite
; middle column of the (shifted) "sprite" (bob)
vmem++
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(vmem)
@@ -128,7 +128,7 @@ main {
for ix in 1 to len(shifted_sprite)-1 step 3
cx16.VERA_DATA1 = cx16.VERA_DATA0 & shifted_mask[ix] | shifted_sprite[ix]
; right column of the (shifted)sprite
; right column of the (shifted) "sprite" (bob)
vmem++
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(vmem)
-6
View File
@@ -69,12 +69,6 @@ main {
return sqrt(sqx + sqy)
}
; sub distance(ubyte cix) -> uword {
; float dx = x as float - circle_x[cix]
; float dy = y as float - circle_y[cix]
; return floats.sqrt(dx*dx + dy*dy) as uword
; }
sub not_edge() -> bool {
if x as word - radius < 0
return false
-59
View File
@@ -1,59 +0,0 @@
; CommanderX16 text datetime example!
%import textio
%zeropage basicsafe
main {
sub start() {
cx16.clock_set_date_time(mkword(8, 2022 - 1900), mkword(19, 27), mkword(30, 16), 0)
txt.lowercase()
repeat {
txt.chrout(19) ; HOME
txt.print("\n yyyy-mm-dd HH:MM:SS.jj\n\n")
void cx16.clock_get_date_time()
txt.spc()
print_date()
txt.spc()
print_time()
txt.nl()
txt.nl()
uword jiffies = cbm.RDTIM16()
txt.print_uw(jiffies)
}
}
sub print_date() {
txt.print_uw(1900 + lsb(cx16.r0))
txt.chrout('-')
if msb(cx16.r0) < 10
txt.chrout('0')
txt.print_ub(msb(cx16.r0))
txt.chrout('-')
if lsb(cx16.r1) < 10
txt.chrout('0')
txt.print_ub(lsb(cx16.r1))
}
sub print_time() {
if msb(cx16.r1) < 10
txt.chrout('0')
txt.print_ub(msb(cx16.r1))
txt.chrout(':')
if lsb(cx16.r2) < 10
txt.chrout('0')
txt.print_ub(lsb(cx16.r2))
txt.chrout(':')
if msb(cx16.r2) < 10
txt.chrout('0')
txt.print_ub(msb(cx16.r2))
txt.chrout('.')
if lsb(cx16.r3) < 10
txt.chrout('0')
txt.print_ub(lsb(cx16.r3))
}
}
-88
View File
@@ -1,88 +0,0 @@
%import textio
%import floats
%zeropage basicsafe
main {
const uword width = 256
const uword height = 240
const ubyte max_iter = 16 ; 32 actually looks pretty nice but takes longer
sub start() {
initialize()
mandel()
repeat {
; do nothing
}
}
sub mandel() {
const float XL=-2.200
const float XU=0.800
const float YL=-1.300
const float YU=1.300
const float dx = (XU-XL)/width
const float dy = (YU-YL)/height
ubyte pixelx
ubyte pixely
for pixely in 0 to height-1 {
float yy = YL+dy*(pixely as float)
cx16.FB_cursor_position(0, pixely)
for pixelx in 0 to width-1 {
float xx = XL+dx*(pixelx as float)
float xsquared = 0.0
float ysquared = 0.0
float x = 0.0
float y = 0.0
ubyte iter = 0
while iter<max_iter and xsquared+ysquared<4.0 {
y = x*y*2.0 + yy
x = xsquared - ysquared + xx
xsquared = x*x
ysquared = y*y
iter++
}
cx16.FB_set_pixel(max_iter-iter)
}
print_time()
}
}
sub initialize() {
void cx16.screen_mode($80, false)
txt.plot(32, 5)
txt.print("256*240")
txt.plot(32, 6)
txt.print("mandel-")
txt.plot(33, 7)
txt.print("brot")
txt.plot(32, 9)
txt.print("floats")
txt.plot(32, 10)
txt.print_b(max_iter)
txt.print(" iter")
cx16.clock_set_date_time(0, 0, 0, 0)
cx16.r0=0
cx16.FB_init()
}
sub print_time() {
void cx16.clock_get_date_time()
txt.plot(33, 12)
if lsb(cx16.r2) < 10
txt.chrout('0')
txt.print_ub(lsb(cx16.r2))
txt.chrout(':')
if msb(cx16.r2) < 10
txt.chrout('0')
txt.print_ub(msb(cx16.r2))
}
}
-414
View File
@@ -1,414 +0,0 @@
%import palette
%import gfx2
%option no_sysinit
main {
sub start() {
; palette.set_rgb(&colors, len(colors))
void cx16.screen_mode(128, false) ; low-res bitmap 256 colors
cx16.FB_init()
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00010000 ; enable only layer 0
cx16.VERA_L0_CONFIG = %00000110 ; 4 bpp = 16 colors
;cx16.VERA_L0_MAPBASE = 0
;cx16.VERA_L0_TILEBASE = 0
ubyte pix=0
ubyte ypos
cx16.FB_cursor_position(0, 0)
for ypos in 0 to 239 {
repeat 320/2 {
cx16.FB_set_pixel((pix&15)<<4 | (pix&15))
pix++
}
pix=0
}
; color index 0 can't be swapped - set it to black in both ranges
palette.set_color(0, 0)
palette.set_color(16, 0)
sys.set_rasterirq(&irq.irqhandler, 0)
repeat {
; don't exit
}
}
}
irq {
ubyte phase = 0
uword next_rasterline = 0
const ubyte increment = 4 ; 4 scanlines = 2 lores pixels per color swap (2 scanlines is too tight)
sub irqhandler() {
if phase & 1 == 0 {
%asm {{
lda #0 ; activate palette #0 (first set of colors)
sta cx16.VERA_L0_HSCROLL_H
stz cx16.VERA_CTRL
lda #<$fa00+32+2
sta cx16.VERA_ADDR_L
lda #>$fa00+32+2
sta cx16.VERA_ADDR_M
lda #%00010001
sta cx16.VERA_ADDR_H
; change 15 palette entries 1..15 (0 is fixed)
lda #<$e000
sta $02
lda #>$e000
sta $02
ldy #0
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$0f
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
}}
}
else {
%asm {{
lda #1 ; activate palette #1 (second set of colors)
sta cx16.VERA_L0_HSCROLL_H
stz cx16.VERA_CTRL
lda #<$fa00+2
sta cx16.VERA_ADDR_L
lda #>$fa00+2
sta cx16.VERA_ADDR_M
lda #%00010001
sta cx16.VERA_ADDR_H
; change 15 palette entries 1..15 (0 is fixed)
lda #<$f000
sta $02
lda #>$f000
sta $02
ldy #0
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
iny
lda (2),y
sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
; lda #$ff
; sta cx16.VERA_DATA0
; lda #0
; sta cx16.VERA_DATA0
}}
}
phase++
next_rasterline += increment
if next_rasterline >= 480 {
next_rasterline = 0
phase = 0
}
sys.set_rasterline(next_rasterline)
;
; uword[16] colors1 = 0
; uword[16] colors2 = 200
;
; palette.set_rgb(colors1, len(colors1))
; palette.set_rgb(colors2, len(colors2))
}
}
+111
View File
@@ -0,0 +1,111 @@
%import syslib
%import textio
%import math
%zeropage basicsafe
; converted from plasma test program for cc65.
; which is (w)2001 by groepaz/hitmen
;
; Cleanup and porting to C by Ullrich von Bassewitz.
; See https://github.com/cc65/cc65/tree/master/samples/cbm/plasma.c
;
; Optimized and Converted to prog8 by Irmen de Jong.
main {
const uword VERA_CHARSET = $f000 ; $1f000
const uword VERA_TXTSCREEN = $b000 ; %1b000
sub start() {
txt.color(6)
txt.clear_screen()
makechar()
uword frames = 0
cbm.SETTIM(0,0,0)
while cbm.GETIN()==0 {
doplasma()
frames ++
}
uword jiffies = cbm.RDTIM16()
; restore screen and displays stats
cx16.screen_set_charset(2, 0)
txt.color(7)
txt.clear_screen()
txt.print("time in jiffies: ")
txt.print_uw(jiffies)
txt.print("\nframes: ")
txt.print_uw(frames)
uword fps = (frames*60)/jiffies
txt.print("\nfps: ")
txt.print_uw(fps)
txt.nl()
}
; several variables outside of doplasma to make them retain their value
ubyte c1A
ubyte c1B
ubyte c2A
ubyte c2B
sub doplasma() {
ubyte[txt.DEFAULT_WIDTH] xbuf
ubyte[txt.DEFAULT_HEIGHT] ybuf
ubyte c1a = c1A
ubyte c1b = c1B
ubyte c2a = c2A
ubyte c2b = c2B
ubyte @zp x
ubyte @zp y
for y in 0 to txt.DEFAULT_HEIGHT-1 {
ybuf[y] = math.sin8u(c1a) + math.sin8u(c1b)
c1a += 4
c1b += 9
}
c1A += 3
c1B -= 5
for x in 0 to txt.DEFAULT_WIDTH-1 {
xbuf[x] = math.sin8u(c2a) + math.sin8u(c2b)
c2a += 3
c2b += 7
}
c2A += 2
c2B -= 3
; sys.waitvsync() ; if you put this in it will run at 30 fps synced which looks really nice and smooth
; use vera auto increment writes to avoid slow txt.setchr(x, y, xbuf[x] + ybuf[y])
for y in 0 to txt.DEFAULT_HEIGHT-1 {
cx16.vaddr_autoincr(1, VERA_TXTSCREEN + y*$0100, 0, 2)
ubyte @zp yvalue = ybuf[y]
for x in 0 to txt.DEFAULT_WIDTH-1 {
cx16.VERA_DATA0 = xbuf[x] + yvalue
}
}
}
sub makechar() {
ubyte[8] bittab = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
ubyte c
cx16.vaddr_autoincr(1, VERA_CHARSET, 0, 1)
for c in 0 to 255 {
ubyte @zp s = math.sin8u(c) ; chance
ubyte i
; for all the pixels in the 8x8 character grid, determine (with a rnd chance) if they should be on or off
for i in 0 to 7 {
ubyte b=0
ubyte @zp ii
for ii in 0 to 7 {
if math.rnd() > s
b |= bittab[ii]
}
cx16.VERA_DATA0 = b
}
}
}
}
+2
View File
@@ -1,4 +1,5 @@
%import math
%import textio
%import palette
; use the mouse to move the cursor around the screen
@@ -16,6 +17,7 @@ main {
ubyte yy
void cx16.screen_mode(128, false)
cx16.mouse_config2(1)
txt.print("move the mouse for the spotlight.")
; prefill
for yy in 0 to HEIGHT-1 {
Binary file not shown.
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python
from PIL import Image
def make_cx16_palette(palette: list[int]) -> bytes:
cx16palette = bytearray()
for pi in range(0, len(palette), 3):
r, g, b = palette[pi] >> 4, palette[pi + 1] >> 4, palette[pi + 2] >> 4
cx16palette.append(g << 4 | b)
cx16palette.append(r)
return cx16palette
img = Image.open("dragonsprite.png")
assert img.size == (64, 128) and img.mode == 'P'
data = bytearray()
for y in range(0, 128):
for x in range(0, 64, 2):
pix1 = img.getpixel((x, y))
pix2 = img.getpixel((x + 1, y))
data.append(pix1 << 4 | pix2)
palette = make_cx16_palette(img.getpalette())
assert len(data) == 64 * 128 / 2
assert len(palette) <= 16*2
open("DRAGONSPRITE.PAL", "wb").write(palette)
open("DRAGONSPRITE.BIN", "wb").write(data)
+58
View File
@@ -0,0 +1,58 @@
%import diskio
%import textio
%import sprites
%zeropage basicsafe
%option no_sysinit
; an example that displays and moves a single dragon (actually 2 sprites).
main {
; we choose arbitrary unused vram location for sprite data: $12000
const ubyte SPRITE_DATA_BANK = 1
const uword SPRITE_DATA_ADDR = $2000
const ubyte SPRITE_PALETTE_OFFSET_IDX = 16
sub start() {
txt.plot(32,30)
txt.print("there be dragons!")
; load the sprite data and color palette directly into Vera ram
void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA_BANK, SPRITE_DATA_ADDR)
void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + SPRITE_PALETTE_OFFSET_IDX*2)
; initialize the dragon sprites
sprites.init(1, SPRITE_DATA_BANK, SPRITE_DATA_ADDR, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET_IDX)
sprites.init(2, SPRITE_DATA_BANK, SPRITE_DATA_ADDR + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16,SPRITE_PALETTE_OFFSET_IDX)
ubyte tt = 0
word xpos = -64
word ypos
bool flippedx = false
repeat {
if flippedx
xpos -= 2
else
xpos += 2
if xpos >= 640 or xpos <= -64
flippedx = not flippedx
ypos = (240-64 as word) + math.sin8(tt)
tt++
txt.plot(32, 32)
txt.print("at: ")
txt.print_w(sprites.getx(1))
txt.chrout(',')
txt.print_w(sprites.gety(1))
txt.print(" ")
sys.waitvsync()
sprites.pos(1, xpos, ypos)
sprites.pos(2, xpos, ypos+64)
sprites.flipx(1, flippedx)
sprites.flipx(2, flippedx)
}
}
}
+58
View File
@@ -0,0 +1,58 @@
%import diskio
%import textio
%import sprites
%zeropage basicsafe
%option no_sysinit
; an example that displays and then moves many sprites at once.
main {
; we choose arbitrary unused vram location for sprite data: $12000
const ubyte SPRITE_DATA_BANK = 1
const uword SPRITE_DATA_ADDR = $2000
const ubyte SPRITE_PALETTE_OFFSET_IDX = 16
const ubyte NUM_DRAGONS = 25
word[NUM_DRAGONS*2] xpositions
word[NUM_DRAGONS*2] ypositions
sub start() {
txt.plot(30,30)
txt.print("there be many dragons!")
; load the sprite data and color palette directly into Vera ram
void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA_BANK, SPRITE_DATA_ADDR)
void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + SPRITE_PALETTE_OFFSET_IDX*2)
; initialize the dragon sprites (every dragon needs 2 sprites, top and bottom half)
ubyte sprite_num
for sprite_num in 0 to NUM_DRAGONS*2-2 step 2 {
sprites.init(sprite_num+1, SPRITE_DATA_BANK, SPRITE_DATA_ADDR, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET_IDX)
sprites.init(sprite_num+2, SPRITE_DATA_BANK, SPRITE_DATA_ADDR + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET_IDX)
xpositions[sprite_num] = math.rndw() % (640-64) as word
xpositions[sprite_num+1] = xpositions[sprite_num]
ypositions[sprite_num] = sprite_num * $0008 as word
ypositions[sprite_num+1] = ypositions[sprite_num]+64
}
repeat {
; move all dragons (remember each one consists of a top and a bottom sprite)
for sprite_num in 0 to NUM_DRAGONS*2-2 step 2 {
xpositions[sprite_num]++
xpositions[sprite_num+1]++
if sprite_num & 2 {
xpositions[sprite_num]++
xpositions[sprite_num+1]++
}
if xpositions[sprite_num] >= 640
xpositions[sprite_num] = -64
if xpositions[sprite_num+1] >= 640
xpositions[sprite_num+1] = -64
}
sys.waitvsync()
sprites.pos_batch(1, NUM_DRAGONS*2, &xpositions, &ypositions)
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

+16 -12
View File
@@ -1,13 +1,12 @@
%import graphics
%import math
%import textio
%zeropage basicsafe
; Draw sine and cosine graphs. The sine and cosine functions are table lookups
; where the tables are generated by 64tass list functions.
; Note: this program is compatible with CX16 only.
; it doesn't work correctly on C64 because the bitmap screen data overlaps
; the program itself in memory $2000-...
; This uses the graphics library which works on multiple targets (C64, CX16)
main {
const uword width = 320
@@ -17,18 +16,19 @@ main {
graphics.enable_bitmap_mode()
sincos255()
sys.wait(120)
sys.wait(60)
graphics.clear_screen(1, 0)
sincos180()
sys.wait(120)
sys.wait(60)
graphics.clear_screen(1, 0)
circles()
sys.wait(60)
repeat {
}
graphics.disable_bitmap_mode()
txt.print("done\n")
}
sub sincos255() {
@@ -38,6 +38,7 @@ main {
ubyte pixelxb
byte pixelys
; unsigned
for pixelxb in 0 to 255 {
pixelyb = math.cos8u(pixelxb) / 2
graphics.plot(pixelxb, pixelyb+20)
@@ -45,11 +46,12 @@ main {
graphics.plot(pixelxb, pixelyb+20)
}
; signed
for pixelxb in 0 to 255 {
pixelys = math.cos8(pixelxb) / 2
graphics.plot(pixelxb, pixelys as uword + 90)
graphics.plot(pixelxb, pixelys as ubyte + 90)
pixelys = math.sin8(pixelxb) / 2
graphics.plot(pixelxb, pixelys as uword + 90)
graphics.plot(pixelxb, pixelys as ubyte + 90)
}
}
@@ -60,6 +62,7 @@ main {
ubyte pixelxb
byte pixelys
; signed
for pixelxb in 0 to 179 {
pixelyb = math.cosr8u(pixelxb) / 2
graphics.plot(pixelxb, pixelyb+20)
@@ -67,11 +70,12 @@ main {
graphics.plot(pixelxb, pixelyb+20)
}
; unsigned
for pixelxb in 0 to 179 {
pixelys = math.cosr8(pixelxb) / 2
graphics.plot(pixelxb, pixelys as uword + 90)
graphics.plot(pixelxb, pixelys as ubyte + 90)
pixelys = math.sinr8(pixelxb) / 2
graphics.plot(pixelxb, pixelys as uword + 90)
graphics.plot(pixelxb, pixelys as ubyte + 90)
}
}
+47 -5
View File
@@ -1,13 +1,55 @@
%import textio
%import floats
%zeropage basicsafe
; TODO fix VM : produces wrong number of primes (and varies too, so it uses uninitialized memory somewhere)
main {
const uword SIZE = 16000
uword @zp flags_ptr = memory("flags", SIZE/8+1, $100)
ubyte[] bitv = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
sub start() {
thing()
txt.print("calculating... (expecting 3431): ")
txt.print_uw(sieve())
txt.print(" primes\n")
}
sub thing() {
ubyte start=22
txt.print_ub(start)
txt.print_uw(&main.start)
sub check_flag(uword idx) -> ubyte
{
ubyte mask = bitv[lsb(idx)&7]
ubyte flag = flags_ptr[idx/8]
return flag & mask
}
sub clear_flag(uword idx)
{
ubyte mask = bitv[lsb(idx)&7]
ubyte flag = flags_ptr[idx/8]
flag &= ~mask
flags_ptr[idx/8] = flag
}
sub sieve() -> uword {
uword prime
uword k
uword count=0
uword i
sys.memset(flags_ptr, SIZE/8+1, $ff)
for i in 0 to SIZE-1 {
if check_flag(i) {
prime = i*2 + 3
k = i + prime
while k < SIZE {
clear_flag(k)
k += prime
}
count++
}
}
return count
}
}
+2 -2
View File
@@ -17,11 +17,11 @@ main {
sys.memset(flags_ptr, SIZEPL, 1)
count = 0
for i in 0 to SIZEPL-1 {
if @(flags_ptr+i) {
if flags_ptr[i] {
prime = i + i + 3
k = i + prime
while k <= SIZEPL-1 {
@(flags_ptr + k) = false
flags_ptr[k] = false
k += prime
}
txt.print_uw(prime)
+1 -1
View File
@@ -5,4 +5,4 @@ org.gradle.daemon=true
kotlin.code.style=official
javaVersion=11
kotlinVersion=1.9.10
version=9.4
version=9.4.1
@@ -37,16 +37,16 @@ All have type b or w or f.
load reg1, value - load immediate value into register. If you supply a symbol, loads the *address* of the symbol! (variable values are loaded from memory via the loadm instruction)
loadm reg1, address - load reg1 with value at memory address
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (only the lsb part used for indexing)
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
loadr reg1, reg2 - load reg1 with value in register reg2
storem reg1, address - store reg1 at memory address
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (only the lsb part used for indexing)
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
storezm address - store zero at memory address
storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg
storezx reg1, address - store zero at memory address, indexed by value in reg1 (only the lsb part used for indexing)
CONTROL FLOW
+9 -3
View File
@@ -153,7 +153,7 @@ augassignment :
assign_target:
scoped_identifier #IdentifierTarget
| scoped_identifier arrayindex #ArrayindexedTarget // TODO expression instead of just scoped_identifier
| arrayindexed #ArrayindexedTarget
| directmemory #MemoryTarget
;
@@ -180,12 +180,18 @@ expression :
| left = expression EOL? bop = 'xor' EOL? right = expression
| literalvalue
| scoped_identifier
| scoped_identifier arrayindex // TODO expression instead of just scoped_identifier
| arrayindexed
| directmemory
| addressof
| expression typecast
;
arrayindexed:
scoped_identifier arrayindex
// TODO to allow chained array indexing: | arrayindexed arrayindex
;
typecast : 'as' datatype;
directmemory : '@' '(' expression ')';
@@ -293,7 +299,7 @@ untilloop: 'do' (statement | statement_block) EOL? 'until' expression ;
repeatloop: 'repeat' expression? EOL? (statement | statement_block) ;
unrollloop: 'unroll' integerliteral? EOL? (statement | statement_block) ;
unrollloop: 'unroll' expression EOL? (statement | statement_block) ; // note: expression must evaluate to a constant
whenstmt: 'when' expression EOL? '{' EOL? (when_choice | EOL) * '}' EOL? ;
@@ -435,18 +435,18 @@ class VirtualMachine(irProgram: IRProgram) {
private fun InsLOADX(i: IRInstruction) {
when (i.type!!) {
IRDataType.BYTE -> {
val value = memory.getUB(i.address!! + registers.getUW(i.reg2!!).toInt())
val value = memory.getUB(i.address!! + registers.getUB(i.reg2!!).toInt())
registers.setUB(i.reg1!!, value)
statusZero = value==0.toUByte()
statusNegative = value>=0x80u
}
IRDataType.WORD -> {
val value = memory.getUW(i.address!! + registers.getUW(i.reg2!!).toInt())
val value = memory.getUW(i.address!! + registers.getUB(i.reg2!!).toInt())
registers.setUW(i.reg1!!, value)
statusZero = value== 0.toUShort()
statusNegative = value>=0x8000u
}
IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, memory.getFloat(i.address!! + registers.getUW(i.reg1!!).toInt()))
IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, memory.getFloat(i.address!! + registers.getUB(i.reg1!!).toInt()))
}
nextPc()
}
@@ -514,9 +514,9 @@ class VirtualMachine(irProgram: IRProgram) {
private fun InsSTOREX(i: IRInstruction) {
when (i.type!!) {
IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt() + i.address!!, registers.getUB(i.reg1!!))
IRDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt() + i.address!!, registers.getUW(i.reg1!!))
IRDataType.FLOAT -> memory.setFloat(registers.getUW(i.reg1!!).toInt() + i.address!!, registers.getFloat(i.fpReg1!!))
IRDataType.BYTE -> memory.setUB(i.address!! + registers.getUB(i.reg2!!).toInt(), registers.getUB(i.reg1!!))
IRDataType.WORD -> memory.setUW(i.address!! + registers.getUB(i.reg2!!).toInt(), registers.getUW(i.reg1!!))
IRDataType.FLOAT -> memory.setFloat(i.address!! + registers.getUB(i.reg1!!).toInt(), registers.getFloat(i.fpReg1!!))
}
nextPc()
}
@@ -559,9 +559,9 @@ class VirtualMachine(irProgram: IRProgram) {
private fun InsSTOREZX(i: IRInstruction) {
when (i.type!!) {
IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg1!!).toInt() + i.address!!, 0u)
IRDataType.WORD -> memory.setUW(registers.getUW(i.reg1!!).toInt() + i.address!!, 0u)
IRDataType.FLOAT -> memory.setFloat(registers.getUW(i.reg1!!).toInt() + i.address!!, 0f)
IRDataType.BYTE -> memory.setUB(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u)
IRDataType.WORD -> memory.setUW(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u)
IRDataType.FLOAT -> memory.setFloat(i.address!! + registers.getUB(i.reg1!!).toInt(), 0f)
}
nextPc()
}