mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 C128 can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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""")
|
||||
}
|
||||
|
||||
@@ -544,7 +544,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, "cx16.r0")
|
||||
asmgen.out(" jsr math.multiply_words")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
@@ -568,7 +568,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, "cx16.r0")
|
||||
asmgen.out(" jsr math.multiply_words")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
@@ -602,7 +602,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 +614,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")
|
||||
|
||||
+34
-19
@@ -163,7 +163,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 -> {
|
||||
@@ -1178,9 +1178,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sta cx16.r0
|
||||
lda $name+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
sta cx16.r0+1
|
||||
lda #<$value
|
||||
ldy #>$value
|
||||
jsr math.multiply_words
|
||||
@@ -1629,15 +1629,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 cx16.r0")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" stz cx16.r0+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda #0 | sta cx16.r0+1")
|
||||
} else {
|
||||
asmgen.out(" lda $otherName")
|
||||
asmgen.signExtendAYlsb(valueDt)
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" sta cx16.r0 | sty cx16.r0+1")
|
||||
}
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
@@ -1773,16 +1773,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
|
||||
"*" -> {
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
ldy $otherName+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $name
|
||||
ldy $name+1
|
||||
jsr math.multiply_words
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
if(otherName=="cx16.r0")
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
ldy $name+1
|
||||
jsr math.multiply_words
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
else if(name=="cx16.r0")
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
ldy $otherName+1
|
||||
jsr math.multiply_words
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
ldy $otherName+1
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
lda $name
|
||||
ldy $name+1
|
||||
jsr math.multiply_words
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
}
|
||||
"/" -> {
|
||||
if(dt==DataType.WORD) {
|
||||
@@ -1963,8 +1978,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 cx16.r0
|
||||
sty cx16.r0+1
|
||||
lda $name
|
||||
ldy $name+1
|
||||
jsr math.multiply_words
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -127,46 +127,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 +175,7 @@ graphics {
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
}}
|
||||
addr += 8
|
||||
pixaddr += 8
|
||||
length += separate_pixels
|
||||
length -= 8
|
||||
}
|
||||
@@ -191,9 +191,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 +227,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 +300,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) {
|
||||
@@ -360,7 +360,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
|
||||
@@ -300,7 +300,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++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+185
-75
@@ -4,78 +4,176 @@
|
||||
; 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, cx16.R0 = second 16-bit number
|
||||
; output: multiply_words.result == cx16.R0:R1, 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY.
|
||||
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
; 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
|
||||
|
||||
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
|
||||
_multiplicand = P8ZP_SCRATCH_W1 ; 2 bytes
|
||||
_multiplier = cx16.r0 ; 2 bytes
|
||||
result = cx16.r0 ; 4 bytes (note: shares memory with multiplier) so is r0 and ALSO r1.
|
||||
|
||||
result .byte 0,0,0,0
|
||||
; 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
|
||||
.pend
|
||||
|
||||
|
||||
@@ -749,74 +847,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -220,7 +220,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)
|
||||
@@ -1068,7 +1068,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 +1120,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 +1421,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,18 @@ 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 arrayvar = scoped_identifier().toAst()
|
||||
val index = arrayindex().toAst()
|
||||
val arrayindexed = ArrayIndexedExpression(arrayvar, index, scoped_identifier().toPosition())
|
||||
AssignTarget(null, arrayindexed, null, toPosition())
|
||||
}
|
||||
else -> throw FatalAstException("weird assign target node $this")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +434,12 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression {
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayindex()!=null) {
|
||||
val identifier = scoped_identifier().toAst()
|
||||
val index = arrayindex().toAst()
|
||||
return ArrayIndexedExpression(identifier, index, scoped_identifier().toPosition())
|
||||
}
|
||||
|
||||
if(scoped_identifier()!=null)
|
||||
return scoped_identifier().toAst()
|
||||
|
||||
@@ -448,9 +461,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 +507,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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
---------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,10 @@
|
||||
TODO
|
||||
====
|
||||
- prefix prog8 subroutines with p8s_ instead of p8_ to not let them clash with variables in the asm?
|
||||
|
||||
- [on branch:] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
|
||||
- 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
|
||||
- [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!
|
||||
|
||||
@@ -48,6 +51,7 @@ 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)
|
||||
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+5
-331
@@ -3,337 +3,11 @@
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
greater()
|
||||
greater_signed()
|
||||
less()
|
||||
less_signed()
|
||||
|
||||
greatereq()
|
||||
greatereq_signed()
|
||||
lesseq()
|
||||
lesseq_signed()
|
||||
thing()
|
||||
}
|
||||
|
||||
sub value(ubyte arg) -> ubyte {
|
||||
cx16.r0++
|
||||
return arg
|
||||
sub thing() {
|
||||
ubyte start=22
|
||||
txt.print_ub(start)
|
||||
txt.print_uw(&main.start)
|
||||
}
|
||||
|
||||
sub svalue(byte arg) -> byte {
|
||||
cx16.r0++
|
||||
return arg
|
||||
}
|
||||
|
||||
sub greater () {
|
||||
ubyte b1 = 10
|
||||
ubyte b2 = 20
|
||||
ubyte b3 = 10
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
||||
+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
|
||||
@@ -34,6 +34,7 @@ class RequestParser : Take {
|
||||
Path(a),
|
||||
optimize = true,
|
||||
writeAssembly = true,
|
||||
warnSymbolShadowing = false,
|
||||
compilationTarget = "c64",
|
||||
symbolDefs = emptyMap(),
|
||||
quietAssembler = false,
|
||||
|
||||
@@ -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
|
||||
| scoped_identifier arrayindex #ArrayindexedTarget // TODO expression instead of just scoped_identifier
|
||||
| directmemory #MemoryTarget
|
||||
;
|
||||
|
||||
postincrdecr : assign_target operator = ('++' | '--') ;
|
||||
@@ -180,7 +180,7 @@ expression :
|
||||
| left = expression EOL? bop = 'xor' EOL? right = expression
|
||||
| literalvalue
|
||||
| scoped_identifier
|
||||
| arrayindexed
|
||||
| scoped_identifier arrayindex // TODO expression instead of just scoped_identifier
|
||||
| directmemory
|
||||
| addressof
|
||||
| expression typecast
|
||||
@@ -188,8 +188,6 @@ expression :
|
||||
|
||||
typecast : 'as' datatype;
|
||||
|
||||
arrayindexed : scoped_identifier arrayindex ;
|
||||
|
||||
directmemory : '@' '(' expression ')';
|
||||
|
||||
addressof : <assoc=right> ADDRESS_OF scoped_identifier ;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -247,6 +247,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.CMP -> InsCMP(ins)
|
||||
Opcode.CMPI -> InsCMPI(ins)
|
||||
Opcode.SQRT -> InsSQRT(ins)
|
||||
Opcode.SQUARE -> InsSQUARE(ins)
|
||||
Opcode.EXT -> InsEXT(ins)
|
||||
Opcode.EXTS -> InsEXTS(ins)
|
||||
Opcode.ANDR -> InsANDR(ins)
|
||||
@@ -1247,6 +1248,24 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSQUARE(i: IRInstruction) {
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
val value = registers.getUB(i.reg2!!).toDouble().toInt()
|
||||
registers.setUB(i.reg1!!, (value*value).toUByte())
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
val value = registers.getUW(i.reg2!!).toDouble().toInt()
|
||||
registers.setUW(i.reg1!!, (value*value).toUShort())
|
||||
}
|
||||
IRDataType.FLOAT -> {
|
||||
val value = registers.getFloat(i.fpReg2!!)
|
||||
registers.setFloat(i.fpReg1!!, value*value)
|
||||
}
|
||||
}
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsCMP(i: IRInstruction) {
|
||||
val comparison: Int
|
||||
when(i.type!!) {
|
||||
|
||||
Reference in new issue
Block a user