mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 23:23:40 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
473efbe67a | |||
aeabf0f324 | |||
80ab552ad8 | |||
7d4695c5b2 | |||
5189eaca36 | |||
cfb31377fc | |||
a07c52e112 | |||
8e1071aa89 | |||
7cb9a6ba60 | |||
350dc731f1 | |||
f690f58bd4 | |||
4bc65e9ef7 | |||
2d600da8b6 | |||
35af53828a |
@ -29,10 +29,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
} else {
|
} else {
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
free.addAll(listOf(
|
free.addAll(listOf(
|
||||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
0x16, 0x17, 0x18, 0x19, 0x1a,
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||||
0x22, 0x23, 0x24, 0x25,
|
|
||||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
@ -48,8 +47,8 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
// remove the zeropage locations used for floating point operations from the free list
|
// remove the zeropage locations used for floating point operations from the free list
|
||||||
free.removeAll(listOf(
|
free.removeAll(listOf(
|
||||||
0x22, 0x23, 0x24, 0x25,
|
0x03, 0x04, 0x10, 0x11, 0x12,
|
||||||
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
@ -45,8 +45,9 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
// note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||||
// however, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||||
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
|
@ -139,20 +139,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||||
|
|
||||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||||
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0
|
||||||
if(bank==null || address==null)
|
|
||||||
throw AssemblyError("callfar (jsrfar) requires constant arguments")
|
|
||||||
|
|
||||||
if(address !in 0xa000..0xbfff)
|
|
||||||
throw AssemblyError("callfar done on address outside of cx16 banked ram")
|
|
||||||
if(bank==0)
|
|
||||||
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
|
|
||||||
|
|
||||||
val argAddrArg = fcall.args[2]
|
val argAddrArg = fcall.args[2]
|
||||||
|
if(bank==null)
|
||||||
|
throw AssemblyError("callfar (jsrfar) bank has to be a constant")
|
||||||
|
if(fcall.args[1].constValue(program) == null) {
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
|
||||||
|
asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word")
|
||||||
|
}
|
||||||
|
|
||||||
if(argAddrArg.constValue(program)?.number == 0.0) {
|
if(argAddrArg.constValue(program)?.number == 0.0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}""")
|
.byte ${bank.toHex()}""")
|
||||||
} else {
|
} else {
|
||||||
when(argAddrArg) {
|
when(argAddrArg) {
|
||||||
@ -162,7 +161,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}
|
.byte ${bank.toHex()}
|
||||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
||||||
}
|
}
|
||||||
@ -170,7 +169,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${argAddrArg.number.toHex()}
|
lda ${argAddrArg.number.toHex()}
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}
|
.byte ${bank.toHex()}
|
||||||
sta ${argAddrArg.number.toHex()}""")
|
sta ${argAddrArg.number.toHex()}""")
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||||
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition:
|
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
||||||
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
||||||
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
||||||
asmgen.out(".endweak")
|
asmgen.out(".endweak")
|
||||||
|
@ -738,10 +738,10 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(origTypeCastExpression.type == DataType.UBYTE) {
|
if(valueDt in WordDatatypes && origTypeCastExpression.type == DataType.UBYTE) {
|
||||||
val parentTc = origTypeCastExpression.parent as? TypecastExpression
|
val parentTc = origTypeCastExpression.parent as? TypecastExpression
|
||||||
if(parentTc!=null && parentTc.type==DataType.UWORD) {
|
if(parentTc!=null && parentTc.type==DataType.UWORD) {
|
||||||
// typecast something to ubyte and directly back to uword
|
// typecast a word value to ubyte and directly back to uword
|
||||||
// generate code for lsb(value) here instead of the ubyte typecast
|
// generate code for lsb(value) here instead of the ubyte typecast
|
||||||
return assignCastViaLsbFunc(value, target)
|
return assignCastViaLsbFunc(value, target)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id "org.jetbrains.kotlin.jvm"
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
id "io.kotest" version "0.3.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@ -31,6 +32,7 @@ dependencies {
|
|||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -42,6 +44,22 @@ sourceSets {
|
|||||||
srcDirs = ["${project.projectDir}/res"]
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDir "${project.projectDir}/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,14 @@
|
|||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
|
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||||
|
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||||
<orderEntry type="module" module-name="codeAst" />
|
<orderEntry type="module" module-name="codeAst" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
<orderEntry type="module" module-name="virtualmachine" />
|
<orderEntry type="module" module-name="virtualmachine" />
|
||||||
|
@ -279,6 +279,7 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
} else {
|
} else {
|
||||||
|
// TODO WHY THID DISTINCTION?
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
code += addConstReg(loopvarDt, indexReg, step)
|
code += addConstReg(loopvarDt, indexReg, step)
|
||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
@ -318,6 +319,7 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
} else {
|
} else {
|
||||||
|
// TODO WHY THIS DISTICTION ?
|
||||||
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
code += addConstReg(loopvarDt, indexReg, step)
|
code += addConstReg(loopvarDt, indexReg, step)
|
||||||
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
|
||||||
@ -383,13 +385,13 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
if(value>0) {
|
if(value>0) {
|
||||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
|
||||||
code += VmCodeInstruction(Opcode.ADDR, dt, reg1 = valueReg, reg2 = operandReg)
|
code += VmCodeInstruction(Opcode.ADDR, dt, reg1 = valueReg, reg2 = operandReg) // TODO USE ADDM?
|
||||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
|
||||||
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
|
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
|
||||||
code += VmCodeInstruction(Opcode.SUBR, dt, reg1 = valueReg, reg2 = operandReg)
|
code += VmCodeInstruction(Opcode.SUBR, dt, reg1 = valueReg, reg2 = operandReg) // TODO USE ADDM?
|
||||||
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
codeGenVirtual/test/Dummies.kt
Normal file
19
codeGenVirtual/test/Dummies.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package prog8tests.vm.helpers
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
internal object DummyMemsizer : IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType) = 0
|
||||||
|
override fun memorySize(arrayDt: DataType, numElements: Int) = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object DummyStringEncoder : IStringEncoding {
|
||||||
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package prog8tests.vm
|
package prog8tests.vm
|
||||||
|
|
||||||
|
import io.kotest.assertions.fail
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
@ -7,8 +8,8 @@ import prog8.code.ast.PtProgram
|
|||||||
import prog8.codegen.virtual.*
|
import prog8.codegen.virtual.*
|
||||||
import prog8.vm.Opcode
|
import prog8.vm.Opcode
|
||||||
import prog8.vm.VmDataType
|
import prog8.vm.VmDataType
|
||||||
import prog8tests.helpers.DummyMemsizer
|
import prog8tests.vm.helpers.DummyMemsizer
|
||||||
import prog8tests.helpers.DummyStringEncoder
|
import prog8tests.vm.helpers.DummyStringEncoder
|
||||||
|
|
||||||
class TestVmPeepholeOpt: FunSpec({
|
class TestVmPeepholeOpt: FunSpec({
|
||||||
fun makeVmProgram(lines: List<VmCodeLine>): Pair<AssemblyProgram, VariableAllocator> {
|
fun makeVmProgram(lines: List<VmCodeLine>): Pair<AssemblyProgram, VariableAllocator> {
|
@ -30,8 +30,9 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
|||||||
val literal = typecast.expression as? NumericLiteral
|
val literal = typecast.expression as? NumericLiteral
|
||||||
if (literal != null) {
|
if (literal != null) {
|
||||||
val newLiteral = literal.cast(typecast.type)
|
val newLiteral = literal.cast(typecast.type)
|
||||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
|
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast)
|
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove redundant nested typecasts
|
// remove redundant nested typecasts
|
||||||
|
@ -34,7 +34,6 @@ dependencies {
|
|||||||
implementation project(':codeGenCpu6502')
|
implementation project(':codeGenCpu6502')
|
||||||
implementation project(':codeGenVirtual')
|
implementation project(':codeGenVirtual')
|
||||||
implementation project(':codeGenExperimental')
|
implementation project(':codeGenExperimental')
|
||||||
implementation project(':virtualmachine')
|
|
||||||
implementation 'org.antlr:antlr4-runtime:4.10.1'
|
implementation 'org.antlr:antlr4-runtime:4.10.1'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
@ -69,7 +68,6 @@ sourceSets {
|
|||||||
test {
|
test {
|
||||||
java {
|
java {
|
||||||
srcDir "${project.projectDir}/test"
|
srcDir "${project.projectDir}/test"
|
||||||
srcDir "${project(':compilerAst').projectDir}/test/helpers"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,5 @@
|
|||||||
<orderEntry type="module" module-name="codeGenCpu6502" />
|
<orderEntry type="module" module-name="codeGenCpu6502" />
|
||||||
<orderEntry type="module" module-name="codeGenExperimental" />
|
<orderEntry type="module" module-name="codeGenExperimental" />
|
||||||
<orderEntry type="module" module-name="codeGenVirtual" />
|
<orderEntry type="module" module-name="codeGenVirtual" />
|
||||||
<orderEntry type="module" module-name="virtualmachine" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -416,6 +416,9 @@ _loop
|
|||||||
beq _stop
|
beq _stop
|
||||||
cmp #7 ; screencode letters A-F are 1-6
|
cmp #7 ; screencode letters A-F are 1-6
|
||||||
bcc _add_letter
|
bcc _add_letter
|
||||||
|
and #127
|
||||||
|
cmp #97
|
||||||
|
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
|
||||||
cmp #'g'
|
cmp #'g'
|
||||||
bcs _stop
|
bcs _stop
|
||||||
cmp #'a'
|
cmp #'a'
|
||||||
@ -451,6 +454,11 @@ _add_letter
|
|||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
pla
|
pla
|
||||||
jmp _calc
|
jmp _calc
|
||||||
|
_try_iso
|
||||||
|
cmp #103
|
||||||
|
bcs _stop
|
||||||
|
and #63
|
||||||
|
bne _add_letter
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,15 +2,20 @@
|
|||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
; Bitmap pixel graphics module for the CommanderX16
|
; Bitmap pixel graphics module for the CommanderX16
|
||||||
; wraps the graphics functions that are in ROM.
|
; Wraps the graphics functions that are in ROM.
|
||||||
; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom)
|
; Only lo-res 320x240 256 color mode for now.
|
||||||
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
|
; Unlike graphics module on the C64, you can use colors() to set new drawing colors for every draw operation.
|
||||||
|
; For other resolutions or other color modes, use the "gfx2" module instead. (which is Cx16-specific)
|
||||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||||
|
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
const uword WIDTH = 320
|
const uword WIDTH = 320
|
||||||
const ubyte HEIGHT = 200
|
const ubyte HEIGHT = 240
|
||||||
|
|
||||||
|
|
||||||
|
ubyte stroke_color = 1
|
||||||
|
ubyte background_color = 0
|
||||||
|
|
||||||
sub enable_bitmap_mode() {
|
sub enable_bitmap_mode() {
|
||||||
; enable bitmap screen, erase it and set colors to black/white.
|
; enable bitmap screen, erase it and set colors to black/white.
|
||||||
@ -27,10 +32,18 @@ graphics {
|
|||||||
|
|
||||||
|
|
||||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||||
|
stroke_color = pixelcolor
|
||||||
|
background_color = bgcolor
|
||||||
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
||||||
cx16.GRAPH_clear()
|
cx16.GRAPH_clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub colors(ubyte stroke, ubyte fill) {
|
||||||
|
; this routine is only available on the cx16, other targets can't change colors on the fly
|
||||||
|
cx16.GRAPH_set_colors(stroke, fill, background_color)
|
||||||
|
stroke_color = stroke
|
||||||
|
}
|
||||||
|
|
||||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||||
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
||||||
}
|
}
|
||||||
@ -71,31 +84,31 @@ graphics {
|
|||||||
cx16.r0 = xcenter + xx
|
cx16.r0 = xcenter + xx
|
||||||
cx16.r1 = ycenter + yy
|
cx16.r1 = ycenter + yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - xx
|
cx16.r0 = xcenter - xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter + xx
|
cx16.r0 = xcenter + xx
|
||||||
cx16.r1 = ycenter - yy
|
cx16.r1 = ycenter - yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - xx
|
cx16.r0 = xcenter - xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter + yy
|
cx16.r0 = xcenter + yy
|
||||||
cx16.r1 = ycenter + xx
|
cx16.r1 = ycenter + xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - yy
|
cx16.r0 = xcenter - yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter + yy
|
cx16.r0 = xcenter + yy
|
||||||
cx16.r1 = ycenter - xx
|
cx16.r1 = ycenter - xx
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
cx16.r0 = xcenter - yy
|
cx16.r0 = xcenter - yy
|
||||||
cx16.FB_cursor_position2()
|
cx16.FB_cursor_position2()
|
||||||
cx16.FB_set_pixel(1)
|
cx16.FB_set_pixel(stroke_color)
|
||||||
yy++
|
yy++
|
||||||
if decisionOver2<=0 {
|
if decisionOver2<=0 {
|
||||||
decisionOver2 += (yy as word)*2+1
|
decisionOver2 += (yy as word)*2+1
|
||||||
|
@ -90,6 +90,10 @@ psg {
|
|||||||
; cx16.r1L = the voice number
|
; cx16.r1L = the voice number
|
||||||
; cx16.r2L = attack value
|
; cx16.r2L = attack value
|
||||||
|
|
||||||
|
pushw(cx16.r0)
|
||||||
|
push(cx16.r1L)
|
||||||
|
push(cx16.r2L)
|
||||||
|
pushw(cx16.r9)
|
||||||
; calculate new volumes
|
; calculate new volumes
|
||||||
for cx16.r1L in 0 to 15 {
|
for cx16.r1L in 0 to 15 {
|
||||||
when envelope_states[cx16.r1L] {
|
when envelope_states[cx16.r1L] {
|
||||||
@ -138,6 +142,10 @@ psg {
|
|||||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||||
}
|
}
|
||||||
cx16.pop_vera_context()
|
cx16.pop_vera_context()
|
||||||
|
popw(cx16.r9)
|
||||||
|
pop(cx16.r2L)
|
||||||
|
pop(cx16.r1L)
|
||||||
|
popw(cx16.r0)
|
||||||
}
|
}
|
||||||
|
|
||||||
ubyte[16] envelope_states
|
ubyte[16] envelope_states
|
||||||
|
@ -735,49 +735,53 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline asmsub push_vera_context() clobbers(A) {
|
asmsub push_vera_context() clobbers(A) {
|
||||||
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||||
%asm {{
|
%asm {{
|
||||||
|
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||||
lda cx16.VERA_ADDR_L
|
lda cx16.VERA_ADDR_L
|
||||||
pha
|
sta _vera_storage
|
||||||
lda cx16.VERA_ADDR_M
|
lda cx16.VERA_ADDR_M
|
||||||
pha
|
sta _vera_storage+1
|
||||||
lda cx16.VERA_ADDR_H
|
lda cx16.VERA_ADDR_H
|
||||||
pha
|
sta _vera_storage+2
|
||||||
lda cx16.VERA_CTRL
|
lda cx16.VERA_CTRL
|
||||||
pha
|
sta _vera_storage+3
|
||||||
eor #1
|
eor #1
|
||||||
sta cx16.VERA_CTRL
|
sta cx16.VERA_CTRL
|
||||||
lda cx16.VERA_ADDR_L
|
lda cx16.VERA_ADDR_L
|
||||||
pha
|
sta _vera_storage+4
|
||||||
lda cx16.VERA_ADDR_M
|
lda cx16.VERA_ADDR_M
|
||||||
pha
|
sta _vera_storage+5
|
||||||
lda cx16.VERA_ADDR_H
|
lda cx16.VERA_ADDR_H
|
||||||
pha
|
sta _vera_storage+6
|
||||||
lda cx16.VERA_CTRL
|
lda cx16.VERA_CTRL
|
||||||
pha
|
sta _vera_storage+7
|
||||||
|
rts
|
||||||
|
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline asmsub pop_vera_context() clobbers(A) {
|
asmsub pop_vera_context() clobbers(A) {
|
||||||
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
||||||
%asm {{
|
%asm {{
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+7
|
||||||
sta cx16.VERA_CTRL
|
sta cx16.VERA_CTRL
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+6
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+5
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+4
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+3
|
||||||
sta cx16.VERA_CTRL
|
sta cx16.VERA_CTRL
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+2
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
pla
|
lda cx16.push_vera_context._vera_storage+0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -864,7 +868,9 @@ sys {
|
|||||||
; Soft-reset the system back to initial power-on Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
stz $01 ; bank the kernal in
|
stz $01 ; bank the kernal in
|
||||||
|
lda #$80
|
||||||
|
sta cx16.VERA_CTRL ; reset Vera (kernal doesn't do this?)
|
||||||
jmp (cx16.RESET_VEC)
|
jmp (cx16.RESET_VEC)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -208,14 +208,16 @@ close_end:
|
|||||||
c64.SETLFS(11, drivenumber, 0)
|
c64.SETLFS(11, drivenumber, 0)
|
||||||
void c64.OPEN() ; open 11,8,0,"filename"
|
void c64.OPEN() ; open 11,8,0,"filename"
|
||||||
if_cc {
|
if_cc {
|
||||||
iteration_in_progress = true
|
if c64.READST()==0 {
|
||||||
have_first_byte = false
|
iteration_in_progress = true
|
||||||
void c64.CHKIN(11) ; use #11 as input channel
|
have_first_byte = false
|
||||||
if_cc {
|
void c64.CHKIN(11) ; use #11 as input channel
|
||||||
first_byte = c64.CHRIN() ; read first byte to test for file not found
|
if_cc {
|
||||||
if not c64.READST() {
|
first_byte = c64.CHRIN() ; read first byte to test for file not found
|
||||||
have_first_byte = true
|
if not c64.READST() {
|
||||||
return true
|
have_first_byte = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,11 +395,14 @@ _end rts
|
|||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
while not c64.READST() {
|
while not c64.READST() {
|
||||||
@(messageptr) = c64.CHRIN()
|
first_byte = c64.CHRIN()
|
||||||
|
if first_byte=='\r' or first_byte=='\n'
|
||||||
|
break
|
||||||
|
@(messageptr) = first_byte
|
||||||
messageptr++
|
messageptr++
|
||||||
}
|
}
|
||||||
|
|
||||||
@(messageptr) = 0
|
@(messageptr) = 0
|
||||||
|
|
||||||
done:
|
done:
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(15)
|
c64.CLOSE(15)
|
||||||
|
@ -665,13 +665,10 @@ greaterequalzero_sb .proc
|
|||||||
|
|
||||||
greaterequalzero_sw .proc
|
greaterequalzero_sw .proc
|
||||||
lda P8ESTACK_HI+1,x
|
lda P8ESTACK_HI+1,x
|
||||||
bmi equalzero_b._false
|
bpl equalzero_b._true
|
||||||
ora P8ESTACK_LO+1,x
|
bmi equalzero_b._false
|
||||||
beq equalzero_b._true
|
|
||||||
bne equalzero_b._false
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
memcopy16_up .proc
|
memcopy16_up .proc
|
||||||
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
|
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
|
||||||
; clobbers register A,X,Y
|
; clobbers register A,X,Y
|
||||||
|
@ -251,4 +251,13 @@ sub bin2uword(str string) -> uword {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub any2uword(str string) -> uword {
|
||||||
|
; -- convert any number string (any prefix allowed) to uword.
|
||||||
|
when string[0] {
|
||||||
|
'$' -> return hex2uword(string)
|
||||||
|
'%' -> return bin2uword(string)
|
||||||
|
else -> return str2uword(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
8.3
|
8.3.1
|
||||||
|
@ -799,8 +799,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
fun isPassByReferenceElement(e: Expression): Boolean {
|
fun isPassByReferenceElement(e: Expression): Boolean {
|
||||||
if(e is IdentifierReference) {
|
if(e is IdentifierReference) {
|
||||||
val decl = e.targetVarDecl(program)!!
|
val decl = e.targetVarDecl(program)
|
||||||
return decl.datatype in PassByReferenceDatatypes
|
return if(decl!=null)
|
||||||
|
decl.datatype in PassByReferenceDatatypes
|
||||||
|
else
|
||||||
|
true // is probably a symbol that needs addr-of
|
||||||
}
|
}
|
||||||
return e is StringLiteral
|
return e is StringLiteral
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||||||
if(mismatch>=0) {
|
if(mismatch>=0) {
|
||||||
val actual = argtypes[mismatch]
|
val actual = argtypes[mismatch]
|
||||||
val expected = consideredParamTypes[mismatch]
|
val expected = consideredParamTypes[mismatch]
|
||||||
if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number !in setOf(0.0, 1.0))
|
return if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number in setOf(0.0, 1.0))
|
||||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
null // specifying a 1 or 0 as a BOOL is okay
|
||||||
|
else
|
||||||
|
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||||
}
|
}
|
||||||
if(target.isAsmSubroutine) {
|
if(target.isAsmSubroutine) {
|
||||||
if(target.asmReturnvaluesRegisters.size>1) {
|
if(target.asmReturnvaluesRegisters.size>1) {
|
||||||
|
@ -94,6 +94,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
|||||||
"amiga",
|
"amiga",
|
||||||
"bdmusic",
|
"bdmusic",
|
||||||
"bobs",
|
"bobs",
|
||||||
|
"circles",
|
||||||
"cobramk3-gfx",
|
"cobramk3-gfx",
|
||||||
"colorbars",
|
"colorbars",
|
||||||
"datetime",
|
"datetime",
|
||||||
|
@ -15,6 +15,25 @@ import prog8tests.helpers.compileText
|
|||||||
|
|
||||||
class TestSubroutines: FunSpec({
|
class TestSubroutines: FunSpec({
|
||||||
|
|
||||||
|
test("string arg for byte param proper errormessage and subroutineptr in array too") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub func(ubyte bb) {
|
||||||
|
bb++
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
func("abc")
|
||||||
|
uword[] commands = ["abc", func]
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 2
|
||||||
|
errors.errors[0] shouldContain "type mismatch, was: STR expected: UBYTE"
|
||||||
|
errors.errors[1] shouldContain "initialisation value has incompatible type"
|
||||||
|
}
|
||||||
|
|
||||||
test("stringParameter") {
|
test("stringParameter") {
|
||||||
val text = """
|
val text = """
|
||||||
main {
|
main {
|
||||||
|
@ -701,11 +701,12 @@ main {
|
|||||||
fl = bb as float
|
fl = bb as float
|
||||||
bb = fl as byte
|
bb = fl as byte
|
||||||
uw = fl as uword
|
uw = fl as uword
|
||||||
|
uw = 8888 + (bb as ubyte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
result.program.entrypoint.statements.size shouldBe 13
|
result.program.entrypoint.statements.size shouldBe 15
|
||||||
}
|
}
|
||||||
|
|
||||||
test("invalid typecasts of numbers") {
|
test("invalid typecasts of numbers") {
|
||||||
|
@ -117,9 +117,9 @@ class TestC64Zeropage: FunSpec({
|
|||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
||||||
zp1.availableBytes() shouldBe 18
|
zp1.availableBytes() shouldBe 18
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, c64target, 999u))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, c64target, 999u))
|
||||||
zp2.availableBytes() shouldBe 85
|
zp2.availableBytes() shouldBe 92
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, c64target, 999u))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, c64target, 999u))
|
||||||
zp3.availableBytes() shouldBe 125
|
zp3.availableBytes() shouldBe 134
|
||||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||||
zp4.availableBytes() shouldBe 239
|
zp4.availableBytes() shouldBe 239
|
||||||
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
||||||
|
@ -197,6 +197,14 @@ interface INameScope: IStatementContainer, INamedStatement {
|
|||||||
else
|
else
|
||||||
statementScope = (statementScope as Node).definingScope
|
statementScope = (statementScope as Node).definingScope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// still not found, maybe it is a symbol in another module
|
||||||
|
for(module in (this as Node).definingModule.program.modules) {
|
||||||
|
val stmt = module.searchSymbol(name)
|
||||||
|
if(stmt!=null)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,9 +757,11 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, select the "biggegst" datatype based on the elements in the array.
|
// otherwise, select the "biggest" datatype based on the elements in the array.
|
||||||
|
require(value.isNotEmpty()) { "can't determine type of empty array" }
|
||||||
val datatypesInArray = value.map { it.inferType(program) }
|
val datatypesInArray = value.map { it.inferType(program) }
|
||||||
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
|
if(datatypesInArray.any{ it.isUnknown })
|
||||||
|
return InferredTypes.InferredType.unknown()
|
||||||
val dts = datatypesInArray.map { it.getOr(DataType.UNDEFINED) }
|
val dts = datatypesInArray.map { it.getOr(DataType.UNDEFINED) }
|
||||||
return when {
|
return when {
|
||||||
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
||||||
|
@ -495,7 +495,9 @@ Breaking out of a loop prematurely is possible with the ``break`` statement.
|
|||||||
languages if this is *not* the case! A for loop from ubyte 10 to ubyte 2, for example, will iterate through
|
languages if this is *not* the case! A for loop from ubyte 10 to ubyte 2, for example, will iterate through
|
||||||
all values 10, 11, 12, 13, .... 254, 255, 0 (wrapped), 1, 2. In other languages the entire loop will
|
all values 10, 11, 12, 13, .... 254, 255, 0 (wrapped), 1, 2. In other languages the entire loop will
|
||||||
be skipped in such cases. But prog8 omits the overhead of an extra loop range check and/or branch for every for loop
|
be skipped in such cases. But prog8 omits the overhead of an extra loop range check and/or branch for every for loop
|
||||||
by assuming the normal ranges.
|
because the most common case is that it is not needed.
|
||||||
|
You should add an explicit range check yourself if the ending value can be less than the start value and
|
||||||
|
a full wrap-around loop is not what you want!
|
||||||
|
|
||||||
|
|
||||||
Conditional Execution
|
Conditional Execution
|
||||||
@ -897,14 +899,17 @@ memory(name, size, alignment)
|
|||||||
|
|
||||||
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
||||||
Calls an assembly routine in another ram-bank on the CommanderX16 (using the ``jsrfar`` routine)
|
Calls an assembly routine in another ram-bank on the CommanderX16 (using the ``jsrfar`` routine)
|
||||||
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte).
|
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte), but you can specify
|
||||||
Notice that bank $00 is used by the Kernal and should not be used by user code.
|
any address in system ram (why this can be useful is explained at the end of this paragraph)
|
||||||
The third argument can be used to designate the memory address
|
The third argument can be used to designate the memory address
|
||||||
of an argument for the routine; it will be loaded into the A register and will
|
of an argument for the routine; it will be loaded into the A register and will
|
||||||
receive the result value returned by the routine in the A register. If you leave this at zero,
|
receive the result value returned by the routine in the A register. If you leave this at zero,
|
||||||
no argument passing will be done.
|
no argument passing will be done.
|
||||||
If the routine requires different arguments or return values, ``callfar`` cannot be used
|
If the routine requires different arguments or return values, ``callfar`` cannot be used
|
||||||
and you'll have to set up a call to ``jsrfar`` yourself to process this.
|
and you'll have to set up a call to ``jsrfar`` yourself to process this.
|
||||||
|
Note: the address can be a variable or other expression, which allows you to use ``callfar`` with bank 0 to do an indirect JSR to a subroutine
|
||||||
|
whose address can vary (jump table, etc. ``goto`` can do an indirect JMP to a variable address): ``callfar(0, &routine, &argument)``
|
||||||
|
This is not very efficient though, so maybe you should write a small piece of inline assembly for this instead.
|
||||||
|
|
||||||
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
||||||
Calls an assembly routine in another rom-bank on the CommanderX16
|
Calls an assembly routine in another rom-bank on the CommanderX16
|
||||||
|
@ -753,14 +753,14 @@ You can still ``break`` out of such a loop if you want though.
|
|||||||
Conditional Execution and Jumps
|
Conditional Execution and Jumps
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Unconditional jump
|
Unconditional jump: goto
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
To jump to another part of the program, you use a ``goto`` statement with an addres or the name
|
To jump to another part of the program, you use a ``goto`` statement with an addres or the name
|
||||||
of a label or subroutine::
|
of a label or subroutine::
|
||||||
|
|
||||||
goto $c000 ; address
|
goto $c000 ; address
|
||||||
goto name ; label or subroutine
|
goto name ; label or subroutine
|
||||||
|
|
||||||
uword address = $4000
|
uword address = $4000
|
||||||
goto address ; jump via address variable
|
goto address ; jump via address variable
|
||||||
@ -770,6 +770,8 @@ to another piece of code that eventually returns).
|
|||||||
|
|
||||||
If you jump to an address variable (uword), it is doing an 'indirect' jump: the jump will be done
|
If you jump to an address variable (uword), it is doing an 'indirect' jump: the jump will be done
|
||||||
to the address that's currently in the variable.
|
to the address that's currently in the variable.
|
||||||
|
Note: to do an indirect *JSR* to a routine with a varying address, you can use the ``callfar`` builtin function
|
||||||
|
(which is not very efficient) or you have to write a small piece of inline assembly.
|
||||||
|
|
||||||
|
|
||||||
Conditional execution
|
Conditional execution
|
||||||
|
@ -17,33 +17,36 @@ Future Things and Ideas
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Compiler:
|
Compiler:
|
||||||
|
|
||||||
- on non-cx16 targets: have an option that if zeropage=FULL, moves the cx16 virtual registers to ZP (same location as on x16?)
|
- add item to XZeropage that enables an option that if zeropage=FULL or KERNALSAFE, moves the cx16 virtual registers to ZP, same location as on x16
|
||||||
needs the dynamic base address for the symbols in syslib.p8
|
(can be done on C64 only for now) Remove those addresses from the ZP free pool = allocate them in ZP like Cx16Zeropage does
|
||||||
also needs a trick to allocate them in ZP like Cx16Zeropage already does
|
Adapt the code in AstPreprocessor that relocates the registers as well.
|
||||||
|
- for uword pointer variables: allow pointer[uword] array indexing >255 , rewrite it to @(pointer+index)
|
||||||
|
DO NOT allow this for regular array indexing because normal arrays can never exceed size 256
|
||||||
|
|
||||||
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
|
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
|
||||||
this info is needed for more advanced optimizations and later code generation steps.
|
this info is needed for more advanced optimizations and later code generation steps.
|
||||||
- vm: implement remaining sin/cos functions in math.p8
|
- vm: implement remaining sin/cos functions in math.p8
|
||||||
- vm: find a solution for the cx16.r0..r15 that "overlap" (r0, r0L, r0H etc) but in the vm each get their own separate variable location now
|
- vm: find a solution for the cx16.r0..r15 that "overlap" (r0, r0L, r0H etc) but in the vm each get their own separate variable location now
|
||||||
- vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8
|
- vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8
|
||||||
- vm: don't store symbol names in instructions to make optimizing the IR easier? but what about jumps to labels. And it's no longer readable by humans.
|
- vm: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us)
|
||||||
- vm: how to remove all unused subroutines? (in the 6502 assembly codegen, we let 64tass solve this for us)
|
|
||||||
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too
|
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too
|
||||||
- vm: add ore optimizations in VmPeepholeOptimizer
|
- vm: add ore optimizations in VmPeepholeOptimizer
|
||||||
- move the vm unit tests to codeGenVirtual module and remove virtualmachine dependency in the compiler module
|
- see if we can let for loops skip the loop if end<start, like other programming languages. Without adding a lot of code size/duplicating the loop condition.
|
||||||
|
this is documented behavior to now loop around but it's too easy to forget about!
|
||||||
|
Lot of work because of so many special cases in ForLoopsAsmgen.....
|
||||||
|
How is it for the vm target? -> just 2 special cases in CodeGen.
|
||||||
- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed.
|
- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed.
|
||||||
It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed.
|
It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed.
|
||||||
So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step.
|
So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step.
|
||||||
- generate WASM from the new ast (or from vm code?) to run prog8 on a browser canvas?
|
- generate WASM from the new ast (or from vm code?) to run prog8 on a browser canvas?
|
||||||
- createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
|
- createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
|
||||||
but probably better to rewrite the 6502 codegen on top of the new Ast.
|
but probably better to rewrite the 6502 codegen on top of the new Ast.
|
||||||
- simplifyConditionalExpression() should not split expression if it still results in stack-based evaluation, but how does it know?
|
- simplifyConditionalExpression() sometimes introduces needless assignment to r9 tempvar, can we detect & prevent this?
|
||||||
- simplifyConditionalExpression() sometimes introduces needless assignment to r9 tempvar (what scenarios?)
|
|
||||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``p8v_``? Or not worth it (most 3 letter opcodes as variables are nonsensical anyway)
|
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``p8v_``? Or not worth it (most 3 letter opcodes as variables are nonsensical anyway)
|
||||||
then we can get rid of the instruction lists in the machinedefinitions as well?
|
then we can get rid of the instruction lists in the machinedefinitions as well?
|
||||||
- [problematic due to using 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
- [problematic due to using 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
||||||
Perhaps replace all uses of .proc/.pend by .block/.bend will fix that?
|
Perhaps replace all uses of .proc/.pend by .block/.bend will fix that with a compiler flag?
|
||||||
(but we lose the optimizing aspect of the assembler where it strips out unused code.
|
But all library code written in asm uses .proc already.....
|
||||||
There's not really a dynamic switch possible as all assembly lib code is static and uses one or the other)
|
|
||||||
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that)
|
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that)
|
||||||
- add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
|
- add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
|
||||||
- ast: don't rewrite by-reference parameter type to uword, but keep the original type (str, array)
|
- ast: don't rewrite by-reference parameter type to uword, but keep the original type (str, array)
|
||||||
@ -59,7 +62,7 @@ Libraries:
|
|||||||
- fix the problems in atari target, and flesh out its libraries.
|
- fix the problems in atari target, and flesh out its libraries.
|
||||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||||
- optimize several inner loops in gfx2 even further?
|
- optimize several inner loops in gfx2 even further?
|
||||||
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)?
|
- add modes 3 and perhaps even 2 to gfx2 (16 color and 4 color)?
|
||||||
- add a flood fill routine to gfx2?
|
- add a flood fill routine to gfx2?
|
||||||
|
|
||||||
|
|
||||||
@ -80,9 +83,7 @@ Optimizations:
|
|||||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. When 6502-codegen is no longer done from
|
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. When 6502-codegen is no longer done from
|
||||||
the old CompilerAst, those checks should probably be removed, or be made permanent
|
the old CompilerAst, those checks should probably be removed, or be made permanent
|
||||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served
|
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served
|
||||||
- AssignmentAsmGen.assignExpression() -> improve code gen for assigning boolean comparison expressions
|
- when a loopvariable of a forloop isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
|
||||||
Check what the vm target does here, maybe just do this as part of the vm -> 6502 codegen.
|
|
||||||
- when a for loop's loopvariable isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
|
|
||||||
but we have no efficient way right now to see if the body references a variable.
|
but we have no efficient way right now to see if the body references a variable.
|
||||||
|
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ STRUCTS again?
|
|||||||
What if we were to re-introduce Structs in prog8? Some thoughts:
|
What if we were to re-introduce Structs in prog8? Some thoughts:
|
||||||
|
|
||||||
- can contain only numeric types (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs
|
- can contain only numeric types (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs
|
||||||
- is just some syntactic sugar for a scoped set of variables -> ast transform to do exactly this before codegen
|
- is just some syntactic sugar for a scoped set of variables -> ast transform to do exactly this before codegen. Codegen doesn't know about struct.
|
||||||
- no arrays of struct -- because too slow on 6502 to access those, rather use struct of arrays instead.
|
- no arrays of struct -- because too slow on 6502 to access those, rather use struct of arrays instead.
|
||||||
can we make this a compiler/codegen only issue? i.e. syntax is just as if it was an array of structs?
|
can we make this a compiler/codegen only issue? i.e. syntax is just as if it was an array of structs?
|
||||||
or make it explicit in the syntax so that it is clear what the memory layout of it is.
|
or make it explicit in the syntax so that it is clear what the memory layout of it is.
|
||||||
|
89
examples/cx16/circles.p8
Normal file
89
examples/cx16/circles.p8
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
%import graphics
|
||||||
|
|
||||||
|
; note: this program is tuned for the CX16, but with some minor modifications can run on other systems too.
|
||||||
|
|
||||||
|
main {
|
||||||
|
const ubyte MAX_NUM_CIRCLES = 80
|
||||||
|
const ubyte GROWTH_RATE = 2
|
||||||
|
uword[MAX_NUM_CIRCLES] circle_x
|
||||||
|
uword[MAX_NUM_CIRCLES] circle_y
|
||||||
|
ubyte[MAX_NUM_CIRCLES] circle_radius
|
||||||
|
ubyte num_circles = 0
|
||||||
|
ubyte background_color
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
graphics.enable_bitmap_mode()
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
background_color = rnd()
|
||||||
|
graphics.clear_screen(0, background_color)
|
||||||
|
num_circles = 0
|
||||||
|
draw_circles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub draw_circles() {
|
||||||
|
uword @zp x
|
||||||
|
uword @zp y
|
||||||
|
ubyte @zp radius
|
||||||
|
|
||||||
|
while num_circles<MAX_NUM_CIRCLES {
|
||||||
|
x = rndw() % graphics.WIDTH
|
||||||
|
y = rndw() % graphics.HEIGHT
|
||||||
|
radius = GROWTH_RATE * 2 ; use a bit of a buffer between circles.
|
||||||
|
if not_colliding() {
|
||||||
|
radius -= GROWTH_RATE
|
||||||
|
ubyte color = rnd()
|
||||||
|
while color==background_color
|
||||||
|
color = rnd()
|
||||||
|
graphics.colors(color, 0)
|
||||||
|
while not_edge() and not_colliding() {
|
||||||
|
graphics.disc(x, y as ubyte, radius)
|
||||||
|
sys.waitvsync()
|
||||||
|
radius += GROWTH_RATE
|
||||||
|
}
|
||||||
|
circle_x[num_circles] = x
|
||||||
|
circle_y[num_circles] = y
|
||||||
|
circle_radius[num_circles] = radius - GROWTH_RATE
|
||||||
|
num_circles++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub not_colliding() -> bool {
|
||||||
|
if num_circles==0
|
||||||
|
return true
|
||||||
|
ubyte @zp c
|
||||||
|
for c in 0 to num_circles-1 {
|
||||||
|
if distance(c) < (radius as uword) + circle_radius[c]
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub distance(ubyte cix) -> uword {
|
||||||
|
word dx = x as word - circle_x[cix]
|
||||||
|
word dy = y as word - circle_y[cix]
|
||||||
|
uword sqx = dx*dx as uword
|
||||||
|
uword sqy = dy*dy as uword
|
||||||
|
return sqrt16(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
|
||||||
|
if x + radius >= graphics.WIDTH
|
||||||
|
return false
|
||||||
|
if y as word - radius < 0
|
||||||
|
return false
|
||||||
|
if y + radius >= graphics.HEIGHT
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,20 +3,14 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte value1 = %1110
|
ubyte ci
|
||||||
ubyte value2 = %1111
|
ubyte from=10
|
||||||
|
ubyte end=1
|
||||||
|
|
||||||
bool[2] @shared barr = [true, false]
|
for ci in from to end {
|
||||||
|
txt.print_ub(ci)
|
||||||
; if value1 and value2 ; TODO value1 isn't converted to bool in 6502 preprocess
|
txt.spc()
|
||||||
; txt.print("ok")
|
}
|
||||||
; else
|
txt.nl()
|
||||||
; txt.print("fail!")
|
|
||||||
; txt.nl()
|
|
||||||
|
|
||||||
if value1 and value2!=255 ; TODO value1 isn't converted to bool in 6502 preprocess
|
|
||||||
txt.print("ok")
|
|
||||||
else
|
|
||||||
txt.print("fail!")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user