mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3caf9108ad | ||
|
|
bde4be8231 | ||
|
|
0bbbb12ed2 | ||
|
|
b570bdaed7 | ||
|
|
8c0843cc87 | ||
|
|
31458ffd81 | ||
|
|
c15c10a94e | ||
|
|
9fca978725 | ||
|
|
b125901717 | ||
|
|
eb018ae660 | ||
|
|
7e5a9474fe | ||
|
|
525a9b5036 | ||
|
|
c3fbdf34ca | ||
|
|
48bd51e1a5 | ||
|
|
10d0b03a90 | ||
|
|
e1b3582f08 | ||
|
|
95be1c9e22 | ||
|
|
1ce8fe06d5 | ||
|
|
15c649024e | ||
|
|
e97303c226 | ||
|
|
3b786c819d | ||
|
|
04959dbd8b | ||
|
|
5cd4b874ea | ||
|
|
f14ea1b3de | ||
|
|
9cc0cda0fb | ||
|
|
09a7a4bbe5 | ||
|
|
cfea8b3745 | ||
|
|
28bf0b61ce | ||
|
|
2dc2429735 | ||
|
|
83d4592526 | ||
|
|
2d528c26ae | ||
|
|
66b3dce794 |
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("""
|
||||
|
||||
+231
-77
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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 ]
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 |
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user