Compare commits

...
26 Commits
Author SHA1 Message Date
Irmen de Jong 1f5706bbeb version 10.5.1 2024-11-20 22:54:26 +01:00
Irmen de Jong 25c9b2fea4 remove an archaic machine code monitor bank setting at program exit
what did it even do?  in any case, $2d is just a user zero page location it should no longer be associated with the monitor nowadays.
2024-11-20 19:28:31 +01:00
Irmen de Jong 154f9b300f fix crash: byte c = if a < b -1 else 1 "both values should be the same type" 2024-11-19 23:46:25 +01:00
Irmen de Jong d78ce77536 improve vm error message when referencing a block name 2024-11-19 20:57:58 +01:00
Irmen de Jong b4fb43bc80 fix the if not check in ir codegen 2024-11-18 22:33:47 +01:00
Irmen de Jong e0e01f794e fix dt compiler crash 2024-11-17 17:39:36 +01:00
Irmen de Jong 08865dbb4e todo 2024-11-16 02:10:57 +01:00
Irmen de Jong b9ad7e0e55 forgot to mention floats 2024-11-15 23:37:08 +01:00
Irmen de Jong 07158a6f1a improve manual about subroutine call convention 2024-11-15 22:59:47 +01:00
Irmen de Jong 957c42bc1d tweak 2024-11-15 02:52:21 +01:00
Irmen de Jong f784da2da6 fix asm optimization regression caused by wrong label prefix comparison 2024-11-14 21:24:46 +01:00
Irmen de Jong c080fbe59a target machine config tweak and fix possible compiler crash on wrong type name 2024-11-13 21:04:46 +01:00
Irmen de Jong d70b8303b1 added sprites.reset() to remove sprites from the screen 2024-11-13 20:26:04 +01:00
Irmen de Jong 1d38c3582a progstart() added to complement progend() 2024-11-13 19:29:50 +01:00
Irmen de Jong 9438e996d7 Fixed math.mul16_last_upper().
Added math.lerpw() a LERP routine for words (to complement the existing math.lerp() for bytes)
Described the LERP routines in the library chapter in the docs.
2024-11-12 18:31:24 +01:00
Irmen de Jong 3b4a5e27f7 renamed gfx_hires4 module to just gfx_hires
to be consistent with gfx_lores
2024-11-12 17:48:35 +01:00
Irmen de Jong 648d9fc269 todo 2024-11-12 00:57:10 +01:00
Irmen de Jong 54fccec7d7 now also support using defer inside if statements 2024-11-12 00:11:19 +01:00
Irmen de Jong 4f9693055e fix compiler crash when extsub has both FAC1 and FAC2 float parameters 2024-11-11 20:48:25 +01:00
Irmen de Jong 555c50ee10 scripts/cx16_images : added an option to keep only the first palette entry fixed to a given color (such as, black.) 2024-11-11 19:27:06 +01:00
Irmen de Jong bf98ceca2c make repeat support 65536 iterations 2024-11-11 01:58:27 +01:00
Irmen de Jong 573cecb087 make memtop adjust automatically when you use %address larger than the default memtop setting. 2024-11-10 23:44:10 +01:00
Irmen de Jong 1b528491c2 make %memtop exclusive i.e. the first address NOT to use (like kernal MEMTOP) 2024-11-10 23:35:25 +01:00
Irmen de Jong 4bdabe1961 move shared cbm diskio to its own file (c64/c128) so that pet/atari/neo targets give better error message when trying to import non existing diskio module there
sorted the command line options alphabetically
2024-11-10 16:38:49 +01:00
Irmen de Jong a3fa527378 move shared cbm diskio to its own file (c64/c128) so that pet/atari/neo targets give better error message when trying to import non existing diskio module there 2024-11-10 15:35:36 +01:00
Irmen de Jong 84f5ffa426 fix generated labels prefix and filtering in the vice symbol dump file 2024-11-10 15:34:35 +01:00
80 changed files with 884 additions and 631 deletions

No files matched your search

+4 -6
View File
@@ -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
+6 -1
View File
@@ -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) {
+1 -1
View File
@@ -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
+7
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
%import shared_cbm_diskio
; No alterations here; everything is taken from the shared module.
+7
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
%import shared_cbm_diskio
; No alterations here; everything is taken from the shared module.
+7
View File
@@ -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
@@ -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 {
+1 -1
View File
@@ -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!
+1 -1
View File
@@ -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!
+16
View File
@@ -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
+7 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+13 -2
View File
@@ -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
}
}
+7
View File
@@ -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
+7
View File
@@ -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
+11
View File
@@ -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
}
}
+16 -16
View File
@@ -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 {
+12 -4
View File
@@ -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
}
}
@@ -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))
}
+1 -1
View File
@@ -147,7 +147,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
"starszoom",
"tehtriz",
"test_gfx_lores",
"test_gfx_hires4",
"test_gfx_hires",
"testmonogfx",
),
listOf(false, true)
+22
View File
@@ -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>()
}
})
+44 -4
View File
@@ -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>()
}
})
+4 -2
View File
@@ -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()
}
}
-1
View File
@@ -5,7 +5,6 @@
%import compression
%import conv
%import cx16logo
%import diskio
%import math
%import prog8_lib
%import string
+1 -1
View File
@@ -9,7 +9,7 @@
%import diskio
%import emudbg
%import floats
%import gfx_hires4
%import gfx_hires
%import gfx_lores
%import graphics
%import math
+10
View File
@@ -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
-1
View File
@@ -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
+5 -1
View File
@@ -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
View File
@@ -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
+26 -3
View File
@@ -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.
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
+73
View File
@@ -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![]<>#$%&*()")
}
}
}
-73
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}