Compare commits

...
20 Commits
Author SHA1 Message Date
Irmen de Jong 93f77a1045 version 9.4 2023-08-29 12:27:09 +02:00
Irmen de Jong aa4d23a3d5 fix register stack saving on certain expression code that was broken on 6502 but not on 65c02 2023-08-29 11:50:35 +02:00
Irmen de Jong 2d7ebff8e9 fix shadowing warnings in asm and library code 2023-08-29 11:00:53 +02:00
Irmen de Jong bad9dd3b3b mention shadowing warnings from assembler 2023-08-28 16:55:28 +02:00
Irmen de Jong 2f4e517857 update to Kotlin 1.9.10 2023-08-28 16:45:59 +02:00
Irmen de Jong ff35ba3696 added warnshadow cli option to enable assembler warnings about symbol shadowing 2023-08-28 16:41:46 +02:00
Irmen de Jong 72768e7fad todo 2023-08-28 16:10:02 +02:00
Irmen de Jong 77f3852cdc added floats.parse_f() 2023-08-16 14:47:20 +02:00
Irmen de Jong 66857ca477 prepare parser to be more flexible with array indexed expressions 2023-08-15 13:07:01 +02:00
Irmen de Jong 75514fc7af fix some invalid instructions on 6502 (instead of 65c02) target for bit shifts 2023-08-14 21:58:26 +02:00
Irmen de Jong be06d871b6 fix code for bitwise shifts by zero 2023-08-14 21:49:13 +02:00
Irmen de Jong f98ee326b4 error when doing txt.print('@') where "@" was intended (byte for string parameter) 2023-08-14 19:25:31 +02:00
Irmen de Jong bc8126eb16 2x faster word multiplication routine 2023-08-14 18:11:30 +02:00
Irmen de Jong 4c8beefdcb slightly faster integer bytes multiplication routine 2023-08-14 17:00:16 +02:00
Irmen de Jong bbb6c53457 slightly faster sqrt() routine for integers 2023-08-14 17:00:02 +02:00
Irmen de Jong d8991894e3 added pet stubs for cbm.SETTIM,RDTIM,RDTIM16 2023-08-14 14:49:59 +02:00
Irmen de Jong c7b7dcfd03 made pet textio more compatible with the other platforms by putting the (dummy) color arguments back 2023-08-14 13:51:15 +02:00
Irmen de Jong 2c9e50873c use math.square for optimized X*X calculation (words only).
Added IR SQUARE instruction.
2023-08-14 01:05:17 +02:00
Irmen de Jong 923367296d fix reset_system() on PET, added some missing kernal routines 2023-08-13 01:46:25 +02:00
Irmen de Jong 151a206617 experimental Commodore PET target 2023-08-12 23:25:07 +02:00
60 changed files with 1904 additions and 824 deletions

No files matched your search

-24
View File
@@ -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>
+2
View File
@@ -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")
@@ -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])
+5 -5
View File
@@ -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 {{
+2 -2
View File
@@ -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
+5 -5
View File
@@ -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
+11 -1
View File
@@ -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"
+25 -25
View File
@@ -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
+2 -2
View File
@@ -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
+7 -7
View File
@@ -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
+28 -28
View File
@@ -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.
+14 -1
View File
@@ -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
View File
@@ -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++
}
}
+8 -8
View File
@@ -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) {
+5 -5
View File
@@ -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()
}
+4 -4
View File
@@ -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)
+9 -9
View File
@@ -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
+16 -16
View File
@@ -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
View File
@@ -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
+452
View File
@@ -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
}}
}
}
+456
View File
@@ -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
}}
}
}
+36 -27
View File
@@ -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
+8 -8
View File
@@ -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
+9
View File
@@ -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
+5 -2
View File
@@ -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,
+3
View File
@@ -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
}
}
+1
View File
@@ -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,
+1 -11
View File
@@ -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"
}
})
+1
View File
@@ -24,6 +24,7 @@ internal fun compileFile(
filepath,
optimize,
writeAssembly = writeAssembly,
warnSymbolShadowing = false,
quietAssembler = true,
asmListfile = false,
includeSourcelines = false,
+19 -15
View File
@@ -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
+19 -4
View File
@@ -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
---------
+5 -8
View File
@@ -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
+1
View File
@@ -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
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"),
+4 -6
View File
@@ -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 ;
+1 -1
View File
@@ -14,7 +14,7 @@
<keywords keywords="&amp;;-&gt;;@;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;|&gt;" />
<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;|&gt;" />
</highlighting>
<extensionMap>
<mapping ext="p8" />
+8 -1
View File
@@ -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!!) {