mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3caf9108ad | ||
|
|
bde4be8231 | ||
|
|
0bbbb12ed2 | ||
|
|
b570bdaed7 | ||
|
|
8c0843cc87 | ||
|
|
31458ffd81 | ||
|
|
c15c10a94e | ||
|
|
9fca978725 | ||
|
|
b125901717 | ||
|
|
eb018ae660 | ||
|
|
7e5a9474fe | ||
|
|
525a9b5036 | ||
|
|
c3fbdf34ca | ||
|
|
48bd51e1a5 | ||
|
|
10d0b03a90 | ||
|
|
e1b3582f08 | ||
|
|
95be1c9e22 | ||
|
|
1ce8fe06d5 | ||
|
|
15c649024e | ||
|
|
e97303c226 | ||
|
|
3b786c819d | ||
|
|
04959dbd8b | ||
|
|
5cd4b874ea | ||
|
|
f14ea1b3de | ||
|
|
9cc0cda0fb | ||
|
|
09a7a4bbe5 | ||
|
|
cfea8b3745 | ||
|
|
28bf0b61ce | ||
|
|
2dc2429735 | ||
|
|
83d4592526 | ||
|
|
2d528c26ae | ||
|
|
66b3dce794 | ||
|
|
93f77a1045 | ||
|
|
aa4d23a3d5 | ||
|
|
2d7ebff8e9 | ||
|
|
bad9dd3b3b | ||
|
|
2f4e517857 | ||
|
|
ff35ba3696 | ||
|
|
72768e7fad | ||
|
|
77f3852cdc | ||
|
|
66857ca477 | ||
|
|
75514fc7af | ||
|
|
be06d871b6 | ||
|
|
f98ee326b4 | ||
|
|
bc8126eb16 | ||
|
|
4c8beefdcb | ||
|
|
bbb6c53457 | ||
|
|
d8991894e3 | ||
|
|
c7b7dcfd03 | ||
|
|
2c9e50873c | ||
|
|
923367296d | ||
|
|
151a206617 |
No files matched your search
-24
@@ -1,24 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.property.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:5.6.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.6.2/kotest-property-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
@@ -74,6 +74,8 @@ What does Prog8 provide?
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||
- "pet32": Commodore PET (experimental)
|
||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||
|
||||
|
||||
|
||||
@@ -78,7 +78,9 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
||||
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
||||
|
||||
@@ -13,6 +13,7 @@ class CompilationOptions(val output: OutputType,
|
||||
val compTarget: ICompilationTarget,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var warnSymbolShadowing: Boolean = false,
|
||||
var optimize: Boolean = false,
|
||||
var asmQuiet: Boolean = false,
|
||||
var asmListfile: Boolean = false,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
import prog8.code.target.pet.PETMachineDefinition
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = PETMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "pet32"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package prog8.code.target.pet
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
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 PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xffffu
|
||||
override val BSSHIGHRAM_END = 0xffffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting PET emulator...")
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = PETZeropage(compilerOptions)
|
||||
// there's no golden ram.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package prog8.code.target.pet
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
|
||||
// reference: http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
||||
|
||||
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x00u..0xffu)
|
||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x00u..0xffu)
|
||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0xb3u..0xbau) // TODO more?
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if PET can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
||||
@@ -3031,7 +3031,11 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
internal fun popFAC1() {
|
||||
out(" jsr floats.popFAC1")
|
||||
out(" clc | jsr floats.popFAC")
|
||||
}
|
||||
|
||||
internal fun popFAC2() {
|
||||
out(" sec | jsr floats.popFAC")
|
||||
}
|
||||
|
||||
internal fun needAsaveForExpr(arg: PtExpression): Boolean =
|
||||
|
||||
@@ -43,6 +43,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeUnneededTempvarInAdd(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
var linesByFourteen = getLinesBy(lines, 14)
|
||||
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
|
||||
if(mods.isNotEmpty()) {
|
||||
@@ -508,4 +515,27 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
|
||||
optimize('y', lines)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
|
||||
// this can be performed without the scratch variable: clc / adc something
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
||||
for(lines in linesByFour) {
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
|
||||
if(fourth.substring(4)==first.substring(4)) {
|
||||
mods.add(Modification(lines[0].index, false, " clc"))
|
||||
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
@@ -22,15 +22,20 @@ internal class AssemblyProgram(
|
||||
val assemblerCommand: List<String>
|
||||
|
||||
when (compTarget.name) {
|
||||
in setOf("c64", "c128", "cx16") -> {
|
||||
in setOf("c64", "c128", "cx16", "pet32") -> {
|
||||
// CBM machines .prg generation.
|
||||
|
||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||
"-Wall", // "-Wno-strict-bool", "-Werror",
|
||||
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
|
||||
)
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
@@ -59,10 +64,15 @@ internal class AssemblyProgram(
|
||||
|
||||
// TODO are these options okay for atari?
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||
"-Wall", // "-Werror", "-Wno-strict-bool"
|
||||
"--no-monitor"
|
||||
)
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
|
||||
@@ -72,12 +72,31 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD)
|
||||
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||
}
|
||||
|
||||
return BuiltinFunctions.getValue(fcall.name).returnType
|
||||
}
|
||||
|
||||
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType) {
|
||||
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
|
||||
when (resultType) {
|
||||
DataType.UBYTE -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A)
|
||||
asmgen.out(" tay | jsr math.multiply_bytes")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr math.square")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("optimized square only for integer types")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcDivmod(fcall: PtBuiltinFunctionCall) {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
@@ -723,11 +742,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($varname),y
|
||||
pha
|
||||
tax
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
}
|
||||
} else fallback()
|
||||
}
|
||||
@@ -740,11 +759,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.out("""
|
||||
lda ($varname),y
|
||||
pha
|
||||
tax
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
} else fallback()
|
||||
}
|
||||
else -> fallback()
|
||||
|
||||
@@ -255,11 +255,11 @@ $loopLabel""")
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
pha
|
||||
tax
|
||||
lda $varname+1
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
pla
|
||||
txa
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
lda $varname+1
|
||||
$modifiedLabel sbc #0 ; modified
|
||||
|
||||
@@ -142,12 +142,12 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
is PtIdentifier -> {
|
||||
val sourceName = asmgen.asmVariableName(value)
|
||||
asmgen.out("""
|
||||
pha
|
||||
tax
|
||||
clc
|
||||
lda $sourceName
|
||||
beq +
|
||||
sec
|
||||
+ pla""")
|
||||
+ txa""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
|
||||
@@ -428,12 +428,12 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("""
|
||||
lda #<${name}_init_value
|
||||
ldy #>${name}_init_value
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
lda #<${name}
|
||||
ldy #>${name}
|
||||
sta cx16.r1L
|
||||
sty cx16.r1H
|
||||
sta cx16.r1
|
||||
sty cx16.r1+1
|
||||
lda #<$size
|
||||
ldy #>$size
|
||||
jsr sys.memcopy""")
|
||||
@@ -452,7 +452,6 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
|
||||
asmgen.out("""+
|
||||
ldx #127 ; init estack ptr (half page)
|
||||
clv
|
||||
clc""")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
//
|
||||
@@ -180,37 +183,25 @@ internal class AnyExprAsmGen(
|
||||
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
assignFloatOperandsToFACandARG(expr.left, expr.right)
|
||||
asmgen.out(" jsr floats.FADDT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
|
||||
if(!expr.left.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
|
||||
if(!expr.left.isSimple()) asmgen.popFAC1()
|
||||
assignFloatOperandsToFACandARG(expr.right, expr.left)
|
||||
asmgen.out(" jsr floats.FSUBT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
assignFloatOperandsToFACandARG(expr.left, expr.right)
|
||||
asmgen.out(" jsr floats.FMULTT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"/" -> {
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
|
||||
if(!expr.left.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
|
||||
if(!expr.left.isSimple()) asmgen.popFAC1()
|
||||
assignFloatOperandsToFACandARG(expr.right, expr.left)
|
||||
asmgen.out(" jsr floats.FDIVT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
@@ -255,6 +246,31 @@ internal class AnyExprAsmGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
|
||||
when(asmgen.options.compTarget.name) {
|
||||
C64Target.NAME -> {
|
||||
// c64 has a quirk: always make sure FAC2 is loaded last (done using CONUPK) otherwise the result will be corrupt on C64
|
||||
// this requires some more forced copying around of float values in certain cases
|
||||
if (right.isSimple()) {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
|
||||
asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
asmgen.popFAC2()
|
||||
}
|
||||
}
|
||||
Cx16Target.NAME -> {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
if (!right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||
if (!right.isSimple()) asmgen.popFAC1()
|
||||
}
|
||||
else -> TODO("don't know how to evaluate float expression for selected compilation target")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
|
||||
@@ -88,7 +88,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda ${arrayVarName}_lsb,y | pha | lda ${arrayVarName}_msb,y | tay | pla")
|
||||
asmgen.out(" lda ${arrayVarName}_lsb,y | tax | lda ${arrayVarName}_msb,y | tay | txa")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
return
|
||||
@@ -122,7 +122,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | pha | lda $arrayVarName+1,y | tay | pla")
|
||||
asmgen.out(" lda $arrayVarName,y | tax | lda $arrayVarName+1,y | tay | txa")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
@@ -257,13 +257,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
when (assign.target.datatype) {
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
jsr prog8_lib.strcpy""")
|
||||
tax
|
||||
lda #<${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
else -> throw AssemblyError("str return value type mismatch with target")
|
||||
@@ -306,14 +306,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
sec
|
||||
eor #255
|
||||
adc #0
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
eor #255
|
||||
adc #0
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
}
|
||||
"~" -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
|
||||
"~" -> asmgen.out(" tax | tya | eor #255 | tay | txa | eor #255")
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
@@ -416,7 +416,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
val translatedOk = when (expr.operator) {
|
||||
in ComparisonOperators -> optimizedComparison(expr, assign)
|
||||
in setOf("&", "|", "^", "and", "or", "xor") -> optimizedLogicalOrBitwiseExpr(expr, assign.target)
|
||||
"==", "!=" -> optimizedEqualityExpr(expr, assign.target)
|
||||
"+", "-" -> optimizedPlusMinExpr(expr, assign.target)
|
||||
"<<", ">>" -> optimizedBitshiftExpr(expr, assign.target)
|
||||
"*" -> optimizedMultiplyExpr(expr, assign.target)
|
||||
@@ -544,7 +543,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier")
|
||||
asmgen.out(" jsr math.multiply_words")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
@@ -568,7 +567,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr math.mul_word_${value}")
|
||||
}
|
||||
else {
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier")
|
||||
asmgen.out(" jsr math.multiply_words")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
@@ -602,7 +601,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" pha")
|
||||
if(dt in ByteDatatypes) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
|
||||
asmgen.out(" ply")
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
if(expr.operator==">>")
|
||||
if(signed)
|
||||
asmgen.out(" jsr math.lsr_byte_A")
|
||||
@@ -614,7 +613,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
} else {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
|
||||
asmgen.out(" plx")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, true)
|
||||
if(expr.operator==">>")
|
||||
if(signed)
|
||||
asmgen.out(" jsr math.lsr_word_AY")
|
||||
@@ -740,15 +739,31 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
|
||||
asmgen.out(" pla")
|
||||
if(expr.operator=="+")
|
||||
asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
|
||||
else
|
||||
asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes)
|
||||
val rightArrayIndexer = expr.right as? PtArrayIndexer
|
||||
if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
|
||||
// special optimization for bytevalue +/- bytearr[y] : no need to use a tempvar, just use adc array,y or sbc array,y
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla")
|
||||
val arrayvarname = if(rightArrayIndexer.usesPointerVariable)
|
||||
"(${rightArrayIndexer.variable.name})"
|
||||
else
|
||||
asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
else
|
||||
asmgen.out(" sec | sbc $arrayvarname,y")
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes)
|
||||
} else {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
|
||||
else
|
||||
asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -760,20 +775,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
adc P8ZP_SCRATCH_W1+1
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc P8ZP_SCRATCH_W1
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_W1+1
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
|
||||
@@ -785,20 +800,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #<$symbol
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
adc #>$symbol
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc #<$symbol
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
sbc #>$symbol
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
@@ -809,20 +824,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $symname
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
adc $symname+1
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $symname
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
sbc $symname+1
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
@@ -832,20 +847,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #<${right.number.toHex()}
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
adc #>${right.number.toHex()}
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
} else if(expr.operator=="-") {
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc #<${right.number.toHex()}
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
sbc #>${right.number.toHex()}
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
@@ -888,63 +903,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedEqualityExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
// expression datatype is BOOL (ubyte) but operands can be anything
|
||||
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes &&
|
||||
expr.left.isSimple() && expr.right.isSimple()) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
if(expr.operator=="==") {
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
bne +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+""")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false)
|
||||
return true
|
||||
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
|
||||
expr.left.isSimple() && expr.right.isSimple()) {
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
if(expr.operator=="==") {
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedLogicalOrBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
||||
if (expr.right.isSimple()) {
|
||||
@@ -984,9 +942,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
when (expr.operator) {
|
||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | pha | tya | and P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | pha | tya | ora P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | pha | tya | eor P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
@@ -1085,7 +1043,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
// checked OK:
|
||||
private fun byteLess(expr: PtBinaryExpression, signed: Boolean) {
|
||||
// note: this is the inverse of byteGreaterEqual
|
||||
when(expr.right) {
|
||||
@@ -1232,78 +1189,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun byteLessEqualsWRONG(expr: PtBinaryExpression, signed: Boolean) {
|
||||
// note: this is the inverse of byteGreater
|
||||
when(expr.right) {
|
||||
is PtNumber -> {
|
||||
// TODO verify if this is correct code on all corner cases
|
||||
val number = (expr.right as PtNumber).number.toInt()
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc #$number
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
cmp #$number
|
||||
lda #0
|
||||
rol a
|
||||
eor #1""")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
// TODO verify if this is correct code on all corner cases
|
||||
val varname = (expr.right as PtIdentifier).name
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
lda #0
|
||||
rol a
|
||||
eor #1""")
|
||||
}
|
||||
else -> {
|
||||
// TODO verify if this is correct code on all corner cases
|
||||
// note: left and right operands get reversed here to reduce code size
|
||||
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc P8ZP_SCRATCH_B1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
lda #0
|
||||
rol a""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checked OK:
|
||||
private fun byteGreater(expr: PtBinaryExpression, signed: Boolean) {
|
||||
// note: this is the inverse of byteLessEqual
|
||||
when(expr.right) {
|
||||
@@ -1381,7 +1266,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
// checked OK:
|
||||
private fun byteGreaterEquals(expr: PtBinaryExpression, signed: Boolean) {
|
||||
// note: this is the inverse of byteLess
|
||||
when(expr.right) {
|
||||
@@ -1621,18 +1505,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
is PtNumber -> {
|
||||
val number = right.number.toHex()
|
||||
when (operator) {
|
||||
"&", "and" -> asmgen.out(" and #<$number | pha | tya | and #>$number | tay | pla")
|
||||
"|", "or" -> asmgen.out(" ora #<$number | pha | tya | ora #>$number | tay | pla")
|
||||
"^", "xor" -> asmgen.out(" eor #<$number | pha | tya | eor #>$number | tay | pla")
|
||||
"&", "and" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa")
|
||||
"|", "or" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa")
|
||||
"^", "xor" -> asmgen.out(" eor #<$number | tax | tya | eor #>$number | tay | txa")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val name = asmgen.asmSymbolName(right)
|
||||
when (operator) {
|
||||
"&", "and" -> asmgen.out(" and $name | pha | tya | and $name+1 | tay | pla")
|
||||
"|", "or" -> asmgen.out(" ora $name | pha | tya | ora $name+1 | tay | pla")
|
||||
"^", "xor" -> asmgen.out(" eor $name | pha | tya | eor $name+1 | tay | pla")
|
||||
"&", "and" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa")
|
||||
"|", "or" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa")
|
||||
"^", "xor" -> asmgen.out(" eor $name | tax | tya | eor $name+1 | tay | txa")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
}
|
||||
@@ -1642,6 +1526,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
// TODO optimized code for (word1 & word2) == 0 :
|
||||
// if(expr.left.type in WordDatatypes && (expr.operator=="==" || expr.operator=="!=") && expr.left is PtBinaryExpression) {
|
||||
// ...
|
||||
// }
|
||||
|
||||
when (expr.operator) {
|
||||
"==" -> {
|
||||
when(val dt = expr.left.type) {
|
||||
@@ -1649,10 +1538,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
lda #1
|
||||
+ eor #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
@@ -1662,10 +1549,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
sty P8ZP_SCRATCH_B1
|
||||
ora P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
lda #1
|
||||
+ eor #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
@@ -1988,7 +1873,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
|
||||
// No more special optmized cases yet. Do the rest via more complex evaluation
|
||||
// No more special optimized cases yet. Do the rest via more complex evaluation
|
||||
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
||||
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
|
||||
asmgen.assignExpressionTo(origTypeCastExpression, target)
|
||||
@@ -2231,12 +2116,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
if(regs!=RegisterOrPair.AY)
|
||||
throw AssemblyError("only supports AY here")
|
||||
asmgen.out("""
|
||||
pha
|
||||
tax
|
||||
lda #<$targetAsmVarName
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$targetAsmVarName
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
pla
|
||||
txa
|
||||
jsr floats.cast_from_uw""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@@ -2259,12 +2144,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
if(regs!=RegisterOrPair.AY)
|
||||
throw AssemblyError("only supports AY here")
|
||||
asmgen.out("""
|
||||
pha
|
||||
tax
|
||||
lda #<$targetAsmVarName
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$targetAsmVarName
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
pla
|
||||
txa
|
||||
jsr floats.cast_from_w""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@@ -2698,12 +2583,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
pla""")
|
||||
RegisterOrPair.AY -> asmgen.out("""
|
||||
lda $sourceName
|
||||
pha
|
||||
tax
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ tay
|
||||
pla""")
|
||||
txa""")
|
||||
RegisterOrPair.XY -> asmgen.out("""
|
||||
lda $sourceName
|
||||
tax
|
||||
@@ -3906,12 +3791,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
sec
|
||||
eor #255
|
||||
adc #0
|
||||
pha
|
||||
tax
|
||||
tya
|
||||
eor #255
|
||||
adc #0
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.out("""
|
||||
|
||||
+224
-55
@@ -57,9 +57,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
// the asm-gen code can deal with situations where you want to assign a byte into a word.
|
||||
// it will create the most optimized code to do this (so it type-extends for us).
|
||||
// But we can't deal with writing a word into a byte - explicit typeconversion should be done
|
||||
if(program.memsizer.memorySize(value.datatype) > program.memsizer.memorySize(target.datatype)) {
|
||||
TODO("missing type cast: value type > target type ${target.position}")
|
||||
}
|
||||
if(program.memsizer.memorySize(value.datatype) > program.memsizer.memorySize(target.datatype))
|
||||
throw AssemblyError("missing type cast: value type > target type ${target.position}")
|
||||
|
||||
fun regName(v: AsmAssignSource) = "cx16.${v.register!!.name.lowercase()}"
|
||||
|
||||
@@ -163,7 +162,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
else -> {
|
||||
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, memory.definingISub(), target.position, register = RegisterOrPair.AY))
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
@@ -385,8 +384,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
|
||||
// copy array value into tempvar
|
||||
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>${target.asmVarname}
|
||||
clc
|
||||
@@ -395,9 +395,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
iny
|
||||
+ sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
pha ; save array ptr lsb
|
||||
tya
|
||||
pha ; save array ptr msb
|
||||
lda #<$tempvar
|
||||
ldy #>$tempvar
|
||||
jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y
|
||||
jsr floats.copy_float""")
|
||||
|
||||
// calculate on tempvar
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
|
||||
@@ -414,18 +419,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy tempvar back into array
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
pha
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
pha
|
||||
lda #<$tempvar
|
||||
ldy #>$tempvar
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ply
|
||||
pla
|
||||
jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y
|
||||
pla ; restore array ptr msb
|
||||
tay
|
||||
pla ; restore array ptr lsb
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||
}
|
||||
@@ -445,11 +449,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $variable
|
||||
pha
|
||||
tay
|
||||
txa
|
||||
adc $variable+1
|
||||
tax
|
||||
pla""")
|
||||
tya""")
|
||||
true
|
||||
} else {
|
||||
asmgen.out("""
|
||||
@@ -469,11 +473,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $variable
|
||||
pha
|
||||
tay
|
||||
txa
|
||||
sbc $variable+1
|
||||
tax
|
||||
pla""")
|
||||
tya""")
|
||||
true
|
||||
} else {
|
||||
asmgen.out("""
|
||||
@@ -508,11 +512,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #<$number
|
||||
pha
|
||||
tay
|
||||
txa
|
||||
adc #>$number
|
||||
tax
|
||||
pla""")
|
||||
tya""")
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -529,11 +533,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc #<$number
|
||||
pha
|
||||
tay
|
||||
txa
|
||||
sbc #>$number
|
||||
tax
|
||||
pla""")
|
||||
tya""")
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -708,10 +712,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||
val tmpVar = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
|
||||
asmgen.assignExpressionToVariable(value, tmpVar, value.type)
|
||||
asmgen.out(" lda $name")
|
||||
inplacemodificationRegisterAwithVariable(operator, tmpVar, dt in SignedDatatypes)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
|
||||
asmgen.out(" sta $name")
|
||||
}
|
||||
|
||||
@@ -722,15 +724,16 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun inplacemodificationRegisterAwithVariable(operator: String, variable: String, signed: Boolean) {
|
||||
// A = A <operator> variable
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" clc | adc $variable")
|
||||
"-" -> asmgen.out(" sec | sbc $variable")
|
||||
"*" -> asmgen.out(" ldy $variable | jsr math.multiply_bytes")
|
||||
"/" -> {
|
||||
if(signed)
|
||||
asmgen.out(" ldy $variable | jsr math.divmod_b_asm | tya")
|
||||
asmgen.out(" ldy $variable | jsr math.divmod_b_asm | tya")
|
||||
else
|
||||
asmgen.out(" ldy $variable | jsr math.divmod_ub_asm | tya")
|
||||
asmgen.out(" ldy $variable | jsr math.divmod_ub_asm | tya")
|
||||
}
|
||||
"%" -> {
|
||||
if(signed)
|
||||
@@ -792,11 +795,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"<" -> {
|
||||
if(!signed) {
|
||||
asmgen.out("""
|
||||
tay
|
||||
cmp $variable
|
||||
bcc +
|
||||
lda #0
|
||||
cpy $variable
|
||||
rol a
|
||||
eor #1""")
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
@@ -815,10 +819,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"<=" -> {
|
||||
if(!signed) {
|
||||
asmgen.out("""
|
||||
tay
|
||||
cmp $variable
|
||||
bcc +
|
||||
beq +
|
||||
lda #0
|
||||
ldy $variable
|
||||
rol a""")
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
@@ -881,6 +888,164 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationRegisterAwithVariableWithSwappedOperands(operator: String, variable: String, signed: Boolean) {
|
||||
// A = variable <operator> A
|
||||
|
||||
if(operator in AssociativeOperators)
|
||||
return inplacemodificationRegisterAwithVariable(operator, variable, signed) // just reuse existing code for associative operators
|
||||
|
||||
// now implement the non-assiciative operators...
|
||||
when (operator) {
|
||||
"-" -> {
|
||||
// A = variable - A
|
||||
val tmpVar = if(variable!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
|
||||
asmgen.out(" sta $tmpVar | lda $variable | sec | sbc $tmpVar")
|
||||
}
|
||||
"/" -> {
|
||||
if(signed)
|
||||
asmgen.out(" tay | lda $variable | jsr math.divmod_b_asm | tya")
|
||||
else
|
||||
asmgen.out(" tay | lda $variable | jsr math.divmod_ub_asm | tya")
|
||||
}
|
||||
"%" -> {
|
||||
if(signed)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" tay | lda $variable | jsr math.divmod_ub_asm")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
tay
|
||||
beq +
|
||||
lda $variable
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
">>" -> {
|
||||
if(!signed) {
|
||||
asmgen.out("""
|
||||
tay
|
||||
beq +
|
||||
lda $variable
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
+""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
tay
|
||||
beq +
|
||||
lda $variable
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- asl a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
dey
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"<" -> {
|
||||
// variable<A --> A>variable?
|
||||
if(!signed) {
|
||||
asmgen.out("""
|
||||
tay
|
||||
lda #0
|
||||
cpy $variable
|
||||
beq +
|
||||
rol a
|
||||
+""")
|
||||
}
|
||||
else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $variable
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"<=" -> {
|
||||
// variable<=A --> A>=variable?
|
||||
if(!signed) {
|
||||
asmgen.out("""
|
||||
tay
|
||||
lda #0
|
||||
cpy $variable
|
||||
rol a""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $variable
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
}
|
||||
">" -> {
|
||||
// variable>A --> A<variable?
|
||||
if(!signed) {
|
||||
asmgen.out("""
|
||||
cmp $variable
|
||||
bcc +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $variable
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
}
|
||||
">=" -> {
|
||||
// variable>=A --> A<=variable?
|
||||
if(!signed) {
|
||||
asmgen.out("""
|
||||
cmp $variable
|
||||
bcc +
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $variable
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationByteVariableWithLiteralval(name: String, dt: DataType, operator: String, value: Int) {
|
||||
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
||||
when (operator) {
|
||||
@@ -1080,6 +1245,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
"-" -> {
|
||||
// name -= @(memory)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
|
||||
asmgen.out("""
|
||||
@@ -1088,7 +1254,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
sec
|
||||
sbc $tmpByte
|
||||
sta $name
|
||||
bcc +
|
||||
bcs +
|
||||
dec $name+1
|
||||
+""")
|
||||
}
|
||||
@@ -1178,9 +1344,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sta math.multiply_words.multiplier
|
||||
lda $name+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
sta math.multiply_words.multiplier+1
|
||||
lda #<$value
|
||||
ldy #>$value
|
||||
jsr math.multiply_words
|
||||
@@ -1629,15 +1795,15 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
"*" -> {
|
||||
if(valueDt==DataType.UBYTE) {
|
||||
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" lda $otherName | sta math.multiply_words.multiplier")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" stz math.multiply_words.multiplier+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda #0 | sta math.multiply_words.multiplier+1")
|
||||
} else {
|
||||
asmgen.out(" lda $otherName")
|
||||
asmgen.signExtendAYlsb(valueDt)
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" sta math.multiply_words.multiplier | sty math.multiply_words.multiplier+1")
|
||||
}
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
@@ -1776,8 +1942,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
ldy $otherName+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
sta math.multiply_words.multiplier
|
||||
sty math.multiply_words.multiplier+1
|
||||
lda $name
|
||||
ldy $name+1
|
||||
jsr math.multiply_words
|
||||
@@ -1963,8 +2129,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||
fun multiplyVarByWordInAY() {
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
sta math.multiply_words.multiplier
|
||||
sty math.multiply_words.multiplier+1
|
||||
lda $name
|
||||
ldy $name+1
|
||||
jsr math.multiply_words
|
||||
@@ -1975,12 +2141,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
fun divideVarByWordInAY() {
|
||||
asmgen.out("""
|
||||
pha
|
||||
tax
|
||||
lda $name
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda $name+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla""")
|
||||
txa""")
|
||||
if (dt == DataType.WORD)
|
||||
asmgen.out(" jsr math.divmod_w_asm")
|
||||
else
|
||||
@@ -1992,12 +2158,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(dt==DataType.WORD)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out("""
|
||||
pha
|
||||
tax
|
||||
lda $name
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda $name+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
txa
|
||||
jsr math.divmod_uw_asm
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
@@ -2011,20 +2177,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
// the other variable is a BYTE type so optimize for that
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt)
|
||||
if(valueDt==DataType.UBYTE)
|
||||
// name += byteexpression
|
||||
if(valueDt==DataType.UBYTE) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
adc $name
|
||||
sta $name
|
||||
bcc +
|
||||
inc $name+1
|
||||
+""")
|
||||
else
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, true)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda P8ZP_SCRATCH_B1
|
||||
cmp #0
|
||||
bpl +
|
||||
dey ; sign extend
|
||||
+ clc
|
||||
@@ -2033,8 +2200,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
tya
|
||||
adc $name+1
|
||||
sta $name+1""")
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
// name -= byteexpression
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt)
|
||||
if(valueDt==DataType.UBYTE)
|
||||
asmgen.out("""
|
||||
|
||||
@@ -45,10 +45,28 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"rol2" -> funcRolRor(Opcode.ROL, call)
|
||||
"ror2" -> funcRolRor(Opcode.ROR, call)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
|
||||
return if(resultType==IRDataType.FLOAT) {
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null)
|
||||
ExpressionCodeResult(result, resultType, -1, resultFpReg)
|
||||
}
|
||||
else {
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null)
|
||||
ExpressionCodeResult(result, resultType, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
@@ -531,6 +549,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
return exprGen.translateExpression(call.args.single())
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
// TODO to be more strict, maybe we *should* introduce a new result register that is of type .b?
|
||||
}
|
||||
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
|
||||
@@ -628,5 +628,4 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -384,7 +384,8 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
override fun before(unrollLoop: UnrollLoop, parent: Node): Iterable<IAstModification> {
|
||||
return if(unrollLoop.iterations<1)
|
||||
val iterations = unrollLoop.iterations.constValue(program)?.number?.toInt()
|
||||
return if(iterations!=null && iterations<1)
|
||||
listOf(IAstModification.Remove(unrollLoop, parent as IStatementContainer))
|
||||
else
|
||||
noModifications
|
||||
|
||||
@@ -32,7 +32,7 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
; (assumes screen and color matrix are at their default addresses)
|
||||
; TODO
|
||||
@@ -42,7 +42,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
; TODO
|
||||
@@ -114,10 +114,10 @@ asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
}
|
||||
|
||||
|
||||
romsub $F2B0 = outchar(ubyte char @ A)
|
||||
romsub $F2B0 = outchar(ubyte character @ A)
|
||||
romsub $F2Fd = waitkey()
|
||||
|
||||
asmsub chrout(ubyte char @ A) {
|
||||
asmsub chrout(ubyte character @ A) {
|
||||
%asm {{
|
||||
sta _tmp_outchar+1
|
||||
txa
|
||||
@@ -364,7 +364,7 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte char, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; TODO
|
||||
%asm {{
|
||||
|
||||
@@ -87,7 +87,7 @@ romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD2 = CHROUT(ubyte character @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
@@ -335,7 +335,7 @@ asmsub init_system() {
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||
; Uppercase charset is activated.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-128
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
@@ -466,7 +466,7 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word $0400 + range(0, 1000, 40)
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -533,7 +533,7 @@ _mod lda $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
lda row
|
||||
@@ -545,13 +545,13 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sta _colormod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc column
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
+ lda char
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
|
||||
@@ -418,8 +418,9 @@ pushFAC1 .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
popFAC1 .proc
|
||||
; -- pop floating point value from cpu stack into FAC1
|
||||
popFAC .proc
|
||||
; -- pop floating point value from cpu stack into FAC1 or FAC2 (
|
||||
; carry flag clear=FAC1, carry set=FAC2
|
||||
; save return address
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2
|
||||
@@ -437,8 +438,11 @@ popFAC1 .proc
|
||||
sta floats.floats_temp_var
|
||||
lda #<floats.floats_temp_var
|
||||
ldy #>floats.floats_temp_var
|
||||
bcs +
|
||||
jsr floats.MOVFM
|
||||
; re-push return address
|
||||
jmp ++
|
||||
+ jsr floats.CONUPK
|
||||
+ ; re-push return address
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
pha
|
||||
lda P8ZP_SCRATCH_W2
|
||||
|
||||
@@ -46,7 +46,7 @@ romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||
romsub $bc3c = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length
|
||||
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length. Also see parse_f()
|
||||
romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||
romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100)
|
||||
|
||||
@@ -165,6 +165,16 @@ sub rndf() -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub parse_f(str value @AY) -> float @FAC1 {
|
||||
%asm {{
|
||||
sta $22
|
||||
sty $23
|
||||
jsr prog8_lib.strlen
|
||||
tya
|
||||
jmp FREADSTR
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
%asminclude "library:c64/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
@@ -1,35 +1,42 @@
|
||||
%import textio
|
||||
|
||||
; bitmap pixel graphics module for the C64
|
||||
; only black/white monochrome 320x200 for now
|
||||
; assumes bitmap screen memory is $2000-$3fff
|
||||
;
|
||||
; Sets character matrix and bitmap screen memory at a higher memory location $5c00-$7fff
|
||||
; so that the program itself can be larger without starting to overwrite the graphics memory.
|
||||
|
||||
graphics {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const uword BITMAP_ADDRESS = $2000
|
||||
const uword WIDTH = 320
|
||||
const ubyte HEIGHT = 200
|
||||
|
||||
const uword BITMAP_ADDRESS = $6000 ; MUST BE IN REGULAR RAM if you are not messing with ROM/RAM banking.
|
||||
const uword CHARS_ADDRESS = $5c00 ; must be in same vic bank as the bitmap
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
c64.SCROLY = %00111011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||
clear_screen(1, 0)
|
||||
c64.SCROLY = %00111011 ; enable bitmap graphics mode
|
||||
c64.SCROLX = %00001000 ; 40 column mode, no scrolling, multicolor bitmap off
|
||||
c64.VMCSB = (lsb(CHARS_ADDRESS >> 6) & $F0) | (((BITMAP_ADDRESS & $3fff) / $0800) << 1) ; set bitmap address
|
||||
c64.CIA2DDRA |= %11
|
||||
c64.CIA2PRA = lsb(BITMAP_ADDRESS >> 14) ^ 3 ; set VIC bank.
|
||||
}
|
||||
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
c64.SCROLY = %00011011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
|
||||
txt.fill_screen(' ', 1)
|
||||
; enables erase the text screen, text mode
|
||||
sys.memset(CHARS_ADDRESS, 40*25, sc:' ')
|
||||
c64.SCROLY = %00011011 ; disable bitmap graphics mode
|
||||
c64.SCROLX = %00001000 ; 40 column mode, no scrolling
|
||||
c64.VMCSB = %00010100 ; screen addresses back to defaults
|
||||
c64.CIA2DDRA |= %11
|
||||
c64.CIA2PRA = %11 ; back to VIC bank 0.
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||
sys.memset(BITMAP_ADDRESS, 320*200/8, 0)
|
||||
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
||||
sys.memset(CHARS_ADDRESS, 40*25, pixelcolor << 4 | bgcolor)
|
||||
sys.memset(cbm.Colors, 40*25, 255)
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||
@@ -127,46 +134,46 @@ graphics {
|
||||
}
|
||||
}
|
||||
|
||||
sub rect(uword x, ubyte y, uword width, ubyte height) {
|
||||
sub rect(uword xx, ubyte yy, uword width, ubyte height) {
|
||||
if width==0 or height==0
|
||||
return
|
||||
horizontal_line(x, y, width)
|
||||
horizontal_line(xx, yy, width)
|
||||
if height==1
|
||||
return
|
||||
horizontal_line(x, y+height-1, width)
|
||||
vertical_line(x, y+1, height-2)
|
||||
horizontal_line(xx, yy+height-1, width)
|
||||
vertical_line(xx, yy+1, height-2)
|
||||
if width==1
|
||||
return
|
||||
vertical_line(x+width-1, y+1, height-2)
|
||||
vertical_line(xx+width-1, yy+1, height-2)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, ubyte y, uword width, ubyte height) {
|
||||
sub fillrect(uword xx, ubyte yy, uword width, ubyte height) {
|
||||
if width==0
|
||||
return
|
||||
repeat height {
|
||||
horizontal_line(x, y, width)
|
||||
y++
|
||||
horizontal_line(xx, yy, width)
|
||||
yy++
|
||||
}
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, ubyte y, uword length) {
|
||||
sub horizontal_line(uword xx, ubyte yy, uword length) {
|
||||
if length<8 {
|
||||
internal_plotx=x
|
||||
internal_plotx=xx
|
||||
repeat lsb(length) {
|
||||
internal_plot(y)
|
||||
internal_plot(yy)
|
||||
internal_plotx++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ubyte separate_pixels = lsb(x) & 7
|
||||
uword addr = get_y_lookup(y) + (x&$fff8)
|
||||
ubyte separate_pixels = lsb(xx) & 7
|
||||
uword pixaddr = get_y_lookup(yy) + (xx&$fff8)
|
||||
|
||||
if separate_pixels {
|
||||
%asm {{
|
||||
lda addr
|
||||
lda pixaddr
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda addr+1
|
||||
lda pixaddr+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
ldy separate_pixels
|
||||
lda hline_filled_right,y
|
||||
@@ -175,7 +182,7 @@ graphics {
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
}}
|
||||
addr += 8
|
||||
pixaddr += 8
|
||||
length += separate_pixels
|
||||
length -= 8
|
||||
}
|
||||
@@ -191,9 +198,9 @@ graphics {
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda addr
|
||||
lda pixaddr
|
||||
sta _modified+1
|
||||
lda addr+1
|
||||
lda pixaddr+1
|
||||
sta _modified+2
|
||||
lda length
|
||||
ora length+1
|
||||
@@ -227,11 +234,11 @@ hline_zero2
|
||||
}
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, ubyte y, ubyte height) {
|
||||
internal_plotx = x
|
||||
sub vertical_line(uword xx, ubyte yy, ubyte height) {
|
||||
internal_plotx = xx
|
||||
repeat height {
|
||||
internal_plot(y)
|
||||
y++
|
||||
internal_plot(yy)
|
||||
yy++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,8 +307,8 @@ hline_zero2
|
||||
; here is the non-asm code for the plot routine below:
|
||||
; sub plot_nonasm(uword px, ubyte py) {
|
||||
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
; uword addr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||
; @(addr) |= ormask[lsb(px) & 7]
|
||||
; uword pixaddr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||
; @(pixaddr) |= ormask[lsb(px) & 7]
|
||||
; }
|
||||
|
||||
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
||||
@@ -352,7 +359,7 @@ _ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||
|
||||
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
||||
_plot_y_values := BITMAP_ADDRESS + 320*(range(200)>>3) + (range(200) & 7)
|
||||
|
||||
_y_lookup_lo .byte <_plot_y_values
|
||||
_y_lookup_hi .byte >_plot_y_values
|
||||
@@ -360,7 +367,7 @@ _y_lookup_hi .byte >_plot_y_values
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_y_lookup(ubyte y @Y) -> uword @AY {
|
||||
asmsub get_y_lookup(ubyte yy @Y) -> uword @AY {
|
||||
%asm {{
|
||||
lda internal_plot._y_lookup_lo,y
|
||||
pha
|
||||
|
||||
@@ -91,7 +91,7 @@ romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD2 = CHROUT(ubyte character @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
@@ -136,6 +136,7 @@ c64 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
; (depending on the VIC bank and screen ram address selection these can be shifted around though)
|
||||
&ubyte SPRPTR0 = 2040
|
||||
&ubyte SPRPTR1 = 2041
|
||||
&ubyte SPRPTR2 = 2042
|
||||
@@ -300,7 +301,7 @@ asmsub init_system() {
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||
; Uppercase charset is activated.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
|
||||
@@ -39,7 +39,7 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
; (assumes screen and color matrix are at their default addresses)
|
||||
|
||||
@@ -54,7 +54,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
@@ -237,7 +237,7 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
romsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
@@ -465,7 +465,7 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word $0400 + range(0, 1000, 40)
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ _mod lda $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
lda row
|
||||
@@ -544,13 +544,13 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sta _colormod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc column
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
+ lda char
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
|
||||
@@ -43,12 +43,12 @@ diskio {
|
||||
ubyte high = cbm.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.spc()
|
||||
ubyte @zp char
|
||||
ubyte @zp character
|
||||
repeat {
|
||||
char = cbm.CHRIN()
|
||||
if char==0
|
||||
character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
txt.chrout(char)
|
||||
txt.chrout(character)
|
||||
}
|
||||
txt.nl()
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
@@ -209,12 +209,12 @@ io_error:
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = cbm.CHRIN()
|
||||
if char==0
|
||||
ubyte character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
if char=='\"'
|
||||
if character=='\"'
|
||||
break
|
||||
@(nameptr) = char
|
||||
@(nameptr) = character
|
||||
nameptr++
|
||||
}
|
||||
|
||||
@@ -297,19 +297,19 @@ close_end:
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
|
||||
; commander X16 supports fast block-read via macptr() kernal call
|
||||
uword size
|
||||
uword readsize
|
||||
while num_bytes {
|
||||
size = 255
|
||||
if num_bytes<size
|
||||
size = num_bytes
|
||||
size = cx16.macptr(lsb(size), bufferpointer, false)
|
||||
readsize = 255
|
||||
if num_bytes<readsize
|
||||
readsize = num_bytes
|
||||
readsize = cx16.macptr(lsb(readsize), bufferpointer, false)
|
||||
if_cs
|
||||
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
||||
list_blocks += size
|
||||
bufferpointer += size
|
||||
list_blocks += readsize
|
||||
bufferpointer += readsize
|
||||
if msb(bufferpointer) == $c0
|
||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||
num_bytes -= size
|
||||
num_bytes -= readsize
|
||||
if cbm.READST() & $40 {
|
||||
f_close() ; end of file, close it
|
||||
break
|
||||
@@ -470,25 +470,25 @@ io_error:
|
||||
|
||||
|
||||
; saves a block of memory to disk, including the default 2 byte prg header.
|
||||
sub save(uword filenameptr, uword address, uword size) -> bool {
|
||||
return internal_save_routine(filenameptr, address, size, false)
|
||||
sub save(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, false)
|
||||
}
|
||||
|
||||
; like save() but omits the 2 byte prg header.
|
||||
sub save_raw(uword filenameptr, uword address, uword size) -> bool {
|
||||
return internal_save_routine(filenameptr, address, size, true)
|
||||
sub save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, true)
|
||||
}
|
||||
|
||||
sub internal_save_routine(uword filenameptr, uword address, uword size, bool headerless) -> bool {
|
||||
sub internal_save_routine(uword filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = address + size
|
||||
uword @shared end_address = startaddress + savesize
|
||||
cx16.r0L = 0
|
||||
|
||||
%asm {{
|
||||
lda address
|
||||
lda startaddress
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
lda startaddress+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
@@ -532,8 +532,8 @@ io_error:
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details.
|
||||
sub load_raw(uword filenameptr, uword address) -> uword {
|
||||
return internal_load_routine(filenameptr, address, true)
|
||||
sub load_raw(uword filenameptr, uword startaddress) -> uword {
|
||||
return internal_load_routine(filenameptr, startaddress, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -606,7 +606,7 @@ io_error:
|
||||
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
||||
}
|
||||
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) clobbers(X, Y) -> ubyte @A {
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> ubyte @A {
|
||||
; -- like the basic command VLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file has to have the usual 2 byte header (which will be skipped)
|
||||
@@ -645,7 +645,7 @@ internal_vload:
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @R1) clobbers(X, Y) -> ubyte @A {
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> ubyte @A {
|
||||
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file is read fully including the first two bytes.
|
||||
|
||||
@@ -28,7 +28,7 @@ romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101
|
||||
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
; romsub $fe09 = VAL_1() clobbers(A,X,Y) ; convert ASCII string to floating point [not yet implemented!!!]
|
||||
; romsub $fe09 = VAL_1() clobbers(A,X,Y) ; convert ASCII string to floating point [not yet implemented!!!] see parse_f() instead
|
||||
|
||||
; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
@@ -136,6 +136,19 @@ asmsub FREADUY (ubyte value @Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub parse_f(str value @AY) -> float @FAC1 {
|
||||
; -- parse a string value of a number to float in FAC1
|
||||
; warning: uses an internal BASIC routine that may be rom version dependent
|
||||
; ($ddf2 is inside the routine for VAL at $ddef)
|
||||
%asm {{
|
||||
sta $a9
|
||||
sty $aa
|
||||
jsr prog8_lib.strlen
|
||||
tya
|
||||
jmp $ddf2
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT
|
||||
|
||||
|
||||
+118
-118
@@ -133,29 +133,29 @@ gfx2 {
|
||||
monochrome_dont_stipple_flag = not enable
|
||||
}
|
||||
|
||||
sub rect(uword x, uword y, uword rwidth, uword rheight, ubyte color) {
|
||||
sub rect(uword xx, uword yy, uword rwidth, uword rheight, ubyte color) {
|
||||
if rwidth==0 or rheight==0
|
||||
return
|
||||
horizontal_line(x, y, rwidth, color)
|
||||
horizontal_line(xx, yy, rwidth, color)
|
||||
if rheight==1
|
||||
return
|
||||
horizontal_line(x, y+rheight-1, rwidth, color)
|
||||
vertical_line(x, y+1, rheight-2, color)
|
||||
horizontal_line(xx, yy+rheight-1, rwidth, color)
|
||||
vertical_line(xx, yy+1, rheight-2, color)
|
||||
if rwidth==1
|
||||
return
|
||||
vertical_line(x+rwidth-1, y+1, rheight-2, color)
|
||||
vertical_line(xx+rwidth-1, yy+1, rheight-2, color)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, uword y, uword rwidth, uword rheight, ubyte color) {
|
||||
sub fillrect(uword xx, uword yy, uword rwidth, uword rheight, ubyte color) {
|
||||
if rwidth==0
|
||||
return
|
||||
repeat rheight {
|
||||
horizontal_line(x, y, rwidth, color)
|
||||
y++
|
||||
horizontal_line(xx, yy, rwidth, color)
|
||||
yy++
|
||||
}
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, uword y, uword length, ubyte color) {
|
||||
sub horizontal_line(uword xx, uword yy, uword length, ubyte color) {
|
||||
ubyte[9] masked_starts = [ 0, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111]
|
||||
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
|
||||
|
||||
@@ -164,30 +164,30 @@ gfx2 {
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome modes, either resolution
|
||||
ubyte separate_pixels = (8-lsb(x)) & 7
|
||||
ubyte separate_pixels = (8-lsb(xx)) & 7
|
||||
if separate_pixels as uword > length
|
||||
separate_pixels = lsb(length)
|
||||
if separate_pixels {
|
||||
if monochrome_dont_stipple_flag {
|
||||
position(x,y)
|
||||
position(xx,yy)
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
|
||||
if color
|
||||
cx16.VERA_DATA0 |= masked_starts[separate_pixels]
|
||||
else
|
||||
cx16.VERA_DATA0 &= ~masked_starts[separate_pixels]
|
||||
x += separate_pixels
|
||||
xx += separate_pixels
|
||||
} else {
|
||||
repeat separate_pixels {
|
||||
plot(x, y, color)
|
||||
x++
|
||||
plot(xx, yy, color)
|
||||
xx++
|
||||
}
|
||||
}
|
||||
length -= separate_pixels
|
||||
}
|
||||
if length {
|
||||
position(x, y)
|
||||
position(xx, yy)
|
||||
separate_pixels = lsb(length) & 7
|
||||
x += length & $fff8
|
||||
xx += length & $fff8
|
||||
%asm {{
|
||||
lsr length+1
|
||||
ror length
|
||||
@@ -203,7 +203,7 @@ gfx2 {
|
||||
beq _stipple
|
||||
ldy #255 ; don't stipple
|
||||
bra _loop
|
||||
_stipple lda y
|
||||
_stipple lda yy
|
||||
and #1 ; determine stipple pattern to use
|
||||
bne +
|
||||
ldy #%01010101
|
||||
@@ -229,8 +229,8 @@ _done
|
||||
cx16.VERA_DATA0 &= ~masked_ends[separate_pixels]
|
||||
} else {
|
||||
repeat separate_pixels {
|
||||
plot(x, y, color)
|
||||
x++
|
||||
plot(xx, yy, color)
|
||||
xx++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +238,7 @@ _done
|
||||
}
|
||||
4 -> {
|
||||
; lores 256c
|
||||
position(x, y)
|
||||
position(xx, yy)
|
||||
%asm {{
|
||||
lda color
|
||||
ldx length+1
|
||||
@@ -266,7 +266,7 @@ _done
|
||||
colorbits[ii] = color
|
||||
color <<= 2
|
||||
}
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
lda cx16.VERA_ADDR_H
|
||||
and #%00000111 ; no auto advance
|
||||
@@ -279,7 +279,7 @@ _done
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
ldx x
|
||||
ldx xx
|
||||
}}
|
||||
|
||||
repeat length {
|
||||
@@ -305,15 +305,15 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword lheight, ubyte color) {
|
||||
sub vertical_line(uword xx, uword yy, uword lheight, ubyte color) {
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome, lo-res
|
||||
cx16.r15L = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||
cx16.r15L = gfx2.plot.maskbits[xx as ubyte & 7] ; bitmask
|
||||
if color {
|
||||
if monochrome_dont_stipple_flag {
|
||||
; draw continuous line.
|
||||
position2(x,y,true)
|
||||
position2(xx,yy,true)
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
@@ -327,11 +327,11 @@ _done
|
||||
}
|
||||
} else {
|
||||
; draw stippled line.
|
||||
if x&1 {
|
||||
y++
|
||||
if xx&1 {
|
||||
yy++
|
||||
lheight--
|
||||
}
|
||||
position2(x,y,true)
|
||||
position2(xx,yy,true)
|
||||
if active_mode==1
|
||||
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
|
||||
else
|
||||
@@ -345,7 +345,7 @@ _done
|
||||
}
|
||||
}
|
||||
} else {
|
||||
position2(x,y,true)
|
||||
position2(xx,yy,true)
|
||||
cx16.r15 = ~cx16.r15 ; erase pixels
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
@@ -363,7 +363,7 @@ _done
|
||||
4 -> {
|
||||
; lores 256c
|
||||
; set vera auto-increment to 320 pixel increment (=next line)
|
||||
position(x,y)
|
||||
position(xx,yy)
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
||||
%asm {{
|
||||
ldy lheight
|
||||
@@ -380,17 +380,17 @@ _done
|
||||
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
|
||||
if lheight==0
|
||||
return
|
||||
position2(x,y,true)
|
||||
position2(xx,yy,true)
|
||||
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||
;; color &= 3
|
||||
;; color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||
cx16.r2L = lsb(x) & 3
|
||||
;; color <<= gfx2.plot.shift4c[lsb(xx) & 3]
|
||||
cx16.r2L = lsb(xx) & 3
|
||||
when color & 3 {
|
||||
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
|
||||
}
|
||||
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||
ubyte @shared mask = gfx2.plot.mask4c[lsb(xx) & 3]
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
@@ -569,8 +569,8 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub plot(uword @zp x, uword @zp y, ubyte @zp color) {
|
||||
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
sub plot(uword @zp xx, uword @zp yy, ubyte @zp color) {
|
||||
ubyte[8] @shared maskbits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
|
||||
ubyte[4] @shared shift4c = [6,4,2,0]
|
||||
ubyte[4] shiftedleft_4c_1 = [1<<6, 1<<4, 1<<2, 1<<0]
|
||||
@@ -583,8 +583,8 @@ _done
|
||||
if color {
|
||||
; solid color or perhaps stipple
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
lda xx
|
||||
eor yy
|
||||
ora monochrome_dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
@@ -605,7 +605,7 @@ _done
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1
|
||||
@@ -624,8 +624,8 @@ _done
|
||||
if color {
|
||||
; solid color or perhaps stipple
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
lda xx
|
||||
eor yy
|
||||
ora monochrome_dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
@@ -645,8 +645,8 @@ _done
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c ....also mostly usable for mode 2, lores 4c?
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = lsb(x) & 3 ; xbits
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = lsb(xx) & 3 ; xbits
|
||||
; color &= 3
|
||||
; color <<= shift4c[cx16.r2L]
|
||||
when color & 3 {
|
||||
@@ -673,65 +673,65 @@ _done
|
||||
|
||||
sub mode_1_prepare() {
|
||||
%asm {{
|
||||
lda x
|
||||
lda xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(320/8)
|
||||
xx /= 8
|
||||
xx += yy*(320/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
lda xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
lda xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda bits,y
|
||||
lda maskbits,y
|
||||
}}
|
||||
}
|
||||
|
||||
sub mode_5_prepare() {
|
||||
%asm {{
|
||||
lda x
|
||||
lda xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(640/8)
|
||||
xx /= 8
|
||||
xx += yy*(640/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
lda xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
lda xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda bits,y
|
||||
lda maskbits,y
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
sub pget(uword @zp x, uword y) -> ubyte {
|
||||
sub pget(uword @zp xx, uword yy) -> ubyte {
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
lda xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(320/8)
|
||||
xx /= 8
|
||||
xx += yy*(320/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
lda xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
lda xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda plot.bits,y
|
||||
lda plot.maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
beq +
|
||||
lda #1
|
||||
@@ -741,7 +741,7 @@ _done
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1
|
||||
@@ -756,21 +756,21 @@ _done
|
||||
5 -> {
|
||||
; hires monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
lda xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(640/8)
|
||||
xx /= 8
|
||||
xx += yy*(640/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
lda xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
lda xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda plot.bits,y
|
||||
lda plot.maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
beq +
|
||||
lda #1
|
||||
@@ -779,7 +779,7 @@ _done
|
||||
}
|
||||
6 -> {
|
||||
; hires 4c
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1L
|
||||
@@ -791,7 +791,7 @@ _done
|
||||
lda cx16.VERA_DATA0
|
||||
sta cx16.r0L
|
||||
}}
|
||||
cx16.r1L = lsb(x) & 3
|
||||
cx16.r1L = lsb(xx) & 3
|
||||
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L]
|
||||
return cx16.r0L & 3
|
||||
}
|
||||
@@ -799,7 +799,7 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub fill(word @zp x, word @zp y, ubyte new_color) {
|
||||
sub fill(word @zp xx, word @zp yy, ubyte new_color) {
|
||||
; Non-recursive scanline flood fill.
|
||||
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
|
||||
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
|
||||
@@ -862,85 +862,85 @@ _done
|
||||
lda stack_xr_msb,y
|
||||
sta x2+1
|
||||
lda stack_y_lsb,y
|
||||
sta y
|
||||
sta yy
|
||||
lda stack_y_msb,y
|
||||
sta y+1
|
||||
sta yy+1
|
||||
ldy cx16.r12L
|
||||
lda stack_dy,y
|
||||
sta dy
|
||||
}}
|
||||
y+=dy
|
||||
yy+=dy
|
||||
}
|
||||
cx16.r11L = pget(x as uword, y as uword) ; old_color
|
||||
cx16.r11L = pget(xx as uword, yy as uword) ; old_color
|
||||
if cx16.r11L == cx16.r10L
|
||||
return
|
||||
if x<0 or x > width-1 or y<0 or y > height-1
|
||||
if xx<0 or xx>width-1 or yy<0 or yy>height-1
|
||||
return
|
||||
push_stack(x, x, y, 1)
|
||||
push_stack(x, x, y + 1, -1)
|
||||
push_stack(xx, xx, yy, 1)
|
||||
push_stack(xx, xx, yy + 1, -1)
|
||||
word left = 0
|
||||
while cx16.r12L {
|
||||
pop_stack()
|
||||
x = x1
|
||||
while x >= 0 and pget(x as uword, y as uword) == cx16.r11L {
|
||||
plot(x as uword, y as uword, cx16.r10L)
|
||||
x--
|
||||
xx = x1
|
||||
while xx >= 0 and pget(xx as uword, yy as uword) == cx16.r11L {
|
||||
plot(xx as uword, yy as uword, cx16.r10L)
|
||||
xx--
|
||||
}
|
||||
if x>= x1
|
||||
if xx >= x1
|
||||
goto skip
|
||||
|
||||
left = x + 1
|
||||
left = xx + 1
|
||||
if left < x1
|
||||
push_stack(left, x1 - 1, y, -dy)
|
||||
x = x1 + 1
|
||||
push_stack(left, x1 - 1, yy, -dy)
|
||||
xx = x1 + 1
|
||||
|
||||
do {
|
||||
while x <= width-1 and pget(x as uword, y as uword) == cx16.r11L {
|
||||
plot(x as uword, y as uword, cx16.r10L)
|
||||
x++
|
||||
while xx <= width-1 and pget(xx as uword, yy as uword) == cx16.r11L {
|
||||
plot(xx as uword, yy as uword, cx16.r10L)
|
||||
xx++
|
||||
}
|
||||
push_stack(left, x - 1, y, dy)
|
||||
if x > x2 + 1
|
||||
push_stack(x2 + 1, x - 1, y, -dy)
|
||||
push_stack(left, xx - 1, yy, dy)
|
||||
if xx > x2 + 1
|
||||
push_stack(x2 + 1, xx - 1, yy, -dy)
|
||||
skip:
|
||||
x++
|
||||
while x <= x2 and pget(x as uword, y as uword) != cx16.r11L
|
||||
x++
|
||||
left = x
|
||||
} until x>x2
|
||||
xx++
|
||||
while xx <= x2 and pget(xx as uword, yy as uword) != cx16.r11L
|
||||
xx++
|
||||
left = xx
|
||||
} until xx>x2
|
||||
}
|
||||
}
|
||||
|
||||
sub position(uword @zp x, uword y) {
|
||||
sub position(uword @zp xx, uword yy) {
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
cx16.r0 = y*(320/8) + x/8
|
||||
cx16.r0 = yy*(320/8) + xx/8
|
||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||
}
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = cx16.r1L
|
||||
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
cx16.r0 = y*(640/8) + x/8
|
||||
cx16.r0 = yy*(640/8) + xx/8
|
||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = cx16.r1L
|
||||
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub position2(uword @zp x, uword y, bool also_port_1) {
|
||||
position(x, y)
|
||||
sub position2(uword @zp xx, uword yy, bool also_port_1) {
|
||||
position(xx, yy)
|
||||
if also_port_1
|
||||
cx16.vaddr_clone(0)
|
||||
}
|
||||
@@ -1011,7 +1011,7 @@ skip:
|
||||
cx16.screen_set_charset(charset, 0)
|
||||
}
|
||||
|
||||
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, uword sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
uword chardataptr
|
||||
@@ -1028,7 +1028,7 @@ skip:
|
||||
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
|
||||
%asm {{
|
||||
; pre-shift the bits
|
||||
lda text.x
|
||||
lda text.xx
|
||||
and #7
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
@@ -1049,7 +1049,7 @@ skip:
|
||||
bne --
|
||||
}}
|
||||
; left part of shifted char
|
||||
position2(x, y, true)
|
||||
position2(xx, yy, true)
|
||||
set_autoincrs_mode1_or_5()
|
||||
if color {
|
||||
%asm {{
|
||||
@@ -1074,8 +1074,8 @@ skip:
|
||||
}}
|
||||
}
|
||||
; right part of shifted char
|
||||
if lsb(x) & 7 {
|
||||
position2(x+8, y, true)
|
||||
if lsb(xx) & 7 {
|
||||
position2(xx+8, yy, true)
|
||||
set_autoincrs_mode1_or_5()
|
||||
if color {
|
||||
%asm {{
|
||||
@@ -1101,7 +1101,7 @@ skip:
|
||||
}
|
||||
}
|
||||
cx16.r3++
|
||||
x += 8
|
||||
xx += 8
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
@@ -1110,8 +1110,8 @@ skip:
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||
repeat 8 {
|
||||
position(x,y)
|
||||
y++
|
||||
position(xx,yy)
|
||||
yy++
|
||||
%asm {{
|
||||
ldx color
|
||||
lda cx16.VERA_DATA1
|
||||
@@ -1126,8 +1126,8 @@ skip:
|
||||
bne -
|
||||
}}
|
||||
}
|
||||
x+=8
|
||||
y-=8
|
||||
xx+=8
|
||||
yy-=8
|
||||
sctextptr++
|
||||
}
|
||||
}
|
||||
@@ -1138,11 +1138,11 @@ skip:
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
|
||||
position(x, y) ; only calculated once, we update vera address in the loop instead
|
||||
position(xx, yy) ; only calculated once, we update vera address in the loop instead
|
||||
cx16.VERA_ADDR_H &= $0f ; no auto increment
|
||||
repeat 8 {
|
||||
cx16.r10L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
|
||||
cx16.r7 = x
|
||||
cx16.r7 = xx
|
||||
repeat 8 {
|
||||
cx16.r10L <<= 1
|
||||
if_cs {
|
||||
@@ -1189,7 +1189,7 @@ skip:
|
||||
sta cx16.VERA_ADDR_H
|
||||
}}
|
||||
}
|
||||
x+=8
|
||||
xx+=8
|
||||
sctextptr++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ graphics {
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
void cx16.screen_mode(0, false)
|
||||
txt.fill_screen(' ', 1) ; doesn't seem to fully clear the text screen after returning from gfx mode
|
||||
txt.clear_screen()
|
||||
}
|
||||
|
||||
|
||||
@@ -50,22 +50,22 @@ graphics {
|
||||
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, uword y, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(x, y, width, height, 0, 1)
|
||||
sub fillrect(uword xx, uword yy, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 1)
|
||||
}
|
||||
|
||||
sub rect(uword x, uword y, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(x, y, width, height, 0, 0)
|
||||
sub rect(uword xx, uword yy, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 0)
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, uword y, uword length) {
|
||||
sub horizontal_line(uword xx, uword yy, uword length) {
|
||||
if length
|
||||
cx16.GRAPH_draw_line(x, y, x+length-1, y)
|
||||
cx16.GRAPH_draw_line(xx, yy, xx+length-1, yy)
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword height) {
|
||||
sub vertical_line(uword xx, uword yy, uword height) {
|
||||
if height
|
||||
cx16.GRAPH_draw_line(x, y, x, y+height-1)
|
||||
cx16.GRAPH_draw_line(xx, yy, xx, yy+height-1)
|
||||
}
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
|
||||
@@ -16,11 +16,11 @@ psg {
|
||||
const ubyte LEFT = %01000000
|
||||
const ubyte RIGHT = %10000000
|
||||
|
||||
sub voice(ubyte voice_num, ubyte channel, ubyte volume, ubyte waveform, ubyte pulsewidth) {
|
||||
sub voice(ubyte voice_num, ubyte channel, ubyte vol, ubyte waveform, ubyte pulsewidth) {
|
||||
; -- Enables a 'voice' on the PSG.
|
||||
; voice_num = 0-15, the voice number.
|
||||
; channel = either LEFT or RIGHT or (LEFT|RIGHT). Specifies the stereo channel(s) to use.
|
||||
; volume = 0-63, the starting volume for the voice
|
||||
; vol = 0-63, the starting volume for the voice
|
||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||
envelope_states[voice_num] = 255
|
||||
@@ -30,11 +30,11 @@ psg {
|
||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||
cx16.VERA_ADDR_H = 1
|
||||
cx16.VERA_DATA0 = channel | volume
|
||||
cx16.VERA_DATA0 = channel | vol
|
||||
cx16.VERA_ADDR_L++
|
||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||
envelope_volumes[voice_num] = mkword(volume, 0)
|
||||
envelope_maxvolumes[voice_num] = volume
|
||||
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||
envelope_maxvolumes[voice_num] = vol
|
||||
sys.irqsafe_clear_irqd()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
; Simple routines to control sprites.
|
||||
; They're not written for high performance, but for simplicity.
|
||||
; That's why they control 1 sprite at a time. The exception is pos_batch().
|
||||
; which is quite efficient to update sprite positions of multiple sprites in one go.
|
||||
|
||||
; note: sprites z-order will be in front of all layers.
|
||||
; note: collision mask is not supported here yet.
|
||||
|
||||
sprites {
|
||||
const ubyte SIZE_8 = 0
|
||||
const ubyte SIZE_16 = 1
|
||||
const ubyte SIZE_32 = 2
|
||||
const ubyte SIZE_64 = 3
|
||||
const ubyte COLORS_16 = 0
|
||||
const ubyte COLORS_256 = 128
|
||||
const uword VERA_SPRITEREGS = $fc00 ; $1fc00
|
||||
uword @zp sprite_reg
|
||||
|
||||
sub init(ubyte spritenum,
|
||||
ubyte databank, uword dataaddr,
|
||||
ubyte width_flag, ubyte height_flag,
|
||||
ubyte colors_flag, ubyte palette_offset_idx) {
|
||||
hide(spritenum)
|
||||
cx16.VERA_DC_VIDEO |= %01000000 ; enable sprites globally
|
||||
dataaddr >>= 5
|
||||
dataaddr |= (databank as uword)<<11
|
||||
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(dataaddr)) ; address 12:5
|
||||
cx16.vpoke(1, sprite_reg+1, colors_flag | msb(dataaddr)) ; 4 bpp + address 16:13
|
||||
cx16.vpoke(1, sprite_reg+6, %00001100) ; z depth %11 = in front of both layers, no flips
|
||||
cx16.vpoke(1, sprite_reg+7, height_flag<<6 | width_flag<<4 | palette_offset_idx>>4) ; 64x64 pixels, palette offset
|
||||
}
|
||||
|
||||
sub data(ubyte spritenum, ubyte bank, uword addr) {
|
||||
addr >>= 5
|
||||
addr |= (bank as uword)<<11
|
||||
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(addr)) ; address 12:5
|
||||
cx16.vpoke_mask(1, sprite_reg+1, %11110000, msb(addr)) ; address 16:13
|
||||
}
|
||||
|
||||
sub pos(ubyte spritenum, word xpos, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+2, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+3, msb(ypos))
|
||||
}
|
||||
|
||||
sub pos_batch(ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr) {
|
||||
; -- note: the x and y positions word arrays must be regular arrays, they cannot be @split arrays!
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
}
|
||||
sprite_reg += 2
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
}
|
||||
}
|
||||
|
||||
sub setx(ubyte spritenum, word xpos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
}
|
||||
|
||||
sub sety(ubyte spritenum, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(ypos))
|
||||
}
|
||||
|
||||
sub getx(ubyte spritenum) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
|
||||
sub gety(ubyte spritenum) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
|
||||
sub hide(ubyte spritenum) {
|
||||
pos(spritenum, -64, -64)
|
||||
}
|
||||
|
||||
sub flipx(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111110, flipped)
|
||||
}
|
||||
|
||||
sub flipy(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111101, flipped<<1)
|
||||
}
|
||||
|
||||
sub set_palette_offset(ubyte spritenum, ubyte offset) {
|
||||
sprite_reg = VERA_SPRITEREGS + 7 + spritenum*$0008
|
||||
cx16.vpoke_mask(1, sprite_reg, %11110000, offset>>4)
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD2 = CHROUT(ubyte character @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
@@ -346,8 +346,8 @@ romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword
|
||||
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
|
||||
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
|
||||
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, bool is_control @Pc) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
|
||||
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte character @A) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_next_char(ubyte character @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
|
||||
|
||||
; framebuffer
|
||||
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
||||
@@ -377,7 +377,7 @@ romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @
|
||||
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
|
||||
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
|
||||
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
||||
romsub $fede = console_put_char(ubyte char @A, bool wrapping @Pc) clobbers(A,X,Y)
|
||||
romsub $fede = console_put_char(ubyte character @A, bool wrapping @Pc) clobbers(A,X,Y)
|
||||
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
|
||||
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
|
||||
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
|
||||
@@ -395,6 +395,7 @@ romsub $febd = kbdbuf_peek() -> ubyte @A, ubyte @X ; key in A, queue length
|
||||
romsub $febd = kbdbuf_peek2() -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
||||
romsub $fec0 = kbdbuf_get_modifiers() -> ubyte @A
|
||||
romsub $fec3 = kbdbuf_put(ubyte key @A) clobbers(X)
|
||||
romsub $fed2 = keymap(uword identifier @XY, bool read @Pc) -> bool @Pc
|
||||
romsub $ff68 = mouse_config(ubyte shape @A, ubyte resX @X, ubyte resY @Y) clobbers (A, X, Y)
|
||||
romsub $ff6b = mouse_get(ubyte zpdataptr @X) -> ubyte @A
|
||||
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||
@@ -403,10 +404,10 @@ romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
||||
|
||||
; Audio (rom bank 10)
|
||||
romsub $C04B = psg_init() clobbers(A,X,Y)
|
||||
romsub $C04B = psg_init() clobbers(A,X,Y) ; (re)init Vera PSG
|
||||
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
|
||||
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
|
||||
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize PSG and YM audio chips
|
||||
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize both vera PSG and YM audio chips
|
||||
; TODO: add more of the audio routines?
|
||||
|
||||
|
||||
@@ -515,7 +516,8 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
||||
; note: inefficient when reading multiple sequential bytes!
|
||||
%asm {{
|
||||
pha
|
||||
lda #1
|
||||
lda cx16.VERA_CTRL
|
||||
ora #1
|
||||
sta cx16.VERA_CTRL
|
||||
pla
|
||||
and #1
|
||||
|
||||
@@ -42,7 +42,7 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A, X) {
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A, X) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
%asm {{
|
||||
sty _ly+1
|
||||
@@ -88,7 +88,7 @@ set_vera_textmatrix_addresses:
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(X, Y) {
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(X, Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
@@ -384,7 +384,7 @@ _nextline
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
romsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
@@ -674,12 +674,12 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: color handling is the same as on the C64: it only sets the foreground color and leaves the background color as is.
|
||||
; Use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors (is faster as well).
|
||||
%asm {{
|
||||
lda column
|
||||
lda col
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
@@ -691,7 +691,7 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
;clc
|
||||
adc #>VERA_TEXTMATRIX_ADDR
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda char
|
||||
lda character
|
||||
sta cx16.VERA_DATA0
|
||||
inc cx16.VERA_ADDR_L
|
||||
lda charcolor
|
||||
@@ -705,12 +705,12 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
sub setcc2 (ubyte col, ubyte row, ubyte character, ubyte colors) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||
; use the high nybble in A to set the Bg color! Is a bit faster than setcc() too.
|
||||
%asm {{
|
||||
lda column
|
||||
lda col
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
@@ -722,7 +722,7 @@ sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
; clc
|
||||
adc #>VERA_TEXTMATRIX_ADDR
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda char
|
||||
lda character
|
||||
sta cx16.VERA_DATA0
|
||||
inc cx16.VERA_ADDR_L
|
||||
lda colors
|
||||
|
||||
@@ -42,12 +42,12 @@ diskio {
|
||||
ubyte high = cbm.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.spc()
|
||||
ubyte @zp char
|
||||
ubyte @zp character
|
||||
repeat {
|
||||
char = cbm.CHRIN()
|
||||
if char==0
|
||||
character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
txt.chrout(char)
|
||||
txt.chrout(character)
|
||||
}
|
||||
txt.nl()
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
@@ -211,12 +211,12 @@ io_error:
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = cbm.CHRIN()
|
||||
if char==0
|
||||
ubyte character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
if char=='\"'
|
||||
if character=='\"'
|
||||
break
|
||||
@(nameptr) = char
|
||||
@(nameptr) = character
|
||||
nameptr++
|
||||
}
|
||||
|
||||
@@ -444,16 +444,16 @@ io_error:
|
||||
goto done
|
||||
}
|
||||
|
||||
sub save(uword filenameptr, uword address, uword size) -> bool {
|
||||
sub save(uword filenameptr, uword start_address, uword savesize) -> bool {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = address + size
|
||||
uword @shared end_address = start_address + savesize
|
||||
cx16.r0L = 0
|
||||
|
||||
%asm {{
|
||||
lda address
|
||||
lda start_address
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
lda start_address+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda #<P8ZP_SCRATCH_W1
|
||||
ldx end_address
|
||||
@@ -504,16 +504,16 @@ io_error:
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details.
|
||||
sub load_raw(uword filenameptr, uword address) -> uword {
|
||||
sub load_raw(uword filenameptr, uword start_address) -> uword {
|
||||
; read the 2 header bytes separately to skip them
|
||||
if not f_open(filenameptr)
|
||||
return 0
|
||||
cx16.r1 = f_read(address, 2)
|
||||
cx16.r1 = f_read(start_address, 2)
|
||||
f_close()
|
||||
if cx16.r1!=2
|
||||
return 0
|
||||
address += 2
|
||||
return load(filenameptr, address)
|
||||
start_address += 2
|
||||
return load(filenameptr, start_address)
|
||||
}
|
||||
|
||||
sub delete(uword filenameptr) {
|
||||
|
||||
+190
-74
@@ -4,78 +4,182 @@
|
||||
; some more interesting routines can be found here:
|
||||
; http://6502org.wikidot.com/software-math
|
||||
; http://codebase64.org/doku.php?id=base:6502_6510_maths
|
||||
;
|
||||
; https://github.com/TobyLobster/multiply_test
|
||||
; https://github.com/TobyLobster/sqrt_test
|
||||
|
||||
|
||||
multiply_bytes .proc
|
||||
; -- multiply 2 bytes A and Y, result as byte in A (signed or unsigned)
|
||||
sta P8ZP_SCRATCH_B1 ; num1
|
||||
sty P8ZP_SCRATCH_REG ; num2
|
||||
lda #0
|
||||
beq _enterloop
|
||||
_doAdd clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
_loop asl P8ZP_SCRATCH_B1
|
||||
_enterloop lsr P8ZP_SCRATCH_REG
|
||||
bcs _doAdd
|
||||
bne _loop
|
||||
rts
|
||||
.pend
|
||||
; https://github.com/TobyLobster/multiply_test/blob/main/tests/mult29.a
|
||||
|
||||
_multiplicand = P8ZP_SCRATCH_B1
|
||||
_multiplier = P8ZP_SCRATCH_REG
|
||||
|
||||
multiply_bytes_into_word .proc
|
||||
; -- multiply 2 bytes A and Y, result as word in A/Y (unsigned)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
ldx #8
|
||||
lsr P8ZP_SCRATCH_B1
|
||||
- bcc +
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
+ ror a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
dex
|
||||
bne -
|
||||
tay
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
sty _multiplicand
|
||||
lsr a
|
||||
sta _multiplier
|
||||
lda #0
|
||||
ldx #2
|
||||
-
|
||||
bcc +
|
||||
clc
|
||||
adc _multiplicand
|
||||
+
|
||||
ror a
|
||||
ror _multiplier
|
||||
bcc +
|
||||
clc
|
||||
adc _multiplicand
|
||||
+
|
||||
ror a
|
||||
ror _multiplier
|
||||
|
||||
bcc +
|
||||
clc
|
||||
adc _multiplicand
|
||||
+
|
||||
ror a
|
||||
ror _multiplier
|
||||
bcc +
|
||||
clc
|
||||
adc _multiplicand
|
||||
+
|
||||
ror a
|
||||
ror _multiplier
|
||||
dex
|
||||
bne -
|
||||
; tay ; if you want 16 bits result in AY, enable this again
|
||||
lda _multiplier
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
multiply_words .proc
|
||||
; -- multiply two 16-bit words into a 32-bit result (signed and unsigned)
|
||||
; input: A/Y = first 16-bit number, P8ZP_SCRATCH_W1 in ZP = second 16-bit number
|
||||
; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY.
|
||||
; input: A/Y = first 16-bit number, multiply_words.multiplier = second 16-bit number
|
||||
; output: multiply_words.result, 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY.
|
||||
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
; NOTE: the result (which includes the multiplier parameter on entry) is a 4-byte array.
|
||||
; this routine could be faster if we could stick that into zeropage,
|
||||
; but there currently is no way to use 4 consecutive bytes in ZP (without disabling irq and saving/restoring them)...
|
||||
|
||||
mult16 lda #0
|
||||
sta result+2 ; clear upper bits of product
|
||||
sta result+3
|
||||
ldx #16 ; for all 16 bits...
|
||||
- lsr P8ZP_SCRATCH_W1+1 ; divide multiplier by 2
|
||||
ror P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
lda result+2 ; get upper half of product and add multiplicand
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W2
|
||||
sta result+2
|
||||
lda result+3
|
||||
adc P8ZP_SCRATCH_W2+1
|
||||
+ ror a ; rotate partial product
|
||||
sta result+3
|
||||
ror result+2
|
||||
ror result+1
|
||||
ror result
|
||||
dex
|
||||
bne -
|
||||
lda result
|
||||
ldy result+1
|
||||
rts
|
||||
; mult62.a
|
||||
; based on Dr Jefyll, http://forum.6502.org/viewtopic.php?f=9&t=689&start=0#p19958
|
||||
; - adjusted to use fixed zero page addresses
|
||||
; - removed 'decrement to avoid clc' as this is slower on average
|
||||
; - rearranged memory use to remove final memory copy and give LSB first order to result
|
||||
; - removed temp zp storage bytes
|
||||
; - unrolled the outer loop
|
||||
; - unrolled the two inner loops once
|
||||
;
|
||||
; 16 bit x 16 bit unsigned multiply, 32 bit result
|
||||
; Average cycles:
|
||||
; 93 bytes
|
||||
|
||||
_multiplicand = P8ZP_SCRATCH_W2 ; 2 bytes
|
||||
multiplier = result
|
||||
|
||||
; 16 bit x 16 bit unsigned multiply, 32 bit result
|
||||
;
|
||||
; On Entry:
|
||||
; (multiplier, multiplier+1): two byte multiplier, four bytes needed for result
|
||||
; (multiplicand, multiplicand+1): two byte multiplicand
|
||||
; On Exit:
|
||||
; (result, result+1, result+2, result+3): product
|
||||
|
||||
sta _multiplicand
|
||||
sty _multiplicand+1
|
||||
|
||||
lda #0 ;
|
||||
sta result+2 ; 16 bits of zero in A, result+2
|
||||
; Note: First 8 shifts are A -> result+2 -> result
|
||||
; Final 8 shifts are A -> result+2 -> result+1
|
||||
|
||||
; --- 1st byte ---
|
||||
ldy #4 ; count for inner loop
|
||||
lsr result
|
||||
|
||||
; inner loop (8 times)
|
||||
_inner_loop
|
||||
; first time
|
||||
bcc +
|
||||
tax ; retain A
|
||||
lda result+2
|
||||
clc
|
||||
adc _multiplicand
|
||||
sta result+2
|
||||
txa ; recall A
|
||||
adc _multiplicand+1
|
||||
|
||||
+
|
||||
ror a ; shift
|
||||
ror result+2
|
||||
ror result
|
||||
|
||||
; second time
|
||||
bcc +
|
||||
tax ; retain A
|
||||
lda result+2
|
||||
clc
|
||||
adc _multiplicand
|
||||
sta result+2
|
||||
txa ; recall A
|
||||
adc _multiplicand+1
|
||||
|
||||
+
|
||||
ror a ; shift
|
||||
ror result+2
|
||||
ror result
|
||||
|
||||
dey
|
||||
bne _inner_loop ; go back for 1 more shift?
|
||||
|
||||
; --- 2nd byte ---
|
||||
ldy #4 ; count for inner loop
|
||||
lsr result+1
|
||||
|
||||
; inner loop (8 times)
|
||||
_inner_loop2
|
||||
; first time
|
||||
bcc +
|
||||
tax ; retain A
|
||||
lda result+2
|
||||
clc
|
||||
adc _multiplicand
|
||||
sta result+2
|
||||
txa ; recall A
|
||||
adc _multiplicand+1
|
||||
|
||||
+
|
||||
ror a ; shift
|
||||
ror result+2
|
||||
ror result+1
|
||||
|
||||
; second time
|
||||
bcc +
|
||||
tax ; retain A
|
||||
lda result+2
|
||||
clc
|
||||
adc _multiplicand
|
||||
sta result+2
|
||||
txa ; recall A
|
||||
adc _multiplicand+1
|
||||
|
||||
+
|
||||
ror a ; shift
|
||||
ror result+2
|
||||
ror result+1
|
||||
dey
|
||||
bne _inner_loop2 ; go back for 1 more shift?
|
||||
|
||||
sta result+3 ; ms byte of hi-word of result
|
||||
|
||||
lda result
|
||||
ldy result+1
|
||||
rts
|
||||
|
||||
result .byte 0,0,0,0
|
||||
|
||||
.pend
|
||||
|
||||
|
||||
@@ -749,74 +853,86 @@ mul_word_640 .proc
|
||||
; support for bit shifting that is too large to be unrolled:
|
||||
|
||||
lsr_byte_A .proc
|
||||
; -- lsr signed byte in A times the value in Y (>1)
|
||||
; -- lsr signed byte in A times the value in Y
|
||||
cpy #0
|
||||
beq +
|
||||
cmp #0
|
||||
bpl lsr_ubyte_A
|
||||
- sec
|
||||
ror a
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
lsr_ubyte_A .proc
|
||||
; -- lsr unsigned byte in A times the value in Y (>1)
|
||||
; -- lsr unsigned byte in A times the value in Y
|
||||
cpy #0
|
||||
beq +
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
asl_byte_A .proc
|
||||
; -- asl any byte in A times the value in Y (>1)
|
||||
; -- asl any byte in A times the value in Y
|
||||
cpy #0
|
||||
beq +
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
|
||||
lsr_word_AY .proc
|
||||
; -- lsr signed word in AY times the value in X (>1)
|
||||
; -- lsr signed word in AY times the value in X
|
||||
cpx #0
|
||||
beq +
|
||||
cpy #0
|
||||
bpl lsr_uword_AY
|
||||
sty P8ZP_SCRATCH_B1
|
||||
_negative sec
|
||||
- sec
|
||||
ror P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
dex
|
||||
bne _negative
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
lsr_uword_AY .proc
|
||||
; -- lsr unsigned word in AY times the value in X (>1)
|
||||
; -- lsr unsigned word in AY times the value in X
|
||||
cpx #0
|
||||
beq +
|
||||
sty P8ZP_SCRATCH_B1
|
||||
- lsr P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
asl_word_AY .proc
|
||||
; -- asl any word in AY times the value in X (>1)
|
||||
; -- asl any word in AY times the value in X
|
||||
cpx #0
|
||||
beq +
|
||||
sty P8ZP_SCRATCH_B1
|
||||
- asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
|
||||
square .proc
|
||||
; -- calculate square root of signed word in AY, result in AY
|
||||
; routine by Lee Davsion, source: http://6502.org/source/integers/square.htm
|
||||
; using this routine is about twice as fast as doing a regular multiplication.
|
||||
; -- calculate square of signed word (actually -255..255) in AY, result in AY
|
||||
; routine by Lee Davison, source: http://6502.org/source/integers/square.htm
|
||||
; using this routine is a lot faster as doing a regular multiplication (for words)
|
||||
;
|
||||
; Calculates the 16 bit unsigned integer square of the signed 16 bit integer in
|
||||
; Numberl/Numberh. The result is always in the range 0 to 65025 and is held in
|
||||
|
||||
@@ -98,6 +98,47 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub log2(ubyte value @A) -> ubyte @Y {
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda #$80
|
||||
ldy #7
|
||||
- bit P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
rts
|
||||
+ dey
|
||||
bne +
|
||||
rts
|
||||
+ lsr a
|
||||
bne -
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub log2w(uword value @AY) -> ubyte @Y {
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$8000
|
||||
sta cx16.r0
|
||||
lda #>$8000
|
||||
sta cx16.r0+1
|
||||
ldy #15
|
||||
- lda P8ZP_SCRATCH_W1
|
||||
and cx16.r0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
and cx16.r0+1
|
||||
ora P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
rts
|
||||
+ dey
|
||||
bne +
|
||||
rts
|
||||
+ lsr cx16.r0+1
|
||||
ror cx16.r0
|
||||
jmp -
|
||||
}}
|
||||
}
|
||||
|
||||
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
|
||||
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
; Prog8 definitions for the Commodore PET
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
; see: https://www.pagetable.com/?p=926 , http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
||||
|
||||
cbm {
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&ubyte TIME_HI = $8d ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $8e ; .. mid byte
|
||||
&ubyte TIME_LO = $8f ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
&ubyte STATUS = $96 ; kernal status variable for I/O
|
||||
|
||||
&uword CINV = $0090 ; IRQ vector (in ram)
|
||||
&uword CBINV = $0092 ; BRK vector (in ram)
|
||||
&uword NMINV = $0094 ; NMI vector (in ram)
|
||||
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
; the default addresses for the character screen chars and colors
|
||||
const uword Screen = $8000 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
|
||||
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte character @ A) ; output a character
|
||||
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
|
||||
asmsub STOP2() clobbers(X) -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) {
|
||||
; PET stub to set the software clock
|
||||
%asm {{
|
||||
sty TIME_HI
|
||||
stx TIME_MID
|
||||
sta TIME_LO
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y {
|
||||
; PET stub to read the software clock (A=lo,X=mid,Y=high)
|
||||
%asm {{
|
||||
ldy TIME_HI
|
||||
ldx TIME_MID
|
||||
lda TIME_LO
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
lda TIME_LO
|
||||
ldy TIME_MID
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 32 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 32=PET
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; Uppercase charset is activated.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
lda #142
|
||||
jsr cbm.CHROUT ; uppercase
|
||||
lda #147
|
||||
jsr cbm.CHROUT ; clear screen
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
rts ; no phase 2 steps on the PET
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
jmp (cbm.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitvsync() clobbers(A) {
|
||||
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||
; Note: on PET this simply waits until the next jiffy clock update, I don't know if a true vsync is possible there
|
||||
%asm {{
|
||||
lda #1
|
||||
ldy #0
|
||||
jmp wait
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_B1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
|
||||
_loop lda P8ZP_SCRATCH_W1
|
||||
ora P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
ldx P8ZP_SCRATCH_B1
|
||||
rts
|
||||
|
||||
+ lda cbm.TIME_LO
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- lda cbm.TIME_LO
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W1+1
|
||||
+ dec P8ZP_SCRATCH_W1
|
||||
jmp _loop
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jmp prog8_lib.strcpy
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||
; note: only works for NON-OVERLAPPING memory regions!
|
||||
; note: can't be inlined because is called from asm as well
|
||||
%asm {{
|
||||
ldx cx16.r0
|
||||
stx P8ZP_SCRATCH_W1 ; source in ZP
|
||||
ldx cx16.r0+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
stx P8ZP_SCRATCH_W2 ; target in ZP
|
||||
ldx cx16.r1+1
|
||||
stx P8ZP_SCRATCH_W2+1
|
||||
cpy #0
|
||||
bne _longcopy
|
||||
|
||||
; copy <= 255 bytes
|
||||
tay
|
||||
bne _copyshort
|
||||
rts ; nothing to copy
|
||||
|
||||
_copyshort
|
||||
; decrease source and target pointers so we can simply index by Y
|
||||
lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W1+1
|
||||
+ dec P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W2+1
|
||||
+ dec P8ZP_SCRATCH_W2
|
||||
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
|
||||
_longcopy
|
||||
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder in last page
|
||||
tya
|
||||
tax ; x = num pages (1+)
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
bne -
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
inc P8ZP_SCRATCH_W2+1
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
bne _copyshort
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memset(uword mem @R0, uword numbytes @R1, ubyte value @A) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
ldy cx16.r0
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy cx16.r0+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
ldy cx16.r1+1
|
||||
jmp prog8_lib.memset
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memsetw(uword mem @R0, uword numwords @R1, uword value @AY) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
ldx cx16.r0
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx cx16.r0+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
stx P8ZP_SCRATCH_W2
|
||||
ldx cx16.r1+1
|
||||
stx P8ZP_SCRATCH_W2+1
|
||||
jmp prog8_lib.memsetw
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub read_flags() -> ubyte @A {
|
||||
%asm {{
|
||||
php
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_carry() {
|
||||
%asm {{
|
||||
clc
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_carry() {
|
||||
%asm {{
|
||||
sec
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_irqd() {
|
||||
%asm {{
|
||||
cli
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_irqd() {
|
||||
%asm {{
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_set_irqd() {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_clear_irqd() {
|
||||
%asm {{
|
||||
plp
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progend() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_end
|
||||
ldy #>prog8_program_end
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the PET as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
; we select the top page of RAM (assume 32Kb)
|
||||
&uword r0 = $7fe0
|
||||
&uword r1 = $7fe2
|
||||
&uword r2 = $7fe4
|
||||
&uword r3 = $7fe6
|
||||
&uword r4 = $7fe8
|
||||
&uword r5 = $7fea
|
||||
&uword r6 = $7fec
|
||||
&uword r7 = $7fee
|
||||
&uword r8 = $7ff0
|
||||
&uword r9 = $7ff2
|
||||
&uword r10 = $7ff4
|
||||
&uword r11 = $7ff6
|
||||
&uword r12 = $7ff8
|
||||
&uword r13 = $7ffa
|
||||
&uword r14 = $7ffc
|
||||
&uword r15 = $7ffe
|
||||
|
||||
&word r0s = $7fe0
|
||||
&word r1s = $7fe2
|
||||
&word r2s = $7fe4
|
||||
&word r3s = $7fe6
|
||||
&word r4s = $7fe8
|
||||
&word r5s = $7fea
|
||||
&word r6s = $7fec
|
||||
&word r7s = $7fee
|
||||
&word r8s = $7ff0
|
||||
&word r9s = $7ff2
|
||||
&word r10s = $7ff4
|
||||
&word r11s = $7ff6
|
||||
&word r12s = $7ff8
|
||||
&word r13s = $7ffa
|
||||
&word r14s = $7ffc
|
||||
&word r15s = $7ffe
|
||||
|
||||
&ubyte r0L = $7fe0
|
||||
&ubyte r1L = $7fe2
|
||||
&ubyte r2L = $7fe4
|
||||
&ubyte r3L = $7fe6
|
||||
&ubyte r4L = $7fe8
|
||||
&ubyte r5L = $7fea
|
||||
&ubyte r6L = $7fec
|
||||
&ubyte r7L = $7fee
|
||||
&ubyte r8L = $7ff0
|
||||
&ubyte r9L = $7ff2
|
||||
&ubyte r10L = $7ff4
|
||||
&ubyte r11L = $7ff6
|
||||
&ubyte r12L = $7ff8
|
||||
&ubyte r13L = $7ffa
|
||||
&ubyte r14L = $7ffc
|
||||
&ubyte r15L = $7ffe
|
||||
|
||||
&ubyte r0H = $7fe1
|
||||
&ubyte r1H = $7fe3
|
||||
&ubyte r2H = $7fe5
|
||||
&ubyte r3H = $7fe7
|
||||
&ubyte r4H = $7fe9
|
||||
&ubyte r5H = $7feb
|
||||
&ubyte r6H = $7fed
|
||||
&ubyte r7H = $7fef
|
||||
&ubyte r8H = $7ff1
|
||||
&ubyte r9H = $7ff3
|
||||
&ubyte r10H = $7ff5
|
||||
&ubyte r11H = $7ff7
|
||||
&ubyte r12H = $7ff9
|
||||
&ubyte r13H = $7ffb
|
||||
&ubyte r14H = $7ffd
|
||||
&ubyte r15H = $7fff
|
||||
|
||||
&byte r0sL = $7fe0
|
||||
&byte r1sL = $7fe2
|
||||
&byte r2sL = $7fe4
|
||||
&byte r3sL = $7fe6
|
||||
&byte r4sL = $7fe8
|
||||
&byte r5sL = $7fea
|
||||
&byte r6sL = $7fec
|
||||
&byte r7sL = $7fee
|
||||
&byte r8sL = $7ff0
|
||||
&byte r9sL = $7ff2
|
||||
&byte r10sL = $7ff4
|
||||
&byte r11sL = $7ff6
|
||||
&byte r12sL = $7ff8
|
||||
&byte r13sL = $7ffa
|
||||
&byte r14sL = $7ffc
|
||||
&byte r15sL = $7ffe
|
||||
|
||||
&byte r0sH = $7fe1
|
||||
&byte r1sH = $7fe3
|
||||
&byte r2sH = $7fe5
|
||||
&byte r3sH = $7fe7
|
||||
&byte r4sH = $7fe9
|
||||
&byte r5sH = $7feb
|
||||
&byte r6sH = $7fed
|
||||
&byte r7sH = $7fef
|
||||
&byte r8sH = $7ff1
|
||||
&byte r9sH = $7ff3
|
||||
&byte r10sH = $7ff5
|
||||
&byte r11sH = $7ff7
|
||||
&byte r12sH = $7ff9
|
||||
&byte r13sH = $7ffb
|
||||
&byte r14sH = $7ffd
|
||||
&byte r15sH = $7fff
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
- lda cx16.r0,y
|
||||
sta _cx16_vreg_storage,y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
- lda save_virtual_registers._cx16_vreg_storage,y
|
||||
sta cx16.r0,y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore PET
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
|
||||
txt {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
txt.chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
txt.chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
txt.chrout(' ')
|
||||
}
|
||||
|
||||
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character. color is ignored on PET
|
||||
%asm {{
|
||||
jmp clear_screenchars
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- sta cbm.Screen+250*0-1,y
|
||||
sta cbm.Screen+250*1-1,y
|
||||
sta cbm.Screen+250*2-1,y
|
||||
sta cbm.Screen+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub clear_screencolors (ubyte color) {
|
||||
; --- dummy function on PET
|
||||
}
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
; --- dummy function on PET
|
||||
}
|
||||
|
||||
|
||||
sub lowercase() {
|
||||
txt.chrout(14)
|
||||
}
|
||||
|
||||
sub uppercase() {
|
||||
txt.chrout(142)
|
||||
}
|
||||
|
||||
asmsub scroll_left () clobbers(A, X, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
|
||||
%asm {{
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda cbm.Screen + 40*row + 1,x
|
||||
sta cbm.Screen + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right () clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda cbm.Screen + 40*row + 0,x
|
||||
sta cbm.Screen + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up () clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down () clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to cbm.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr cbm.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr cbm.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubbin
|
||||
pla
|
||||
clc
|
||||
jmp print_ubbin
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubhex
|
||||
pla
|
||||
clc
|
||||
jmp print_ubhex
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
iny
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
pla
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp print_uw
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr cbm.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
iny
|
||||
bne -
|
||||
+ lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||
rts
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
|
||||
; ---- sets the character in the screen matrix at the given position
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the character in the screen matrix at the given location
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setclr (ubyte col, ubyte row, ubyte color) {
|
||||
; --- dummy function on PET
|
||||
}
|
||||
|
||||
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char at the given position on the screen. charcolor is ignored on PET
|
||||
%asm {{
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc col
|
||||
sta _charmod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
jsr home
|
||||
cpy #0
|
||||
beq +
|
||||
- lda #17
|
||||
jsr chrout
|
||||
dey
|
||||
bne -
|
||||
+ cpx #0
|
||||
beq +
|
||||
- lda #29
|
||||
jsr chrout
|
||||
dex
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
; -- returns the text screen width (number of columns)
|
||||
%asm {{
|
||||
lda $d5
|
||||
clc
|
||||
adc #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||
; -- returns the text screen height (number of rows)
|
||||
%asm {{
|
||||
lda #25
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -127,33 +127,42 @@ _possibly_zero cmp #0
|
||||
|
||||
|
||||
func_sqrt16_into_A .proc
|
||||
; integer square root from http://6502org.wikidot.com/software-math-sqrt
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
ldx #8
|
||||
- sec
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sbc #$40
|
||||
tay
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sbc P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
+ rol P8ZP_SCRATCH_B1
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
rol P8ZP_SCRATCH_REG
|
||||
dex
|
||||
bne -
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
; integer square root
|
||||
; http://6502org.wikidot.com/software-math-sqrt
|
||||
; https://github.com/TobyLobster/sqrt_test/blob/main/sqrt/sqrt7.a
|
||||
; Tweaked by TobyLobster and 0xC0DE to be smaller and faster
|
||||
_numl = P8ZP_SCRATCH_W1
|
||||
_numh = P8ZP_SCRATCH_W1+1
|
||||
_loop_counter = P8ZP_SCRATCH_REG
|
||||
_root = P8ZP_SCRATCH_B1
|
||||
sta _numl
|
||||
sty _numh
|
||||
ldx #$ff
|
||||
stx _loop_counter
|
||||
inx
|
||||
stx _root
|
||||
sec
|
||||
_loop lda _numh
|
||||
sbc #$40
|
||||
tay
|
||||
txa
|
||||
sbc _root
|
||||
bcc +
|
||||
sty _numh
|
||||
bcs ++
|
||||
+ txa
|
||||
+ rol _root
|
||||
asl _numl
|
||||
rol _numh
|
||||
rol a
|
||||
asl _numl
|
||||
rol _numh
|
||||
rol a
|
||||
tax
|
||||
lsr _loop_counter
|
||||
bne _loop
|
||||
lda _root
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ _done rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub lowerchar(ubyte char @A) -> ubyte @A {
|
||||
asmsub lowerchar(ubyte character @A) -> ubyte @A {
|
||||
%asm {{
|
||||
and #$7f
|
||||
cmp #97
|
||||
@@ -237,7 +237,7 @@ _done rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub upperchar(ubyte char @A) -> ubyte @A {
|
||||
asmsub upperchar(ubyte character @A) -> ubyte @A {
|
||||
%asm {{
|
||||
cmp #65
|
||||
bcc +
|
||||
@@ -280,10 +280,10 @@ _done rts
|
||||
;
|
||||
; see http://6502.org/source/strings/patmatch.htm
|
||||
|
||||
str = P8ZP_SCRATCH_W1
|
||||
strptr = P8ZP_SCRATCH_W1
|
||||
|
||||
sta str
|
||||
sty str+1
|
||||
sta strptr
|
||||
sty strptr+1
|
||||
lda cx16.r0
|
||||
sta modify_pattern1+1
|
||||
sta modify_pattern2+1
|
||||
@@ -306,9 +306,9 @@ next lda $ffff,x ; look at next pattern character MODIFIED
|
||||
iny ; no, let's look at the string
|
||||
cmp #'?' ; is the pattern caracter a ques?
|
||||
bne reg ; no, it's a regular character
|
||||
lda (str),y ; yes, so it will match anything
|
||||
lda (strptr),y ; yes, so it will match anything
|
||||
beq fail ; except the end of string
|
||||
reg cmp (str),y ; are both characters the same?
|
||||
reg cmp (strptr),y ; are both characters the same?
|
||||
bne fail ; no, so no match
|
||||
inx ; yes, keep checking
|
||||
cmp #0 ; are we at end of string?
|
||||
@@ -330,7 +330,7 @@ stloop txa ; we first try to match with * = ""
|
||||
tax
|
||||
bcs found ; we found a match, return with c=1
|
||||
iny ; no match yet, try to grow * string
|
||||
lda (str),y ; are we at the end of string?
|
||||
lda (strptr),y ; are we at the end of string?
|
||||
bne stloop ; not yet, add a character
|
||||
fail clc ; yes, no match found, return with c=0
|
||||
rts
|
||||
|
||||
@@ -16,6 +16,15 @@ sub print_f(float value) {
|
||||
}}
|
||||
}
|
||||
|
||||
sub parse_f(str value) -> float {
|
||||
; -- parse a string value of a number to float
|
||||
%ir {{
|
||||
loadm.w r65535,floats.parse_f.value
|
||||
syscall 45 (r65535.w): fr0.f
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub pow(float value, float power) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.pow.value
|
||||
|
||||
@@ -183,6 +183,31 @@ math {
|
||||
}}
|
||||
}
|
||||
|
||||
sub log2(ubyte value) -> ubyte {
|
||||
ubyte result = 7
|
||||
ubyte compare = $80
|
||||
repeat {
|
||||
if value&compare
|
||||
return result
|
||||
result--
|
||||
if_z
|
||||
return 0
|
||||
compare >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
sub log2w(uword value) -> ubyte {
|
||||
ubyte result = 15
|
||||
uword compare = $8000
|
||||
repeat {
|
||||
if value&compare
|
||||
return result
|
||||
result--
|
||||
if_z
|
||||
return 0
|
||||
compare >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
|
||||
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
|
||||
|
||||
@@ -45,10 +45,11 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any 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 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 compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}') (required)")
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${PETTarget.NAME}', '${VMTarget.NAME}') (required)")
|
||||
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 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.")
|
||||
@@ -83,7 +84,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) {
|
||||
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, PETTarget.NAME, VMTarget.NAME)) {
|
||||
System.err.println("Invalid compilation target: $compilationTarget")
|
||||
return false
|
||||
}
|
||||
@@ -113,6 +114,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
dontWriteAssembly != true,
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
includeSourcelines == true,
|
||||
@@ -180,6 +182,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
dontWriteAssembly != true,
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
includeSourcelines == true,
|
||||
|
||||
@@ -30,6 +30,7 @@ class CompilationResult(val compilerAst: Program, // deprecated, use codegenAs
|
||||
class CompilerArguments(val filepath: Path,
|
||||
val optimize: Boolean,
|
||||
val writeAssembly: Boolean,
|
||||
val warnSymbolShadowing: Boolean,
|
||||
val quietAssembler: Boolean,
|
||||
val asmListfile: Boolean,
|
||||
val includeSourcelines: Boolean,
|
||||
@@ -52,6 +53,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
C64Target.NAME -> C64Target()
|
||||
C128Target.NAME -> C128Target()
|
||||
Cx16Target.NAME -> Cx16Target()
|
||||
PETTarget.NAME -> PETTarget()
|
||||
AtariTarget.NAME -> AtariTarget()
|
||||
VMTarget.NAME -> VMTarget()
|
||||
else -> throw IllegalArgumentException("invalid compilation target")
|
||||
@@ -66,6 +68,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
compilationOptions = options
|
||||
|
||||
with(compilationOptions) {
|
||||
warnSymbolShadowing = args.warnSymbolShadowing
|
||||
optimize = args.optimize
|
||||
asmQuiet = args.quietAssembler
|
||||
asmListfile = args.asmListfile
|
||||
|
||||
@@ -76,14 +76,19 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(unrollLoop: UnrollLoop) {
|
||||
if(unrollLoop.iterations<0 || unrollLoop.iterations>65535)
|
||||
errors.err("invalid number of unrolls", unrollLoop.position)
|
||||
unrollLoop.body.statements.forEach {
|
||||
if(it !is InlineAssembly && it !is Assignment && it !is BuiltinFunctionCallStatement && it !is FunctionCallStatement && it !is PostIncrDecr)
|
||||
errors.err("invalid statement in unroll loop", it.position)
|
||||
}
|
||||
if(unrollLoop.iterations * unrollLoop.body.statements.size > 256) {
|
||||
errors.warn("large number of unrolls, potential code size issue", unrollLoop.position)
|
||||
val iterations = unrollLoop.iterations.constValue(program)?.number?.toInt()
|
||||
if(iterations==null) {
|
||||
errors.err("unroll needs constant number of iterations", unrollLoop.position)
|
||||
} else {
|
||||
if (iterations < 0 || iterations > 65535)
|
||||
errors.err("invalid number of unrolls", unrollLoop.position)
|
||||
unrollLoop.body.statements.forEach {
|
||||
if (it !is InlineAssembly && it !is Assignment && it !is BuiltinFunctionCallStatement && it !is FunctionCallStatement && it !is PostIncrDecr)
|
||||
errors.err("invalid statement in unroll loop", it.position)
|
||||
}
|
||||
if (iterations * unrollLoop.body.statements.size > 256) {
|
||||
errors.warn("large number of unrolls, potential code size issue", unrollLoop.position)
|
||||
}
|
||||
}
|
||||
super.visit(unrollLoop)
|
||||
}
|
||||
@@ -220,7 +225,7 @@ internal class AstChecker(private val program: Program,
|
||||
override fun visit(jump: Jump) {
|
||||
val ident = jump.identifier
|
||||
if(ident!=null) {
|
||||
val targetStatement = checkFunctionOrLabelExists(ident, jump)
|
||||
val targetStatement = ident.checkFunctionOrLabelExists(program, jump, errors)
|
||||
if(targetStatement!=null) {
|
||||
if(targetStatement is BuiltinFunctionPlaceholder)
|
||||
errors.err("can't jump to a builtin function", jump.position)
|
||||
@@ -306,7 +311,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(subroutine.name in BuiltinFunctions)
|
||||
err("cannot redefine a built-in function")
|
||||
|
||||
if(subroutine.parameters.size>6 && !subroutine.isAsmSubroutine)
|
||||
if(subroutine.parameters.size>6 && !subroutine.isAsmSubroutine && !subroutine.definingBlock.isInLibrary)
|
||||
errors.warn("subroutine has a large number of parameters, this slows down code execution a lot", subroutine.position)
|
||||
|
||||
val uniqueNames = subroutine.parameters.asSequence().map { it.name }.toSet()
|
||||
@@ -1068,7 +1073,7 @@ internal class AstChecker(private val program: Program,
|
||||
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
|
||||
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCallExpr.target, stmtOfExpression)
|
||||
val targetStatement = functionCallExpr.target.checkFunctionOrLabelExists(program, stmtOfExpression, errors)
|
||||
if(targetStatement!=null)
|
||||
checkFunctionCall(targetStatement, functionCallExpr.args, functionCallExpr.position)
|
||||
|
||||
@@ -1120,7 +1125,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||
val targetStatement = functionCallStatement.target.checkFunctionOrLabelExists(program, functionCallStatement, errors)
|
||||
if(targetStatement!=null) {
|
||||
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
||||
checkUnusedReturnValues(functionCallStatement, targetStatement, errors)
|
||||
@@ -1421,25 +1426,6 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("%asm containing IR code cannot be translated to 6502 assembly", inlineAssembly.position)
|
||||
}
|
||||
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||
when (val targetStatement = target.targetStatement(program)) {
|
||||
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
|
||||
is VarDecl -> {
|
||||
if(statement is Jump) {
|
||||
if (targetStatement.datatype == DataType.UWORD)
|
||||
return targetStatement
|
||||
else
|
||||
errors.err("wrong address variable datatype, expected uword", target.position)
|
||||
}
|
||||
else
|
||||
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
|
||||
}
|
||||
null -> errors.undefined(target.nameInSource, target.position)
|
||||
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
|
||||
return if (targetDt == DataType.STR) {
|
||||
when {
|
||||
|
||||
@@ -5,10 +5,7 @@ import prog8.ast.Program
|
||||
import prog8.ast.expressions.CharLiteral
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.ast.statements.InlineAssembly
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDeclOrigin
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
@@ -151,4 +148,25 @@ internal fun Subroutine.hasRtsInAsm(): Boolean {
|
||||
.asSequence()
|
||||
.filterIsInstance<InlineAssembly>()
|
||||
.any { it.hasReturnOrRts() }
|
||||
}
|
||||
|
||||
internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, statement: Statement, errors: IErrorReporter): Statement? {
|
||||
when (val targetStatement = this.targetStatement(program)) {
|
||||
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
|
||||
is VarDecl -> {
|
||||
if(statement is Jump) {
|
||||
if (targetStatement.datatype == DataType.UWORD)
|
||||
return targetStatement
|
||||
else
|
||||
errors.err("wrong address variable datatype, expected uword", this.position)
|
||||
}
|
||||
else
|
||||
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
|
||||
}
|
||||
null -> {
|
||||
errors.undefined(this.nameInSource, this.position)
|
||||
}
|
||||
else -> errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.findParentNode
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
@@ -201,4 +204,42 @@ class AstPreprocessor(val program: Program,
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
val stmtOfExpression = findParentNode<Statement>(bfc)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${bfc.position}")
|
||||
checkStringParam(bfc as IFunctionCall, stmtOfExpression)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
checkStringParam(bfcs as IFunctionCall, bfcs)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
|
||||
|
||||
checkStringParam(functionCallExpr as IFunctionCall, stmtOfExpression)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
checkStringParam(functionCallStatement as IFunctionCall, functionCallStatement)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun checkStringParam(call: IFunctionCall, stmt: Statement) {
|
||||
val targetStatement = call.target.checkFunctionOrLabelExists(program, stmt, errors)
|
||||
if(targetStatement!=null) {
|
||||
if(targetStatement is Subroutine) {
|
||||
for(arg in call.args.zip(targetStatement.parameters)) {
|
||||
if(arg.first.inferType(program).isBytes && arg.second.type==DataType.STR) {
|
||||
errors.err("cannot use byte value for string parameter", arg.first.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,16 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
val typeCast = binExpr.left as? TypecastExpression
|
||||
if(typeCast!=null && typeCast.expression isSameAs assignment.target)
|
||||
return noModifications
|
||||
|
||||
if(binExpr.operator in "+-") {
|
||||
val leftDt = binExpr.left.inferType(program)
|
||||
val rightDt = binExpr.right.inferType(program)
|
||||
if(leftDt==rightDt && leftDt.isInteger && rightDt.isInteger && binExpr.right is ArrayIndexedExpression) {
|
||||
// don't split array[i] +/- array[i] (the codegen has an optimized path for this)
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
val sourceDt = binExpr.left.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||
val (_, left) = binExpr.left.typecastTo(assignment.target.inferType(program).getOrElse { throw AssemblyError(
|
||||
"unknown dt"
|
||||
|
||||
@@ -200,6 +200,15 @@ _after:
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.operator=="*" && expr.inferType(program).isInteger && expr.left isSameAs expr.right) {
|
||||
// replace squaring with call to builtin function to do this in a more optimized way
|
||||
val function = if(expr.left.inferType(program).isBytes) "prog8_lib_square_byte" else "prog8_lib_square_word"
|
||||
val squareCall = BuiltinFunctionCall(
|
||||
IdentifierReference(listOf(function), expr.position),
|
||||
mutableListOf(expr.left.copy()), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, squareCall, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ class IntermediateAstMaker(private val program: Program) {
|
||||
|
||||
private fun transform(srcUnroll: UnrollLoop): PtNodeGroup {
|
||||
val result = PtNodeGroup()
|
||||
repeat(srcUnroll.iterations) {
|
||||
repeat(srcUnroll.iterations.constValue(program)!!.number.toInt()) {
|
||||
srcUnroll.body.statements.forEach {
|
||||
result.add(transformStatement(it))
|
||||
}
|
||||
|
||||
@@ -143,8 +143,6 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
||||
|
||||
private fun simplifyConditionalExpression(expr: BinaryExpression): CondExprSimplificationResult {
|
||||
|
||||
// TODO: somehow figure out if the expr will result in stack-evaluation STILL after being split off,
|
||||
// in that case: do *not* split it off but just keep it as it is (otherwise code size increases)
|
||||
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
|
||||
|
||||
if(compTarget.name == VMTarget.NAME) // don't apply this optimization for Vm target
|
||||
|
||||
@@ -27,6 +27,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
filepath,
|
||||
optimize,
|
||||
writeAssembly = true,
|
||||
warnSymbolShadowing = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
@@ -97,6 +98,8 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"vtui/testvtui",
|
||||
"pcmaudio/play-adpcm",
|
||||
"pcmaudio/stream-wav",
|
||||
"sprites/dragon",
|
||||
"sprites/dragons",
|
||||
"amiga",
|
||||
"bdmusic",
|
||||
"bobs",
|
||||
@@ -106,17 +109,14 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"colorbars",
|
||||
"cube3d",
|
||||
"cxlogo",
|
||||
"datetime",
|
||||
"diskspeed",
|
||||
"fileseek",
|
||||
"highresbitmap",
|
||||
"kefrenbars",
|
||||
"keyboardhandler",
|
||||
"mandelbrot",
|
||||
"mandelbrot-gfx-colors",
|
||||
"multipalette",
|
||||
"plasma",
|
||||
"rasterbars",
|
||||
"sincos",
|
||||
"snow",
|
||||
"spotlight",
|
||||
"tehtriz",
|
||||
@@ -141,7 +141,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
listOf(
|
||||
"animals",
|
||||
"balls",
|
||||
"bsieve",
|
||||
"cube3d",
|
||||
"cube3d-float",
|
||||
"cube3d-gfx",
|
||||
@@ -155,6 +154,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"numbergame",
|
||||
"primes",
|
||||
"screencodes",
|
||||
"sincos",
|
||||
"sorting",
|
||||
"swirl",
|
||||
"swirl-float",
|
||||
|
||||
@@ -44,6 +44,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
filepath = filePath,
|
||||
optimize = false,
|
||||
writeAssembly = true,
|
||||
warnSymbolShadowing = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
|
||||
@@ -5,7 +5,7 @@ import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.compiler.astprocessing.IntermediateAstMaker
|
||||
import prog8tests.helpers.compileText
|
||||
@@ -26,16 +26,6 @@ class TestIntermediateAst: FunSpec({
|
||||
}
|
||||
"""
|
||||
val target = C64Target()
|
||||
val options = CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
emptyList(),
|
||||
floats = false,
|
||||
noSysInit = true,
|
||||
compTarget = target,
|
||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||
)
|
||||
val result = compileText(target, false, text, writeAssembly = false)!!
|
||||
val ast = IntermediateAstMaker(result.compilerAst).transform()
|
||||
ast.name shouldBe result.compilerAst.name
|
||||
|
||||
@@ -351,5 +351,20 @@ main
|
||||
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("char as str param is error") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
print('@')
|
||||
}
|
||||
|
||||
sub print(str message) {
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.single() shouldContain "cannot use byte value"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -24,6 +24,7 @@ internal fun compileFile(
|
||||
filepath,
|
||||
optimize,
|
||||
writeAssembly = writeAssembly,
|
||||
warnSymbolShadowing = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
|
||||
@@ -8,6 +8,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
import prog8.parser.Prog8ANTLRParser
|
||||
import prog8.parser.Prog8ANTLRParser.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
@@ -310,12 +311,19 @@ private fun Prog8ANTLRParser.Sub_paramsContext.toAst(): List<SubroutineParameter
|
||||
}
|
||||
|
||||
private fun Prog8ANTLRParser.Assign_targetContext.toAst() : AssignTarget {
|
||||
val identifier = scoped_identifier()
|
||||
return when {
|
||||
identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition())
|
||||
arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition())
|
||||
directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
|
||||
else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition())
|
||||
return when(this) {
|
||||
is IdentifierTargetContext ->
|
||||
AssignTarget(scoped_identifier().toAst(), null, null, scoped_identifier().toPosition())
|
||||
is MemoryTargetContext ->
|
||||
AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()), toPosition())
|
||||
is ArrayindexedTargetContext -> {
|
||||
val ax = arrayindexed()
|
||||
val arrayvar = ax.scoped_identifier().toAst()
|
||||
val index = ax.arrayindex().toAst()
|
||||
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
|
||||
AssignTarget(null, arrayindexed, null, toPosition())
|
||||
}
|
||||
else -> throw FatalAstException("weird assign target node $this")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +435,13 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression {
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayindexed()!=null) {
|
||||
val ax = arrayindexed()
|
||||
val identifier = ax.scoped_identifier().toAst()
|
||||
val index = ax.arrayindex().toAst()
|
||||
return ArrayIndexedExpression(identifier, index, ax.toPosition())
|
||||
}
|
||||
|
||||
if(scoped_identifier()!=null)
|
||||
return scoped_identifier().toAst()
|
||||
|
||||
@@ -448,9 +463,6 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression {
|
||||
if(childCount==3 && children[0].text=="(" && children[2].text==")")
|
||||
return expression(0).toAst() // expression within ( )
|
||||
|
||||
if(arrayindexed()!=null)
|
||||
return arrayindexed().toAst()
|
||||
|
||||
if(typecast()!=null)
|
||||
return TypecastExpression(expression(0).toAst(), typecast().datatype().toAst(), false, toPosition())
|
||||
|
||||
@@ -497,12 +509,6 @@ private fun Prog8ANTLRParser.StringliteralContext.toAst(): StringLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Prog8ANTLRParser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
|
||||
return ArrayIndexedExpression(scoped_identifier().toAst(),
|
||||
arrayindex().toAst(),
|
||||
toPosition())
|
||||
}
|
||||
|
||||
private fun Prog8ANTLRParser.Expression_listContext.toAst() = expression().map{ it.toAst() }
|
||||
|
||||
private fun Prog8ANTLRParser.Scoped_identifierContext.toAst() : IdentifierReference =
|
||||
@@ -577,7 +583,7 @@ private fun Prog8ANTLRParser.RepeatloopContext.toAst(): RepeatLoop {
|
||||
}
|
||||
|
||||
private fun Prog8ANTLRParser.UnrollloopContext.toAst(): UnrollLoop {
|
||||
val iterations = integerliteral().toAst().number.toInt()
|
||||
val iterations = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
|
||||
@@ -184,29 +184,32 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
||||
val leftDt = left.inferType(program)
|
||||
val rightDt = right.inferType(program)
|
||||
|
||||
// fun dynamicBooleanType(): InferredTypes.InferredType {
|
||||
// // as a special case, an expression yielding a boolean result, adapts the result
|
||||
// // type to what is required (byte or word), to avoid useless type casting
|
||||
// return when (parent) {
|
||||
// is TypecastExpression -> InferredTypes.InferredType.known((parent as TypecastExpression).type)
|
||||
// is Assignment -> (parent as Assignment).target.inferType(program)
|
||||
// else -> InferredTypes.InferredType.known(DataType.BOOL) // or UBYTE?
|
||||
// }
|
||||
// }
|
||||
|
||||
return when (operator) {
|
||||
"+", "-", "*", "%", "/" -> {
|
||||
if (!leftDt.isKnown || !rightDt.isKnown)
|
||||
InferredTypes.unknown()
|
||||
else {
|
||||
try {
|
||||
InferredTypes.knownFor(
|
||||
val dt = InferredTypes.knownFor(
|
||||
commonDatatype(
|
||||
leftDt.getOr(DataType.BYTE),
|
||||
rightDt.getOr(DataType.BYTE),
|
||||
null, null
|
||||
).first
|
||||
)
|
||||
if(operator=="*") {
|
||||
// if both operands are the same, X*X is always positive.
|
||||
if(left isSameAs right) {
|
||||
if(dt.istype(DataType.BYTE))
|
||||
InferredTypes.knownFor(DataType.UBYTE)
|
||||
else if(dt.istype(DataType.WORD))
|
||||
InferredTypes.knownFor(DataType.UWORD)
|
||||
else
|
||||
dt
|
||||
} else
|
||||
dt
|
||||
} else
|
||||
dt
|
||||
} catch (x: FatalAstException) {
|
||||
InferredTypes.unknown()
|
||||
}
|
||||
@@ -951,7 +954,15 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
|
||||
override fun copy() = IdentifierReference(nameInSource, position)
|
||||
override fun constValue(program: Program): NumericLiteral? {
|
||||
val node = definingScope.lookup(nameInSource) ?: throw UndefinedSymbolError(this)
|
||||
val node = definingScope.lookup(nameInSource)
|
||||
if(node==null) {
|
||||
// maybe not a statement but perhaps a subroutine parameter?
|
||||
(definingScope as? Subroutine)?.let { sub ->
|
||||
if(sub.parameters.any { it.name==nameInSource.last() })
|
||||
return null
|
||||
}
|
||||
throw UndefinedSymbolError(this)
|
||||
}
|
||||
val vardecl = node as? VarDecl
|
||||
if(vardecl==null) {
|
||||
return null
|
||||
|
||||
@@ -931,11 +931,13 @@ class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override
|
||||
iterations?.referencesIdentifier(nameInSource)==true || body.referencesIdentifier(nameInSource)
|
||||
}
|
||||
|
||||
class UnrollLoop(val iterations: Int, var body: AnonymousScope, override val position: Position) : Statement() {
|
||||
class UnrollLoop(var iterations: Expression, var body: AnonymousScope, override val position: Position) : Statement() {
|
||||
// note: the iterations needs to evaluate to a constant number once parsed.
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
iterations.linkParents(this)
|
||||
body.linkParents(this)
|
||||
}
|
||||
|
||||
@@ -943,13 +945,14 @@ class UnrollLoop(val iterations: Int, var body: AnonymousScope, override val pos
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
if (node===body) body = replacement as AnonymousScope
|
||||
else if (node===iterations) iterations = replacement as Expression
|
||||
else throw FatalAstException("invalid replace")
|
||||
replacement.parent = this
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = body.referencesIdentifier(nameInSource)
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = iterations.referencesIdentifier(nameInSource) || body.referencesIdentifier(nameInSource)
|
||||
}
|
||||
|
||||
class UntilLoop(var body: AnonymousScope,
|
||||
|
||||
@@ -398,6 +398,7 @@ abstract class AstWalker {
|
||||
|
||||
fun visit(unrollLoop: UnrollLoop, parent: Node) {
|
||||
track(before(unrollLoop, parent), unrollLoop, parent)
|
||||
unrollLoop.iterations.accept(this, unrollLoop)
|
||||
unrollLoop.body.accept(this, unrollLoop)
|
||||
track(after(unrollLoop, parent), unrollLoop, parent)
|
||||
}
|
||||
|
||||
@@ -113,8 +113,8 @@ One or more .p8 module files
|
||||
|
||||
``-target <compilation target>``
|
||||
Sets the target output of the compiler. This option is required.
|
||||
``c64`` = Commodore 64, ``c128`` = Commodore 128, ``cx16`` = Commander X16, ``atari`` = Atari 800 XL,
|
||||
``virtual`` = builtin virtual machine.
|
||||
``c64`` = Commodore 64, ``c128`` = Commodore 128, ``cx16`` = Commander X16, ``pet32`` - Commodore PET model 4032,
|
||||
``atari`` = Atari 800 XL, ``virtual`` = builtin virtual machine.
|
||||
|
||||
``-srcdirs <pathlist>``
|
||||
Specify a list of extra paths (separated with ':'), to search in for imported modules.
|
||||
@@ -151,6 +151,15 @@ One or more .p8 module files
|
||||
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)
|
||||
|
||||
``-quietasm``
|
||||
Don't print assembler output results.
|
||||
|
||||
@@ -291,8 +300,8 @@ You can do one of two things to fix the build error:
|
||||
- install a JDK with that version,
|
||||
- or change the version number to match the JDK version that *is* installed on your system (must be >= 11)
|
||||
|
||||
Strange assembler error
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Strange assembler errors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
If the compilation of your program fails in the assembly step, please check that you have
|
||||
the required version of the 64tass assembler installed. See :ref:`requirements`.
|
||||
Also make sure that inside hand-written inlined assembly,
|
||||
@@ -300,6 +309,12 @@ you don't use symbols named just a single letter (especially 'a', 'x' and 'y').
|
||||
Sometimes these are interpreted as the CPU register of that name. To avoid such confusions,
|
||||
always use 2 or more letters for symbols in your assembly code.
|
||||
|
||||
'shadowing' warnings form the assembler
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Avoid using 'a', 'x' or 'y' as symbols in your inlined assembly code.
|
||||
Also avoid using 64tass' built-in function or type names as symbols in your inlined assembly code.
|
||||
The 64tass manual contains `a list of those <https://tass64.sourceforge.net/#functions>`_.
|
||||
|
||||
|
||||
Community
|
||||
---------
|
||||
|
||||
@@ -52,7 +52,7 @@ Language features
|
||||
|
||||
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
|
||||
It generates a machine code program runnable on actual 8-bit 6502 hardware.
|
||||
- Fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
||||
- Fast execution speed due to compilation to native assembly code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects.
|
||||
- Provides a very convenient edit/compile/run cycle by being able to directly launch
|
||||
the compiled program in an emulator and provide debugging information to this emulator.
|
||||
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
|
||||
@@ -62,19 +62,18 @@ Language features
|
||||
and inline assembly to have full control when every register, cycle or byte matters
|
||||
- Subroutines with parameters and return values
|
||||
- Complex nested expressions are possible
|
||||
- Variables are allocated statically
|
||||
- Variables are all allocated statically
|
||||
- Conditional branches to map directly on processor branch instructions
|
||||
- ``when`` statement to avoid if-else chains
|
||||
- ``in`` expression for concise and efficient multi-value/containment test
|
||||
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
||||
- Several powerful built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``, ``sort`` and ``reverse``
|
||||
- Variable data types include signed and unsigned bytes and words, arrays, strings.
|
||||
- Floating point math also supported on select compiler targets (C64, Cx16 and virtual).
|
||||
- Floating point math is supported on select compiler targets.
|
||||
- Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents.
|
||||
- High-level code optimizations, such as const-folding (zero-allocation constants that are optimized away in expressions), expression and statement simplifications/rewriting.
|
||||
- Many built-in functions, such as ``sin``, ``cos``, ``abs``, ``sqrt``, ``msb``, ``min``, ``max``, ``rol``, ``ror``, ``sort`` and ``reverse``
|
||||
- Programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the other machines.
|
||||
- If you only use standard Kernal and core prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compilation target flag)!
|
||||
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 as defined on the Commander X16, also on the other machines.
|
||||
- If you only use standard Kernal and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines (just change the compilation target flag)
|
||||
|
||||
|
||||
Code example
|
||||
|
||||
+25
-12
@@ -35,7 +35,7 @@ as ROM/Kernal subroutine definitions, memory location constants, and utility sub
|
||||
Many of these definitions overlap for the C64 and Commander X16 targets so it is still possible
|
||||
to write programs that work on both targets without modifications.
|
||||
|
||||
This module is usually imported automatically and can provide definitions in the ``sys``, ``c64``, ``cx16``, ``c128``, ``atari`` blocks
|
||||
This module is usually imported automatically and can provide definitions in the ``sys``, ``cbm``, ``c64``, ``cx16``, ``c128``, ``atari`` blocks
|
||||
depending on the chosen compilation target. Read the `syslib source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib>`_ for the correct compilation target to see exactly what is there.
|
||||
|
||||
|
||||
@@ -312,13 +312,10 @@ point variables. This includes ``print_f``, the routine used to print floating
|
||||
``rndseedf (seed)``
|
||||
Sets a new seed for the float pseudo-RNG sequence. Use a negative non-zero number as seed value.
|
||||
|
||||
.. attention::
|
||||
A "parse" routine to convert a string into a floating point number is suspiciously absent.
|
||||
This is because unfortunately there is no such routine available on each of the compiler targets.
|
||||
Only the C64 target has the ``floats.FREADSTR()`` kernal routine that parses a string into a float into FAC1,
|
||||
but the X16 has no equivalent in the kernal rom at this time.
|
||||
The same is true for parsing an *integer* number however you can use the parse routines in the ``conv`` module as
|
||||
a stepping stone to eventually load the number into FAC1 using one of the available kernal routines.
|
||||
``parse_f (stringvalue)``
|
||||
Parses the string value as floating point number.
|
||||
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.
|
||||
|
||||
|
||||
graphics
|
||||
@@ -329,10 +326,9 @@ Bitmap graphics routines:
|
||||
- drawing individual pixels
|
||||
- drawing lines, rectangles, filled rectangles, circles, discs
|
||||
|
||||
This library is available both on the C64 and the Cx16.
|
||||
This library is available both on the C64 and the cx16.
|
||||
It uses the ROM based graphics routines on the latter, and it is a very small library because of that.
|
||||
That also means though that it is constrained to 320*200 resolution on the Cx16 as well.
|
||||
Use the ``gfx2`` library if you want full-screen graphics or non-monochrome drawing (only on Cx16). See below for that one.
|
||||
On the cx16 there's also the ``gfx2`` library if you want full-screen graphics or non-monochrome drawing. See below for that one.
|
||||
|
||||
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/c64/graphics.p8>`_
|
||||
to see what's in there. (Note: slight variations for different compiler targets)
|
||||
@@ -346,6 +342,11 @@ Various 8-bit integer trig functions that use lookup tables to quickly calculate
|
||||
Usually a custom lookup table is the way to go if your application needs these,
|
||||
but perhaps the provided ones can be of service too.
|
||||
|
||||
``log2 (ubyte v)``
|
||||
Returns the 2-Log of the byte value v.
|
||||
|
||||
``log2w (uword v)``
|
||||
Returns the 2-Log of the word value v.
|
||||
|
||||
``rnd ()``
|
||||
Returns next random byte 0-255 from the pseudo-RNG sequence.
|
||||
@@ -470,5 +471,17 @@ It includes an interrupt routine to handle simple Attack/Release envelopes as we
|
||||
See the examples/cx16/bdmusic.p8 program for ideas how to use it.
|
||||
|
||||
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/psg.p8>`_
|
||||
to see what's in there. (Note: slight variations for different compiler targets)
|
||||
to see what's in there.
|
||||
|
||||
|
||||
sprites (cx16 only)
|
||||
--------------------
|
||||
Available for the Cx16 target. Simple routines to manipulate sprites.
|
||||
They're not written for high performance, but for simplicity.
|
||||
That's why they control one sprite at a time. The exception is the ``pos_batch`` routine,
|
||||
which is quite efficient to update sprite positions of multiple sprites in one go.
|
||||
See the examples/cx16/sprites/dragon.p8 and dragons.p8 programs for ideas how to use it.
|
||||
|
||||
|
||||
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/sprites.p8>`_
|
||||
to see what's in there.
|
||||
@@ -14,6 +14,7 @@ Currently these machines can be selected as a compilation target (via the ``-tar
|
||||
- 'c64': the Commodore 64
|
||||
- 'cx16': the `Commander X16 <https://www.commanderx16.com/>`_
|
||||
- 'c128': the Commodore 128 (*limited support*)
|
||||
- 'pet32': the Commodore PET 4032 (*limited support*)
|
||||
- 'atari': the Atari 800 XL (*experimental support*)
|
||||
- 'virtual': a builtin virtual machine
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- [on branch:] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
|
||||
- prefix prog8 subroutines with p8s_ instead of p8_ to not let them clash with variables in the asm??
|
||||
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
|
||||
- IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction
|
||||
- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified!
|
||||
|
||||
@@ -19,6 +20,9 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- allow 'chained' array indexing for expressions: value = ptrarray[0][0]
|
||||
- allow 'chained' array indexing for assign targets: ptrarray[0][0] = 42 this is just evaluating the lhs as a uword pointer expression
|
||||
|
||||
- [much work:] more support for (64tass) SEGMENTS ?
|
||||
- (What, how, isn't current BSS support enough?)
|
||||
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
|
||||
@@ -48,11 +52,13 @@ Libraries:
|
||||
|
||||
- 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)?
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||
|
||||
|
||||
Optimizations:
|
||||
|
||||
- optimize assembly output for ( word1 & word2 ==0) ... (no need for stack pushes) see attemptAssignToByteCompareZero().
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
main {
|
||||
|
||||
sub crc16(uword data, uword length) -> uword {
|
||||
uword crc = 0
|
||||
repeat length {
|
||||
crc ^= @(data) << $0008
|
||||
crc ^= mkword(@(data), 0)
|
||||
repeat 8 {
|
||||
if crc & $8000
|
||||
crc = (crc<<1)^$1021
|
||||
@@ -18,13 +19,13 @@ main {
|
||||
}
|
||||
|
||||
sub start() {
|
||||
txt.print("calculating...")
|
||||
txt.print("calculating (expecting $ffd0)...")
|
||||
cbm.SETTIM(0,0,0)
|
||||
uword crc = crc16($e000, $2000)
|
||||
txt.print_uwhex(crc, true) ; should be $ffd0
|
||||
txt.print_uwhex(crc, true)
|
||||
txt.nl()
|
||||
txt.print_uw(cbm.RDTIM16())
|
||||
txt.print(" jiffies")
|
||||
sys.wait(100)
|
||||
floats.print_f(cbm.RDTIM16() / 60.0)
|
||||
txt.print(" seconds")
|
||||
sys.wait(9999)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub crc32(uword data, uword length) {
|
||||
@@ -7,7 +8,7 @@ main {
|
||||
cx16.r0 = 0
|
||||
cx16.r1 = 0
|
||||
repeat length {
|
||||
cx16.r0 ^= @(data) << $0008
|
||||
cx16.r0 ^= mkword(@(data), 0)
|
||||
repeat 8 {
|
||||
if cx16.r0 & $8000 {
|
||||
sys.clear_carry()
|
||||
@@ -29,14 +30,14 @@ main {
|
||||
}
|
||||
|
||||
sub start() {
|
||||
txt.print("calculating...")
|
||||
txt.print("calculating (expecting $e1fa84c6)...\n")
|
||||
cbm.SETTIM(0,0,0)
|
||||
crc32($e000, $2000)
|
||||
txt.print_uwhex(cx16.r0, true)
|
||||
txt.print_uwhex(cx16.r1, false)
|
||||
txt.nl()
|
||||
txt.print_uw(cbm.RDTIM16())
|
||||
txt.print(" jiffies")
|
||||
sys.wait(100)
|
||||
floats.print_f(cbm.RDTIM16() / 60.0)
|
||||
txt.print(" seconds")
|
||||
sys.wait(9999)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
main {
|
||||
|
||||
@@ -18,13 +19,13 @@ main {
|
||||
}
|
||||
|
||||
sub start() {
|
||||
txt.print("calculating...")
|
||||
txt.print("calculating (expecting $a2)...")
|
||||
cbm.SETTIM(0,0,0)
|
||||
ubyte crc = crc8($e000, $2000)
|
||||
txt.print_ubhex(crc, true) ; should be $a2
|
||||
txt.print_ubhex(crc, true)
|
||||
txt.nl()
|
||||
txt.print_uw(cbm.RDTIM16())
|
||||
txt.print(" jiffies")
|
||||
sys.wait(100)
|
||||
floats.print_f(cbm.RDTIM16() / 60.0)
|
||||
txt.print(" seconds")
|
||||
sys.wait(9999)
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,10 @@ main {
|
||||
}
|
||||
|
||||
sub start() {
|
||||
txt.print("calculating...")
|
||||
txt.print("calculating (expecting 3.614007e+12)...\n")
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
float res
|
||||
float res=0.0
|
||||
uword i
|
||||
ubyte j
|
||||
for i in 0 to N_ITER-1 {
|
||||
@@ -35,8 +35,8 @@ main {
|
||||
|
||||
floats.print_f(res)
|
||||
txt.nl()
|
||||
txt.print_uw(cbm.RDTIM16())
|
||||
txt.print(" jiffies")
|
||||
sys.wait(100)
|
||||
floats.print_f(cbm.RDTIM16() / 60.0)
|
||||
txt.print(" seconds")
|
||||
sys.wait(9999)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
main {
|
||||
const ubyte N_ITER = 4
|
||||
const uword SIZE = 16000
|
||||
|
||||
uword @zp flags_ptr = memory("flags", SIZE/8+1, $100)
|
||||
ubyte[] bitv = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
|
||||
|
||||
sub start() {
|
||||
txt.print_ub(N_ITER)
|
||||
txt.print(" iterations, calculating... (expecting 3431)\n")
|
||||
|
||||
cbm.SETTIM(0, 0, 0)
|
||||
uword prime_count
|
||||
repeat N_ITER {
|
||||
prime_count = sieve()
|
||||
}
|
||||
|
||||
txt.print_uw(prime_count)
|
||||
txt.print(" primes\n")
|
||||
|
||||
float time = cbm.RDTIM16() as float / 60.0
|
||||
floats.print_f(time)
|
||||
txt.print(" sec total = ")
|
||||
floats.print_f(time/N_ITER)
|
||||
txt.print(" sec per iteration\n")
|
||||
sys.wait(9999)
|
||||
}
|
||||
|
||||
sub check_flag(uword idx) -> ubyte
|
||||
{
|
||||
return flags_ptr[idx/8] & bitv[lsb(idx)&7]
|
||||
}
|
||||
|
||||
sub clear_flag(uword idx)
|
||||
{
|
||||
flags_ptr[idx/8] &= ~bitv[lsb(idx)&7]
|
||||
}
|
||||
|
||||
sub sieve() -> uword {
|
||||
uword prime
|
||||
uword k
|
||||
uword count=0
|
||||
uword i
|
||||
sys.memset(flags_ptr, SIZE/8+1, $ff)
|
||||
|
||||
for i in 0 to SIZE-1 {
|
||||
if check_flag(i) {
|
||||
prime = i*2 + 3
|
||||
k = i + prime
|
||||
while k < SIZE {
|
||||
clear_flag(k)
|
||||
k += prime
|
||||
}
|
||||
; txt.print_uw(prime)
|
||||
; txt.spc()
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
; The "Byte Sieve" test. https://en.wikipedia.org/wiki/Byte_Sieve
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
@@ -9,7 +7,6 @@
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
cbm.SETTIM(0, 0, 0)
|
||||
|
||||
const ubyte ITERS = 10
|
||||
uword count
|
||||
@@ -22,15 +19,17 @@ main {
|
||||
txt.print_ub(ITERS)
|
||||
txt.print(" iterations, calculating...\n")
|
||||
|
||||
cbm.SETTIM(0, 0, 0)
|
||||
|
||||
repeat ITERS {
|
||||
sys.memset(flags_ptr, SIZEPL, 1)
|
||||
count = 0
|
||||
for i in 0 to SIZEPL-1 {
|
||||
if @(flags_ptr+i) {
|
||||
prime = i + i + 3
|
||||
if flags_ptr[i] {
|
||||
prime = i*2 + 3
|
||||
k = i + prime
|
||||
while k <= SIZEPL-1 {
|
||||
@(flags_ptr + k) = false
|
||||
while k < SIZEPL {
|
||||
flags_ptr[k] = false
|
||||
k += prime
|
||||
}
|
||||
; txt.print_uw(prime)
|
||||
@@ -48,5 +47,6 @@ main {
|
||||
txt.print(" sec total = ")
|
||||
floats.print_f(time/ITERS)
|
||||
txt.print(" sec per iteration\n")
|
||||
sys.wait(9999)
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,7 @@ main {
|
||||
charset.make_custom_charset()
|
||||
|
||||
; activate the new charset in RAM
|
||||
ubyte block = c64.CIA2PRA
|
||||
const ubyte PAGE1 = ((cbm.Screen >> 6) & $F0) | ((charset.CHARSET >> 10) & $0E)
|
||||
|
||||
c64.CIA2PRA = (block & $FC) | (lsb(cbm.Screen >> 14) ^ $03)
|
||||
c64.VMCSB = PAGE1
|
||||
|
||||
txt.print("\n @ @ @ @\n")
|
||||
|
||||
+43
-34
@@ -1,5 +1,4 @@
|
||||
%import syslib
|
||||
%import test_stack
|
||||
%import textio
|
||||
%import math
|
||||
|
||||
@@ -7,8 +6,9 @@
|
||||
; which is (w)2001 by groepaz/hitmen
|
||||
;
|
||||
; Cleanup and porting to C by Ullrich von Bassewitz.
|
||||
; Converted to prog8 by Irmen de Jong.
|
||||
|
||||
; See https://github.com/cc65/cc65/tree/master/samples/cbm/plasma.c
|
||||
;
|
||||
; Optimized and Converted to prog8 by Irmen de Jong.
|
||||
|
||||
|
||||
main {
|
||||
@@ -22,27 +22,39 @@ main {
|
||||
sub start() {
|
||||
txt.color(1)
|
||||
txt.clear_screen()
|
||||
txt.print("creating charset...\n")
|
||||
txt.print("creating charset...\n\nwhile running, press key to stop.\n\n")
|
||||
makechar()
|
||||
|
||||
ubyte block = c64.CIA2PRA
|
||||
; ubyte v = cbm.VMCSB
|
||||
ubyte v = c64.VMCSB
|
||||
c64.CIA2PRA = (block & $FC) | (lsb(SCREEN1 >> 14) ^ $03)
|
||||
|
||||
repeat {
|
||||
uword frames = 0
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
while cbm.GETIN()==0 {
|
||||
doplasma(SCREEN1)
|
||||
c64.VMCSB = PAGE1
|
||||
doplasma(SCREEN2)
|
||||
c64.VMCSB = PAGE2
|
||||
frames += 2
|
||||
}
|
||||
|
||||
; restore screen (if you want)
|
||||
;c64.VMCSB = v
|
||||
;c64.CIA2PRA = block
|
||||
;txt.print("done!\n")
|
||||
;test_stack.test()
|
||||
;repeat {
|
||||
;}
|
||||
uword jiffies = cbm.RDTIM16()
|
||||
|
||||
; restore screen and displays stats
|
||||
c64.VMCSB = v
|
||||
c64.CIA2PRA = block
|
||||
txt.print("time in jiffies: ")
|
||||
txt.print_uw(jiffies)
|
||||
txt.print("\nframes: ")
|
||||
txt.print_uw(frames)
|
||||
uword fps = (frames*60)/jiffies
|
||||
txt.print("\nfps: ")
|
||||
txt.print_uw(fps)
|
||||
txt.print("\ndone!\n")
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
|
||||
; several variables outside of doplasma to make them retain their value
|
||||
@@ -51,7 +63,7 @@ main {
|
||||
ubyte c2A
|
||||
ubyte c2B
|
||||
|
||||
sub doplasma(uword screen) {
|
||||
sub doplasma(uword @zp screen) {
|
||||
ubyte[40] xbuf
|
||||
ubyte[25] ybuf
|
||||
ubyte c1a = c1A
|
||||
@@ -61,14 +73,14 @@ main {
|
||||
ubyte @zp x
|
||||
ubyte @zp y
|
||||
|
||||
for y in 24 downto 0 {
|
||||
for y in 0 to 24 {
|
||||
ybuf[y] = math.sin8u(c1a) + math.sin8u(c1b)
|
||||
c1a += 4
|
||||
c1b += 9
|
||||
}
|
||||
c1A += 3
|
||||
c1B -= 5
|
||||
for x in 39 downto 0 {
|
||||
for x in 0 to 39 {
|
||||
xbuf[x] = math.sin8u(c2a) + math.sin8u(c2b)
|
||||
c2a += 3
|
||||
c2b += 7
|
||||
@@ -76,20 +88,18 @@ main {
|
||||
c2A += 2
|
||||
c2B -= 3
|
||||
|
||||
for y in 24 downto 0 {
|
||||
for x in 39 downto 0 {
|
||||
; using a temp var here to enable expression optimization that can't be done on a 'problematic' ROM/RAM memory location
|
||||
ubyte @zp cc = xbuf[x] + ybuf[y]
|
||||
@(screen+x) = cc
|
||||
; this is the fastest way to do this inner part:
|
||||
; %asm {{
|
||||
; ldy y
|
||||
; lda ybuf,y
|
||||
; ldy x
|
||||
; clc
|
||||
; adc xbuf,y
|
||||
; sta (screen),y
|
||||
; }}
|
||||
for y in 0 to 24 {
|
||||
ubyte @zp @shared yvalue = ybuf[y]
|
||||
for x in 0 to 39 {
|
||||
; @(screen+x) = xbuf[x] + yvalue
|
||||
; max optimized asm is this: (achieving ~21 fps on the C64):
|
||||
%asm {{
|
||||
lda p8_yvalue
|
||||
ldy p8_x
|
||||
clc
|
||||
adc p8_xbuf,y
|
||||
sta (p8_screen),y
|
||||
}}
|
||||
}
|
||||
screen += 40
|
||||
}
|
||||
@@ -99,16 +109,15 @@ main {
|
||||
ubyte[8] bittab = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
|
||||
ubyte c
|
||||
for c in 0 to 255 {
|
||||
ubyte @zp s = math.sin8u(c)
|
||||
ubyte @zp s = math.sin8u(c) ; chance
|
||||
ubyte i
|
||||
; for all the pixels in the 8x8 character grid, determine (with a rnd chance) if they should be on or off
|
||||
for i in 0 to 7 {
|
||||
ubyte b=0
|
||||
ubyte @zp ii
|
||||
for ii in 0 to 7 {
|
||||
; use 16 bit rng for a bit more randomness instead of the 8-bit rng
|
||||
if math.rnd() > s {
|
||||
if math.rnd() > s
|
||||
b |= bittab[ii]
|
||||
}
|
||||
}
|
||||
@(CHARSET + i + c*$0008) = b
|
||||
}
|
||||
|
||||
+30
-34
@@ -1,6 +1,5 @@
|
||||
%import floats
|
||||
%import graphics
|
||||
%import test_stack
|
||||
%zeropage floatsafe
|
||||
|
||||
|
||||
@@ -9,9 +8,10 @@ main {
|
||||
sub start() {
|
||||
graphics.enable_bitmap_mode()
|
||||
turtle.init()
|
||||
; turtle.pu()
|
||||
; turtle.pos(150, 110)
|
||||
; turtle.pd()
|
||||
|
||||
turtle.pu()
|
||||
turtle.pos(150, 110)
|
||||
turtle.pd()
|
||||
|
||||
ubyte i
|
||||
for i in 0 to 100 {
|
||||
@@ -19,8 +19,6 @@ main {
|
||||
turtle.rt(94)
|
||||
}
|
||||
|
||||
; test_stack.test()
|
||||
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
@@ -38,7 +36,11 @@ turtle {
|
||||
angle = 0.0
|
||||
pendown = true
|
||||
|
||||
c64.SPRPTR[0] = $0d00 / 64
|
||||
const uword SPRITE_MEMORY = $5800
|
||||
const uword SPRITE_ADDR_POINTERS = (graphics.CHARS_ADDRESS & $fc00) + 1016 ; no longer the default location 2040!
|
||||
|
||||
sys.memcopy(&turtlesprite, SPRITE_MEMORY, len(turtlesprite))
|
||||
@(SPRITE_ADDR_POINTERS) = (SPRITE_MEMORY & $3fff) / 64
|
||||
c64.SPENA = 1
|
||||
c64.SP0COL = 5
|
||||
|
||||
@@ -88,32 +90,26 @@ turtle {
|
||||
sub pd() {
|
||||
pendown = true
|
||||
}
|
||||
}
|
||||
|
||||
spritedata $0d00 {
|
||||
; this memory block contains the sprite data
|
||||
; it must start on an address aligned to 64 bytes.
|
||||
%option force_output ; make sure the data in this block appears in the resulting program
|
||||
|
||||
ubyte[] balloonsprite = [ %00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%01111110,%00000000,
|
||||
%00000000,%11000011,%00000000,
|
||||
%00000000,%11000011,%00000000,
|
||||
%00000000,%11000011,%00000000,
|
||||
%00000000,%01111110,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000 ]
|
||||
ubyte[] turtlesprite = [ %00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%01111110,%00000000,
|
||||
%00000000,%11000011,%00000000,
|
||||
%00000000,%11000011,%00000000,
|
||||
%00000000,%11000011,%00000000,
|
||||
%00000000,%01111110,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000,
|
||||
%00000000,%00000000,%00000000 ]
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
; "unlimited sprites / bobs" demo effect.
|
||||
; Note that everything is prog8, no inline assembly used/required.
|
||||
|
||||
; Note2: these aren't actual sprites, it's a bitmap backbuffer display trick.
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
@@ -104,7 +104,7 @@ main {
|
||||
|
||||
bitshift(lsb(blit_x) & 7)
|
||||
|
||||
; left column of the (shifted)sprite
|
||||
; left column of the (shifted) "sprite" (bob)
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = lsb(vmem)
|
||||
cx16.VERA_ADDR_M = msb(vmem)
|
||||
@@ -117,7 +117,7 @@ main {
|
||||
for ix in 0 to len(shifted_sprite)-1 step 3
|
||||
cx16.VERA_DATA1 = cx16.VERA_DATA0 & shifted_mask[ix] | shifted_sprite[ix]
|
||||
|
||||
; middle column of the (shifted)sprite
|
||||
; middle column of the (shifted) "sprite" (bob)
|
||||
vmem++
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = lsb(vmem)
|
||||
@@ -128,7 +128,7 @@ main {
|
||||
for ix in 1 to len(shifted_sprite)-1 step 3
|
||||
cx16.VERA_DATA1 = cx16.VERA_DATA0 & shifted_mask[ix] | shifted_sprite[ix]
|
||||
|
||||
; right column of the (shifted)sprite
|
||||
; right column of the (shifted) "sprite" (bob)
|
||||
vmem++
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = lsb(vmem)
|
||||
|
||||
@@ -69,12 +69,6 @@ main {
|
||||
return sqrt(sqx + sqy)
|
||||
}
|
||||
|
||||
; sub distance(ubyte cix) -> uword {
|
||||
; float dx = x as float - circle_x[cix]
|
||||
; float dy = y as float - circle_y[cix]
|
||||
; return floats.sqrt(dx*dx + dy*dy) as uword
|
||||
; }
|
||||
|
||||
sub not_edge() -> bool {
|
||||
if x as word - radius < 0
|
||||
return false
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
; CommanderX16 text datetime example!
|
||||
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
cx16.clock_set_date_time(mkword(8, 2022 - 1900), mkword(19, 27), mkword(30, 16), 0)
|
||||
txt.lowercase()
|
||||
|
||||
repeat {
|
||||
txt.chrout(19) ; HOME
|
||||
txt.print("\n yyyy-mm-dd HH:MM:SS.jj\n\n")
|
||||
void cx16.clock_get_date_time()
|
||||
txt.spc()
|
||||
print_date()
|
||||
txt.spc()
|
||||
print_time()
|
||||
|
||||
txt.nl()
|
||||
txt.nl()
|
||||
uword jiffies = cbm.RDTIM16()
|
||||
txt.print_uw(jiffies)
|
||||
}
|
||||
}
|
||||
|
||||
sub print_date() {
|
||||
txt.print_uw(1900 + lsb(cx16.r0))
|
||||
txt.chrout('-')
|
||||
if msb(cx16.r0) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(msb(cx16.r0))
|
||||
txt.chrout('-')
|
||||
if lsb(cx16.r1) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(lsb(cx16.r1))
|
||||
}
|
||||
|
||||
sub print_time() {
|
||||
if msb(cx16.r1) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(msb(cx16.r1))
|
||||
txt.chrout(':')
|
||||
if lsb(cx16.r2) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(lsb(cx16.r2))
|
||||
txt.chrout(':')
|
||||
if msb(cx16.r2) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(msb(cx16.r2))
|
||||
txt.chrout('.')
|
||||
if lsb(cx16.r3) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(lsb(cx16.r3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
const uword width = 256
|
||||
const uword height = 240
|
||||
const ubyte max_iter = 16 ; 32 actually looks pretty nice but takes longer
|
||||
|
||||
sub start() {
|
||||
initialize()
|
||||
mandel()
|
||||
repeat {
|
||||
; do nothing
|
||||
}
|
||||
}
|
||||
|
||||
sub mandel() {
|
||||
const float XL=-2.200
|
||||
const float XU=0.800
|
||||
const float YL=-1.300
|
||||
const float YU=1.300
|
||||
const float dx = (XU-XL)/width
|
||||
const float dy = (YU-YL)/height
|
||||
ubyte pixelx
|
||||
ubyte pixely
|
||||
|
||||
for pixely in 0 to height-1 {
|
||||
float yy = YL+dy*(pixely as float)
|
||||
|
||||
cx16.FB_cursor_position(0, pixely)
|
||||
|
||||
for pixelx in 0 to width-1 {
|
||||
float xx = XL+dx*(pixelx as float)
|
||||
|
||||
float xsquared = 0.0
|
||||
float ysquared = 0.0
|
||||
float x = 0.0
|
||||
float y = 0.0
|
||||
ubyte iter = 0
|
||||
|
||||
while iter<max_iter and xsquared+ysquared<4.0 {
|
||||
y = x*y*2.0 + yy
|
||||
x = xsquared - ysquared + xx
|
||||
xsquared = x*x
|
||||
ysquared = y*y
|
||||
iter++
|
||||
}
|
||||
cx16.FB_set_pixel(max_iter-iter)
|
||||
}
|
||||
|
||||
print_time()
|
||||
}
|
||||
}
|
||||
|
||||
sub initialize() {
|
||||
void cx16.screen_mode($80, false)
|
||||
|
||||
txt.plot(32, 5)
|
||||
txt.print("256*240")
|
||||
txt.plot(32, 6)
|
||||
txt.print("mandel-")
|
||||
txt.plot(33, 7)
|
||||
txt.print("brot")
|
||||
txt.plot(32, 9)
|
||||
txt.print("floats")
|
||||
txt.plot(32, 10)
|
||||
txt.print_b(max_iter)
|
||||
txt.print(" iter")
|
||||
|
||||
cx16.clock_set_date_time(0, 0, 0, 0)
|
||||
|
||||
cx16.r0=0
|
||||
cx16.FB_init()
|
||||
}
|
||||
|
||||
sub print_time() {
|
||||
void cx16.clock_get_date_time()
|
||||
txt.plot(33, 12)
|
||||
if lsb(cx16.r2) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(lsb(cx16.r2))
|
||||
txt.chrout(':')
|
||||
if msb(cx16.r2) < 10
|
||||
txt.chrout('0')
|
||||
txt.print_ub(msb(cx16.r2))
|
||||
}
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
%import palette
|
||||
%import gfx2
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
; palette.set_rgb(&colors, len(colors))
|
||||
void cx16.screen_mode(128, false) ; low-res bitmap 256 colors
|
||||
|
||||
cx16.FB_init()
|
||||
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00010000 ; enable only layer 0
|
||||
cx16.VERA_L0_CONFIG = %00000110 ; 4 bpp = 16 colors
|
||||
;cx16.VERA_L0_MAPBASE = 0
|
||||
;cx16.VERA_L0_TILEBASE = 0
|
||||
|
||||
ubyte pix=0
|
||||
ubyte ypos
|
||||
cx16.FB_cursor_position(0, 0)
|
||||
for ypos in 0 to 239 {
|
||||
repeat 320/2 {
|
||||
cx16.FB_set_pixel((pix&15)<<4 | (pix&15))
|
||||
pix++
|
||||
}
|
||||
pix=0
|
||||
}
|
||||
|
||||
; color index 0 can't be swapped - set it to black in both ranges
|
||||
palette.set_color(0, 0)
|
||||
palette.set_color(16, 0)
|
||||
|
||||
sys.set_rasterirq(&irq.irqhandler, 0)
|
||||
|
||||
repeat {
|
||||
; don't exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
irq {
|
||||
ubyte phase = 0
|
||||
uword next_rasterline = 0
|
||||
const ubyte increment = 4 ; 4 scanlines = 2 lores pixels per color swap (2 scanlines is too tight)
|
||||
|
||||
sub irqhandler() {
|
||||
if phase & 1 == 0 {
|
||||
%asm {{
|
||||
lda #0 ; activate palette #0 (first set of colors)
|
||||
sta cx16.VERA_L0_HSCROLL_H
|
||||
|
||||
stz cx16.VERA_CTRL
|
||||
lda #<$fa00+32+2
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda #>$fa00+32+2
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #%00010001
|
||||
sta cx16.VERA_ADDR_H
|
||||
|
||||
; change 15 palette entries 1..15 (0 is fixed)
|
||||
lda #<$e000
|
||||
sta $02
|
||||
lda #>$e000
|
||||
sta $02
|
||||
ldy #0
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
|
||||
|
||||
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$0f
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
|
||||
}}
|
||||
}
|
||||
else {
|
||||
%asm {{
|
||||
lda #1 ; activate palette #1 (second set of colors)
|
||||
sta cx16.VERA_L0_HSCROLL_H
|
||||
|
||||
stz cx16.VERA_CTRL
|
||||
lda #<$fa00+2
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda #>$fa00+2
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #%00010001
|
||||
sta cx16.VERA_ADDR_H
|
||||
|
||||
; change 15 palette entries 1..15 (0 is fixed)
|
||||
|
||||
lda #<$f000
|
||||
sta $02
|
||||
lda #>$f000
|
||||
sta $02
|
||||
ldy #0
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
lda (2),y
|
||||
sta cx16.VERA_DATA0
|
||||
|
||||
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #$ff
|
||||
; sta cx16.VERA_DATA0
|
||||
; lda #0
|
||||
; sta cx16.VERA_DATA0
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
phase++
|
||||
next_rasterline += increment
|
||||
|
||||
if next_rasterline >= 480 {
|
||||
next_rasterline = 0
|
||||
phase = 0
|
||||
}
|
||||
|
||||
sys.set_rasterline(next_rasterline)
|
||||
|
||||
;
|
||||
; uword[16] colors1 = 0
|
||||
; uword[16] colors2 = 200
|
||||
;
|
||||
; palette.set_rgb(colors1, len(colors1))
|
||||
; palette.set_rgb(colors2, len(colors2))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
%import syslib
|
||||
%import textio
|
||||
%import math
|
||||
%zeropage basicsafe
|
||||
|
||||
; converted from plasma test program for cc65.
|
||||
; which is (w)2001 by groepaz/hitmen
|
||||
;
|
||||
; Cleanup and porting to C by Ullrich von Bassewitz.
|
||||
; See https://github.com/cc65/cc65/tree/master/samples/cbm/plasma.c
|
||||
;
|
||||
; Optimized and Converted to prog8 by Irmen de Jong.
|
||||
|
||||
|
||||
main {
|
||||
const uword VERA_CHARSET = $f000 ; $1f000
|
||||
const uword VERA_TXTSCREEN = $b000 ; %1b000
|
||||
|
||||
sub start() {
|
||||
txt.color(6)
|
||||
txt.clear_screen()
|
||||
makechar()
|
||||
|
||||
uword frames = 0
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
while cbm.GETIN()==0 {
|
||||
doplasma()
|
||||
frames ++
|
||||
}
|
||||
|
||||
uword jiffies = cbm.RDTIM16()
|
||||
|
||||
; restore screen and displays stats
|
||||
|
||||
cx16.screen_set_charset(2, 0)
|
||||
txt.color(7)
|
||||
txt.clear_screen()
|
||||
txt.print("time in jiffies: ")
|
||||
txt.print_uw(jiffies)
|
||||
txt.print("\nframes: ")
|
||||
txt.print_uw(frames)
|
||||
uword fps = (frames*60)/jiffies
|
||||
txt.print("\nfps: ")
|
||||
txt.print_uw(fps)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
; several variables outside of doplasma to make them retain their value
|
||||
ubyte c1A
|
||||
ubyte c1B
|
||||
ubyte c2A
|
||||
ubyte c2B
|
||||
|
||||
sub doplasma() {
|
||||
ubyte[txt.DEFAULT_WIDTH] xbuf
|
||||
ubyte[txt.DEFAULT_HEIGHT] ybuf
|
||||
ubyte c1a = c1A
|
||||
ubyte c1b = c1B
|
||||
ubyte c2a = c2A
|
||||
ubyte c2b = c2B
|
||||
ubyte @zp x
|
||||
ubyte @zp y
|
||||
|
||||
for y in 0 to txt.DEFAULT_HEIGHT-1 {
|
||||
ybuf[y] = math.sin8u(c1a) + math.sin8u(c1b)
|
||||
c1a += 4
|
||||
c1b += 9
|
||||
}
|
||||
c1A += 3
|
||||
c1B -= 5
|
||||
for x in 0 to txt.DEFAULT_WIDTH-1 {
|
||||
xbuf[x] = math.sin8u(c2a) + math.sin8u(c2b)
|
||||
c2a += 3
|
||||
c2b += 7
|
||||
}
|
||||
c2A += 2
|
||||
c2B -= 3
|
||||
|
||||
; sys.waitvsync() ; if you put this in it will run at 30 fps synced which looks really nice and smooth
|
||||
|
||||
; use vera auto increment writes to avoid slow txt.setchr(x, y, xbuf[x] + ybuf[y])
|
||||
for y in 0 to txt.DEFAULT_HEIGHT-1 {
|
||||
cx16.vaddr_autoincr(1, VERA_TXTSCREEN + y*$0100, 0, 2)
|
||||
ubyte @zp yvalue = ybuf[y]
|
||||
for x in 0 to txt.DEFAULT_WIDTH-1 {
|
||||
cx16.VERA_DATA0 = xbuf[x] + yvalue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub makechar() {
|
||||
ubyte[8] bittab = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
|
||||
ubyte c
|
||||
cx16.vaddr_autoincr(1, VERA_CHARSET, 0, 1)
|
||||
for c in 0 to 255 {
|
||||
ubyte @zp s = math.sin8u(c) ; chance
|
||||
ubyte i
|
||||
; for all the pixels in the 8x8 character grid, determine (with a rnd chance) if they should be on or off
|
||||
for i in 0 to 7 {
|
||||
ubyte b=0
|
||||
ubyte @zp ii
|
||||
for ii in 0 to 7 {
|
||||
if math.rnd() > s
|
||||
b |= bittab[ii]
|
||||
}
|
||||
cx16.VERA_DATA0 = b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
%import math
|
||||
%import textio
|
||||
%import palette
|
||||
|
||||
; use the mouse to move the cursor around the screen
|
||||
@@ -16,6 +17,7 @@ main {
|
||||
ubyte yy
|
||||
void cx16.screen_mode(128, false)
|
||||
cx16.mouse_config2(1)
|
||||
txt.print("move the mouse for the spotlight.")
|
||||
|
||||
; prefill
|
||||
for yy in 0 to HEIGHT-1 {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from PIL import Image
|
||||
|
||||
def make_cx16_palette(palette: list[int]) -> bytes:
|
||||
cx16palette = bytearray()
|
||||
for pi in range(0, len(palette), 3):
|
||||
r, g, b = palette[pi] >> 4, palette[pi + 1] >> 4, palette[pi + 2] >> 4
|
||||
cx16palette.append(g << 4 | b)
|
||||
cx16palette.append(r)
|
||||
return cx16palette
|
||||
|
||||
|
||||
img = Image.open("dragonsprite.png")
|
||||
assert img.size == (64, 128) and img.mode == 'P'
|
||||
|
||||
data = bytearray()
|
||||
for y in range(0, 128):
|
||||
for x in range(0, 64, 2):
|
||||
pix1 = img.getpixel((x, y))
|
||||
pix2 = img.getpixel((x + 1, y))
|
||||
data.append(pix1 << 4 | pix2)
|
||||
|
||||
palette = make_cx16_palette(img.getpalette())
|
||||
|
||||
assert len(data) == 64 * 128 / 2
|
||||
assert len(palette) <= 16*2
|
||||
open("DRAGONSPRITE.PAL", "wb").write(palette)
|
||||
open("DRAGONSPRITE.BIN", "wb").write(data)
|
||||
@@ -0,0 +1,58 @@
|
||||
%import diskio
|
||||
%import textio
|
||||
%import sprites
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
; an example that displays and moves a single dragon (actually 2 sprites).
|
||||
|
||||
main {
|
||||
; we choose arbitrary unused vram location for sprite data: $12000
|
||||
const ubyte SPRITE_DATA_BANK = 1
|
||||
const uword SPRITE_DATA_ADDR = $2000
|
||||
const ubyte SPRITE_PALETTE_OFFSET_IDX = 16
|
||||
|
||||
sub start() {
|
||||
txt.plot(32,30)
|
||||
txt.print("there be dragons!")
|
||||
|
||||
; load the sprite data and color palette directly into Vera ram
|
||||
void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA_BANK, SPRITE_DATA_ADDR)
|
||||
void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + SPRITE_PALETTE_OFFSET_IDX*2)
|
||||
|
||||
; initialize the dragon sprites
|
||||
sprites.init(1, SPRITE_DATA_BANK, SPRITE_DATA_ADDR, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET_IDX)
|
||||
sprites.init(2, SPRITE_DATA_BANK, SPRITE_DATA_ADDR + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16,SPRITE_PALETTE_OFFSET_IDX)
|
||||
|
||||
ubyte tt = 0
|
||||
word xpos = -64
|
||||
word ypos
|
||||
bool flippedx = false
|
||||
|
||||
repeat {
|
||||
if flippedx
|
||||
xpos -= 2
|
||||
else
|
||||
xpos += 2
|
||||
|
||||
if xpos >= 640 or xpos <= -64
|
||||
flippedx = not flippedx
|
||||
|
||||
ypos = (240-64 as word) + math.sin8(tt)
|
||||
tt++
|
||||
|
||||
txt.plot(32, 32)
|
||||
txt.print("at: ")
|
||||
txt.print_w(sprites.getx(1))
|
||||
txt.chrout(',')
|
||||
txt.print_w(sprites.gety(1))
|
||||
txt.print(" ")
|
||||
|
||||
sys.waitvsync()
|
||||
sprites.pos(1, xpos, ypos)
|
||||
sprites.pos(2, xpos, ypos+64)
|
||||
sprites.flipx(1, flippedx)
|
||||
sprites.flipx(2, flippedx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
%import diskio
|
||||
%import textio
|
||||
%import sprites
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
; an example that displays and then moves many sprites at once.
|
||||
|
||||
main {
|
||||
; we choose arbitrary unused vram location for sprite data: $12000
|
||||
const ubyte SPRITE_DATA_BANK = 1
|
||||
const uword SPRITE_DATA_ADDR = $2000
|
||||
const ubyte SPRITE_PALETTE_OFFSET_IDX = 16
|
||||
|
||||
const ubyte NUM_DRAGONS = 25
|
||||
word[NUM_DRAGONS*2] xpositions
|
||||
word[NUM_DRAGONS*2] ypositions
|
||||
|
||||
sub start() {
|
||||
txt.plot(30,30)
|
||||
txt.print("there be many dragons!")
|
||||
|
||||
; load the sprite data and color palette directly into Vera ram
|
||||
void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA_BANK, SPRITE_DATA_ADDR)
|
||||
void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + SPRITE_PALETTE_OFFSET_IDX*2)
|
||||
|
||||
; initialize the dragon sprites (every dragon needs 2 sprites, top and bottom half)
|
||||
ubyte sprite_num
|
||||
for sprite_num in 0 to NUM_DRAGONS*2-2 step 2 {
|
||||
sprites.init(sprite_num+1, SPRITE_DATA_BANK, SPRITE_DATA_ADDR, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET_IDX)
|
||||
sprites.init(sprite_num+2, SPRITE_DATA_BANK, SPRITE_DATA_ADDR + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET_IDX)
|
||||
|
||||
xpositions[sprite_num] = math.rndw() % (640-64) as word
|
||||
xpositions[sprite_num+1] = xpositions[sprite_num]
|
||||
ypositions[sprite_num] = sprite_num * $0008 as word
|
||||
ypositions[sprite_num+1] = ypositions[sprite_num]+64
|
||||
}
|
||||
|
||||
repeat {
|
||||
; move all dragons (remember each one consists of a top and a bottom sprite)
|
||||
for sprite_num in 0 to NUM_DRAGONS*2-2 step 2 {
|
||||
xpositions[sprite_num]++
|
||||
xpositions[sprite_num+1]++
|
||||
if sprite_num & 2 {
|
||||
xpositions[sprite_num]++
|
||||
xpositions[sprite_num+1]++
|
||||
}
|
||||
if xpositions[sprite_num] >= 640
|
||||
xpositions[sprite_num] = -64
|
||||
if xpositions[sprite_num+1] >= 640
|
||||
xpositions[sprite_num+1] = -64
|
||||
}
|
||||
|
||||
sys.waitvsync()
|
||||
sprites.pos_batch(1, NUM_DRAGONS*2, &xpositions, &ypositions)
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
+51
-2
@@ -14,6 +14,7 @@ main {
|
||||
maze.initialize()
|
||||
maze.drawStartFinish()
|
||||
maze.generate()
|
||||
maze.openpassages()
|
||||
maze.drawStartFinish()
|
||||
maze.solve()
|
||||
maze.drawStartFinish()
|
||||
@@ -66,6 +67,8 @@ maze {
|
||||
}
|
||||
}
|
||||
|
||||
ubyte[4] directionflags = [LEFT,RIGHT,UP,DOWN]
|
||||
|
||||
sub generate() {
|
||||
ubyte cx = startCx
|
||||
ubyte cy = startCy
|
||||
@@ -163,15 +166,61 @@ carve_restart_after_repath:
|
||||
if not candidates
|
||||
return 0
|
||||
|
||||
ubyte[4] bitflags = [LEFT,RIGHT,UP,DOWN]
|
||||
repeat {
|
||||
ubyte choice = candidates & bitflags[math.rnd() & 3]
|
||||
ubyte choice = candidates & directionflags[math.rnd() & 3]
|
||||
if choice
|
||||
return choice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub openpassages() {
|
||||
; open just a few extra passages, so that multiple routes are possible in theory.
|
||||
ubyte cell
|
||||
ubyte numpassages
|
||||
ubyte cx
|
||||
ubyte cy
|
||||
do {
|
||||
do {
|
||||
cx = math.rnd() % (numCellsHoriz-2) + 1
|
||||
cy = math.rnd() % (numCellsVert-2) + 1
|
||||
} until not @(celladdr(cx, cy)) & STONE
|
||||
ubyte direction = directionflags[math.rnd() & 3]
|
||||
if not @(celladdr(cx, cy)) & direction {
|
||||
when direction {
|
||||
LEFT -> {
|
||||
if not @(celladdr(cx-1,cy)) & STONE {
|
||||
@(celladdr(cx,cy)) |= LEFT
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
RIGHT -> {
|
||||
if not @(celladdr(cx+1,cy)) & STONE {
|
||||
@(celladdr(cx,cy)) |= RIGHT
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
UP -> {
|
||||
if not @(celladdr(cx,cy-1)) & STONE {
|
||||
@(celladdr(cx,cy)) |= UP
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
DOWN -> {
|
||||
if not @(celladdr(cx,cy+1)) & STONE {
|
||||
@(celladdr(cx,cy)) |= DOWN
|
||||
drawCell(cx,cy)
|
||||
numpassages++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} until numpassages==10
|
||||
}
|
||||
|
||||
sub solve() {
|
||||
ubyte cx = startCx
|
||||
ubyte cy = startCy
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
%import graphics
|
||||
%import math
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
; Draw sine and cosine graphs. The sine and cosine functions are table lookups
|
||||
; where the tables are generated by 64tass list functions.
|
||||
|
||||
; Note: this program is compatible with CX16 only.
|
||||
; it doesn't work correctly on C64 because the bitmap screen data overlaps
|
||||
; the program itself in memory $2000-...
|
||||
|
||||
; This uses the graphics library which works on multiple targets (C64, CX16)
|
||||
|
||||
main {
|
||||
const uword width = 320
|
||||
@@ -17,18 +16,19 @@ main {
|
||||
graphics.enable_bitmap_mode()
|
||||
|
||||
sincos255()
|
||||
sys.wait(120)
|
||||
sys.wait(60)
|
||||
|
||||
graphics.clear_screen(1, 0)
|
||||
|
||||
sincos180()
|
||||
sys.wait(120)
|
||||
sys.wait(60)
|
||||
|
||||
graphics.clear_screen(1, 0)
|
||||
circles()
|
||||
sys.wait(60)
|
||||
|
||||
repeat {
|
||||
}
|
||||
graphics.disable_bitmap_mode()
|
||||
txt.print("done\n")
|
||||
}
|
||||
|
||||
sub sincos255() {
|
||||
@@ -38,6 +38,7 @@ main {
|
||||
ubyte pixelxb
|
||||
byte pixelys
|
||||
|
||||
; unsigned
|
||||
for pixelxb in 0 to 255 {
|
||||
pixelyb = math.cos8u(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
@@ -45,11 +46,12 @@ main {
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
}
|
||||
|
||||
; signed
|
||||
for pixelxb in 0 to 255 {
|
||||
pixelys = math.cos8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as uword + 90)
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
pixelys = math.sin8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as uword + 90)
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +62,7 @@ main {
|
||||
ubyte pixelxb
|
||||
byte pixelys
|
||||
|
||||
; signed
|
||||
for pixelxb in 0 to 179 {
|
||||
pixelyb = math.cosr8u(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
@@ -67,11 +70,12 @@ main {
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
}
|
||||
|
||||
; unsigned
|
||||
for pixelxb in 0 to 179 {
|
||||
pixelys = math.cosr8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as uword + 90)
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
pixelys = math.sinr8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as uword + 90)
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
}
|
||||
}
|
||||
|
||||
+41
-325
@@ -1,339 +1,55 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%zeropage basicsafe
|
||||
|
||||
; TODO fix VM : produces wrong number of primes (and varies too, so it uses uninitialized memory somewhere)
|
||||
|
||||
|
||||
main {
|
||||
const uword SIZE = 16000
|
||||
|
||||
uword @zp flags_ptr = memory("flags", SIZE/8+1, $100)
|
||||
ubyte[] bitv = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
|
||||
|
||||
sub start() {
|
||||
greater()
|
||||
greater_signed()
|
||||
less()
|
||||
less_signed()
|
||||
|
||||
greatereq()
|
||||
greatereq_signed()
|
||||
lesseq()
|
||||
lesseq_signed()
|
||||
txt.print("calculating... (expecting 3431): ")
|
||||
txt.print_uw(sieve())
|
||||
txt.print(" primes\n")
|
||||
}
|
||||
|
||||
sub value(ubyte arg) -> ubyte {
|
||||
cx16.r0++
|
||||
return arg
|
||||
sub check_flag(uword idx) -> ubyte
|
||||
{
|
||||
ubyte mask = bitv[lsb(idx)&7]
|
||||
ubyte flag = flags_ptr[idx/8]
|
||||
return flag & mask
|
||||
}
|
||||
|
||||
sub svalue(byte arg) -> byte {
|
||||
cx16.r0++
|
||||
return arg
|
||||
sub clear_flag(uword idx)
|
||||
{
|
||||
ubyte mask = bitv[lsb(idx)&7]
|
||||
ubyte flag = flags_ptr[idx/8]
|
||||
flag &= ~mask
|
||||
flags_ptr[idx/8] = flag
|
||||
}
|
||||
|
||||
sub greater () {
|
||||
ubyte b1 = 10
|
||||
ubyte b2 = 20
|
||||
ubyte b3 = 10
|
||||
sub sieve() -> uword {
|
||||
uword prime
|
||||
uword k
|
||||
uword count=0
|
||||
uword i
|
||||
sys.memset(flags_ptr, SIZE/8+1, $ff)
|
||||
|
||||
txt.print(">(u): 101010: ")
|
||||
ubyte xx
|
||||
xx = b2>10
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b3>b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>value(10)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b3>value(20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
for i in 0 to SIZE-1 {
|
||||
if check_flag(i) {
|
||||
prime = i*2 + 3
|
||||
k = i + prime
|
||||
while k < SIZE {
|
||||
clear_flag(k)
|
||||
k += prime
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
sub greater_signed () {
|
||||
byte b1 = -20
|
||||
byte b2 = -10
|
||||
byte b3 = -20
|
||||
|
||||
txt.print(">(s): 101010: ")
|
||||
ubyte xx
|
||||
xx = b2 > -20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2 > -10
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b3>b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>svalue(-20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b3>svalue(-10)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub less () {
|
||||
ubyte b1 = 20
|
||||
ubyte b2 = 10
|
||||
ubyte b3 = 20
|
||||
|
||||
txt.print("<(u): 101010: ")
|
||||
ubyte xx
|
||||
xx = b2<20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<10
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b3<b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<value(20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2<value(10)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub less_signed () {
|
||||
byte b1 = -10
|
||||
byte b2 = -20
|
||||
byte b3 = -10
|
||||
|
||||
txt.print("<(s): 101010: ")
|
||||
ubyte xx
|
||||
xx = b2 < -10
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2 < -20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b3<b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<svalue(-10)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b3<svalue(-20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub greatereq () {
|
||||
ubyte b1 = 19
|
||||
ubyte b2 = 20
|
||||
ubyte b3 = 21
|
||||
ubyte b4 = 20
|
||||
|
||||
txt.print(">=(u): 110110110: ")
|
||||
ubyte xx
|
||||
xx = b2>=19
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=21
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=b4
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=b3
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=value(19)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2>=value(20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2>=value(21)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub greatereq_signed () {
|
||||
byte b1 = -19
|
||||
byte b2 = -20
|
||||
byte b3 = -21
|
||||
byte b4 = -20
|
||||
|
||||
txt.print(">=(s): 011011011: ")
|
||||
ubyte xx
|
||||
xx = b2>= -19
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>= -20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>= -21
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=b4
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=b3
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2>=value(-19)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2>=value(-20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2>=value(-21)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub lesseq () {
|
||||
ubyte b1 = 19
|
||||
ubyte b2 = 20
|
||||
ubyte b3 = 21
|
||||
ubyte b4 = 20
|
||||
|
||||
txt.print("<=(u): 011011011: ")
|
||||
ubyte xx
|
||||
xx = b2<=19
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=21
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=b4
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=b3
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=value(19)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2<=value(20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2<=value(21)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub lesseq_signed () {
|
||||
byte b1 = -19
|
||||
byte b2 = -20
|
||||
byte b3 = -21
|
||||
byte b4 = -20
|
||||
|
||||
txt.print("<=(s): 110110110: ")
|
||||
ubyte xx
|
||||
xx = b2<= -19
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<= -20
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<= -21
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=b1
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=b4
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=b3
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
xx = b2<=value(-19)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2<=value(-20)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
xx = b2<=value(-21)
|
||||
txt.print_ub(xx)
|
||||
txt.spc()
|
||||
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,11 +17,11 @@ main {
|
||||
sys.memset(flags_ptr, SIZEPL, 1)
|
||||
count = 0
|
||||
for i in 0 to SIZEPL-1 {
|
||||
if @(flags_ptr+i) {
|
||||
if flags_ptr[i] {
|
||||
prime = i + i + 3
|
||||
k = i + prime
|
||||
while k <= SIZEPL-1 {
|
||||
@(flags_ptr + k) = false
|
||||
flags_ptr[k] = false
|
||||
k += prime
|
||||
}
|
||||
txt.print_uw(prime)
|
||||
|
||||
+2
-2
@@ -4,5 +4,5 @@ org.gradle.parallel=true
|
||||
org.gradle.daemon=true
|
||||
kotlin.code.style=official
|
||||
javaVersion=11
|
||||
kotlinVersion=1.9.0
|
||||
version=9.3
|
||||
kotlinVersion=1.9.10
|
||||
version=9.4.1
|
||||
@@ -34,6 +34,7 @@ class RequestParser : Take {
|
||||
Path(a),
|
||||
optimize = true,
|
||||
writeAssembly = true,
|
||||
warnSymbolShadowing = false,
|
||||
compilationTarget = "c64",
|
||||
symbolDefs = emptyMap(),
|
||||
quietAssembler = false,
|
||||
|
||||
@@ -37,16 +37,16 @@ All have type b or w or f.
|
||||
load reg1, value - load immediate value into register. If you supply a symbol, loads the *address* of the symbol! (variable values are loaded from memory via the loadm instruction)
|
||||
loadm reg1, address - load reg1 with value at memory address
|
||||
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (only the lsb part used for indexing)
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
|
||||
loadr reg1, reg2 - load reg1 with value in register reg2
|
||||
storem reg1, address - store reg1 at memory address
|
||||
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2
|
||||
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (only the lsb part used for indexing)
|
||||
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
|
||||
storezm address - store zero at memory address
|
||||
storezi reg1 - store zero at memory pointed to by reg1
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg1 (only the lsb part used for indexing)
|
||||
|
||||
|
||||
CONTROL FLOW
|
||||
@@ -144,6 +144,7 @@ mod reg1, value - remainder (modulo) of unsigned div
|
||||
divmodr reg1, reg2 - unsigned division reg1/reg2, storing division and remainder on value stack (so need to be POPped off)
|
||||
divmod reg1, value - unsigned division reg1/value, storing division and remainder on value stack (so need to be POPped off)
|
||||
sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
|
||||
square reg1, reg2 - reg1 is the square of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
|
||||
sgn reg1, reg2 - reg1 is the sign of reg2 (0.b, 1.b or -1.b)
|
||||
cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction)
|
||||
cmpi reg1, value - set processor status bits C, N, Z according to comparison of reg1 with immediate value. (semantics taken from 6502/68000 CMP instruction)
|
||||
@@ -311,6 +312,7 @@ enum class Opcode {
|
||||
DIVMODR,
|
||||
DIVMOD,
|
||||
SQRT,
|
||||
SQUARE,
|
||||
SGN,
|
||||
CMP,
|
||||
CMPI,
|
||||
@@ -601,6 +603,7 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.DIVS to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.DIVSM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
|
||||
Opcode.SQRT to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.SQUARE to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.SGN to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.MODR to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.MOD to InstructionFormat.from("BW,<>r1,<i"),
|
||||
|
||||
@@ -152,9 +152,9 @@ augassignment :
|
||||
;
|
||||
|
||||
assign_target:
|
||||
scoped_identifier
|
||||
| arrayindexed
|
||||
| directmemory
|
||||
scoped_identifier #IdentifierTarget
|
||||
| arrayindexed #ArrayindexedTarget
|
||||
| directmemory #MemoryTarget
|
||||
;
|
||||
|
||||
postincrdecr : assign_target operator = ('++' | '--') ;
|
||||
@@ -186,9 +186,13 @@ expression :
|
||||
| expression typecast
|
||||
;
|
||||
|
||||
typecast : 'as' datatype;
|
||||
arrayindexed:
|
||||
scoped_identifier arrayindex
|
||||
// TODO to allow chained array indexing: | arrayindexed arrayindex
|
||||
;
|
||||
|
||||
arrayindexed : scoped_identifier arrayindex ;
|
||||
|
||||
typecast : 'as' datatype;
|
||||
|
||||
directmemory : '@' '(' expression ')';
|
||||
|
||||
@@ -295,7 +299,7 @@ untilloop: 'do' (statement | statement_block) EOL? 'until' expression ;
|
||||
|
||||
repeatloop: 'repeat' expression? EOL? (statement | statement_block) ;
|
||||
|
||||
unrollloop: 'unroll' integerliteral? EOL? (statement | statement_block) ;
|
||||
unrollloop: 'unroll' expression EOL? (statement | statement_block) ; // note: expression must evaluate to a constant
|
||||
|
||||
whenstmt: 'when' expression EOL? '{' EOL? (when_choice | EOL) * '}' EOL? ;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<keywords keywords="&;->;@;and;as;asmsub;break;clobbers;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
|
||||
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%ir;%launcher;%option;%output;%zeropage;%zpreserved;iso:;petscii:;sc:" />
|
||||
<keywords3 keywords="@requirezp;@shared;@split;@zp;bool;byte;const;float;str;ubyte;uword;void;word" />
|
||||
<keywords4 keywords="abs;all;any;callfar;callram;callrom;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekw;poke;pokew;pop;popw;push;pushw;reverse;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;sgn;sizeof;sort;sqrt;sqrt16;swap;|>" />
|
||||
<keywords4 keywords="abs;all;any;callfar;callram;callrom;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekw;poke;pokew;pop;popw;push;pushw;reverse;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;sgn;sizeof;sort;sqrt;swap;|>" />
|
||||
</highlighting>
|
||||
<extensionMap>
|
||||
<mapping ext="p8" />
|
||||
|
||||
@@ -52,6 +52,7 @@ SYSCALLS:
|
||||
42 = CLAMP_UWORD
|
||||
43 = CLAMP_FLOAT
|
||||
44 = ATAN
|
||||
45 = STR_TO_FLOAT
|
||||
*/
|
||||
|
||||
enum class Syscall {
|
||||
@@ -99,7 +100,8 @@ enum class Syscall {
|
||||
CLAMP_WORD,
|
||||
CLAMP_UWORD,
|
||||
CLAMP_FLOAT,
|
||||
ATAN
|
||||
ATAN,
|
||||
STR_TO_FLOAT
|
||||
;
|
||||
|
||||
companion object {
|
||||
@@ -366,6 +368,11 @@ object SysCalls {
|
||||
}
|
||||
return returnValue(callspec.returns!!, value, vm)
|
||||
}
|
||||
Syscall.STR_TO_FLOAT -> {
|
||||
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
|
||||
val memstring = vm.memory.getString(stringAddr.toInt())
|
||||
returnValue(callspec.returns!!, memstring.toFloat(), vm)
|
||||
}
|
||||
Syscall.COMPARE_STRINGS -> {
|
||||
val (firstV, secondV) = getArgValues(callspec.arguments, vm)
|
||||
val firstAddr = firstV as UShort
|
||||
|
||||
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user