mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f5706bbeb | ||
|
|
25c9b2fea4 | ||
|
|
154f9b300f | ||
|
|
d78ce77536 | ||
|
|
b4fb43bc80 | ||
|
|
e0e01f794e | ||
|
|
08865dbb4e | ||
|
|
b9ad7e0e55 | ||
|
|
07158a6f1a | ||
|
|
957c42bc1d | ||
|
|
f784da2da6 | ||
|
|
c080fbe59a | ||
|
|
d70b8303b1 | ||
|
|
1d38c3582a | ||
|
|
9438e996d7 | ||
|
|
3b4a5e27f7 | ||
|
|
648d9fc269 | ||
|
|
54fccec7d7 | ||
|
|
4f9693055e | ||
|
|
555c50ee10 | ||
|
|
bf98ceca2c | ||
|
|
573cecb087 | ||
|
|
1b528491c2 | ||
|
|
4bdabe1961 | ||
|
|
a3fa527378 | ||
|
|
84f5ffa426 |
No files matched your search
@@ -1,12 +1,10 @@
|
||||
.PHONY: all clean emu
|
||||
.PHONY: clean run
|
||||
|
||||
all: benchmark.prg
|
||||
run:
|
||||
prog8c -target cx16 benchmark.p8
|
||||
x16emu -run -prg benchmark.prg -warp
|
||||
|
||||
clean:
|
||||
rm -f *.prg *.PRG *.asm *.vice-* *.BIN *.PAL *.zip *.7z
|
||||
|
||||
emu: benchmark.prg
|
||||
x16emu -run -prg $< -warp
|
||||
|
||||
benchmark.prg: benchmark.p8 b_3d.p8 b_adpcm.p8 b_circles.p8 b_life.p8 b_mandelbrot.p8 b_maze.p8 b_queens.p8 b_textelite.p8
|
||||
prog8c $< -target cx16
|
||||
@@ -96,7 +96,12 @@ class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Positi
|
||||
}
|
||||
|
||||
|
||||
class PtLabel(name: String, position: Position) : PtNamedNode(name, position)
|
||||
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
|
||||
companion object {
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GeneratedLabelPrefix = "p8_label_gen_"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtBreakpoint(position: Position): PtNode(position)
|
||||
|
||||
@@ -82,6 +82,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
}
|
||||
|
||||
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
|
||||
fun asConstValue(): Double? = (this as? PtNumber)?.number ?: (this as? PtBool)?.asInt()?.toDouble()
|
||||
|
||||
fun isSimple(): Boolean {
|
||||
return when(this) {
|
||||
@@ -349,12 +350,11 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
|
||||
|
||||
val fromLv = from as? PtNumber
|
||||
val toLv = to as? PtNumber
|
||||
val stepLv = step as? PtNumber
|
||||
if(fromLv==null || toLv==null || stepLv==null)
|
||||
if(fromLv==null || toLv==null)
|
||||
return null
|
||||
val fromVal = fromLv.number.toInt()
|
||||
val toVal = toLv.number.toInt()
|
||||
val stepVal = stepLv.number.toInt()
|
||||
val stepVal = step.number.toInt()
|
||||
return makeRange(fromVal, toVal, stepVal)
|
||||
}
|
||||
}
|
||||
@@ -378,7 +378,3 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
|
||||
|
||||
// special node that isn't created from compiling user code, but used internally in the Intermediate Code
|
||||
class PtIrRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
||||
|
||||
|
||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else if(expr is PtBool) expr.asInt().toDouble() else null
|
||||
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else if(expr is PtBool) expr.asInt() else null
|
||||
@@ -49,11 +49,14 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
return when {
|
||||
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||
actualParamTypes.size==1 -> {
|
||||
// one parameter goes via register/registerpair
|
||||
// One parameter goes via register/registerpair.
|
||||
// this avoids repeated code for every caller to store the value in the subroutine's argument variable.
|
||||
// (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site).
|
||||
// TODO can we start using the X register as well for a third byte or word+byte combo
|
||||
val paramConv = when(val paramType = actualParamTypes[0]) {
|
||||
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // TODO is this correct? shouldn't it be FAC1?
|
||||
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ParamConvention(paramType, null, false)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@ interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val PROGRAM_TOP_ADDRESS: UInt
|
||||
val PROGRAM_MEMTOP_ADDRESS: UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
val BSSGOLDENRAM_START: UInt
|
||||
|
||||
@@ -11,8 +11,9 @@ class AtariMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||
override val PROGRAM_TOP_ADDRESS = 0xffffu // TODO what's memtop
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // TODO what's memtop?
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
@@ -13,8 +13,9 @@ class C128MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_TOP_ADDRESS = 0xfeffu
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xff00u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
@@ -14,8 +14,9 @@ class C64MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_TOP_ADDRESS = 0xcfe0u // $9fff if floats are used
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
|
||||
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
|
||||
@@ -13,8 +13,9 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_TOP_ADDRESS = 0x9effu
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||
|
||||
@@ -11,8 +11,9 @@ class Neo6502MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0800u
|
||||
override val PROGRAM_TOP_ADDRESS = 0xfbffu
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xfc00u // kernal starts here
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
@@ -13,8 +13,9 @@ class PETMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
override val PROGRAM_TOP_ADDRESS = 0x7fffu
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u
|
||||
override val BSSHIGHRAM_END = 0u
|
||||
|
||||
@@ -13,8 +13,9 @@ class VirtualMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
override val PROGRAM_TOP_ADDRESS = 0xffffu // not actually used
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
|
||||
@@ -16,15 +16,15 @@ import kotlin.io.path.writeLines
|
||||
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
|
||||
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
|
||||
|
||||
class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSequenceNr: Int): ICodeGeneratorBackend {
|
||||
override fun generate(
|
||||
program: PtProgram,
|
||||
symbolTable: SymbolTable,
|
||||
options: CompilationOptions,
|
||||
errors: IErrorReporter
|
||||
errors: IErrorReporter,
|
||||
): IAssemblyProgram? {
|
||||
val st = if(prefixSymbols) prefixSymbols(program, options, symbolTable) else symbolTable
|
||||
val asmgen = AsmGen6502Internal(program, st, options, errors)
|
||||
val asmgen = AsmGen6502Internal(program, st, options, errors, lastGeneratedLabelSequenceNr)
|
||||
return asmgen.compileToAssembly()
|
||||
}
|
||||
|
||||
@@ -36,10 +36,9 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
when(node) {
|
||||
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
|
||||
is PtBlock -> node.name = "p8b_${node.name}"
|
||||
is PtLabel -> node.name = "p8l_${node.name}"
|
||||
is PtLabel -> if(!node.name.startsWith(PtLabel.GeneratedLabelPrefix)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
|
||||
is PtConstant -> node.name = "p8c_${node.name}"
|
||||
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
|
||||
else -> node.name = "p8_${node.name}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,11 +125,15 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
}
|
||||
|
||||
private fun prefixScopedName(name: String, type: Char): String {
|
||||
if('.' !in name)
|
||||
if('.' !in name) {
|
||||
if(name.startsWith(PtLabel.GeneratedLabelPrefix))
|
||||
return name
|
||||
return "p8${type}_$name"
|
||||
}
|
||||
val parts = name.split('.')
|
||||
val firstPrefixed = "p8b_${parts[0]}"
|
||||
val lastPrefixed = "p8${type}_${parts.last()}"
|
||||
val lastPart = parts.last()
|
||||
val lastPrefixed = if(lastPart.startsWith(PtLabel.GeneratedLabelPrefix)) lastPart else "p8${type}_$lastPart"
|
||||
// the parts in between are assumed to be subroutine scopes.
|
||||
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
|
||||
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
|
||||
@@ -221,7 +224,8 @@ class AsmGen6502Internal (
|
||||
val program: PtProgram,
|
||||
internal val symbolTable: SymbolTable,
|
||||
internal val options: CompilationOptions,
|
||||
internal val errors: IErrorReporter
|
||||
internal val errors: IErrorReporter,
|
||||
private var generatedLabelSequenceNumber: Int
|
||||
) {
|
||||
|
||||
internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
|
||||
@@ -785,7 +789,7 @@ class AsmGen6502Internal (
|
||||
when {
|
||||
iterations == 0 -> {}
|
||||
iterations == 1 -> translate(stmt.statements)
|
||||
iterations<0 || iterations>65535 -> throw AssemblyError("invalid number of iterations")
|
||||
iterations<0 || iterations>65536 -> throw AssemblyError("invalid number of iterations")
|
||||
iterations <= 256 -> repeatByteCount(iterations, stmt)
|
||||
else -> repeatWordCount(iterations, stmt)
|
||||
}
|
||||
@@ -825,10 +829,10 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
|
||||
require(iterations in 257..65535) { "invalid repeat count ${stmt.position}" }
|
||||
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val counterVar = createRepeatCounterVar(DataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt)
|
||||
val loopcount = if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
|
||||
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
|
||||
out("""
|
||||
ldy #>$loopcount
|
||||
lda #<$loopcount
|
||||
@@ -1364,11 +1368,9 @@ $repeatLabel""")
|
||||
extra
|
||||
}
|
||||
|
||||
private var generatedLabelSequenceNumber: Int = 0
|
||||
|
||||
internal fun makeLabel(postfix: String): String {
|
||||
generatedLabelSequenceNumber++
|
||||
return "label_asm_${generatedLabelSequenceNumber}_$postfix"
|
||||
return "${PtLabel.GeneratedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
|
||||
}
|
||||
|
||||
fun assignConstFloatToPointerAY(number: PtNumber) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
|
||||
import prog8.code.StConstant
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtLabel
|
||||
import prog8.code.core.IMachineDefinition
|
||||
|
||||
|
||||
@@ -361,15 +362,16 @@ or *_afterif labels.
|
||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||
*/
|
||||
|
||||
val autoLabelPrefix = PtLabel.GeneratedLabelPrefix
|
||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
|
||||
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
else if(fourth.startsWith("label_") && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
||||
if((fifth.startsWith("beq label_") || fifth.startsWith("bne label_")) &&
|
||||
else if(fourth.startsWith(autoLabelPrefix) && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
||||
if((fifth.startsWith("beq $autoLabelPrefix") || fifth.startsWith("bne $autoLabelPrefix")) &&
|
||||
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
|
||||
@@ -11,18 +11,22 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
||||
// 1) cx16 virtual word registers,
|
||||
// 2) paired CPU registers,
|
||||
// 3) single CPU registers (order Y,X,A),
|
||||
// 4) CPU Carry status flag
|
||||
// 4) floating point registers (FAC1, FAC2),
|
||||
// 5) CPU Carry status flag
|
||||
val args = sub.parameters.withIndex()
|
||||
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
||||
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
||||
val (singleRegs, rest) = args3.partition { it.value.first.registerOrPair != null }
|
||||
val (singleRegsMixed, rest) = args3.partition { it.value.first.registerOrPair != null }
|
||||
val (singleCpuRegs, floatRegs) = singleRegsMixed.partition {it.value.first.registerOrPair != RegisterOrPair.FAC1 && it.value.first.registerOrPair != RegisterOrPair.FAC2 }
|
||||
|
||||
cx16regs.forEach { order += it.index }
|
||||
pairedRegs.forEach { order += it.index }
|
||||
singleRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
|
||||
singleCpuRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
|
||||
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null})
|
||||
floatRegs.forEach { order += it.index }
|
||||
rest.forEach { order += it.index }
|
||||
require(order.size==sub.parameters.size)
|
||||
|
||||
return order
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtLabel
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.AtariTarget
|
||||
import prog8.code.target.C64Target
|
||||
@@ -9,7 +10,7 @@ import java.nio.file.Path
|
||||
|
||||
internal class AssemblyProgram(
|
||||
override val name: String,
|
||||
outputDir: Path,
|
||||
private val outputDir: Path,
|
||||
private val compTarget: ICompilationTarget) : IAssemblyProgram {
|
||||
|
||||
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||
@@ -147,7 +148,7 @@ internal class AssemblyProgram(
|
||||
}
|
||||
|
||||
private fun removeGeneratedLabelsFromMonlist() {
|
||||
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
|
||||
val pattern = Regex("""al (\w+) \S+${PtLabel.GeneratedLabelPrefix}.+?""")
|
||||
val lines = viceMonListFile.toFile().readLines()
|
||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||
for (line in lines) {
|
||||
|
||||
@@ -15,6 +15,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// just ignore any result values from the function call.
|
||||
}
|
||||
|
||||
// TODO tweak subroutine call convention to also make use of X register to pass a third byte?
|
||||
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
|
||||
|
||||
@@ -91,6 +91,7 @@ internal class ProgramAndVarsGen(
|
||||
OutputType.RAW -> {
|
||||
asmgen.out("; ---- raw assembler program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
@@ -106,6 +107,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
asmgen.out("; ---- basic program with sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
val year = LocalDate.now().year
|
||||
asmgen.out(" .word (+), $year")
|
||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||
@@ -122,6 +124,7 @@ internal class ProgramAndVarsGen(
|
||||
// this is the same as RAW
|
||||
asmgen.out("; ---- program without basic sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
@@ -134,6 +137,7 @@ internal class ProgramAndVarsGen(
|
||||
OutputType.XEX -> {
|
||||
asmgen.out("; ---- atari xex program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
@@ -287,7 +291,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
asmgen.out(" ; memtop check")
|
||||
asmgen.out(" .cerror * > ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${options.memtopAddress.toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||
}
|
||||
|
||||
private fun block2asm(block: PtBlock) {
|
||||
|
||||
@@ -44,7 +44,7 @@ class TestCodegen: FunSpec({
|
||||
// xx += cx16.r0
|
||||
// }
|
||||
//}
|
||||
val codegen = AsmGen6502(prefixSymbols = false)
|
||||
val codegen = AsmGen6502(prefixSymbols = false, 0)
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
|
||||
@@ -359,7 +359,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val variable = targetArray.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
|
||||
|
||||
val fixedIndex = constIntValue(targetArray.index)
|
||||
val fixedIndex = targetArray.index.asConstInteger()
|
||||
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
|
||||
if(zero) {
|
||||
if(fixedIndex!=null) {
|
||||
|
||||
@@ -608,6 +608,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> TODO("floating point register parameters not supported")
|
||||
in Cx16VirtualRegisters -> {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1=tr.resultReg, labelSymbol = "cx16.${parameter.register.registerOrPair.toString().lowercase()}"), null)
|
||||
}
|
||||
@@ -894,7 +895,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) {
|
||||
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
||||
} else {
|
||||
return if(constValue(binExpr.right)==0.0) {
|
||||
return if(binExpr.right.asConstValue()==0.0) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val opcode = if (notEquals) Opcode.SNZ else Opcode.SZ
|
||||
|
||||
@@ -92,7 +92,6 @@ class IRCodeGen(
|
||||
when (node) {
|
||||
is PtBuiltinFunctionCall -> require('.' !in node.name) { "builtin function call name should not be scoped: ${node.name}" }
|
||||
is PtFunctionCall -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtIdentifier -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtAsmSub -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtBlock -> require('.' !in node.name) { "block name should not be scoped: ${node.name}" }
|
||||
is PtConstant -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
@@ -102,8 +101,7 @@ class IRCodeGen(
|
||||
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
|
||||
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
else -> { /* node has no name */
|
||||
}
|
||||
else -> { /* node has no name or is ok to have no dots in the name */ }
|
||||
}
|
||||
node.children.forEach { verifyPtNode(it) }
|
||||
}
|
||||
@@ -1330,7 +1328,7 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translateIfElse(ifElse: PtIfElse): IRCodeChunks {
|
||||
if((ifElse.condition as? PtPrefix)?.operator=="not")
|
||||
if((ifElse.condition as? PtPrefix)?.operator=="not" && ifElse.hasElse())
|
||||
throw AssemblyError("not prefix in ifelse should have been replaced by swapped if-else blocks")
|
||||
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
@@ -1589,7 +1587,7 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translate(repeat: PtRepeatLoop): IRCodeChunks {
|
||||
when (constIntValue(repeat.count)) {
|
||||
when (repeat.count.asConstInteger()) {
|
||||
0 -> return emptyList()
|
||||
1 -> return translateGroup(repeat.children)
|
||||
256 -> {
|
||||
@@ -1600,18 +1598,30 @@ class IRCodeGen(
|
||||
|
||||
val repeatLabel = createLabelName()
|
||||
val skipRepeatLabel = createLabelName()
|
||||
val irDt = irType(repeat.count.type)
|
||||
val constRepeats = repeat.count.asConstInteger()
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val countTr = expressionEval.translateExpression(repeat.count)
|
||||
addToResult(result, countTr, countTr.resultReg, -1)
|
||||
if(constIntValue(repeat.count)==null) {
|
||||
// check if the counter is already zero
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipRepeatLabel), null)
|
||||
}
|
||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg) // sets status bits
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = repeatLabel)
|
||||
if(constRepeats==65536) {
|
||||
// make use of the word wrap around to count to 65536
|
||||
val resultRegister = registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, immediate = 0), null)
|
||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.WORD, reg1 = resultRegister) // sets status bits
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = repeatLabel)
|
||||
}
|
||||
} else {
|
||||
val irDt = irType(repeat.count.type)
|
||||
val countTr = expressionEval.translateExpression(repeat.count)
|
||||
addToResult(result, countTr, countTr.resultReg, -1)
|
||||
if (repeat.count.asConstValue() == null) {
|
||||
// check if the counter is already zero
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipRepeatLabel), null)
|
||||
}
|
||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg) // sets status bits
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = repeatLabel)
|
||||
}
|
||||
}
|
||||
result += IRCodeChunk(skipRepeatLabel, null)
|
||||
return result
|
||||
@@ -1752,7 +1762,7 @@ class IRCodeGen(
|
||||
private var labelSequenceNumber = 0
|
||||
internal fun createLabelName(): String {
|
||||
labelSequenceNumber++
|
||||
return "label_gen_$labelSequenceNumber"
|
||||
return "${PtLabel.GeneratedLabelPrefix}$labelSequenceNumber"
|
||||
}
|
||||
|
||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
||||
|
||||
@@ -336,6 +336,13 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progstart() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_start
|
||||
ldy #>prog8_program_start
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
%import shared_cbm_diskio
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
|
||||
@@ -887,6 +887,13 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progstart() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_start
|
||||
ldy #>prog8_program_start
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
%import shared_cbm_diskio
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
|
||||
@@ -903,6 +903,13 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progstart() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_start
|
||||
ldy #>prog8_program_start
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
|
||||
+13
-13
@@ -12,7 +12,7 @@
|
||||
; This is compatible with the CX16's screen mode 128. (void cx16.set_screen_mode(128))
|
||||
;
|
||||
|
||||
gfx_hires4 {
|
||||
gfx_hires {
|
||||
|
||||
%option ignore_unused
|
||||
|
||||
@@ -144,7 +144,7 @@ gfx_hires4 {
|
||||
and #3
|
||||
tay
|
||||
lda cx16.VERA_DATA0
|
||||
and p8b_gfx_hires4.p8s_plot.p8v_mask4c,y
|
||||
and p8b_gfx_hires.p8s_plot.p8v_mask4c,y
|
||||
ora p8v_colorbits,y
|
||||
sta cx16.VERA_DATA0
|
||||
cpy #%00000011 ; next vera byte?
|
||||
@@ -189,11 +189,11 @@ gfx_hires4 {
|
||||
cx16.r2L = lsb(xx) & 3
|
||||
when color & 3 {
|
||||
0 -> color = 0
|
||||
1 -> color = gfx_hires4.plot.shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> color = gfx_hires4.plot.shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> color = gfx_hires4.plot.shiftedleft_4c_3[cx16.r2L]
|
||||
1 -> color = gfx_hires.plot.shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> color = gfx_hires.plot.shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> color = gfx_hires.plot.shiftedleft_4c_3[cx16.r2L]
|
||||
}
|
||||
ubyte @shared mask = gfx_hires4.plot.mask4c[lsb(xx) & 3]
|
||||
ubyte @shared mask = gfx_hires.plot.mask4c[lsb(xx) & 3]
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
@@ -561,7 +561,7 @@ gfx_hires4 {
|
||||
lda p8v_xx
|
||||
and #3
|
||||
tay
|
||||
lda p8b_gfx_hires4.p8s_plot.p8v_shift4c,y
|
||||
lda p8b_gfx_hires.p8s_plot.p8v_shift4c,y
|
||||
tay
|
||||
pla
|
||||
cpy #0
|
||||
@@ -704,7 +704,7 @@ skip:
|
||||
|
||||
sub fill_scanline_left_2bpp() -> bool {
|
||||
uword vx = xx as uword
|
||||
void gfx_hires4.addr_mul_24_for_highres_4c(yy as uword,vx)
|
||||
void gfx_hires.addr_mul_24_for_highres_4c(yy as uword,vx)
|
||||
cx16.r1L |= %0001_1000 ; auto decrement
|
||||
set_vera_address()
|
||||
cmask = amask[lsb(vx) & 3] ; set the color mask for the first color pel
|
||||
@@ -748,7 +748,7 @@ set_byte:
|
||||
}
|
||||
|
||||
sub fill_scanline_right_2bpp() {
|
||||
void gfx_hires4.addr_mul_24_for_highres_4c(yy as uword,xx as uword)
|
||||
void gfx_hires.addr_mul_24_for_highres_4c(yy as uword,xx as uword)
|
||||
cx16.r1L |= %00010000 ; auto increment
|
||||
set_vera_address()
|
||||
cmask = amask[lsb(xx) & 3] ; set the color mask for the first color pel
|
||||
@@ -891,12 +891,12 @@ set_byte:
|
||||
if_cs {
|
||||
cx16.r2L = cx16.r7L & 3 ; xbits
|
||||
when cx16.r11L & 3 {
|
||||
1 -> cx16.r12L = gfx_hires4.plot.shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> cx16.r12L = gfx_hires4.plot.shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> cx16.r12L = gfx_hires4.plot.shiftedleft_4c_3[cx16.r2L]
|
||||
1 -> cx16.r12L = gfx_hires.plot.shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> cx16.r12L = gfx_hires.plot.shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> cx16.r12L = gfx_hires.plot.shiftedleft_4c_3[cx16.r2L]
|
||||
else -> cx16.r12L = 0
|
||||
}
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA0 & gfx_hires4.plot.mask4c[cx16.r2L] | cx16.r12L
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA0 & gfx_hires.plot.mask4c[cx16.r2L] | cx16.r12L
|
||||
}
|
||||
cx16.r7++
|
||||
if (cx16.r7 & 3) == 0 {
|
||||
@@ -4,7 +4,7 @@
|
||||
; Wraps the graphics functions that are in ROM.
|
||||
; Only lo-res 320x240 256 color mode for now.
|
||||
; 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 "gfx_lores", "gfx_hires4", or "monogfx" module instead.
|
||||
; For other resolutions or other color modes, use the "gfx_lores", "gfx_hires", or "monogfx" module instead.
|
||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||
;
|
||||
; NOTE: For sake of speed, NO BOUNDS CHECKING is performed in most routines!
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
; Using the full-screen 640x480 and 320x240 screen modes, in 1 bpp mode (black/white).
|
||||
;
|
||||
; No text layer is currently shown, but text can be drawn as part of the bitmap itself.
|
||||
; For color bitmap graphics, see the gfx_lores or gfx_hires4 libraries.
|
||||
; For color bitmap graphics, see the gfx_lores or gfx_hires libraries.
|
||||
;
|
||||
; NOTE: For sake of speed, NO BOUNDS CHECKING is performed in most routines!
|
||||
; You'll have to make sure yourself that you're not writing outside of bitmap boundaries!
|
||||
|
||||
@@ -34,6 +34,22 @@ sprites {
|
||||
cx16.vpoke(1, sprite_reg+7, height_flag<<6 | width_flag<<4 | palette_offset&15) ; 64x64 pixels, palette offset
|
||||
}
|
||||
|
||||
sub reset(ubyte spritenum_start, ubyte count) {
|
||||
; resets all sprite attributes for the given sprite range
|
||||
; this removes these sprites from the screen completely
|
||||
; (without disabling sprites globally so the mouse cursor remains visible)
|
||||
if spritenum_start > 127
|
||||
return
|
||||
if count + spritenum_start > 128
|
||||
return
|
||||
cx16.VERA_CTRL = $00
|
||||
cx16.VERA_ADDR_H = $11
|
||||
cx16.VERA_ADDR = VERA_SPRITEREGS + spritenum_start * $0008
|
||||
repeat count {
|
||||
unroll 8 cx16.VERA_DATA0 = $00
|
||||
}
|
||||
}
|
||||
|
||||
sub data(ubyte spritenum, ubyte bank, uword addr) {
|
||||
addr >>= 5
|
||||
addr |= (bank as uword)<<11
|
||||
|
||||
@@ -1822,6 +1822,13 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progstart() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_start
|
||||
ldy #>prog8_program_start
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
@@ -1922,7 +1929,6 @@ asmsub cleanup_at_exit() {
|
||||
sta $00 ; ram bank 1
|
||||
lda #4
|
||||
sta $01 ; rom bank 4 (basic)
|
||||
stz $2d ; hack to reset machine code monitor bank to 0
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
_exitcodeCarry = *+1
|
||||
lda #0
|
||||
|
||||
@@ -28,7 +28,7 @@ verafx {
|
||||
|
||||
sub clear(ubyte vbank, uword vaddr, ubyte data, uword num_longwords) {
|
||||
; use cached 4-byte write to quickly clear a portion of the video memory to a given byte value
|
||||
; this routine is around 3 times faster as gfx_hires4/gfx_lores.clear_screen()
|
||||
; this routine is around 3 times faster as gfx_hires/gfx_lores.clear_screen()
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = vbank | %00110000 ; 4-byte increment
|
||||
cx16.VERA_ADDR_M = msb(vaddr)
|
||||
|
||||
@@ -175,8 +175,8 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
; as soon as a negative word value (or 2) was used in the multiplication, these upper 16 bits are not valid!!
|
||||
; Suggestion (if you are on the Commander X16): use verafx.muls() to get a hardware accelerated 32 bit signed multplication.
|
||||
%asm {{
|
||||
lda multiply_words.result+2
|
||||
ldy multiply_words.result+3
|
||||
lda prog8_math.multiply_words.result+2
|
||||
ldy prog8_math.multiply_words.result+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -651,4 +651,15 @@ log2_tab
|
||||
; guarantees v = v1 when t = 255
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
}
|
||||
|
||||
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||
; Linear interpolation (LERP) on word values
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
; guarantees v = v1 when t = 65535
|
||||
t *= v1-v0
|
||||
cx16.r0 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r0++
|
||||
return v0 + cx16.r0
|
||||
}
|
||||
}
|
||||
@@ -270,6 +270,13 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progstart() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_start
|
||||
ldy #>prog8_program_start
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
|
||||
@@ -435,6 +435,13 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progstart() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_start
|
||||
ldy #>prog8_program_start
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; C64/C128 disk drive I/O routines.
|
||||
; shared CBM (C64/C128) disk drive I/O routines.
|
||||
|
||||
%import textio
|
||||
%import conv
|
||||
@@ -6,7 +6,7 @@
|
||||
%import syslib
|
||||
|
||||
diskio {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
%option merge, no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
@@ -406,4 +406,15 @@ math {
|
||||
; guarantees v = v1 when t = 255
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
}
|
||||
|
||||
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||
; Linear interpolation (LERP) on word values
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
; guarantees v = v1 when t = 65535
|
||||
t *= v1-v0
|
||||
cx16.r0 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r0++
|
||||
return v0 + cx16.r0
|
||||
}
|
||||
}
|
||||
@@ -41,35 +41,35 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
|
||||
|
||||
private fun compileMain(args: Array<String>): Boolean {
|
||||
val cli = ArgParser("prog8c", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
|
||||
val addMissingRts by cli.option(ArgType.Boolean, fullName = "addmissingrts", description="enable old behavior that silently adds RTS to asmsubs that don't have one (deprecated, may go away in future version)")
|
||||
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
|
||||
val breakpointCpuInstruction by cli.option(ArgType.Choice(listOf("brk", "stp"), { it }), fullName = "breakinstr", description = "the CPU instruction to use as well for %breakpoint")
|
||||
val bytes2float by cli.option(ArgType.String, fullName = "bytes2float", description = "convert a comma separated list of bytes from the target system to a float value. NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!")
|
||||
val checkSource by cli.option(ArgType.Boolean, fullName = "check", description = "quickly check program for errors, no output will be produced")
|
||||
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
|
||||
val dumpSymbols by cli.option(ArgType.Boolean, fullName = "dumpsymbols", description = "print a dump of the variable declarations and subroutine signatures")
|
||||
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
|
||||
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
||||
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
|
||||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
||||
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
|
||||
val dumpSymbols by cli.option(ArgType.Boolean, fullName = "dumpsymbols", description = "print a dump of the variable declarations and subroutine signatures")
|
||||
val float2bytes by cli.option(ArgType.String, fullName = "float2bytes", description = "convert floating point number to a list of bytes for the target system. NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!")
|
||||
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations")
|
||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
|
||||
val addMissingRts by cli.option(ArgType.Boolean, fullName = "addmissingrts", description="enable old behavior that silently adds RTS to asmsubs that don't have one (deprecated, may go away in future version)")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
|
||||
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
||||
val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the compiler AST")
|
||||
val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the intermediate AST that is used for code generation")
|
||||
val breakpointCpuInstruction by cli.option(ArgType.Choice(listOf("brk", "stp"), { it }), fullName = "breakinstr", description = "the CPU instruction to use as well for %breakpoint")
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")}) (required)")
|
||||
val bytes2float by cli.option(ArgType.String, fullName = "bytes2float", description = "convert a comma separated list of bytes from the target system to a float value. NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!")
|
||||
val float2bytes by cli.option(ArgType.String, fullName = "float2bytes", description = "convert floating point number to a list of bytes for the target system. NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
|
||||
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")}) (required)")
|
||||
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
|
||||
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
try {
|
||||
|
||||
@@ -149,7 +149,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions)) {
|
||||
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions, program.generatedLabelSequenceNumber)) {
|
||||
System.err.println("Error in codegeneration or assembler")
|
||||
return null
|
||||
}
|
||||
@@ -264,7 +264,14 @@ internal fun determineProgramLoadAddress(program: Program, options: CompilationO
|
||||
}
|
||||
|
||||
options.loadAddress = loadAddress
|
||||
options.memtopAddress = program.toplevelModule.memtopAddress?.first ?: options.compTarget.machine.PROGRAM_TOP_ADDRESS
|
||||
|
||||
options.memtopAddress = program.toplevelModule.memtopAddress?.first ?: options.compTarget.machine.PROGRAM_MEMTOP_ADDRESS
|
||||
|
||||
if(loadAddress>options.memtopAddress) {
|
||||
errors.warn("program load address ${loadAddress.toHex()} is beyond default memtop address ${options.memtopAddress.toHex()}. " +
|
||||
"Memtop has been adjusted to ${'$'}ffff to avoid assembler error. Set a valid %memtop yourself to get rid of this warning.", program.toplevelModule.position)
|
||||
options.memtopAddress = 0xffffu
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -489,13 +496,14 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
|
||||
|
||||
private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
errors: IErrorReporter,
|
||||
compilerOptions: CompilationOptions
|
||||
compilerOptions: CompilationOptions,
|
||||
lastGeneratedLabelSequenceNr: Int
|
||||
): Boolean {
|
||||
|
||||
val asmgen = if(compilerOptions.experimentalCodegen)
|
||||
prog8.codegen.experimental.ExperiCodeGen()
|
||||
else if (compilerOptions.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
|
||||
prog8.codegen.cpu6502.AsmGen6502(prefixSymbols = true)
|
||||
prog8.codegen.cpu6502.AsmGen6502(prefixSymbols = true, lastGeneratedLabelSequenceNr+1)
|
||||
else if (compilerOptions.compTarget.name == VMTarget.NAME)
|
||||
VmCodeGen()
|
||||
else
|
||||
|
||||
@@ -283,8 +283,9 @@ internal class AstChecker(private val program: Program,
|
||||
if (addr > 65535u)
|
||||
errors.err("block address must be valid integer 0..\$ffff", block.position)
|
||||
if(compilerOptions.loadAddress!=0u) {
|
||||
if (addr < compilerOptions.loadAddress + 20u)
|
||||
errors.err("block address must be at least program load address + 20 (to allow for startup logic)", block.position)
|
||||
val gapsize = compilerOptions.compTarget.machine.STARTUP_CODE_RESERVED_SIZE
|
||||
if (addr < compilerOptions.loadAddress + gapsize)
|
||||
errors.err("block address must be at least program load address + $gapsize (to allow for startup logic)", block.position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +548,7 @@ internal class AstChecker(private val program: Program,
|
||||
val iterations = repeatLoop.iterations?.constValue(program)
|
||||
if (iterations != null) {
|
||||
require(floor(iterations.number)==iterations.number)
|
||||
if (iterations.number.toInt() > 65535) errors.err("repeat cannot go over 65535 iterations", iterations.position)
|
||||
if (iterations.number.toInt() > 65536) errors.err("repeat cannot exceed 65536 iterations", iterations.position)
|
||||
}
|
||||
|
||||
val ident = repeatLoop.iterations as? IdentifierReference
|
||||
@@ -1184,9 +1185,6 @@ internal class AstChecker(private val program: Program,
|
||||
defer.scope.accept(s)
|
||||
if(s.count>0)
|
||||
errors.err("defer cannot contain jumps or returns", defer.position)
|
||||
|
||||
if(defer.parent !is Subroutine)
|
||||
errors.err("currently defer is only supported in subroutine scope, not in nested scopes", defer.position)
|
||||
}
|
||||
|
||||
override fun visit(expr: BinaryExpression) {
|
||||
@@ -1700,8 +1698,10 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
if(expression.inferType(program).istype(DataType.LONG)) {
|
||||
if(errors.noErrorForLine(expression.position))
|
||||
errors.err("integer overflow", expression.position)
|
||||
if(expression.parent !is RepeatLoop) {
|
||||
if (errors.noErrorForLine(expression.position))
|
||||
errors.err("integer overflow", expression.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -401,7 +401,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
val prefixedFcall = prefix.expression as? FunctionCallExpression
|
||||
if (prefixedFcall != null) {
|
||||
val returnRegs = prefixedFcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
|
||||
if (returnRegs != null && returnRegs.size == 1 && returnRegs[0].statusflag != null) {
|
||||
return codeForStatusflag(prefixedFcall, returnRegs[0].statusflag!!, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,15 +60,16 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
|
||||
|
||||
for((deferIndex, defer) in defers.withIndex()) {
|
||||
// replace the defer statement with one that enables the bit in the mask for this defer
|
||||
val idx = defer.parent.children.indexOf(defer)
|
||||
val scope = defer.parent
|
||||
val idx = scope.children.indexOf(defer)
|
||||
val enableDefer = PtAugmentedAssign("|=", defer.position)
|
||||
val target = PtAssignTarget(true, defer.position)
|
||||
target.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, defer.position))
|
||||
enableDefer.add(target)
|
||||
// enable the bit for this defer (beginning with high bits so the handler can simply shift right to check them in reverse order)
|
||||
enableDefer.add(PtNumber(DataType.UBYTE, (1 shl (defers.size-1 - deferIndex)).toDouble(), defer.position))
|
||||
enableDefer.parent = sub
|
||||
sub.children[idx] = enableDefer
|
||||
enableDefer.parent = scope
|
||||
scope.children[idx] = enableDefer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -148,9 +148,11 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
||||
// not(~x) -> x!=0
|
||||
if((expr.expression as? PrefixExpression)?.operator=="~") {
|
||||
val x = (expr.expression as PrefixExpression).expression
|
||||
val dt = x.inferType(program).getOrElse { throw FatalAstException("invalid dt ${x.position}") }
|
||||
val notZero = BinaryExpression(x, "!=", NumericLiteral(dt, 0.0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, notZero, parent))
|
||||
val dt = x.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(dt != DataType.UNDEFINED) {
|
||||
val notZero = BinaryExpression(x, "!=", NumericLiteral(dt, 0.0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, notZero, parent))
|
||||
}
|
||||
}
|
||||
|
||||
// not X <compare> Y -> X <invertedcompare> Y
|
||||
|
||||
@@ -383,6 +383,25 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(ifExpr: IfExpression, parent: Node): Iterable<IAstModification> {
|
||||
val trueDt = ifExpr.truevalue.inferType(program)
|
||||
val falseDt = ifExpr.falsevalue.inferType(program)
|
||||
if (trueDt != falseDt) {
|
||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(
|
||||
trueDt.getOr(DataType.UNDEFINED),
|
||||
falseDt.getOr(DataType.UNDEFINED),
|
||||
ifExpr.truevalue,
|
||||
ifExpr.falsevalue
|
||||
)
|
||||
if (toFix != null) {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, toFix, commonDt, ifExpr)
|
||||
return modifications
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun adjustRangeDts(
|
||||
range: RangeExpression,
|
||||
fromConst: NumericLiteral?,
|
||||
|
||||
@@ -58,8 +58,8 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
|
||||
if(parent is Assignment) {
|
||||
val targetDt = parent.target.inferType(program).getOrElse { throw FatalAstException("invalid dt ${parent.target.position}") }
|
||||
if(sourceDt istype targetDt) {
|
||||
val targetDt = parent.target.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(targetDt!=DataType.UNDEFINED && sourceDt istype targetDt) {
|
||||
// we can get rid of this typecast because the type is already the target type
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"starszoom",
|
||||
"tehtriz",
|
||||
"test_gfx_lores",
|
||||
"test_gfx_hires4",
|
||||
"test_gfx_hires",
|
||||
"testmonogfx",
|
||||
),
|
||||
listOf(false, true)
|
||||
|
||||
@@ -887,4 +887,26 @@ main {
|
||||
errors.errors[0] shouldContain "17:16: type UBYTE of return value doesn't match subroutine's return type BYTE"
|
||||
errors.errors[1] shouldContain "20:16: type UWORD of return value doesn't match subroutine's return type WORD"
|
||||
}
|
||||
|
||||
test("if-expression adjusts different value types to common type") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0sL = if cx16.r0L < cx16.r1L -1 else 1
|
||||
}
|
||||
}"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(C64Target(), false, src, writeAssembly = false, errors = errors)!!
|
||||
val program = result.compilerAst
|
||||
val st = program.entrypoint.statements
|
||||
st.size shouldBe 1
|
||||
val assign = st[0] as Assignment
|
||||
assign.target.inferType(program).getOr(DataType.UNDEFINED) shouldBe DataType.BYTE
|
||||
val ifexpr = assign.value as IfExpression
|
||||
ifexpr.truevalue.inferType(program).getOr(DataType.UNDEFINED) shouldBe DataType.BYTE
|
||||
ifexpr.falsevalue.inferType(program).getOr(DataType.UNDEFINED) shouldBe DataType.BYTE
|
||||
ifexpr.truevalue shouldBe instanceOf<NumericLiteral>()
|
||||
ifexpr.falsevalue shouldBe instanceOf<NumericLiteral>()
|
||||
}
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
package prog8tests.ast
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.comparables.shouldBeGreaterThan
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
@@ -459,6 +460,23 @@ main {
|
||||
}
|
||||
|
||||
context("various") {
|
||||
test("no crash for all sorts of undefined variables in complex expression") {
|
||||
val src = """
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
x_position = (((floats.cos(CORNER_ANGLE + theta) * distance_to_corner) + (position_offset as float)) * 256.0) as word
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBeGreaterThan 1
|
||||
errors.clear()
|
||||
compileText(C64Target(), optimize=true, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBeGreaterThan 1
|
||||
}
|
||||
|
||||
test("symbol names in inline assembly blocks") {
|
||||
val names1 = InlineAssembly("""
|
||||
|
||||
@@ -850,6 +868,10 @@ main {
|
||||
cx16.r0++
|
||||
cx16.r1++
|
||||
}
|
||||
|
||||
if cx16.r0==0 {
|
||||
defer cx16.r1++
|
||||
}
|
||||
|
||||
if cx16.r0==0
|
||||
return cx16.r0+cx16.r1
|
||||
@@ -863,7 +885,24 @@ main {
|
||||
|
||||
// check the desugaring of the defer statements
|
||||
(sub.children[0] as PtVariable).name shouldBe "p8v_prog8_defers_mask"
|
||||
val ifelse = sub.children[3] as PtIfElse
|
||||
|
||||
val firstDefer = sub.children[2] as PtAugmentedAssign
|
||||
firstDefer.operator shouldBe "|="
|
||||
firstDefer.target.identifier?.name shouldBe "p8b_main.p8s_test.p8v_prog8_defers_mask"
|
||||
firstDefer.value.asConstInteger() shouldBe 4
|
||||
|
||||
val firstIf = sub.children[3] as PtIfElse
|
||||
val deferInIf = firstIf.ifScope.children[0] as PtAugmentedAssign
|
||||
deferInIf.operator shouldBe "|="
|
||||
deferInIf.target.identifier?.name shouldBe "p8b_main.p8s_test.p8v_prog8_defers_mask"
|
||||
deferInIf.value.asConstInteger() shouldBe 2
|
||||
|
||||
val lastDefer = sub.children[5] as PtAugmentedAssign
|
||||
lastDefer.operator shouldBe "|="
|
||||
lastDefer.target.identifier?.name shouldBe "p8b_main.p8s_test.p8v_prog8_defers_mask"
|
||||
lastDefer.value.asConstInteger() shouldBe 1
|
||||
|
||||
val ifelse = sub.children[4] as PtIfElse
|
||||
val ifscope = ifelse.ifScope.children[0] as PtNodeGroup
|
||||
val ifscope_push = ifscope.children[0] as PtFunctionCall
|
||||
val ifscope_defer = ifscope.children[1] as PtFunctionCall
|
||||
@@ -871,10 +910,11 @@ main {
|
||||
ifscope_defer.name shouldBe "p8b_main.p8s_test.p8s_prog8_invoke_defers"
|
||||
ifscope_push.name shouldBe "sys.pushw"
|
||||
(ifscope_return.value as PtFunctionCall).name shouldBe "sys.popw"
|
||||
val ending = sub.children[5] as PtFunctionCall
|
||||
|
||||
val ending = sub.children[6] as PtFunctionCall
|
||||
ending.name shouldBe "p8b_main.p8s_test.p8s_prog8_invoke_defers"
|
||||
sub.children[6] shouldBe instanceOf<PtReturn>()
|
||||
val handler = sub.children[7] as PtSub
|
||||
sub.children[7] shouldBe instanceOf<PtReturn>()
|
||||
val handler = sub.children[8] as PtSub
|
||||
handler.name shouldBe "p8s_prog8_invoke_defers"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class TestAsmGenSymbols: StringSpec({
|
||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, C64Target(), 999u, 0xffffu)
|
||||
val ptProgram = IntermediateAstMaker(program, errors).transform()
|
||||
val st = SymbolTableMaker(ptProgram, options).make()
|
||||
return AsmGen6502Internal(ptProgram, st, options, errors)
|
||||
return AsmGen6502Internal(ptProgram, st, options, errors, 0)
|
||||
}
|
||||
|
||||
"symbol and variable names from strings" {
|
||||
|
||||
@@ -9,6 +9,9 @@ import io.kotest.matchers.string.shouldStartWith
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.code.ast.PtAssignment
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtFunctionCall
|
||||
import prog8.code.ast.PtIfElse
|
||||
import prog8.code.ast.PtPrefix
|
||||
import prog8.code.ast.PtVariable
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.*
|
||||
@@ -514,4 +517,50 @@ main {
|
||||
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("if not without else is not swapped") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
if not thing()
|
||||
cx16.r0++
|
||||
}
|
||||
|
||||
sub thing() -> bool {
|
||||
cx16.r0++
|
||||
return false
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, writeAssembly = true)!!
|
||||
val st = result.codegenAst!!.entrypoint()!!.children
|
||||
st.size shouldBe 2
|
||||
val ifelse = st[0] as PtIfElse
|
||||
ifelse.hasElse() shouldBe false
|
||||
(ifelse.condition as PtPrefix).operator shouldBe "not"
|
||||
}
|
||||
|
||||
test("if not with else is swapped") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
if not thing()
|
||||
cx16.r0++
|
||||
else
|
||||
cx16.r1++
|
||||
}
|
||||
|
||||
sub thing() -> bool {
|
||||
cx16.r0++
|
||||
return false
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, writeAssembly = true)!!
|
||||
val st = result.codegenAst!!.entrypoint()!!.children
|
||||
st.size shouldBe 2
|
||||
val ifelse = st[0] as PtIfElse
|
||||
ifelse.hasElse() shouldBe true
|
||||
ifelse.condition shouldBe instanceOf<PtFunctionCall>()
|
||||
}
|
||||
})
|
||||
@@ -5,6 +5,7 @@ import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.ast.PtLabel
|
||||
import prog8.code.core.*
|
||||
|
||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||
@@ -144,11 +145,12 @@ class Program(val name: String,
|
||||
}
|
||||
}
|
||||
|
||||
private var generatedLabelSequenceNumber: Int = 0
|
||||
var generatedLabelSequenceNumber: Int = 0
|
||||
private set
|
||||
|
||||
fun makeLabel(postfix: String): String {
|
||||
generatedLabelSequenceNumber++
|
||||
return "label_${generatedLabelSequenceNumber}_$postfix"
|
||||
return "${PtLabel.GeneratedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
|
||||
}
|
||||
|
||||
fun makeLabel(postfix: String, position: Position): Label {
|
||||
|
||||
@@ -410,7 +410,13 @@ private fun AugassignmentContext.toAst(): Assignment {
|
||||
return Assignment(assign_target().toAst(), expression, AssignmentOrigin.USERCODE, toPosition())
|
||||
}
|
||||
|
||||
private fun DatatypeContext.toAst() = DataType.valueOf(text.uppercase())
|
||||
private fun DatatypeContext.toAst(): DataType {
|
||||
try {
|
||||
return DataType.valueOf(text.uppercase())
|
||||
} catch (_: IllegalArgumentException) {
|
||||
return DataType.UNDEFINED
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArrayindexContext.toAst() : ArrayIndex =
|
||||
ArrayIndex(expression().toAst(), toPosition())
|
||||
|
||||
@@ -1182,7 +1182,12 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
|
||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||
return when (val targetStmt = targetStatement(program)) {
|
||||
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
|
||||
is VarDecl -> {
|
||||
if(targetStmt.datatype==DataType.UNDEFINED)
|
||||
InferredTypes.unknown()
|
||||
else
|
||||
InferredTypes.knownFor(targetStmt.datatype)
|
||||
}
|
||||
else -> InferredTypes.InferredType.unknown()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
%import compression
|
||||
%import conv
|
||||
%import cx16logo
|
||||
%import diskio
|
||||
%import math
|
||||
%import prog8_lib
|
||||
%import string
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
%import diskio
|
||||
%import emudbg
|
||||
%import floats
|
||||
%import gfx_hires4
|
||||
%import gfx_hires
|
||||
%import gfx_lores
|
||||
%import graphics
|
||||
%import math
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
; all library modules for the atari compiler target
|
||||
|
||||
%import anyall
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
%import math
|
||||
%import prog8_lib
|
||||
%import string
|
||||
%import syslib
|
||||
@@ -5,7 +5,6 @@
|
||||
%import compression
|
||||
%import conv
|
||||
%import cx16logo
|
||||
%import diskio
|
||||
%import math
|
||||
%import prog8_lib
|
||||
%import string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.5 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.5.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-atari.p8
|
||||
@@ -98,48 +98,6 @@ cx16logo {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: diskio
|
||||
---------------------------
|
||||
|
||||
diskio {
|
||||
const ubyte READ_IO_CHANNEL
|
||||
const ubyte WRITE_IO_CHANNEL
|
||||
ubyte @shared drivenumber
|
||||
bool iteration_in_progress
|
||||
uword list_blocks
|
||||
str list_filename
|
||||
str list_filetype
|
||||
uword list_pattern
|
||||
bool list_skip_disk_name
|
||||
bool write_iteration_in_progress
|
||||
delete (uword filenameptr)
|
||||
directory () -> bool
|
||||
diskname () -> uword
|
||||
exists (str filename) -> bool
|
||||
f_close ()
|
||||
f_close_w ()
|
||||
f_open (uword filenameptr) -> bool
|
||||
f_open_w (uword filenameptr) -> bool
|
||||
f_read (uword bufferpointer, uword num_bytes) -> uword
|
||||
f_read_all (uword bufferpointer) -> uword
|
||||
f_readline (uword bufptr @AY) -> clobbers (X) -> ubyte @Y, ubyte @A
|
||||
f_write (uword bufferpointer, uword num_bytes) -> bool
|
||||
lf_end_list ()
|
||||
lf_next_entry () -> bool
|
||||
lf_start_list (uword pattern_ptr) -> bool
|
||||
list_filenames (uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte
|
||||
load (uword filenameptr, uword address_override) -> uword
|
||||
load_raw (uword filenameptr, uword start_address) -> uword
|
||||
rename (uword oldfileptr, uword newfileptr)
|
||||
reset_read_channel ()
|
||||
reset_write_channel ()
|
||||
save (uword filenameptr, uword start_address, uword savesize) -> bool
|
||||
send_command (uword commandptr)
|
||||
status () -> uword
|
||||
status_code () -> ubyte
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: math
|
||||
-------------------------
|
||||
|
||||
@@ -163,6 +121,7 @@ math {
|
||||
direction_qd (ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
lerpw (uword v0, uword v1, uword t) -> uword
|
||||
log2 (ubyte value @A) -> ubyte @Y
|
||||
log2w (uword value @AY) -> ubyte @Y
|
||||
mul16_last_upper () -> uword @AY
|
||||
@@ -257,6 +216,7 @@ sys {
|
||||
pop () -> ubyte @A
|
||||
popw () -> uword @AY
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.5 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.5.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c128.p8
|
||||
@@ -98,8 +98,47 @@ cx16logo {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: diskio
|
||||
---------------------------
|
||||
LIBRARY MODULE NAME: math
|
||||
-------------------------
|
||||
|
||||
math {
|
||||
atan2 (ubyte x1 @R0, ubyte y1 @R1, ubyte x2 @R2, ubyte y2 @R3) -> ubyte @A
|
||||
cos8 (ubyte angle @A) -> clobbers (Y) -> byte @A
|
||||
cos8u (ubyte angle @A) -> clobbers (Y) -> ubyte @A
|
||||
cosr8 (ubyte radians @A) -> clobbers (Y) -> byte @A
|
||||
cosr8u (ubyte radians @A) -> clobbers (Y) -> ubyte @A
|
||||
crc16 (uword data, uword length) -> uword
|
||||
crc16_end () -> uword
|
||||
crc16_start ()
|
||||
crc16_update (ubyte value @A)
|
||||
crc32 (uword data, uword length)
|
||||
crc32_end ()
|
||||
crc32_start ()
|
||||
crc32_update (ubyte value @A)
|
||||
diff (ubyte v1 @A, ubyte v2 @Y) -> ubyte @A
|
||||
diffw (uword w1 @R0, uword w2 @AY) -> uword @AY
|
||||
direction (ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte
|
||||
direction_qd (ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
lerpw (uword v0, uword v1, uword t) -> uword
|
||||
log2 (ubyte value @A) -> ubyte @Y
|
||||
log2w (uword value @AY) -> ubyte @Y
|
||||
mul16_last_upper () -> uword @AY
|
||||
randrange (ubyte n) -> ubyte
|
||||
randrangew (uword n) -> uword
|
||||
rnd () -> clobbers (Y) -> ubyte @A
|
||||
rndseed (uword seed1 @AY, uword seed2 @R0) -> clobbers (A,Y)
|
||||
rndw () -> uword @AY
|
||||
sin8 (ubyte angle @A) -> clobbers (Y) -> byte @A
|
||||
sin8u (ubyte angle @A) -> clobbers (Y) -> ubyte @A
|
||||
sinr8 (ubyte radians @A) -> clobbers (Y) -> byte @A
|
||||
sinr8u (ubyte radians @A) -> clobbers (Y) -> ubyte @A
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: shared_cbm_diskio
|
||||
--------------------------------------
|
||||
|
||||
diskio {
|
||||
const ubyte READ_IO_CHANNEL
|
||||
@@ -140,44 +179,6 @@ diskio {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: math
|
||||
-------------------------
|
||||
|
||||
math {
|
||||
atan2 (ubyte x1 @R0, ubyte y1 @R1, ubyte x2 @R2, ubyte y2 @R3) -> ubyte @A
|
||||
cos8 (ubyte angle @A) -> clobbers (Y) -> byte @A
|
||||
cos8u (ubyte angle @A) -> clobbers (Y) -> ubyte @A
|
||||
cosr8 (ubyte radians @A) -> clobbers (Y) -> byte @A
|
||||
cosr8u (ubyte radians @A) -> clobbers (Y) -> ubyte @A
|
||||
crc16 (uword data, uword length) -> uword
|
||||
crc16_end () -> uword
|
||||
crc16_start ()
|
||||
crc16_update (ubyte value @A)
|
||||
crc32 (uword data, uword length)
|
||||
crc32_end ()
|
||||
crc32_start ()
|
||||
crc32_update (ubyte value @A)
|
||||
diff (ubyte v1 @A, ubyte v2 @Y) -> ubyte @A
|
||||
diffw (uword w1 @R0, uword w2 @AY) -> uword @AY
|
||||
direction (ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte
|
||||
direction_qd (ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
log2 (ubyte value @A) -> ubyte @Y
|
||||
log2w (uword value @AY) -> ubyte @Y
|
||||
mul16_last_upper () -> uword @AY
|
||||
randrange (ubyte n) -> ubyte
|
||||
randrangew (uword n) -> uword
|
||||
rnd () -> clobbers (Y) -> ubyte @A
|
||||
rndseed (uword seed1 @AY, uword seed2 @R0) -> clobbers (A,Y)
|
||||
rndw () -> uword @AY
|
||||
sin8 (ubyte angle @A) -> clobbers (Y) -> byte @A
|
||||
sin8u (ubyte angle @A) -> clobbers (Y) -> ubyte @A
|
||||
sinr8 (ubyte radians @A) -> clobbers (Y) -> byte @A
|
||||
sinr8u (ubyte radians @A) -> clobbers (Y) -> ubyte @A
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: string
|
||||
---------------------------
|
||||
|
||||
@@ -478,6 +479,7 @@ sys {
|
||||
pop () -> ubyte @A
|
||||
popw () -> uword @AY
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.5 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.5.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c64.p8
|
||||
@@ -98,48 +98,6 @@ cx16logo {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: diskio
|
||||
---------------------------
|
||||
|
||||
diskio {
|
||||
const ubyte READ_IO_CHANNEL
|
||||
const ubyte WRITE_IO_CHANNEL
|
||||
ubyte @shared drivenumber
|
||||
bool iteration_in_progress
|
||||
uword list_blocks
|
||||
str list_filename
|
||||
str list_filetype
|
||||
uword list_pattern
|
||||
bool list_skip_disk_name
|
||||
bool write_iteration_in_progress
|
||||
delete (uword filenameptr)
|
||||
directory () -> bool
|
||||
diskname () -> uword
|
||||
exists (str filename) -> bool
|
||||
f_close ()
|
||||
f_close_w ()
|
||||
f_open (uword filenameptr) -> bool
|
||||
f_open_w (uword filenameptr) -> bool
|
||||
f_read (uword bufferpointer, uword num_bytes) -> uword
|
||||
f_read_all (uword bufferpointer) -> uword
|
||||
f_readline (uword bufptr @AY) -> clobbers (X) -> ubyte @Y, ubyte @A
|
||||
f_write (uword bufferpointer, uword num_bytes) -> bool
|
||||
lf_end_list ()
|
||||
lf_next_entry () -> bool
|
||||
lf_start_list (uword pattern_ptr) -> bool
|
||||
list_filenames (uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte
|
||||
load (uword filenameptr, uword address_override) -> uword
|
||||
load_raw (uword filenameptr, uword start_address) -> uword
|
||||
rename (uword oldfileptr, uword newfileptr)
|
||||
reset_read_channel ()
|
||||
reset_write_channel ()
|
||||
save (uword filenameptr, uword start_address, uword savesize) -> bool
|
||||
send_command (uword commandptr)
|
||||
status () -> uword
|
||||
status_code () -> ubyte
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: floats
|
||||
---------------------------
|
||||
|
||||
@@ -284,6 +242,7 @@ math {
|
||||
direction_qd (ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
lerpw (uword v0, uword v1, uword t) -> uword
|
||||
log2 (ubyte value @A) -> ubyte @Y
|
||||
log2w (uword value @AY) -> ubyte @Y
|
||||
mul16_last_upper () -> uword @AY
|
||||
@@ -299,6 +258,48 @@ math {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: shared_cbm_diskio
|
||||
--------------------------------------
|
||||
|
||||
diskio {
|
||||
const ubyte READ_IO_CHANNEL
|
||||
const ubyte WRITE_IO_CHANNEL
|
||||
ubyte @shared drivenumber
|
||||
bool iteration_in_progress
|
||||
uword list_blocks
|
||||
str list_filename
|
||||
str list_filetype
|
||||
uword list_pattern
|
||||
bool list_skip_disk_name
|
||||
bool write_iteration_in_progress
|
||||
delete (uword filenameptr)
|
||||
directory () -> bool
|
||||
diskname () -> uword
|
||||
exists (str filename) -> bool
|
||||
f_close ()
|
||||
f_close_w ()
|
||||
f_open (uword filenameptr) -> bool
|
||||
f_open_w (uword filenameptr) -> bool
|
||||
f_read (uword bufferpointer, uword num_bytes) -> uword
|
||||
f_read_all (uword bufferpointer) -> uword
|
||||
f_readline (uword bufptr @AY) -> clobbers (X) -> ubyte @Y, ubyte @A
|
||||
f_write (uword bufferpointer, uword num_bytes) -> bool
|
||||
lf_end_list ()
|
||||
lf_next_entry () -> bool
|
||||
lf_start_list (uword pattern_ptr) -> bool
|
||||
list_filenames (uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte
|
||||
load (uword filenameptr, uword address_override) -> uword
|
||||
load_raw (uword filenameptr, uword start_address) -> uword
|
||||
rename (uword oldfileptr, uword newfileptr)
|
||||
reset_read_channel ()
|
||||
reset_write_channel ()
|
||||
save (uword filenameptr, uword start_address, uword savesize) -> bool
|
||||
send_command (uword commandptr)
|
||||
status () -> uword
|
||||
status_code () -> ubyte
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: string
|
||||
---------------------------
|
||||
|
||||
@@ -600,6 +601,7 @@ sys {
|
||||
pop () -> ubyte @A
|
||||
popw () -> uword @AY
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.5 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.5.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-cx16.p8
|
||||
@@ -329,10 +329,10 @@ floats {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: gfx_hires4
|
||||
-------------------------------
|
||||
LIBRARY MODULE NAME: gfx_hires
|
||||
------------------------------
|
||||
|
||||
gfx_hires4 {
|
||||
gfx_hires {
|
||||
const uword HEIGHT
|
||||
const uword WIDTH
|
||||
const uword charset_addr
|
||||
@@ -457,6 +457,7 @@ math {
|
||||
direction_qd (ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
lerpw (uword v0, uword v1, uword t) -> uword
|
||||
log2 (ubyte value @A) -> ubyte @Y
|
||||
log2w (uword value @AY) -> ubyte @Y
|
||||
mul16_last_upper () -> uword @AY
|
||||
@@ -588,6 +589,7 @@ sprites {
|
||||
movey (ubyte spritenum, word dy)
|
||||
pos (ubyte spritenum, word xpos, word ypos)
|
||||
pos_batch (ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr)
|
||||
reset (ubyte spritenum_start, ubyte count)
|
||||
set_mousepointer_hand ()
|
||||
set_mousepointer_image (uword data, bool compressed)
|
||||
set_palette_offset (ubyte spritenum, ubyte offset)
|
||||
@@ -1152,6 +1154,7 @@ sys {
|
||||
popw () -> uword @AY
|
||||
poweroff_system ()
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.5 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.5.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-neo.p8
|
||||
@@ -111,6 +111,7 @@ math {
|
||||
direction_qd (ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
lerpw (uword v0, uword v1, uword t) -> uword
|
||||
log2 (ubyte value @A) -> ubyte @Y
|
||||
log2w (uword value @AY) -> ubyte @Y
|
||||
mul16_last_upper () -> uword @AY
|
||||
@@ -199,6 +200,7 @@ sys {
|
||||
pop () -> ubyte @A
|
||||
popw () -> uword @AY
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.5 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.5.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-pet32.p8
|
||||
@@ -98,48 +98,6 @@ cx16logo {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: diskio
|
||||
---------------------------
|
||||
|
||||
diskio {
|
||||
const ubyte READ_IO_CHANNEL
|
||||
const ubyte WRITE_IO_CHANNEL
|
||||
ubyte @shared drivenumber
|
||||
bool iteration_in_progress
|
||||
uword list_blocks
|
||||
str list_filename
|
||||
str list_filetype
|
||||
uword list_pattern
|
||||
bool list_skip_disk_name
|
||||
bool write_iteration_in_progress
|
||||
delete (uword filenameptr)
|
||||
directory () -> bool
|
||||
diskname () -> uword
|
||||
exists (str filename) -> bool
|
||||
f_close ()
|
||||
f_close_w ()
|
||||
f_open (uword filenameptr) -> bool
|
||||
f_open_w (uword filenameptr) -> bool
|
||||
f_read (uword bufferpointer, uword num_bytes) -> uword
|
||||
f_read_all (uword bufferpointer) -> uword
|
||||
f_readline (uword bufptr @AY) -> clobbers (X) -> ubyte @Y, ubyte @A
|
||||
f_write (uword bufferpointer, uword num_bytes) -> bool
|
||||
lf_end_list ()
|
||||
lf_next_entry () -> bool
|
||||
lf_start_list (uword pattern_ptr) -> bool
|
||||
list_filenames (uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte
|
||||
load (uword filenameptr, uword address_override) -> uword
|
||||
load_raw (uword filenameptr, uword start_address) -> uword
|
||||
rename (uword oldfileptr, uword newfileptr)
|
||||
reset_read_channel ()
|
||||
reset_write_channel ()
|
||||
save (uword filenameptr, uword start_address, uword savesize) -> bool
|
||||
send_command (uword commandptr)
|
||||
status () -> uword
|
||||
status_code () -> ubyte
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: math
|
||||
-------------------------
|
||||
|
||||
@@ -163,6 +121,7 @@ math {
|
||||
direction_qd (ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
lerpw (uword v0, uword v1, uword t) -> uword
|
||||
log2 (ubyte value @A) -> ubyte @Y
|
||||
log2w (uword value @AY) -> ubyte @Y
|
||||
mul16_last_upper () -> uword @AY
|
||||
@@ -275,6 +234,7 @@ sys {
|
||||
pop () -> ubyte @A
|
||||
popw () -> uword @AY
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.5 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.5.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-virtual.p8
|
||||
@@ -201,6 +201,7 @@ math {
|
||||
direction_qd (ubyte quadrant, ubyte xdelta, ubyte ydelta) -> ubyte
|
||||
direction_sc (byte x1, byte y1, byte x2, byte y2) -> ubyte
|
||||
lerp (ubyte v0, ubyte v1, ubyte t) -> ubyte
|
||||
lerpw (uword v0, uword v1, uword t) -> uword
|
||||
log2 (ubyte value) -> ubyte
|
||||
log2w (uword value) -> ubyte
|
||||
mul16_last_upper () -> uword
|
||||
|
||||
@@ -41,6 +41,9 @@ Data types
|
||||
- strings and arrays are mutable: you can change their contents, but always keep the original storage size in mind to avoid overwriting memory outside of the buffer.
|
||||
- maximum string length is 255 characters + a trailing 0 byte.
|
||||
- maximum storage size for arrays is 256 bytes (512 for split word arrays) , the maximum number of elements in the array depends on the size of a single element value.
|
||||
you can use larger "arrays" via pointer indexing, see below at Pointers. One way of obtaining a piece of memory to store
|
||||
such an "array" is by using ``memory()`` builtin function.
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
@@ -55,6 +58,7 @@ Variables
|
||||
|
||||
Subroutines
|
||||
-----------
|
||||
- There is no call stack. Subroutine parameters are overwritten when called again (recursion is not easily possible, but you can do it with manual stack manipulations).
|
||||
- There is no function overloading (except for a couple of builtin functions).
|
||||
- Some subroutine types can return multiple return values, and you can multi-assign those in a single statement.
|
||||
- Because every declared variable allocates some memory, it might be beneficial to share the same variables over different subroutines
|
||||
@@ -72,7 +76,7 @@ Pointers
|
||||
You have to deal with the uword manually if the object it points to is something different.
|
||||
- Note that there is the ``peekw`` builtin function that *does* allow you to directy obtain the *word* value at the given memory location.
|
||||
So if you use this, you can use uword pointers as pointers to word values without much hassle.
|
||||
- "dereferencing" a uword pointer is done via array indexing (where index value can be 0-65535!) or via the memory read operator ``@(ptr)``, or ``peek/peekw(ptr)``.
|
||||
- "dereferencing" a uword pointer is done via array indexing ``ptr[index]`` (where index value can be 0-65535!) or via the memory read operator ``@(ptr)``, or ``peek/peekw(ptr)``.
|
||||
- Pointers don't have to be a variable, you can immediately access the value of a given memory location using ``@($d020)`` for instance.
|
||||
Reading is done by assigning it to a variable, writing is done by just assigning the new value to it.
|
||||
|
||||
|
||||
+81
-83
@@ -133,69 +133,14 @@ One or more .p8 module files
|
||||
``-help``, ``-h``
|
||||
Prints short command line usage information.
|
||||
|
||||
``-target <compilation target>``
|
||||
Sets the target output of the compiler. This option is required.
|
||||
``c64`` = Commodore 64, ``c128`` = Commodore 128, ``cx16`` = Commander X16, ``pet32`` - Commodore PET model 4032,
|
||||
``atari`` = Atari 800 XL, ``neo`` = Neo6502, ``virtual`` = builtin virtual machine.
|
||||
|
||||
``-srcdirs <pathlist>``
|
||||
Specify a list of extra paths (separated with ':'), to search in for imported modules.
|
||||
Useful if you have library modules somewhere that you want to re-use,
|
||||
or to switch implementations of certain routines via a command line switch.
|
||||
|
||||
``-emu``, ``-emu2``
|
||||
Auto-starts target system emulator after successful compilation.
|
||||
emu2 starts the alternative emulator if available.
|
||||
The compiled program and the symbol and breakpoint lists
|
||||
(for the machine code monitor) are immediately loaded into the emulator (if it supports them)
|
||||
|
||||
``-out <directory>``
|
||||
sets directory location for output files instead of current directory
|
||||
|
||||
``-noasm``
|
||||
Do not create assembly code and output program.
|
||||
Useful for debugging or doing quick syntax checks.
|
||||
|
||||
``-noopt``
|
||||
Don't perform any code optimizations.
|
||||
Useful for debugging or faster compilation cycles.
|
||||
|
||||
``-optfloatx``
|
||||
Also optimize float expressions if optimizations are enabled.
|
||||
Warning: can increase program size significantly if a lot of floating point expressions are used.
|
||||
|
||||
``-watch``
|
||||
Enables continuous compilation mode (watches for file changes).
|
||||
This greatly increases compilation speed on subsequent runs:
|
||||
almost instant compilation times (less than a second) can be achieved in this mode.
|
||||
The compiler will compile your program and then instead of exiting, it waits for any changes in the module source files.
|
||||
As soon as a change happens, the program gets compiled again.
|
||||
Note that it is possible to use the watch mode with multiple modules as well, but it will
|
||||
recompile everything in that list even if only one of the files got updated.
|
||||
|
||||
``-warnshadow``
|
||||
Tells the assembler to issue warning messages about symbol shadowing.
|
||||
These *can* be problematic, but usually aren't because prog8 has different scoping rules
|
||||
than the assembler has.
|
||||
You may want to watch out for shadowing of builtin names though. Especially 'a', 'x' and 'y'
|
||||
as those are the cpu register names and if you shadow those, the assembler might
|
||||
interpret certain instructions differently and produce unexpected opcodes (like LDA X getting
|
||||
turned into TXA, or not, depending on the symbol 'x' being defined in your own assembly code or not)
|
||||
|
||||
``-addmissingrts``
|
||||
Enables old compiler behavior that silently adds RTS to asmsubs that don't have one.
|
||||
This was done to fix asmsubs so they return properly to the caller instead of crashing the program.
|
||||
However the new compiler behavior is to not silently modify the code anymore and instead give an error message
|
||||
that tells you how to fix the problem. This option may go away in future version.
|
||||
|
||||
``-quietasm``
|
||||
Don't print assembler output results.
|
||||
|
||||
``-asmlist``
|
||||
Generate an assembler listing file as well.
|
||||
|
||||
``-check``
|
||||
Quickly check the program for errors. No actual compilation will be performed.
|
||||
Also generate an assembler listing file <program>.list
|
||||
|
||||
``-breakinstr <instruction>``
|
||||
Also output the specified CPU instruction for a ``%breakpoint``, as well as the entry in the vice monitor list file.
|
||||
@@ -205,9 +150,49 @@ One or more .p8 module files
|
||||
For example for the Commander X16 emulator, ``stp`` is useful because it can actually tyrigger
|
||||
a breakpoint halt in the debugger when this is enabled by running the emulator with -debug.
|
||||
|
||||
``-bytes2float <bytes>``
|
||||
convert a comma separated list of bytes from the target system to a float value.
|
||||
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
|
||||
Also see -float2bytes
|
||||
|
||||
``-check``
|
||||
Quickly check the program for errors. No actual compilation will be performed.
|
||||
|
||||
``-D SYMBOLNAME=VALUE``
|
||||
Add this user-defined symbol directly to the beginning of the generated assembly file.
|
||||
Can be repeated to define multiple symbols.
|
||||
|
||||
``-dumpsymbols``
|
||||
print a dump of the variable declarations and subroutine signatures
|
||||
|
||||
``-dumpvars``
|
||||
print a dump of the variables in the program
|
||||
|
||||
``-emu``, ``-emu2``
|
||||
Auto-starts target system emulator after successful compilation.
|
||||
emu2 starts the alternative emulator if available.
|
||||
The compiled program and the symbol and breakpoint lists
|
||||
(for the machine code monitor) are immediately loaded into the emulator (if it supports them)
|
||||
|
||||
``-expericodegen``
|
||||
Use experimental code generation backend (*incomplete*).
|
||||
|
||||
``-float2bytes <number>``
|
||||
convert floating point number to a list of bytes for the target system.
|
||||
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
|
||||
Also see -bytes2float
|
||||
|
||||
``-noasm``
|
||||
Do not create assembly code and output program.
|
||||
Useful for debugging or doing quick syntax checks.
|
||||
|
||||
``-noopt``
|
||||
Don't perform any code optimizations.
|
||||
Useful for debugging or faster compilation cycles.
|
||||
|
||||
``-out <directory>``
|
||||
sets directory location for output files instead of current directory
|
||||
|
||||
``-printast1``
|
||||
Prints the "compiler AST" (the internal representation of the program) after all processing steps.
|
||||
|
||||
@@ -215,11 +200,16 @@ One or more .p8 module files
|
||||
Prints the "intermediate AST" which is the reduced representation of the program.
|
||||
This is what is used in the code generators, to generate the executable code from.
|
||||
|
||||
``-dumpvars``
|
||||
print a dump of the variables in the program
|
||||
``-quietasm``
|
||||
Don't print assembler output results.
|
||||
|
||||
``-dumpsymbols``
|
||||
print a dump of the variable declarations and subroutine signatures
|
||||
``-slabsgolden``
|
||||
put memory() slabs in 'golden ram' memory area instead of at the end of the program.
|
||||
On the cx16 target this is $0400-07ff. This is unavailable on other systems.
|
||||
|
||||
``-slabshigh``
|
||||
put memory() slabs in high memory area instead of at the end of the program.
|
||||
On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.
|
||||
|
||||
``-sourcelines``
|
||||
Also include the original prog8 source code lines as comments in the generated assembly code file,
|
||||
@@ -231,12 +221,20 @@ One or more .p8 module files
|
||||
This removes the need to add @split yourself but some programs may fail to compile with
|
||||
this option as not all array operations are implemented yet on split arrays.
|
||||
|
||||
``-vm``
|
||||
load and run a p8-virt or p8-ir listing in the internal VirtualMachine instead of compiling a prog8 program file..
|
||||
``-srcdirs <pathlist>``
|
||||
Specify a list of extra paths (separated with ':'), to search in for imported modules.
|
||||
Useful if you have library modules somewhere that you want to re-use,
|
||||
or to switch implementations of certain routines via a command line switch.
|
||||
|
||||
``-D SYMBOLNAME=VALUE``
|
||||
Add this user-defined symbol directly to the beginning of the generated assembly file.
|
||||
Can be repeated to define multiple symbols.
|
||||
``-target <compilation target>``
|
||||
Sets the target output of the compiler. This option is required.
|
||||
``c64`` = Commodore 64, ``c128`` = Commodore 128, ``cx16`` = Commander X16, ``pet32`` - Commodore PET model 4032,
|
||||
``atari`` = Atari 800 XL, ``neo`` = Neo6502, ``virtual`` = builtin virtual machine.
|
||||
|
||||
``-varsgolden``
|
||||
Like ``-varshigh``, but places the variables in the $0400-$07FF "golden ram" area instead.
|
||||
Because this is in normal system memory, there are no bank switching issues.
|
||||
This mode is only available on the Commander X16.
|
||||
|
||||
``-varshigh <rambank>``
|
||||
Places uninitialized non-zeropage variables in a separate memory area, instead of inside the program itself.
|
||||
@@ -254,26 +252,26 @@ One or more .p8 module files
|
||||
(it's called 'BSS' section or Gap at the address mentioned above).
|
||||
Assembling the program will fail if there are too many variables to fit in a single high ram bank.
|
||||
|
||||
``-varsgolden``
|
||||
Like ``-varshigh``, but places the variables in the $0400-$07FF "golden ram" area instead.
|
||||
Because this is in normal system memory, there are no bank switching issues.
|
||||
This mode is only available on the Commander X16.
|
||||
``-vm``
|
||||
load and run a p8-virt or p8-ir listing in the internal VirtualMachine instead of compiling a prog8 program file..
|
||||
|
||||
``-slabshigh``
|
||||
put memory() slabs in high memory area instead of at the end of the program.
|
||||
On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.
|
||||
``-warnshadow``
|
||||
Tells the assembler to issue warning messages about symbol shadowing.
|
||||
These *can* be problematic, but usually aren't because prog8 has different scoping rules
|
||||
than the assembler has.
|
||||
You may want to watch out for shadowing of builtin names though. Especially 'a', 'x' and 'y'
|
||||
as those are the cpu register names and if you shadow those, the assembler might
|
||||
interpret certain instructions differently and produce unexpected opcodes (like LDA X getting
|
||||
turned into TXA, or not, depending on the symbol 'x' being defined in your own assembly code or not)
|
||||
|
||||
``-slabsgolden``
|
||||
put memory() slabs in 'golden ram' memory area instead of at the end of the program.
|
||||
On the cx16 target this is $0400-07ff. This is unavailable on other systems.
|
||||
|
||||
``-bytes2float <bytes>``
|
||||
convert a comma separated list of bytes from the target system to a float value.
|
||||
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
|
||||
|
||||
``-float2bytes <number>``
|
||||
convert floating point number to a list of bytes for the target system.
|
||||
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
|
||||
``-watch``
|
||||
Enables continuous compilation mode (watches for file changes).
|
||||
This greatly increases compilation speed on subsequent runs:
|
||||
almost instant compilation times (less than a second) can be achieved in this mode.
|
||||
The compiler will compile your program and then instead of exiting, it waits for any changes in the module source files.
|
||||
As soon as a change happens, the program gets compiled again.
|
||||
Note that it is possible to use the watch mode with multiple modules as well, but it will
|
||||
recompile everything in that list even if only one of the files got updated.
|
||||
|
||||
|
||||
Module source code files
|
||||
|
||||
@@ -330,6 +330,11 @@ sys (part of syslib)
|
||||
Returns the last address of the program in memory + 1. This means: the memory address directly after all the program code and variables,
|
||||
including the uninitialized ones ("BSS" variables) and the uninitialized memory blocks reserved by the `memory()` function.
|
||||
Can be used to load dynamic data after the program, instead of hardcoding something.
|
||||
On the assembly level: it returns the address of the symbol "``prog8_program_end``".
|
||||
|
||||
``progstart ()``
|
||||
Returns the first address of the program in memory. This usually is $0801 on the C64 and the X16, for example.
|
||||
On the assembly level: it returns the address of the symbol "``prog8_program_start``".
|
||||
|
||||
``wait (uword jiffies)``
|
||||
wait approximately the given number of jiffies (1/60th seconds)
|
||||
@@ -677,6 +682,14 @@ Provides definitions for the ROM/Kernal subroutines and utility routines dealing
|
||||
Warning: this routine may stop working on the Commander X16 when a new ROM version is released,
|
||||
because it uses an internal BASIC routine. Then it will require a fix.
|
||||
|
||||
``lerp(v0, v1, t)``
|
||||
Linear interpolation (LERP). Precise method, which guarantees v = v1 when t = 1.
|
||||
Returns an interpolation between two inputs (v0, v1) for a parameter t in the closed unit interval [0.0, 1.0]
|
||||
|
||||
``lerp_fast(v0, v1, t)```
|
||||
Linear interpolation (LERP). Imprecise (but faster) method, which does not guarantee v = v1 when t = 1
|
||||
Teturns an interpolation between two inputs (v0, v1) for a parameter t in the closed unit interval [0.0, 1.0]
|
||||
|
||||
|
||||
graphics
|
||||
--------
|
||||
@@ -823,6 +836,17 @@ but perhaps the provided ones can be of service too.
|
||||
Call the start() routine first, feed it bytes with the update() routine, finalize with calling the end() routine.
|
||||
The 32 bits result is stored in cx16.r14 (low word) and cx16.r15 (high word).
|
||||
|
||||
``lerp(v0, v1, t)``
|
||||
Linear interpolation routine for unsigned byte values.
|
||||
Returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
||||
Guarantees v = v1 when t = 255.
|
||||
|
||||
``lerpw(v0, v1, t)``
|
||||
Linear interpolation routine for unsigned word values.
|
||||
Returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
Guarantees v = v1 when t = 65535.
|
||||
|
||||
|
||||
cx16logo
|
||||
--------
|
||||
Just a fun module that contains the Commander X16 logo in PETSCII graphics
|
||||
@@ -919,8 +943,7 @@ gfx_lores and gfx_hires (cx16 only)
|
||||
Full-screen multicolor bitmap graphics routines, available on the Cx16 machine only.
|
||||
|
||||
- gfx_lores: optimized routines for 320x240 256 color bitmap graphics mode. Compatible with X16 screen mode 128.
|
||||
- gfx_hires4: optimized routines for 640x480 4 color bitmap graphics mode
|
||||
- bitmap mode
|
||||
- gfx_hires: optimized routines for 640x480 4 color bitmap graphics mode
|
||||
- enable bitmap graphics mode, also back to text mode
|
||||
- drawing and reading individual pixels
|
||||
- drawing lines, rectangles, filled rectangles, circles, discs
|
||||
@@ -928,7 +951,7 @@ Full-screen multicolor bitmap graphics routines, available on the Cx16 machine o
|
||||
- drawing text inside the bitmap
|
||||
|
||||
Read the `gfx_lores source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/gfx_lores.p8>`_
|
||||
or `gfx_hires4 source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/gfx_hires4.p8>`_
|
||||
or `gfx_hires source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/gfx_hires.p8>`_
|
||||
to see what's in there.
|
||||
|
||||
They share the same routines.
|
||||
|
||||
@@ -891,9 +891,9 @@ and the logic is declared very close to the spot where the allocation of the res
|
||||
It's possible to write a defer for a block of statements, but the advice is to keep such cleanup code as simple and short as possible.
|
||||
|
||||
.. caution::
|
||||
Defers only work for subroutines that are written as regular Prog8 code.
|
||||
If a piece of inlined assembly somehow causes the routine to exit, the compiler cannot detect this.
|
||||
Defers will not be handled in such cases.
|
||||
Defers only work for subroutines that are written in regular Prog8 code.
|
||||
If a piece of inlined assembly somehow causes the routine to exit, the compiler cannot detect this,
|
||||
and defers won't be handled in such cases.
|
||||
|
||||
|
||||
Library routines and builtin functions
|
||||
|
||||
@@ -191,8 +191,8 @@ Directives
|
||||
but with this you can change it to another value. This can be useful for example to 'reserve' a piece
|
||||
of memory at the end of program space where other data such as external library files can be loaded into.
|
||||
This memtop value is used for a check instruction for the assembler to see if the resulting program size
|
||||
exceeds the given memtop address. This value is inclusive, so $9eff means that the program can use up to
|
||||
and including the address $9eff and that $9f00 is the first address out of bounds.
|
||||
exceeds the given memtop address. This value is exclusive, so $a000 means that $a000 is the first address
|
||||
that program can no longer use. Everything up to and including $9fff is still usable.
|
||||
|
||||
|
||||
.. data:: %option <option> [, <option> ...]
|
||||
|
||||
+57
-30
@@ -137,42 +137,69 @@ Calling a subroutine requires three steps:
|
||||
#. preparing the return value (if any) and returning that from the call.
|
||||
|
||||
|
||||
``asmsub`` routines
|
||||
Regular subroutines
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
These are usually declarations of Kernal (ROM) routines or low-level assembly only routines,
|
||||
that have their arguments solely passed into specific registers.
|
||||
- Each subroutine parameter is represented as a variable scoped to the subroutine. Prog8 doesn't have a call stack.
|
||||
- The arguments passed in a subroutine call are evaluated by the caller, and then put into those variables by the caller.
|
||||
The order of evaluation of subroutine call arguments *is unspecified* and should not be relied upon.
|
||||
- The subroutine is invoked.
|
||||
- The return value is not put into a variable, but the subroutine passes it back to the caller via register(s):
|
||||
|
||||
- A byte value will be put in ``A`` .
|
||||
- A boolean value will be put in ``A`` too, as 0 or 1.
|
||||
- A word value will be put in ``A`` + ``Y`` register pair (lsb in A, msb in Y).
|
||||
- A float value will be put in the ``FAC1`` float 'register'.
|
||||
|
||||
**Builtin functions can be different:**
|
||||
some builtin functions are special and won't exactly follow these rules.
|
||||
|
||||
**Some arguments will be passed in registers:**
|
||||
For single byte and word arguments, the values are simply loaded in cpu registers by the caller before calling the subroutine.
|
||||
*The subroutine itself will take care of putting the values into the parameter variables.* This saves on code size because
|
||||
otherwise all callers would have to store the values in those variables themselves. The rules for this are as follows:
|
||||
|
||||
Single byte parameter: ``sub foo(ubyte bar) { ... }``
|
||||
gets bar in the accumulator A, *subroutine* stores it into parameter variable
|
||||
|
||||
Two byte parameters: ``sub foo(ubyte bar, ubyte baz) { ... }``
|
||||
gets bar in the accumulator A, and baz in Y, *subroutine* stores it into parameter variable
|
||||
|
||||
Single word parameter: ``sub foo(uword bar) { ... }``
|
||||
gets bar in the register pair A + Y (lsb in A, msb in Y), *subroutine* stores it into parameter variable
|
||||
|
||||
Floating point parameter: ``sub foo(float bar) { ... }``
|
||||
value for bar gets copied into the parameter variable *by the caller*
|
||||
|
||||
Other: ``sub foo(ubyte bar, ubyte baz, ubyte zoo) { ... }``
|
||||
register values indeterminate, values all get stored in the parameter variables *by the caller*
|
||||
|
||||
|
||||
|
||||
``asmsub`` and ``extsub`` routines
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
These are kernal (ROM) routines or low-level assembly routines, that get their arguments via specific registers.
|
||||
Sometimes even via a processor status flag such as the Carry flag.
|
||||
Return values also via designated registers.
|
||||
The processor status flag is preserved on returning so you can immediately act on that for instance
|
||||
Note that word values can be put in a "CPU register pair" such as AY (meaning A+Y registers) but also
|
||||
in one of the 16 'virtual' 16 bit registers introduced by the Commander X16, R0-R15.
|
||||
Float values can be put in the FAC1 or FAC2 floating point 'registers'.
|
||||
The return values also get returned via designated registers, or via processor status flags again.
|
||||
This means that after calling such a routine you can immediately act on the status
|
||||
via a special branch instruction such as ``if_z`` or ``if_cs`` etc.
|
||||
The register/status flag usage is fully specified in the asmsub or extsub signature defintion
|
||||
for both the parameters and the return values::
|
||||
|
||||
extsub $2000 = extfunction(ubyte arg1 @A, uword arg2 @XY, uword arg3 @R0,
|
||||
float frac @FAC1, bool flag @Pc) -> ubyte @Y, bool @Pz
|
||||
|
||||
regular subroutines
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- subroutine parameters are just variables scoped to the subroutine.
|
||||
- the arguments passed in a call are evaluated and then copied into those variables.
|
||||
Using variables for this sometimes can seem inefficient but it's required to allow subroutines to work locally
|
||||
with their parameters and allow them to modify them as required, without changing the
|
||||
variables used in the call's arguments. If you want to get rid of this overhead you'll
|
||||
have to make an ``asmsub`` routine in assembly instead.
|
||||
- the order of evaluation of subroutine call arguments *is unspecified* and should not be relied upon.
|
||||
- the return value is passed back to the caller via cpu register(s):
|
||||
Byte values will be put in ``A`` .
|
||||
Word values will be put in ``A`` + ``Y`` register pair.
|
||||
Float values will be put in the ``FAC1`` float 'register' (BASIC allocated this somewhere in ram).
|
||||
|
||||
|
||||
Calls to builtin functions are treated in a special way:
|
||||
Generally if they have a single argument it's passed in a register or register pair.
|
||||
Multiple arguments are passed like a normal subroutine, into variables.
|
||||
Some builtin functions have a fully custom implementation.
|
||||
|
||||
|
||||
The compiler will warn about routines that are called and that return a value, if you're not
|
||||
doing something with that returnvalue. This can be on purpose if you're simply not interested in it.
|
||||
Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case.
|
||||
asmsub function(ubyte arg1 @A, uword arg2 @XY, uword arg3 @R0,
|
||||
float frac @FAC1, bool flag @Pc) -> ubyte @Y, bool @Pz {
|
||||
%asm {{
|
||||
...
|
||||
...
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
Compiler Internals
|
||||
|
||||
+27
-24
@@ -7,9 +7,9 @@ TODO
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Port the diskio module to the PET using lower level serial kernal IO calls?
|
||||
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
|
||||
- Why are blocks without an addr moved BEHIND a block with an address? That's done in the StatementReorderer.
|
||||
- rename 'string' module to 'strings' to be consistent with 'floats'?
|
||||
- on the C64: make the floating point routines @banked so that basic can be permanently banked out even if you use floats? But this will crash when the call is done from program code at $a000+
|
||||
- Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.
|
||||
Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
|
||||
Add a -library $xxxx command line option to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?
|
||||
@@ -18,18 +18,14 @@ Future Things and Ideas
|
||||
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
|
||||
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
|
||||
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
|
||||
- remove 'extsub' as a recognised alternative for 'extsub'
|
||||
- remove the 'addmissingrts' compiler option.
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
- Does it make codegen easier if everything is an expression? Start with the PtProgram ast , get rid of the statements there -> expressions that have Void data type
|
||||
- Can we support signed % (remainder) somehow?
|
||||
- IR: implement missing operators in AssignmentGen (array shifts etc)
|
||||
- instead of copy-pasting inline asmsubs, make them into a 64tass macro and use that instead.
|
||||
that will allow them to be reused from custom user written assembly code as well.
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful if we have typed pointers.
|
||||
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
|
||||
- [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)
|
||||
@@ -39,20 +35,6 @@ Future Things and Ideas
|
||||
- OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram
|
||||
- OR.... make all this more generic and use some %segment option to create real segments for 64tass?
|
||||
- (need separate step in codegen and IR to write the "golden" variables)
|
||||
|
||||
- ir: support %align on code chunks
|
||||
- ir: fix call() return value handling
|
||||
- ir: proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
- ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
||||
- ir: add more optimizations in IRPeepholeOptimizer
|
||||
- ir: the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
|
||||
maybe another representation is needed once actual codegeneration is done from the IR...?
|
||||
- ir: split word arrays, both _msb and _lsb arrays are tagged with an alignment. This is not what's intended; only the one put in memory first should be aligned (the other one should follow straight after it)
|
||||
- ir: getting it in shape for code generation...
|
||||
- ir: make optimizeBitTest work for IR too to use the BIT instruction?
|
||||
- ir: make sure that a 6502 codegen based off the IR, still generates BIT instructions when testing bit 7 or 6 of a byte var.
|
||||
- [problematic due to using 64tass:] better support for building library programs, where unused .proc are NOT deleted from the assembly.
|
||||
Perhaps replace all uses of .proc/.pend/.endproc by .block/.bend will fix that with a compiler flag?
|
||||
But all library code written in asm uses .proc already..... (textual search/replace when writing the actual asm?)
|
||||
@@ -60,16 +42,37 @@ Future Things and Ideas
|
||||
- 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) But the V flag is also set on certain normal instructions
|
||||
|
||||
|
||||
Libraries:
|
||||
IR/VM
|
||||
-----
|
||||
- constants are not retained in the IR file, they should. (need to be able to make asm labels from them eventually)
|
||||
- implement missing operators in AssignmentGen (array shifts etc)
|
||||
- support %align on code chunks
|
||||
- fix call() return value handling
|
||||
- fix float register parameters (FAC1,FAC2) for extsubs, search for TODO("floating point register parameters not supported")
|
||||
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
||||
- add more optimizations in IRPeepholeOptimizer
|
||||
- the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
|
||||
maybe another representation is needed once actual codegeneration is done from the IR...?
|
||||
- split word arrays, both _msb and _lsb arrays are tagged with an alignment. This is not what's intended; only the one put in memory first should be aligned (the other one should follow straight after it)
|
||||
- getting it in shape for code generation...
|
||||
- make optimizeBitTest work for IR too to use the BIT instruction?
|
||||
- make sure that a 6502 codegen based off the IR, still generates BIT instructions when testing bit 7 or 6 of a byte var.
|
||||
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- pet32 target: make syslib more complete (missing kernal routines)?
|
||||
- need help with: PET disk routines (OPEN, SETLFS etc are not exposed as kernal calls)
|
||||
- fix the problems in atari target, and flesh out its libraries.
|
||||
- c128 target: make syslib more complete (missing kernal routines)?
|
||||
- pet32 target: make syslib more complete (missing kernal routines)?
|
||||
- VM: implement the last diskio support (file listings)
|
||||
|
||||
|
||||
Optimizations:
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"
|
||||
- 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 (or these first??), then the rest
|
||||
|
||||
+63
-63
@@ -1,38 +1,38 @@
|
||||
%import textio
|
||||
%import palette
|
||||
%import string
|
||||
%import gfx_hires4
|
||||
%import gfx_hires
|
||||
|
||||
; Mockup of a classic Amiga Workbench screen.
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
gfx_hires4.graphics_mode() ; select 640*480 mode, 4 colors
|
||||
gfx_hires.graphics_mode() ; select 640*480 mode, 4 colors
|
||||
mouse.set_pointer_image()
|
||||
cx16.mouse_config(-1, 640/8, 240/8)
|
||||
uword[4] amigacolors = [$aaa, $000, $fff, $68c] ; gray, black, white, lightblue
|
||||
palette.set_rgb(amigacolors, len(amigacolors))
|
||||
|
||||
cx16.VERA_DC_VSCALE = 64 ; have the vertical resolution so it is 640*240 - more or less Amiga's default non interlaced mode
|
||||
gfx_hires4.text_charset(1)
|
||||
gfx_hires.text_charset(1)
|
||||
|
||||
screen_titlebar()
|
||||
window_workbench()
|
||||
window_system()
|
||||
window_shell()
|
||||
gfx_hires4.text(240, 210, 1, iso:"640x480(240) 4 colors")
|
||||
gfx_hires4.text(240, 220, 1, iso:"Mockup drawn using Prog8 gfx_hires4 library")
|
||||
gfx_hires.text(240, 210, 1, iso:"640x480(240) 4 colors")
|
||||
gfx_hires.text(240, 220, 1, iso:"Mockup drawn using Prog8 gfx_hires library")
|
||||
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
|
||||
sub screen_titlebar() {
|
||||
gfx_hires4.fillrect(0, 0, gfx_hires4.WIDTH, 10, 2)
|
||||
gfx_hires4.text(8,1, 1, iso:"AmigaOS 3.1 2,002,448 graphics mem 16,504,384 other mem")
|
||||
gfx_hires4.horizontal_line(0, 10, gfx_hires4.WIDTH, 1)
|
||||
widget.window_order_icon(gfx_hires4.WIDTH-widget.window_order_icon.width, 0, false)
|
||||
gfx_hires.fillrect(0, 0, gfx_hires.WIDTH, 10, 2)
|
||||
gfx_hires.text(8,1, 1, iso:"AmigaOS 3.1 2,002,448 graphics mem 16,504,384 other mem")
|
||||
gfx_hires.horizontal_line(0, 10, gfx_hires.WIDTH, 1)
|
||||
widget.window_order_icon(gfx_hires.WIDTH-widget.window_order_icon.width, 0, false)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ main {
|
||||
const uword height = 220
|
||||
|
||||
widget.window_titlebar(win_x, win_y, width, iso:"Workbench", false)
|
||||
; gfx_hires4.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
|
||||
; gfx_hires.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
|
||||
widget.window_leftborder(win_x, win_y, height, false)
|
||||
widget.window_bottomborder(win_x, win_y, width, height)
|
||||
widget.window_rightborder(win_x, win_y, width, height, false)
|
||||
@@ -56,12 +56,12 @@ main {
|
||||
}
|
||||
|
||||
sub vector_v(uword x, uword y) {
|
||||
gfx_hires4.horizontal_line(x, y, 12, 1)
|
||||
gfx_hires4.horizontal_line(x+16, y+16, 11,1)
|
||||
gfx_hires4.line(x,y,x+16,y+16,1)
|
||||
gfx_hires4.line(x+11,y,x+16+5,y+10,1)
|
||||
gfx_hires4.line(x+16+5,y+10,x+47,y-16,1)
|
||||
gfx_hires4.line(x+16+10,y+16,x+46+12,y-16,1)
|
||||
gfx_hires.horizontal_line(x, y, 12, 1)
|
||||
gfx_hires.horizontal_line(x+16, y+16, 11,1)
|
||||
gfx_hires.line(x,y,x+16,y+16,1)
|
||||
gfx_hires.line(x+11,y,x+16+5,y+10,1)
|
||||
gfx_hires.line(x+16+5,y+10,x+47,y-16,1)
|
||||
gfx_hires.line(x+16+10,y+16,x+46+12,y-16,1)
|
||||
}
|
||||
|
||||
sub window_system() {
|
||||
@@ -71,7 +71,7 @@ main {
|
||||
const uword win_y = 40
|
||||
|
||||
widget.window_titlebar(win_x, win_y, width, iso:"System", false)
|
||||
gfx_hires4.fillrect(win_x+3, win_y+11, width-4, height-11-2, 0) ; clear window pane
|
||||
gfx_hires.fillrect(win_x+3, win_y+11, width-4, height-11-2, 0) ; clear window pane
|
||||
widget.window_leftborder(win_x, win_y, height, false)
|
||||
widget.window_bottomborder(win_x, win_y, width, height)
|
||||
widget.window_rightborder(win_x, win_y, width, height, false)
|
||||
@@ -90,14 +90,14 @@ main {
|
||||
const uword height = 65
|
||||
|
||||
widget.window_titlebar(win_x, win_y, width, iso:"AmigaShell", true)
|
||||
gfx_hires4.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
|
||||
gfx_hires.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
|
||||
widget.window_leftborder(win_x, win_y, height, true)
|
||||
widget.window_bottomborder(win_x, win_y, width, height)
|
||||
widget.window_rightborder(win_x, win_y, width, height, true)
|
||||
|
||||
gfx_hires4.text(win_x+5, win_y+12, 1, iso:"New Shell process 3")
|
||||
gfx_hires4.text(win_x+5, win_y+12+8, 1, iso:"3.Workbench3.1:>")
|
||||
gfx_hires4.fillrect(win_x+5+17*8, win_y+12+8, 8, 8, 1) ; cursor
|
||||
gfx_hires.text(win_x+5, win_y+12, 1, iso:"New Shell process 3")
|
||||
gfx_hires.text(win_x+5, win_y+12+8, 1, iso:"3.Workbench3.1:>")
|
||||
gfx_hires.fillrect(win_x+5+17*8, win_y+12+8, 8, 8, 1) ; cursor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,15 +147,15 @@ mouse {
|
||||
widget {
|
||||
|
||||
sub highlightedrect(uword x, uword y, uword width, uword height, bool fill, bool active) {
|
||||
gfx_hires4.horizontal_line(x, y, width, 2)
|
||||
gfx_hires4.vertical_line(x, y+1, height-1, 2)
|
||||
gfx_hires4.vertical_line(x+width-1, y+1, height-1, 1)
|
||||
gfx_hires4.horizontal_line(x+1, y+height-1, width-2, 1)
|
||||
gfx_hires.horizontal_line(x, y, width, 2)
|
||||
gfx_hires.vertical_line(x, y+1, height-1, 2)
|
||||
gfx_hires.vertical_line(x+width-1, y+1, height-1, 1)
|
||||
gfx_hires.horizontal_line(x+1, y+height-1, width-2, 1)
|
||||
if fill {
|
||||
if active
|
||||
gfx_hires4.fillrect(x+1,y+1,width-2,height-2, 3)
|
||||
gfx_hires.fillrect(x+1,y+1,width-2,height-2, 3)
|
||||
else
|
||||
gfx_hires4.fillrect(x+1,y+1,width-2,height-2, 0)
|
||||
gfx_hires.fillrect(x+1,y+1,width-2,height-2, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,17 +165,17 @@ widget {
|
||||
highlightedrect(x, y, width, height, false, false)
|
||||
uword middlex = x+width/2+1
|
||||
ubyte halfstring = string.length(caption) * 4
|
||||
gfx_hires4.text(middlex-halfstring,y+height+1,1,caption)
|
||||
gfx_hires4.disc(x+width/4+4, y+height/2, height/2-3, 1)
|
||||
gfx_hires4.fillrect(x+20,y+12,width/2,height/2-4,3)
|
||||
gfx_hires.text(middlex-halfstring,y+height+1,1,caption)
|
||||
gfx_hires.disc(x+width/4+4, y+height/2, height/2-3, 1)
|
||||
gfx_hires.fillrect(x+20,y+12,width/2,height/2-4,3)
|
||||
}
|
||||
|
||||
|
||||
sub window_titlebar(uword x, uword y, uword width, uword titlestr, bool active) {
|
||||
const ubyte height = 11
|
||||
widget.highlightedrect(x+widget.window_close_icon.width, y, width-64, height, true, active)
|
||||
gfx_hires4.plot(x+widget.window_close_icon.width, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires4.text(x+26, y+1, 1, titlestr)
|
||||
gfx_hires.plot(x+widget.window_close_icon.width, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires.text(x+26, y+1, 1, titlestr)
|
||||
widget.window_close_icon(x, y, active)
|
||||
widget.window_order_icon(x+width-22, y, active)
|
||||
widget.window_flipsize_icon(x+width-44, y, active)
|
||||
@@ -185,66 +185,66 @@ widget {
|
||||
const uword width = 22
|
||||
const uword height = 11
|
||||
highlightedrect(x, y, width, height, true, active)
|
||||
gfx_hires4.plot(x, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires4.rect(x+5, y+2, width-9, height-4, 1)
|
||||
gfx_hires4.rect(x+5, y+2, 7, 4, 1)
|
||||
gfx_hires4.fillrect(x+6, y+3, 5, 2, 2)
|
||||
gfx_hires.plot(x, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires.rect(x+5, y+2, width-9, height-4, 1)
|
||||
gfx_hires.rect(x+5, y+2, 7, 4, 1)
|
||||
gfx_hires.fillrect(x+6, y+3, 5, 2, 2)
|
||||
}
|
||||
|
||||
sub window_order_icon(uword x, uword y, bool active) {
|
||||
const uword width = 22
|
||||
const uword height = 11
|
||||
highlightedrect(x, y, width, height, true, active)
|
||||
gfx_hires4.plot(x, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires4.rect(x+4, y+2, 10, 5, 1) ; back
|
||||
gfx_hires4.fillrect(x+9, y+5, 8, 3, 2) ; white front
|
||||
gfx_hires4.rect(x+8, y+4, 10, 5, 1) ; front
|
||||
gfx_hires.plot(x, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires.rect(x+4, y+2, 10, 5, 1) ; back
|
||||
gfx_hires.fillrect(x+9, y+5, 8, 3, 2) ; white front
|
||||
gfx_hires.rect(x+8, y+4, 10, 5, 1) ; front
|
||||
}
|
||||
|
||||
sub window_close_icon(uword x, uword y, bool active) {
|
||||
const uword width = 20
|
||||
const uword height = 11
|
||||
highlightedrect(x, y, width, height, true, active)
|
||||
gfx_hires4.plot(x, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires4.rect(x+7, y+3, 5, 5, 1)
|
||||
gfx_hires4.fillrect(x+8, y+4, 3, 3, 2)
|
||||
gfx_hires.plot(x, y+height-1, 1) ; correct bottom left corner
|
||||
gfx_hires.rect(x+7, y+3, 5, 5, 1)
|
||||
gfx_hires.fillrect(x+8, y+4, 3, 3, 2)
|
||||
}
|
||||
|
||||
sub window_leftborder(uword x, uword y, uword height, bool active) {
|
||||
gfx_hires4.vertical_line(x, y, height, 2)
|
||||
gfx_hires.vertical_line(x, y, height, 2)
|
||||
ubyte color = 0
|
||||
if active
|
||||
color = 3
|
||||
gfx_hires4.vertical_line(x+1, y+11, height-11, color)
|
||||
gfx_hires4.vertical_line(x+2, y+11, height-11, 1)
|
||||
gfx_hires.vertical_line(x+1, y+11, height-11, color)
|
||||
gfx_hires.vertical_line(x+2, y+11, height-11, 1)
|
||||
}
|
||||
|
||||
sub window_bottomborder(uword x, uword y, uword width, uword height) {
|
||||
gfx_hires4.horizontal_line(x+3, y+height-2, width-3, 2)
|
||||
gfx_hires4.horizontal_line(x, y+height-1, width, 1)
|
||||
gfx_hires.horizontal_line(x+3, y+height-2, width-3, 2)
|
||||
gfx_hires.horizontal_line(x, y+height-1, width, 1)
|
||||
}
|
||||
|
||||
sub window_rightborder(uword x, uword y, uword width, uword height, bool active) {
|
||||
gfx_hires4.vertical_line(x+width-1-16, y+11, height-13,2)
|
||||
gfx_hires4.vertical_line(x+width-1, y+11, height-11,1)
|
||||
gfx_hires.vertical_line(x+width-1-16, y+11, height-13,2)
|
||||
gfx_hires.vertical_line(x+width-1, y+11, height-11,1)
|
||||
ubyte color = 0
|
||||
if active
|
||||
color = 3
|
||||
gfx_hires4.fillrect(x+width-1-15, y+11, 15, height-12, color)
|
||||
gfx_hires.fillrect(x+width-1-15, y+11, 15, height-12, color)
|
||||
|
||||
gfx_hires4.horizontal_line(x+width-1-13, y+height-3, 11, 1)
|
||||
gfx_hires4.vertical_line(x+width-1-3, y+height-3-5, 5, 1)
|
||||
gfx_hires4.line(x+width-1-13,y+height-3, x+width-1-3, y+height-3-5, 1)
|
||||
gfx_hires4.horizontal_line(x+width-1-16, y+height-10, 16, 2)
|
||||
gfx_hires.horizontal_line(x+width-1-13, y+height-3, 11, 1)
|
||||
gfx_hires.vertical_line(x+width-1-3, y+height-3-5, 5, 1)
|
||||
gfx_hires.line(x+width-1-13,y+height-3, x+width-1-3, y+height-3-5, 1)
|
||||
gfx_hires.horizontal_line(x+width-1-16, y+height-10, 16, 2)
|
||||
|
||||
highlightedrect(x+width-13, y+12, 10, height-43, false, false)
|
||||
gfx_hires4.horizontal_line(x+width-1-16, y+height-11, 16, 1)
|
||||
gfx_hires4.horizontal_line(x+width-1-16, y+height-20, 16, 2)
|
||||
gfx_hires4.horizontal_line(x+width-1-16, y+height-21, 16, 1)
|
||||
gfx_hires4.horizontal_line(x+width-1-16, y+height-30, 16, 2)
|
||||
gfx_hires4.line(x+width-1-13, y+height-23, x+width-9, y+height-28, 1)
|
||||
gfx_hires4.line(x+width-1-3, y+height-23, x+width-9, y+height-28, 1)
|
||||
gfx_hires4.line(x+width-1-13, y+height-18, x+width-9, y+height-13, 1)
|
||||
gfx_hires4.line(x+width-1-3, y+height-18, x+width-9, y+height-13, 1)
|
||||
gfx_hires.horizontal_line(x+width-1-16, y+height-11, 16, 1)
|
||||
gfx_hires.horizontal_line(x+width-1-16, y+height-20, 16, 2)
|
||||
gfx_hires.horizontal_line(x+width-1-16, y+height-21, 16, 1)
|
||||
gfx_hires.horizontal_line(x+width-1-16, y+height-30, 16, 2)
|
||||
gfx_hires.line(x+width-1-13, y+height-23, x+width-9, y+height-28, 1)
|
||||
gfx_hires.line(x+width-1-3, y+height-23, x+width-9, y+height-28, 1)
|
||||
gfx_hires.line(x+width-1-13, y+height-18, x+width-9, y+height-13, 1)
|
||||
gfx_hires.line(x+width-1-3, y+height-18, x+width-9, y+height-13, 1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
%import gfx_hires
|
||||
%import textio
|
||||
%import math
|
||||
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
gfx_hires.graphics_mode()
|
||||
draw()
|
||||
sys.wait(120)
|
||||
gfx_hires.text_mode()
|
||||
txt.print("done!\n")
|
||||
}
|
||||
|
||||
sub draw() {
|
||||
gfx_hires.rect(10,10, 1, 1, 4)
|
||||
gfx_hires.rect(20,10, 2, 1, 4)
|
||||
gfx_hires.rect(30,10, 3, 1, 4)
|
||||
gfx_hires.rect(40,10, 1, 2, 4)
|
||||
gfx_hires.rect(50,10, 1, 3, 4)
|
||||
gfx_hires.rect(60,10, 2, 2, 4)
|
||||
gfx_hires.rect(70,10, 3, 3, 4)
|
||||
gfx_hires.rect(80,10, 4, 4, 4)
|
||||
gfx_hires.rect(90,10, 5, 5, 4)
|
||||
gfx_hires.rect(100,10, 8, 8, 4)
|
||||
gfx_hires.rect(110,10, 20, 5, 4)
|
||||
gfx_hires.rect(80, 80, 200, 140, 4)
|
||||
|
||||
gfx_hires.fillrect(10,40, 1, 1, 5)
|
||||
gfx_hires.fillrect(20,40, 2, 1, 5)
|
||||
gfx_hires.fillrect(30,40, 3, 1, 5)
|
||||
gfx_hires.fillrect(40,40, 1, 2, 5)
|
||||
gfx_hires.fillrect(50,40, 1, 3, 5)
|
||||
gfx_hires.fillrect(60,40, 2, 2, 5)
|
||||
gfx_hires.fillrect(70,40, 3, 3, 5)
|
||||
gfx_hires.fillrect(80,40, 4, 4, 5)
|
||||
gfx_hires.fillrect(90,40, 5, 5, 5)
|
||||
gfx_hires.fillrect(100,40, 8, 8, 5)
|
||||
gfx_hires.fillrect(110,40, 20, 5, 5)
|
||||
gfx_hires.fillrect(82, 82, 200-4, 140-4, 5)
|
||||
|
||||
ubyte i
|
||||
for i in 0 to 254 step 4 {
|
||||
uword x1 = ((gfx_hires.WIDTH-256)/2 as uword) + math.sin8u(i)
|
||||
uword y1 = (gfx_hires.HEIGHT-128)/2 + math.cos8u(i)/2
|
||||
uword x2 = ((gfx_hires.WIDTH-64)/2 as uword) + math.sin8u(i)/4
|
||||
uword y2 = (gfx_hires.HEIGHT-64)/2 + math.cos8u(i)/4
|
||||
gfx_hires.line(x1, y1, x2, y2, i+1)
|
||||
}
|
||||
|
||||
sys.wait(60)
|
||||
gfx_hires.clear_screen(2)
|
||||
gfx_hires.clear_screen(0)
|
||||
|
||||
ubyte radius
|
||||
|
||||
for radius in 110 downto 8 step -4 {
|
||||
gfx_hires.circle(gfx_hires.WIDTH/2, (gfx_hires.HEIGHT/2 as ubyte), radius, radius)
|
||||
}
|
||||
|
||||
gfx_hires.disc(gfx_hires.WIDTH/2, gfx_hires.HEIGHT/2, 80, 2)
|
||||
|
||||
ubyte tp
|
||||
for tp in 0 to 15 {
|
||||
gfx_hires.text(19+tp,20+tp*11, 7, sc:"ScreenCODE text! 1234![]<>#$%&*()")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
%import gfx_hires4
|
||||
%import textio
|
||||
%import math
|
||||
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
gfx_hires4.graphics_mode()
|
||||
draw()
|
||||
sys.wait(120)
|
||||
gfx_hires4.text_mode()
|
||||
txt.print("done!\n")
|
||||
}
|
||||
|
||||
sub draw() {
|
||||
gfx_hires4.rect(10,10, 1, 1, 4)
|
||||
gfx_hires4.rect(20,10, 2, 1, 4)
|
||||
gfx_hires4.rect(30,10, 3, 1, 4)
|
||||
gfx_hires4.rect(40,10, 1, 2, 4)
|
||||
gfx_hires4.rect(50,10, 1, 3, 4)
|
||||
gfx_hires4.rect(60,10, 2, 2, 4)
|
||||
gfx_hires4.rect(70,10, 3, 3, 4)
|
||||
gfx_hires4.rect(80,10, 4, 4, 4)
|
||||
gfx_hires4.rect(90,10, 5, 5, 4)
|
||||
gfx_hires4.rect(100,10, 8, 8, 4)
|
||||
gfx_hires4.rect(110,10, 20, 5, 4)
|
||||
gfx_hires4.rect(80, 80, 200, 140, 4)
|
||||
|
||||
gfx_hires4.fillrect(10,40, 1, 1, 5)
|
||||
gfx_hires4.fillrect(20,40, 2, 1, 5)
|
||||
gfx_hires4.fillrect(30,40, 3, 1, 5)
|
||||
gfx_hires4.fillrect(40,40, 1, 2, 5)
|
||||
gfx_hires4.fillrect(50,40, 1, 3, 5)
|
||||
gfx_hires4.fillrect(60,40, 2, 2, 5)
|
||||
gfx_hires4.fillrect(70,40, 3, 3, 5)
|
||||
gfx_hires4.fillrect(80,40, 4, 4, 5)
|
||||
gfx_hires4.fillrect(90,40, 5, 5, 5)
|
||||
gfx_hires4.fillrect(100,40, 8, 8, 5)
|
||||
gfx_hires4.fillrect(110,40, 20, 5, 5)
|
||||
gfx_hires4.fillrect(82, 82, 200-4, 140-4, 5)
|
||||
|
||||
ubyte i
|
||||
for i in 0 to 254 step 4 {
|
||||
uword x1 = ((gfx_hires4.WIDTH-256)/2 as uword) + math.sin8u(i)
|
||||
uword y1 = (gfx_hires4.HEIGHT-128)/2 + math.cos8u(i)/2
|
||||
uword x2 = ((gfx_hires4.WIDTH-64)/2 as uword) + math.sin8u(i)/4
|
||||
uword y2 = (gfx_hires4.HEIGHT-64)/2 + math.cos8u(i)/4
|
||||
gfx_hires4.line(x1, y1, x2, y2, i+1)
|
||||
}
|
||||
|
||||
sys.wait(60)
|
||||
gfx_hires4.clear_screen(2)
|
||||
gfx_hires4.clear_screen(0)
|
||||
|
||||
ubyte radius
|
||||
|
||||
for radius in 110 downto 8 step -4 {
|
||||
gfx_hires4.circle(gfx_hires4.WIDTH/2, (gfx_hires4.HEIGHT/2 as ubyte), radius, radius)
|
||||
}
|
||||
|
||||
gfx_hires4.disc(gfx_hires4.WIDTH/2, gfx_hires4.HEIGHT/2, 80, 2)
|
||||
|
||||
ubyte tp
|
||||
for tp in 0 to 15 {
|
||||
gfx_hires4.text(19+tp,20+tp*11, 7, sc:"ScreenCODE text! 1234![]<>#$%&*()")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
%import diskio
|
||||
%import textio
|
||||
%import zsmkit_high
|
||||
%memtop $8bff ; zsmkit is loaded from 8c00 onwards
|
||||
%memtop $8c00 ; zsmkit is loaded from 8c00 onwards
|
||||
|
||||
;; Proof Of Concept ZSM player using a binary blob version of zsmkit by MooingLemur
|
||||
;; This version is a bit simpler as "demo1".
|
||||
|
||||
+2
-25
@@ -1,31 +1,8 @@
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.print_ubbin(c128.getbanks(), true)
|
||||
txt.spc()
|
||||
txt.print_ub(@($4000))
|
||||
txt.spc()
|
||||
txt.print_ub(@($8000))
|
||||
txt.nl()
|
||||
|
||||
@($4000)++
|
||||
@($8000)++
|
||||
txt.print_ub(@($4000))
|
||||
txt.spc()
|
||||
txt.print_ub(@($8000))
|
||||
txt.nl()
|
||||
|
||||
c128.banks(0)
|
||||
txt.print_ubbin(c128.getbanks(), true)
|
||||
txt.spc()
|
||||
@($4000)++
|
||||
@($8000)++
|
||||
txt.print_ub(@($4000))
|
||||
txt.spc()
|
||||
txt.print_ub(@($8000))
|
||||
txt.nl()
|
||||
cx16.r0s = if cx16.r0L < cx16.r1L -1 else 1
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,4 +5,4 @@ org.gradle.daemon=true
|
||||
kotlin.code.style=official
|
||||
javaVersion=11
|
||||
kotlinVersion=2.0.21
|
||||
version=10.5
|
||||
version=10.5.1
|
||||
+13
-7
@@ -9,7 +9,7 @@ Requirements: Pillow (pip install pillow)
|
||||
"""
|
||||
|
||||
from PIL import Image, PyAccess
|
||||
from typing import TypeAlias
|
||||
from typing import TypeAlias, Tuple, Optional
|
||||
|
||||
RGBList: TypeAlias = list[tuple[int, int, int]]
|
||||
|
||||
@@ -182,26 +182,29 @@ class BitmapImage:
|
||||
palette_image.putpalette(palette)
|
||||
self.img = self.img.quantize(dither=dither, palette=palette_image)
|
||||
|
||||
def quantize(self, bits_per_pixel: int, preserve_first_16_colors: bool,
|
||||
def quantize(self, bits_per_pixel: int, preserve_first_16_colors: bool = False, fixed_color_zero: Optional[Tuple[int, int, int]] = None,
|
||||
dither: Image.Dither = Image.Dither.FLOYDSTEINBERG) -> None:
|
||||
"""
|
||||
Convert the image to one with indexed colors (12 bits colorspace palette extended back into 8 bits per channel).
|
||||
If you want to display the image on the actual Commander X16, simply take the lower (or upper) 4 bits of every color channel.
|
||||
There is support for either 8 or 4 bits per pixel (256 or 16 color modes).
|
||||
Dithering is applied as given (default is Floyd-Steinberg).
|
||||
preserve_first_16_colors: set to True to keep the first 16 colors in the palette the same as the X16's default palette.
|
||||
fixed_color_zero: set to tuple (R,G,B) to keep the first color entry in the palette to the given fixed color (4 bit per color channel color space).
|
||||
"""
|
||||
assert not(preserve_first_16_colors and fixed_color_zero), "preserve 16 and fixed color 0 are mutually exclusive"
|
||||
if bits_per_pixel == 8:
|
||||
num_colors = 240 if preserve_first_16_colors else 256
|
||||
num_colors = 240 if preserve_first_16_colors else (255 if fixed_color_zero else 256)
|
||||
elif bits_per_pixel == 4:
|
||||
num_colors = 16
|
||||
if preserve_first_16_colors:
|
||||
num_colors = 15 if fixed_color_zero else 16
|
||||
if num_colors==16 and preserve_first_16_colors:
|
||||
return self.quantize_to(default_colors[:16])
|
||||
elif bits_per_pixel == 2:
|
||||
assert preserve_first_16_colors==False, "bpp is too small for 16 default colors"
|
||||
num_colors = 4
|
||||
num_colors = 3 if fixed_color_zero else 4
|
||||
elif bits_per_pixel == 1:
|
||||
assert preserve_first_16_colors==False, "bpp is too small for 16 default colors"
|
||||
num_colors = 2
|
||||
num_colors = 1 if fixed_color_zero else 2
|
||||
else:
|
||||
raise ValueError("only 8,4,2,1 bpp supported")
|
||||
image = self.img.convert("RGB")
|
||||
@@ -212,6 +215,9 @@ class BitmapImage:
|
||||
palette_rgb = list(reversed(sorted(set(palette_8to4(palette_rgb)))))
|
||||
if preserve_first_16_colors:
|
||||
palette_rgb = default_colors[:16] + palette_rgb
|
||||
elif fixed_color_zero:
|
||||
assert fixed_color_zero[0]<16 and fixed_color_zero[1]<16 and fixed_color_zero[2]<16, "fixed color 0 must to be 4 bits per channel"
|
||||
palette_rgb = [fixed_color_zero] + palette_rgb
|
||||
self.img = image
|
||||
self.quantize_to(palette_rgb, dither)
|
||||
|
||||
|
||||
@@ -153,12 +153,13 @@ class VmProgramLoader {
|
||||
// placeholder is not a variable, so it must be a label of a code chunk instead
|
||||
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
|
||||
if(target==null)
|
||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||
throw IRParseException("label '$label' not found in variables nor labels. VM cannot reference other things such as blocks")
|
||||
else if(instr.opcode in OpcodesThatBranch)
|
||||
chunk.instructions[line] = instr.copy(branchTarget = target, address = null)
|
||||
else {
|
||||
var address = artificialLabelAddresses[label]
|
||||
if(address==null) {
|
||||
// generate an artificial address
|
||||
address = 0xa000 + artificialLabelAddresses.size
|
||||
artificialLabelAddresses[label] = address
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user