mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
Merge branch 'remove_evalstack'
This commit is contained in:
commit
9167ba499d
@ -26,8 +26,6 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY),
|
||||
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY)
|
||||
).forEach {
|
||||
it.parent = program
|
||||
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||
|
@ -126,7 +126,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
else
|
||||
"->"
|
||||
}
|
||||
else -> throw InternalCompilerException("unrecognised ast node $node")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,9 +124,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
|
||||
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
|
||||
"rsave" to FSignature(false, emptyList(), null),
|
||||
"rsavex" to FSignature(false, emptyList(), null),
|
||||
"rrestore" to FSignature(false, emptyList(), null),
|
||||
"rrestorex" to FSignature(false, emptyList(), null),
|
||||
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
)
|
||||
|
@ -13,15 +13,12 @@ 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 slowCodegenWarnings: Boolean = false,
|
||||
var optimize: Boolean = false,
|
||||
var optimizeFloatExpressions: Boolean = false,
|
||||
var asmQuiet: Boolean = false,
|
||||
var asmListfile: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var evalStackBaseAddress: UInt? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
|
@ -13,8 +13,6 @@ interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
var ESTACK_LO: UInt
|
||||
var ESTACK_HI: UInt
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
@ -29,11 +27,4 @@ interface IMachineDefinition {
|
||||
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
fun overrideEvalStack(evalStackBaseAddress: UInt) {
|
||||
require(evalStackBaseAddress and 255u == 0u)
|
||||
ESTACK_LO = evalStackBaseAddress
|
||||
ESTACK_HI = evalStackBaseAddress + 256u
|
||||
require(ESTACK_LO !in golden.region && ESTACK_HI !in golden.region) { "user-set ESTACK can't be in GOLDEN ram" }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,10 +13,6 @@ class AtariMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive // TODO
|
||||
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive // TODO
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
|
@ -15,10 +15,6 @@ class C128MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive
|
||||
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
|
@ -16,12 +16,8 @@ class C64MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0xcf00u // $cf00-$cf7f inclusive
|
||||
override var ESTACK_HI = 0xcf80u // $cf80-$cfff inclusive
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = ESTACK_LO
|
||||
override val BSSHIGHRAM_END = 0xd000u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
@ -62,7 +58,7 @@ class C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0xc000u until ESTACK_LO)
|
||||
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,10 +15,6 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x0700u // $0700-$077f inclusive
|
||||
override var ESTACK_HI = 0x0780u // $0780-$07ff inclusive
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
|
||||
|
||||
@ -64,7 +60,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
|
@ -15,8 +15,6 @@ class VirtualMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
|
||||
override var ESTACK_LO = 0u // not actually used
|
||||
override var ESTACK_HI = 0u // not actually used
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
|
@ -207,8 +207,8 @@ class AsmGen6502Internal (
|
||||
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, symbolTable, this, allocator)
|
||||
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
|
||||
private val anyExprGen = AnyExprAsmGen(this)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
|
||||
fun compileToAssembly(): IAssemblyProgram? {
|
||||
@ -394,32 +394,6 @@ class AsmGen6502Internal (
|
||||
return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars -> reference zp symbols directly
|
||||
}
|
||||
|
||||
internal fun saveRegisterLocal(register: CpuRegister, scope: IPtSubroutine) {
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
// just use the cpu's stack for all registers, shorter code
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
CpuRegister.X -> out(" phx")
|
||||
CpuRegister.Y -> out(" phy")
|
||||
}
|
||||
} else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
// just use the stack, only for A
|
||||
out(" pha")
|
||||
}
|
||||
CpuRegister.X -> {
|
||||
out(" stx prog8_regsaveX")
|
||||
subroutineExtra(scope).usedRegsaveX = true
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
out(" sty prog8_regsaveY")
|
||||
subroutineExtra(scope).usedRegsaveY = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun saveRegisterStack(register: CpuRegister, keepA: Boolean) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
@ -444,24 +418,6 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreRegisterLocal(register: CpuRegister) {
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
when (register) {
|
||||
// this just used the stack, for all registers. Shorter code.
|
||||
CpuRegister.A -> out(" pla")
|
||||
CpuRegister.X -> out(" plx")
|
||||
CpuRegister.Y -> out(" ply")
|
||||
}
|
||||
|
||||
} else {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pla") // this just used the stack but only for A
|
||||
CpuRegister.X -> out(" ldx prog8_regsaveX")
|
||||
CpuRegister.Y -> out(" ldy prog8_regsaveY")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreRegisterStack(register: CpuRegister, keepA: Boolean) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
@ -581,22 +537,12 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||
internal fun translateExpression(expression: PtExpression) =
|
||||
expressionsAsmGen.translateExpression(expression)
|
||||
|
||||
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? =
|
||||
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
|
||||
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? =
|
||||
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultRegister)
|
||||
|
||||
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCallExpr)
|
||||
|
||||
internal fun saveXbeforeCall(functionCall: PtFunctionCall) =
|
||||
functioncallAsmGen.saveXbeforeCall(functionCall)
|
||||
|
||||
internal fun restoreXafterCall(functionCall: PtFunctionCall) =
|
||||
functioncallAsmGen.restoreXafterCall(functionCall)
|
||||
|
||||
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) =
|
||||
assignmentAsmGen.translateNormalAssignment(assign, scope)
|
||||
|
||||
@ -625,7 +571,6 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) {
|
||||
// don't use translateExpression() to avoid evalstack
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
@ -1086,20 +1031,6 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun signExtendStackLsb(valueDt: DataType) {
|
||||
// sign extend signed byte on stack to signed word on stack
|
||||
when(valueDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(isTargetCpu(CpuType.CPU65c02))
|
||||
out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte")
|
||||
else -> throw AssemblyError("need byte type")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
|
||||
// sign extend signed byte in a var to a full word in that variable
|
||||
when(valueDt) {
|
||||
@ -1728,7 +1659,7 @@ $repeatLabel""")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
return if(rightConstVal.number.toInt()!=0) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
}
|
||||
else
|
||||
@ -1875,7 +1806,7 @@ $repeatLabel""")
|
||||
out(" beq $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@ -2036,7 +1967,7 @@ $repeatLabel""")
|
||||
out(" bne $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@ -2200,7 +2131,7 @@ $repeatLabel""")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
if(rightConstVal.number.toInt()!=0) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
}
|
||||
return
|
||||
@ -2363,7 +2294,7 @@ $repeatLabel""")
|
||||
out(" bne $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@ -2409,7 +2340,7 @@ $repeatLabel""")
|
||||
out(" beq $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@ -2865,22 +2796,14 @@ $repeatLabel""")
|
||||
+""")
|
||||
}
|
||||
|
||||
internal fun translateDirectMemReadExpressionToRegAorStack(expr: PtMemoryByte, pushResultOnEstack: Boolean) {
|
||||
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
|
||||
|
||||
fun assignViaExprEval() {
|
||||
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
if (pushResultOnEstack) {
|
||||
out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
out(" lda (P8ZP_SCRATCH_W2)")
|
||||
}
|
||||
out(" lda (P8ZP_SCRATCH_W2)")
|
||||
} else {
|
||||
if (pushResultOnEstack) {
|
||||
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y | dex | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
|
||||
@ -2888,21 +2811,14 @@ $repeatLabel""")
|
||||
is PtNumber -> {
|
||||
val address = (expr.address as PtNumber).number.toInt()
|
||||
out(" lda ${address.toHex()}")
|
||||
if(pushResultOnEstack)
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
// the identifier is a pointer variable, so read the value from the address in it
|
||||
loadByteFromPointerIntoA(expr.address as PtIdentifier)
|
||||
if(pushResultOnEstack)
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = expr.address as PtBinaryExpression
|
||||
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(pushResultOnEstack)
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
if(!tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
assignViaExprEval()
|
||||
}
|
||||
}
|
||||
@ -3098,6 +3014,14 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun pushFAC1() {
|
||||
out(" jsr floats.pushFAC1")
|
||||
}
|
||||
|
||||
internal fun popFAC1() {
|
||||
out(" jsr floats.popFAC1")
|
||||
}
|
||||
|
||||
internal fun needAsaveForExpr(arg: PtExpression): Boolean =
|
||||
arg !is PtNumber && arg !is PtIdentifier && (arg !is PtMemoryByte || !arg.isSimple())
|
||||
|
||||
@ -3130,9 +3054,6 @@ $repeatLabel""")
|
||||
* it's more consistent to only define these attributes on a Subroutine node.
|
||||
*/
|
||||
internal class SubroutineExtraAsmInfo {
|
||||
var usedRegsaveA = false
|
||||
var usedRegsaveX = false
|
||||
var usedRegsaveY = false
|
||||
var usedFloatEvalResultVar1 = false
|
||||
var usedFloatEvalResultVar2 = false
|
||||
|
||||
|
@ -15,21 +15,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
||||
|
||||
var linesByFour = getLinesBy(lines, 4)
|
||||
|
||||
var mods = optimizeUselessStackByteWrites(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeIncDec(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeCmpSequence(linesByFour)
|
||||
var mods = optimizeIncDec(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
@ -97,44 +83,6 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
|
||||
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// when statement (on bytes) generates a sequence of:
|
||||
// lda $ce01,x
|
||||
// cmp #$20
|
||||
// beq check_prog8_s72choice_32
|
||||
// lda $ce01,x
|
||||
// cmp #$21
|
||||
// beq check_prog8_s73choice_33
|
||||
// the repeated lda can be removed
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||
lines[1].value.trim().startsWith("cmp ") &&
|
||||
lines[2].value.trim().startsWith("beq ") &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||
// this is a lot harder for word values because the instruction sequence varies.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||
lines[1].value.trim()=="dex" &&
|
||||
lines[2].value.trim()=="inx" &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeSameAssignments(
|
||||
linesByFourteen: List<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
|
@ -10,34 +10,31 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val assignAsmGen: AssignmentAsmGen) {
|
||||
|
||||
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||
return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
|
||||
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? {
|
||||
return translateFunctioncall(fcall, discardResult = false, resultRegister = resultRegister)
|
||||
}
|
||||
|
||||
internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) {
|
||||
translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null)
|
||||
translateFunctioncall(fcall, discardResult = true, resultRegister = null)
|
||||
}
|
||||
|
||||
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||
if (discardResult && fcall.hasNoSideEffects)
|
||||
return null // can just ignore the whole function call altogether
|
||||
|
||||
if(discardResult && resultToStack)
|
||||
throw AssemblyError("cannot both discard the result AND put it onto stack")
|
||||
|
||||
val sscope = fcall.definingISub()
|
||||
|
||||
when (fcall.name) {
|
||||
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
|
||||
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
||||
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultToStack, resultRegister)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultToStack, resultRegister)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultToStack, resultRegister)
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
|
||||
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
|
||||
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope)
|
||||
"msb" -> funcMsb(fcall, resultRegister)
|
||||
"lsb" -> funcLsb(fcall, resultRegister)
|
||||
"mkword" -> funcMkword(fcall, resultRegister)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultRegister)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
|
||||
"any", "all" -> funcAnyAll(fcall, resultRegister, sscope)
|
||||
"sgn" -> funcSgn(fcall, resultRegister, sscope)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
|
||||
"divmod__ubyte" -> funcDivmod(fcall)
|
||||
"divmod__uword" -> funcDivmodW(fcall)
|
||||
"rol" -> funcRol(fcall)
|
||||
@ -46,8 +43,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"ror2" -> funcRor2(fcall)
|
||||
"sort" -> funcSort(fcall)
|
||||
"reverse" -> funcReverse(fcall)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultToStack, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultToStack, resultRegister)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokemon" -> { /* meme function */ }
|
||||
@ -71,12 +68,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
|
||||
}
|
||||
"rsave" -> funcRsave()
|
||||
"rsavex" -> funcRsaveX()
|
||||
"rrestore" -> funcRrestore()
|
||||
"rrestorex" -> funcRrestoreX()
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultToStack)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall)
|
||||
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||
}
|
||||
|
||||
@ -115,11 +110,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
sty $remainderVar+1""")
|
||||
}
|
||||
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall) {
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[0], fcall.args[1], "P8ZP_SCRATCH_W2")
|
||||
asmgen.out(" jsr prog8_lib.strcmp_mem")
|
||||
if(resultToStack)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
|
||||
private fun funcRsave() {
|
||||
@ -142,13 +135,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
}
|
||||
|
||||
private fun funcRsaveX() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" phx")
|
||||
else
|
||||
asmgen.out(" txa | pha")
|
||||
}
|
||||
|
||||
private fun funcRrestore() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out("""
|
||||
@ -166,13 +152,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
plp""")
|
||||
}
|
||||
|
||||
private fun funcRrestoreX() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" plx")
|
||||
else
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
private fun funcCallFar(fcall: PtBuiltinFunctionCall) {
|
||||
if(asmgen.options.compTarget.name != "cx16")
|
||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||
@ -253,7 +232,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
@ -263,41 +242,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
val target =
|
||||
if(resultToStack)
|
||||
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null, fcall.position)
|
||||
else
|
||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
when(fcall.args[0].type) {
|
||||
DataType.UBYTE -> {
|
||||
if(resultToStack)
|
||||
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_stack")
|
||||
else {
|
||||
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(resultToStack)
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||
else {
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
|
||||
if(resultToStack)
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
|
||||
else {
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@ -466,16 +429,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@ -571,16 +531,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@ -633,88 +590,56 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
|
||||
}
|
||||
|
||||
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
val dt = fcall.args.single().type
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_stack")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_stack")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
} else {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
|
||||
}
|
||||
|
||||
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||
val dt = fcall.args.single().type
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
|
||||
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
} else {
|
||||
when (dt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
|
||||
when (dt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
|
||||
}
|
||||
|
||||
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
val dt = fcall.args.single().type
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
|
||||
else -> {
|
||||
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
|
||||
}
|
||||
when (dt) {
|
||||
DataType.BYTE -> {
|
||||
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false)
|
||||
}
|
||||
} else {
|
||||
when (dt) {
|
||||
DataType.BYTE -> {
|
||||
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false)
|
||||
}
|
||||
DataType.WORD -> {
|
||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
DataType.WORD -> {
|
||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
@ -730,7 +655,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
asmgen.out("""
|
||||
@ -746,7 +670,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
}
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -756,18 +679,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
val scope = fcall.definingISub()!!
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.saveRegisterLocal(CpuRegister.Y, scope)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
asmgen.restoreRegisterLocal(CpuRegister.Y)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out("""
|
||||
sta ($varname),y
|
||||
txa
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -779,7 +699,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||
}
|
||||
|
||||
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
fun fallback() {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||
@ -830,23 +750,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> fallback()
|
||||
}
|
||||
|
||||
if(resultToStack){
|
||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
when(resultRegister ?: RegisterOrPair.AY) {
|
||||
RegisterOrPair.AY -> {}
|
||||
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
||||
RegisterOrPair.XY -> asmgen.out(" tax")
|
||||
in Cx16VirtualRegisters -> asmgen.out(
|
||||
" sta cx16.${
|
||||
resultRegister.toString().lowercase()
|
||||
} | sty cx16.${resultRegister.toString().lowercase()}+1")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
when(resultRegister ?: RegisterOrPair.AY) {
|
||||
RegisterOrPair.AY -> {}
|
||||
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
||||
RegisterOrPair.XY -> asmgen.out(" tax")
|
||||
in Cx16VirtualRegisters -> asmgen.out(
|
||||
" sta cx16.${
|
||||
resultRegister.toString().lowercase()
|
||||
} | sty cx16.${resultRegister.toString().lowercase()}+1")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
when(fcall.type) {
|
||||
in ByteDatatypes -> {
|
||||
@ -854,30 +770,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
|
||||
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value
|
||||
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMin(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcMin(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
if(fcall.type in ByteDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
|
||||
@ -887,12 +795,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+""")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
} else if(fcall.type in WordDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
@ -928,18 +832,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
+""")
|
||||
}
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
} else {
|
||||
throw AssemblyError("min float not supported")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMax(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
if(fcall.type in ByteDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||
@ -949,12 +849,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+""")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
} else if(fcall.type in WordDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
@ -990,72 +886,61 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
}
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
} else {
|
||||
throw AssemblyError("max float not supported")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(resultToStack) {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta P8ESTACK_LO,x | pla | sta P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val reg = resultRegister ?: RegisterOrPair.AY
|
||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
if(!needAsave) {
|
||||
val mr0 = fcall.args[0] as? PtMemoryByte
|
||||
val mr1 = fcall.args[1] as? PtMemoryByte
|
||||
if (mr0 != null)
|
||||
needAsave = mr0.address !is PtNumber
|
||||
if (mr1 != null)
|
||||
needAsave = needAsave or (mr1.address !is PtNumber)
|
||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val reg = resultRegister ?: RegisterOrPair.AY
|
||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
if(!needAsave) {
|
||||
val mr0 = fcall.args[0] as? PtMemoryByte
|
||||
val mr1 = fcall.args[1] as? PtMemoryByte
|
||||
if (mr0 != null)
|
||||
needAsave = mr0.address !is PtNumber
|
||||
if (mr1 != null)
|
||||
needAsave = needAsave or (mr1.address !is PtNumber)
|
||||
}
|
||||
when(reg) {
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when(reg) {
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" tax")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid mkword target reg")
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" tax")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid mkword target reg")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.type !in WordDatatypes)
|
||||
throw AssemblyError("msb required word argument")
|
||||
@ -1063,53 +948,44 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
if(resultToStack) {
|
||||
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
if(resultToStack) {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tya")
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tya")
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.type !in WordDatatypes)
|
||||
throw AssemblyError("lsb required word argument")
|
||||
@ -1118,72 +994,60 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
if(resultToStack) {
|
||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
if(resultToStack) {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
|
||||
// NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what X is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tay | cpy #0")
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" ldy #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
|
||||
asmgen.out(" ldx #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
|
||||
asmgen.out(" ldy #0 | cpx #0")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister)
|
||||
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
zero.parent=fcall
|
||||
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
|
||||
asmgen.out(" lda cx16.r0L")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
|
||||
// NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what X is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tay | cpy #0")
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" ldy #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
|
||||
asmgen.out(" ldx #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
|
||||
asmgen.out(" ldy #0 | cpx #0")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister)
|
||||
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
zero.parent=fcall
|
||||
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
|
||||
asmgen.out(" lda cx16.r0L")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,933 +0,0 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
internal class ExpressionsAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val allocator: VariableAllocator) {
|
||||
|
||||
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||
internal fun translateExpression(expression: PtExpression) {
|
||||
if (this.asmgen.options.slowCodegenWarnings) {
|
||||
asmgen.errors.warn("slow stack evaluation used for expression", expression.position)
|
||||
}
|
||||
translateExpressionInternal(expression)
|
||||
}
|
||||
|
||||
|
||||
// the rest of the methods are all PRIVATE
|
||||
|
||||
|
||||
private fun translateExpressionInternal(expression: PtExpression) {
|
||||
|
||||
when(expression) {
|
||||
is PtPrefix -> translateExpression(expression)
|
||||
is PtBinaryExpression -> translateExpression(expression)
|
||||
is PtArrayIndexer -> translateExpression(expression)
|
||||
is PtTypeCast -> translateExpression(expression)
|
||||
is PtAddressOf -> translateExpression(expression)
|
||||
is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
|
||||
is PtNumber -> translateExpression(expression)
|
||||
is PtIdentifier -> translateExpression(expression)
|
||||
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||
is PtContainmentCheck -> translateContainmentCheck(expression)
|
||||
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
|
||||
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
|
||||
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
|
||||
else -> TODO("missing expression asmgen for $expression")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateContainmentCheck(check: PtContainmentCheck) {
|
||||
asmgen.assignExpressionToRegister(check, RegisterOrPair.A)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
|
||||
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
|
||||
// only for use in nested expression evaluation
|
||||
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
asmgen.saveXbeforeCall(call)
|
||||
asmgen.translateFunctionCall(call)
|
||||
if(sub.regXasResult()) {
|
||||
// store the return value in X somewhere that we can access again below
|
||||
asmgen.out(" stx P8ZP_SCRATCH_REG")
|
||||
}
|
||||
asmgen.restoreXafterCall(call)
|
||||
|
||||
val returns: List<Pair<RegisterOrStatusflag, DataType>> = sub.returnsWhatWhere()
|
||||
for ((reg, _) in returns) {
|
||||
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
|
||||
if (reg.registerOrPair != null) {
|
||||
when (reg.registerOrPair!!) {
|
||||
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
|
||||
RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
|
||||
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
RegisterOrPair.R2,
|
||||
RegisterOrPair.R3,
|
||||
RegisterOrPair.R4,
|
||||
RegisterOrPair.R5,
|
||||
RegisterOrPair.R6,
|
||||
RegisterOrPair.R7,
|
||||
RegisterOrPair.R8,
|
||||
RegisterOrPair.R9,
|
||||
RegisterOrPair.R10,
|
||||
RegisterOrPair.R11,
|
||||
RegisterOrPair.R12,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
asmgen.out(
|
||||
"""
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
}
|
||||
}
|
||||
} else when(reg.statusflag) {
|
||||
Statusflag.Pc -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
rol a
|
||||
sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pz -> {
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pv -> {
|
||||
asmgen.out("""
|
||||
bvs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pn -> {
|
||||
asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(typecast: PtTypeCast) {
|
||||
translateExpressionInternal(typecast.value)
|
||||
when(typecast.value.type) {
|
||||
DataType.UBYTE, DataType.BOOL -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when(typecast.type) {
|
||||
DataType.BYTE, DataType.UBYTE -> {}
|
||||
DataType.WORD, DataType.UWORD -> {}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(typecast.type) {
|
||||
DataType.BYTE, DataType.UBYTE -> {}
|
||||
DataType.WORD, DataType.UWORD -> {}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
|
||||
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
|
||||
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
|
||||
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
|
||||
DataType.FLOAT -> {}
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.STR -> {
|
||||
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
|
||||
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
||||
}
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtAddressOf) {
|
||||
val name = asmgen.asmVariableName(expr.identifier)
|
||||
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtNumber) {
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||
lda #<${expr.number.toHex()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda #>${expr.number.toHex()}
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
DataType.FLOAT -> {
|
||||
val floatConst = allocator.getFloatAsmConst(expr.number)
|
||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
||||
}
|
||||
in SplitWordArrayTypes -> {
|
||||
throw AssemblyError("can't push address of split-word array ${expr.position}")
|
||||
}
|
||||
in IterableDatatypes -> {
|
||||
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtBinaryExpression) {
|
||||
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
|
||||
return
|
||||
|
||||
val leftDt = expr.left.type
|
||||
val rightDt = expr.right.type
|
||||
|
||||
// compare with zero
|
||||
if(expr.operator in ComparisonOperators) {
|
||||
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||
val rightVal = expr.right.asConstInteger()
|
||||
if(rightVal==0)
|
||||
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
||||
}
|
||||
}
|
||||
|
||||
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators)
|
||||
return translateCompareStrings(expr.left, expr.operator, expr.right)
|
||||
|
||||
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
||||
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
||||
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
||||
|
||||
// the general, non-optimized cases
|
||||
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
||||
translateExpressionInternal(expr.left)
|
||||
translateExpressionInternal(expr.right)
|
||||
when (leftDt) {
|
||||
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||
else -> throw AssemblyError("non-numerical datatype")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateSomewhatOptimized(left: PtExpression, operator: String, right: PtExpression): Boolean {
|
||||
val leftDt = left.type
|
||||
val rightDt = right.type
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val leftVal = left.asConstInteger()
|
||||
val rightVal = right.asConstInteger()
|
||||
if (leftVal!=null && leftVal in -4..4) {
|
||||
translateExpressionInternal(right)
|
||||
if(rightDt in ByteDatatypes) {
|
||||
val incdec = if(leftVal<0) "dec" else "inc"
|
||||
repeat(leftVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(leftVal<0) {
|
||||
repeat(leftVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(leftVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
else if (rightVal!=null && rightVal in -4..4)
|
||||
{
|
||||
translateExpressionInternal(left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
val incdec = if(rightVal<0) "dec" else "inc"
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(rightVal<0) {
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(rightVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val rightVal = right.asConstInteger()
|
||||
if (rightVal!=null && rightVal in -4..4)
|
||||
{
|
||||
translateExpressionInternal(left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
val incdec = if(rightVal<0) "inc" else "dec"
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(rightVal>0) {
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(rightVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
">>" -> {
|
||||
val amount = right.asConstInteger()
|
||||
if(amount!=null) {
|
||||
translateExpressionInternal(left)
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" lsr a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
|
||||
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount>=16) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||
return true
|
||||
}
|
||||
var amountLeft = amount
|
||||
while (amountLeft >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_uw_7")
|
||||
amountLeft -= 7
|
||||
}
|
||||
if (amountLeft in 0..2)
|
||||
repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_uw_$amountLeft")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount>=16) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi +
|
||||
lda #0
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
beq ++
|
||||
+ lda #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
+""")
|
||||
return true
|
||||
}
|
||||
var amountLeft = amount
|
||||
while (amountLeft >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_w_7")
|
||||
amountLeft -= 7
|
||||
}
|
||||
if (amountLeft in 0..2)
|
||||
repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w_$amountLeft")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
"<<" -> {
|
||||
val amount = right.asConstInteger()
|
||||
if(amount!=null) {
|
||||
translateExpressionInternal(left)
|
||||
if (leftDt in ByteDatatypes) {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" asl a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
var amountLeft = amount
|
||||
while (amountLeft >= 7) {
|
||||
asmgen.out(" jsr math.shift_left_w_7")
|
||||
amountLeft -= 7
|
||||
}
|
||||
if (amountLeft in 0..2)
|
||||
repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_left_w_$amountLeft")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val leftVar = left as? PtIdentifier
|
||||
val rightVar = right as? PtIdentifier
|
||||
if(leftVar!=null && rightVar!=null && leftVar==rightVar) {
|
||||
translateSquared(leftVar, leftDt)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
val value = right as? PtNumber
|
||||
if(value!=null) {
|
||||
if(rightDt in IntegerDatatypes) {
|
||||
val amount = value.number.toInt()
|
||||
if(amount==2) {
|
||||
// optimize x*2 common case
|
||||
translateExpressionInternal(left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
||||
}
|
||||
return true
|
||||
}
|
||||
when(rightDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return true
|
||||
}
|
||||
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return true
|
||||
}
|
||||
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val rightVal = right.asConstInteger()
|
||||
if(rightVal!=null && rightVal==2) {
|
||||
translateExpressionInternal(left)
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bpl +
|
||||
inc P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_LO+1,x
|
||||
+ asl a
|
||||
ror P8ESTACK_LO+1,x""")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl ++
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+ lda P8ESTACK_HI+1,x
|
||||
+ asl a
|
||||
ror P8ESTACK_HI+1,x
|
||||
ror P8ESTACK_LO+1,x""")
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
|
||||
if(expr.isSimple()) {
|
||||
if(operator=="!=") {
|
||||
when (dt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
return
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
|
||||
asmgen.out("""
|
||||
sty P8ZP_SCRATCH_B1
|
||||
ora P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
return
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
|
||||
asmgen.out("""
|
||||
jsr floats.SIGN
|
||||
sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
return
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
/* operator == is not worth it to special case, the code is mostly larger */
|
||||
}
|
||||
translateExpressionInternal(expr)
|
||||
when(operator) {
|
||||
"==" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.equal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
"!=" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.notequalzero_b")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.notequalzero_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.notequal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
"<" -> {
|
||||
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||
return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position))
|
||||
when(dt) {
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.less_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
">" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.greaterzero_ub")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterzero_sb")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.greaterzero_uw")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterzero_sw")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.greater_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
"<=" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzero_sb")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
">=" -> {
|
||||
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||
return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position))
|
||||
when(dt) {
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.greaterequal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateSquared(variable: PtIdentifier, dt: DataType) {
|
||||
val asmVar = asmgen.asmVariableName(variable)
|
||||
when(dt) {
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" lda $asmVar")
|
||||
asmgen.signExtendAYlsb(dt)
|
||||
asmgen.out(" jsr math.square")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out(" lda $asmVar | ldy $asmVar+1 | jsr math.square")
|
||||
}
|
||||
else -> throw AssemblyError("require integer dt for square")
|
||||
}
|
||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtPrefix) {
|
||||
translateExpressionInternal(expr.value)
|
||||
when(expr.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes ->
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
eor #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(arrayExpr: PtArrayIndexer) {
|
||||
val elementDt = arrayExpr.type
|
||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
|
||||
|
||||
if(arrayExpr.variable.type==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(elementDt !in ByteDatatypes)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(arrayExpr.index.type != DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
if(asmgen.isZpVar(arrayExpr.variable)) {
|
||||
asmgen.out(" lda ($arrayVarName),y")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
return
|
||||
}
|
||||
|
||||
if(arrayExpr.splitWords)
|
||||
TODO("split words expression ${arrayExpr.position}")
|
||||
|
||||
val constIndexNum = arrayExpr.index.asConstInteger()
|
||||
if(constIndexNum!=null) {
|
||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue) | jsr floats.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird element type")
|
||||
}
|
||||
} else {
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>$arrayVarName
|
||||
clc
|
||||
adc #<$arrayVarName
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.push_float""")
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||
when(operator) {
|
||||
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
|
||||
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
||||
"%" -> {
|
||||
if(types==DataType.BYTE)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||
}
|
||||
"+" -> asmgen.out("""
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"-" -> asmgen.out("""
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
|
||||
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
|
||||
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
||||
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
|
||||
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
|
||||
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
|
||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
|
||||
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
|
||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
|
||||
when(operator) {
|
||||
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
||||
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||
"%" -> {
|
||||
if(dt==DataType.WORD)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||
}
|
||||
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||
"<<" -> asmgen.out(" jsr math.shift_left_w")
|
||||
">>" -> {
|
||||
if(dt==DataType.UWORD)
|
||||
asmgen.out(" jsr math.shift_right_uw")
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w")
|
||||
}
|
||||
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
|
||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
|
||||
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorFloats(operator: String) {
|
||||
when(operator) {
|
||||
"*" -> asmgen.out(" jsr floats.mul_f")
|
||||
"/" -> asmgen.out(" jsr floats.div_f")
|
||||
"+" -> asmgen.out(" jsr floats.add_f")
|
||||
"-" -> asmgen.out(" jsr floats.sub_f")
|
||||
"<" -> asmgen.out(" jsr floats.less_f")
|
||||
">" -> asmgen.out(" jsr floats.greater_f")
|
||||
"<=" -> asmgen.out(" jsr floats.lesseq_f")
|
||||
">=" -> asmgen.out(" jsr floats.greatereq_f")
|
||||
"==" -> asmgen.out(" jsr floats.equal_f")
|
||||
"!=" -> asmgen.out(" jsr floats.notequal_f")
|
||||
"%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
|
||||
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
|
||||
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||
compareStringsProcessResultInA(operator)
|
||||
}
|
||||
|
||||
private fun compareStringsProcessResultInA(operator: String) {
|
||||
when(operator) {
|
||||
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
||||
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
||||
"<=" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
">=" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
"<" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
">" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
}
|
||||
asmgen.out(" dex")
|
||||
}
|
||||
}
|
@ -6,15 +6,6 @@ import prog8.code.ast.PtSub
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal fun IPtSubroutine.regXasResult(): Boolean =
|
||||
(this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
|
||||
internal fun IPtSubroutine.shouldSaveX(): Boolean =
|
||||
this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam()))
|
||||
|
||||
internal fun PtAsmSub.regXasParam(): Boolean =
|
||||
parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
|
||||
internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
|
||||
|
||||
internal fun PtAsmSub.shouldKeepA(): KeepAresult {
|
||||
|
@ -11,42 +11,10 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||
|
||||
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
|
||||
saveXbeforeCall(stmt)
|
||||
translateFunctionCall(stmt)
|
||||
restoreXafterCall(stmt)
|
||||
// just ignore any result values from the function call.
|
||||
}
|
||||
|
||||
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
|
||||
val symbol = asmgen.symbolTable.lookup(stmt.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
if(sub.shouldSaveX()) {
|
||||
if(sub is PtAsmSub) {
|
||||
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||
if (regSaveOnStack)
|
||||
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
|
||||
else
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
|
||||
} else
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreXafterCall(stmt: PtFunctionCall) {
|
||||
val symbol = asmgen.symbolTable.lookup(stmt.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
if(sub.shouldSaveX()) {
|
||||
if(sub is PtAsmSub) {
|
||||
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||
if (regSaveOnStack)
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
|
||||
else
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
||||
|
@ -13,7 +13,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
val targetIdent = stmt.target.identifier
|
||||
val targetMemory = stmt.target.memory
|
||||
val targetArrayIdx = stmt.target.array
|
||||
val scope = stmt.definingISub()
|
||||
when {
|
||||
targetIdent!=null -> {
|
||||
val what = asmgen.asmVariableName(targetIdent)
|
||||
@ -76,7 +75,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
dec ${asmArrayvarname}_msb+$constIndex
|
||||
+ dec ${asmArrayvarname}_lsb+$constIndex""")
|
||||
} else {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
|
||||
@ -86,7 +84,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
bne +
|
||||
dec ${asmArrayvarname}_msb,x
|
||||
+ dec ${asmArrayvarname}_lsb,x""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -113,7 +110,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
else
|
||||
{
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
@ -141,7 +137,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +75,6 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
||||
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
||||
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
||||
asmgen.out(".endweak")
|
||||
|
||||
if(options.symbolDefs.isNotEmpty()) {
|
||||
@ -371,12 +369,6 @@ internal class ProgramAndVarsGen(
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
||||
asmgen.out("prog8_regsaveA .byte ?")
|
||||
if(asmGenInfo.usedRegsaveX)
|
||||
asmgen.out("prog8_regsaveX .byte ?")
|
||||
if(asmGenInfo.usedRegsaveY)
|
||||
asmgen.out("prog8_regsaveY .byte ?")
|
||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||
|
@ -0,0 +1,265 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
//
|
||||
// This contains codegen for stack-based evaluation of binary expressions.
|
||||
// It uses the CPU stack so depth is limited.
|
||||
// It is called "as a last resort" if the optimized codegen path is unable
|
||||
// to come up with a special case of the expression.
|
||||
//
|
||||
internal class AnyExprAsmGen(
|
||||
private val asmgen: AsmGen6502Internal
|
||||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> {
|
||||
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
|
||||
return assignByteBinExpr(expr, assign)
|
||||
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
TODO("words operands comparison -> byte")
|
||||
}
|
||||
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
TODO("weird expr operand types")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
"both operands must be words"
|
||||
}
|
||||
return assignWordBinExpr(expr)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
"both operands must be floats"
|
||||
}
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
else -> throw AssemblyError("weird expression type in assignment")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
TODO("word + at ${expr.position}")
|
||||
}
|
||||
"-" -> {
|
||||
TODO("word - at ${expr.position}")
|
||||
}
|
||||
"*" -> {
|
||||
TODO("word * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("word / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("word << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("word >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("word % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
TODO("word and at ${expr.position}")
|
||||
}
|
||||
"|", "or" -> {
|
||||
TODO("word or at ${expr.position}")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
TODO("word xor at ${expr.position}")
|
||||
}
|
||||
"==" -> {
|
||||
TODO("word == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("word != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("word < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("word <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("word > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("word >= at ${expr.position}")
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
TODO("byte * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("byte / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("byte << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("byte >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("byte % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"==" -> {
|
||||
TODO("byte == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("byte != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("byte < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("byte <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("byte > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("byte >= at ${expr.position}")
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" jsr floats.FADDT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
|
||||
if(!expr.left.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
|
||||
if(!expr.left.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" jsr floats.FSUBT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" jsr floats.FMULTT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"/" -> {
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
|
||||
if(!expr.left.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
|
||||
if(!expr.left.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" jsr floats.FDIVT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"==" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_equal_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"!=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_notequal_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"<" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_less_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
">" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_greater_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"<=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_lesseq_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
">=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_greatereq_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
else -> TODO("float expression operator ${expr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
||||
}
|
||||
}
|
@ -10,8 +10,7 @@ internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK
|
||||
REGISTER
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@ -20,7 +19,6 @@ internal enum class SourceStorageKind {
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK, // value is already present on stack
|
||||
EXPRESSION, // expression in ast-form, still to be evaluated
|
||||
}
|
||||
|
||||
@ -120,7 +118,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.REGISTER, TargetStorageKind.STACK -> {
|
||||
TargetStorageKind.REGISTER -> {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -81,7 +81,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
@ -100,25 +99,47 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, value.number!!.number, target.scope!!)
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, value.asmVarname, target.scope!!)
|
||||
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, regName(value), target.scope!!)
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
|
||||
target.asmVarname,
|
||||
operator,
|
||||
value.number!!.number
|
||||
)
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(
|
||||
target.asmVarname,
|
||||
operator,
|
||||
value.asmVarname
|
||||
)
|
||||
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(
|
||||
target.asmVarname,
|
||||
operator,
|
||||
regName(value)
|
||||
)
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float")
|
||||
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value.array!!, target.scope!!)
|
||||
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(
|
||||
target.asmVarname,
|
||||
operator,
|
||||
value.array!!
|
||||
)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplaceModification_float_value_to_variable(target.asmVarname, operator, value.expression, target.scope!!)
|
||||
inplaceModification_float_value_to_variable(
|
||||
target.asmVarname,
|
||||
operator,
|
||||
value.expression
|
||||
)
|
||||
} else {
|
||||
inplaceModification_float_value_to_variable(target.asmVarname, operator, value.expression!!, target.scope!!)
|
||||
inplaceModification_float_value_to_variable(
|
||||
target.asmVarname,
|
||||
operator,
|
||||
value.expression!!
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||
@ -143,7 +164,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
@ -162,13 +182,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_byte_value_to_pointer(pointer, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
|
||||
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingISub(), target.position))
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
||||
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.out(" jsr prog8_lib.read_byte_from_address_in_AY | sta P8ZP_SCRATCH_B1")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.asmVarname)
|
||||
@ -182,9 +202,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
asmgen.out(" lda P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_to_address_on_stack | inx")
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_X_to_address_in_AY")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -213,7 +234,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
@ -231,25 +251,47 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(targetVarName, operator, value.number!!.number, target.scope!!)
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(targetVarName, operator, value.asmVarname, target.scope!!)
|
||||
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(targetVarName, operator, regName(value), target.scope!!)
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
|
||||
targetVarName,
|
||||
operator,
|
||||
value.number!!.number
|
||||
)
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(
|
||||
targetVarName,
|
||||
operator,
|
||||
value.asmVarname
|
||||
)
|
||||
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(
|
||||
targetVarName,
|
||||
operator,
|
||||
regName(value)
|
||||
)
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float array")
|
||||
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(targetVarName, operator, value.array!!, target.scope!!)
|
||||
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(
|
||||
targetVarName,
|
||||
operator,
|
||||
value.array!!
|
||||
)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplaceModification_float_value_to_variable(targetVarName, operator, value.expression, target.scope!!)
|
||||
inplaceModification_float_value_to_variable(
|
||||
targetVarName,
|
||||
operator,
|
||||
value.expression
|
||||
)
|
||||
} else {
|
||||
inplaceModification_float_value_to_variable(targetVarName, operator, value.expression!!, target.scope!!)
|
||||
inplaceModification_float_value_to_variable(
|
||||
targetVarName,
|
||||
operator,
|
||||
value.expression!!
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||
@ -264,7 +306,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
|
||||
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_B1")
|
||||
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.asmVarname)
|
||||
@ -278,9 +320,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
asmgen.restoreRegisterLocal(CpuRegister.Y)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda P8ZP_SCRATCH_B1 | sta ${target.array.variable.name},y")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
@ -310,7 +351,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
if(target.array.splitWords) {
|
||||
@ -336,21 +376,44 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
ldy #>$tempvar
|
||||
jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(tempvar, operator, value.number!!.number, target.scope!!)
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(tempvar, operator, value.asmVarname, target.scope!!)
|
||||
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(tempvar, operator, regName(value), target.scope!!)
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
|
||||
tempvar,
|
||||
operator,
|
||||
value.number!!.number
|
||||
)
|
||||
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(
|
||||
tempvar,
|
||||
operator,
|
||||
value.asmVarname
|
||||
)
|
||||
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(
|
||||
tempvar,
|
||||
operator,
|
||||
regName(value)
|
||||
)
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float")
|
||||
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(tempvar, operator, value.array!!, target.scope!!)
|
||||
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(
|
||||
tempvar,
|
||||
operator,
|
||||
value.array!!
|
||||
)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
|
||||
return
|
||||
inplaceModification_float_value_to_variable(tempvar, operator, value.expression, target.scope!!)
|
||||
inplaceModification_float_value_to_variable(
|
||||
tempvar,
|
||||
operator,
|
||||
value.expression
|
||||
)
|
||||
} else {
|
||||
inplaceModification_float_value_to_variable(tempvar, operator, value.expression!!, target.scope!!)
|
||||
inplaceModification_float_value_to_variable(
|
||||
tempvar,
|
||||
operator,
|
||||
value.expression!!
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
@ -372,7 +435,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
|
||||
TargetStorageKind.STACK -> throw AssemblyError("no asm gen for stack in-place modification")
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,8 +654,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||
// this should be the last resort for code generation for this,
|
||||
// because the value is evaluated onto the eval stack (=slow).
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
@ -1071,14 +1131,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: PtMemoryByte) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $name
|
||||
sta $name""")
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
|
||||
asmgen.out("""
|
||||
sta $tmpByte
|
||||
@ -1088,15 +1148,15 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
sta $name""")
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out(" ora $name | sta $name")
|
||||
}
|
||||
"&" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
else -> {
|
||||
@ -1108,7 +1168,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: PtMemoryByte) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $name
|
||||
@ -1118,7 +1178,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
|
||||
asmgen.out("""
|
||||
sta $tmpByte
|
||||
@ -1131,11 +1191,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out(" ora $name | sta $name")
|
||||
}
|
||||
"&" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
if(dt in WordDatatypes) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
@ -1145,7 +1205,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||
asmgen.translateDirectMemReadExpressionToRegA(memread)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
else -> {
|
||||
@ -1998,8 +2058,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||
// this should be the last resort for code generation for this,
|
||||
// because the value is evaluated onto the eval stack (=slow).
|
||||
fun multiplyVarByWordInAY() {
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@ -2286,9 +2344,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression, scope: IPtSubroutine) {
|
||||
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
@ -2330,11 +2387,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
ldy #>$name
|
||||
jsr floats.MOVMF
|
||||
""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
|
||||
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, otherName: String, scope: IPtSubroutine) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, otherName: String) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
@ -2446,12 +2501,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
ldy #>$name
|
||||
jsr floats.MOVMF
|
||||
""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
|
||||
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: IPtSubroutine) {
|
||||
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double) {
|
||||
val constValueName = allocator.getFloatAsmConst(value)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
if (value == 0.0)
|
||||
@ -2569,6 +2622,5 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
ldy #>$name
|
||||
jsr floats.MOVMF
|
||||
""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
|
@ -96,8 +96,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
|
||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
if (codeGen.options.slowCodegenWarnings)
|
||||
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
||||
val value: PtExpression
|
||||
if(origAssign.operator in PrefixOperators) {
|
||||
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||
|
@ -24,10 +24,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"popw" -> funcPopw(call)
|
||||
"push" -> funcPush(call)
|
||||
"pushw" -> funcPushw(call)
|
||||
"rsave",
|
||||
"rsavex",
|
||||
"rrestore",
|
||||
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||
"callfar" -> funcCallfar(call)
|
||||
"msb" -> funcMsb(call)
|
||||
"lsb" -> funcLsb(call)
|
||||
|
@ -29,9 +29,6 @@ class IRCodeGen(
|
||||
irSymbolTable = IRSymbolTable(symbolTable)
|
||||
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||
|
||||
if(options.evalStackBaseAddress!=null)
|
||||
throw AssemblyError("IR doesn't use eval-stack")
|
||||
|
||||
// collect global variables initializers
|
||||
program.allBlocks().forEach {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
@ -1,71 +0,0 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.AssignmentOrigin
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.AugmentAssignmentOperators
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
class BinExprSplitter(private val program: Program, private val options: CompilationOptions) : AstWalker() {
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(options.compTarget.name == VMTarget.NAME)
|
||||
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
|
||||
|
||||
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
|
||||
return noModifications
|
||||
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if (binExpr != null) {
|
||||
|
||||
if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
|
||||
if(assignment.target isSameAs binExpr.right)
|
||||
return noModifications
|
||||
if(assignment.target isSameAs binExpr.left) {
|
||||
if(binExpr.right.isSimple)
|
||||
return noModifications
|
||||
val leftBx = binExpr.left as? BinaryExpression
|
||||
if(leftBx!=null && (!leftBx.left.isSimple || !leftBx.right.isSimple))
|
||||
return noModifications
|
||||
val rightBx = binExpr.right as? BinaryExpression
|
||||
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(binExpr.right.isSimple) {
|
||||
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, AssignmentOrigin.OPTIMIZER, binExpr.left.position)
|
||||
val targetExpr = assignment.target.toExpression()
|
||||
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
|
||||
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Further unraveling of binary expressions is really complicated here and
|
||||
// often results in much bigger code, thereby defeating the purpose a bit.
|
||||
// All in all this should probably be fixed in a better code generation backend
|
||||
// that doesn't require this at all.
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun isSimpleTarget(target: AssignTarget) =
|
||||
if (target.identifier!=null || target.memoryAddress!=null)
|
||||
!target.isIOAddress(options.compTarget.machine)
|
||||
else
|
||||
false
|
||||
|
||||
}
|
@ -235,6 +235,47 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
}
|
||||
}
|
||||
|
||||
if(rightconst!=null && (expr.operator=="<<" || expr.operator==">>")) {
|
||||
val dt = expr.left.inferType(program)
|
||||
if(dt.isBytes && rightconst.number>=8) {
|
||||
if(dt.istype(DataType.UBYTE)) {
|
||||
val zeroUB = NumericLiteral(DataType.UBYTE, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUB, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroB = NumericLiteral(DataType.BYTE, 0.0, expr.position)
|
||||
val minusoneB = NumericLiteral(DataType.BYTE, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneB, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(dt.isWords && rightconst.number>=16) {
|
||||
if(dt.istype(DataType.UWORD)) {
|
||||
val zeroUW = NumericLiteral(DataType.UWORD, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUW, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroW = NumericLiteral(DataType.WORD, 0.0, expr.position)
|
||||
val minusoneW = NumericLiteral(DataType.WORD, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneW, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modifications
|
||||
}
|
||||
|
@ -65,9 +65,3 @@ fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarg
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.splitBinaryExpressions(options: CompilationOptions) : Int {
|
||||
val opti = BinExprSplitter(this, options)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
@ -111,8 +111,6 @@ class StatementOptimizer(private val program: Program,
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
if(options.slowCodegenWarnings)
|
||||
errors.warn("else can be omitted", ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
|
@ -153,10 +153,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@ -164,16 +163,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr chrout
|
||||
txa
|
||||
jsr chrout
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@ -189,16 +185,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr chrout
|
||||
_ones txa
|
||||
jsr chrout
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@ -210,10 +203,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
@ -222,16 +214,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr chrout
|
||||
tya
|
||||
jsr chrout
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
@ -244,12 +233,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr chrout
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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
|
||||
@ -261,7 +249,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
@ -274,10 +262,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@ -285,17 +272,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr chrout
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@ -316,7 +300,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
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
|
||||
@ -388,7 +372,7 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) {
|
||||
; ---- set cursor at specific position
|
||||
; TODO
|
||||
%asm {{
|
||||
|
@ -104,34 +104,26 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
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 {{
|
||||
txa
|
||||
pha
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr cbm.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -446,10 +438,6 @@ _irq_handler_init
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||
ldx #32
|
||||
cld
|
||||
rts
|
||||
|
||||
@ -774,111 +762,108 @@ cx16 {
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the C128 as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack.
|
||||
; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless
|
||||
; you're doing insane nesting of expressions...)
|
||||
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
|
||||
&uword r0 = $1b00
|
||||
&uword r1 = $1b02
|
||||
&uword r2 = $1b04
|
||||
&uword r3 = $1b06
|
||||
&uword r4 = $1b08
|
||||
&uword r5 = $1b0a
|
||||
&uword r6 = $1b0c
|
||||
&uword r7 = $1b0e
|
||||
&uword r8 = $1b10
|
||||
&uword r9 = $1b12
|
||||
&uword r10 = $1b14
|
||||
&uword r11 = $1b16
|
||||
&uword r12 = $1b18
|
||||
&uword r13 = $1b1a
|
||||
&uword r14 = $1b1c
|
||||
&uword r15 = $1b1e
|
||||
; $1300-$1bff is unused RAM on C128.
|
||||
&uword r0 = $1be0
|
||||
&uword r1 = $1be2
|
||||
&uword r2 = $1be4
|
||||
&uword r3 = $1be6
|
||||
&uword r4 = $1be8
|
||||
&uword r5 = $1bea
|
||||
&uword r6 = $1bec
|
||||
&uword r7 = $1bee
|
||||
&uword r8 = $1bf0
|
||||
&uword r9 = $1bf2
|
||||
&uword r10 = $1bf4
|
||||
&uword r11 = $1bf6
|
||||
&uword r12 = $1bf8
|
||||
&uword r13 = $1bfa
|
||||
&uword r14 = $1bfc
|
||||
&uword r15 = $1bfe
|
||||
|
||||
&word r0s = $1b00
|
||||
&word r1s = $1b02
|
||||
&word r2s = $1b04
|
||||
&word r3s = $1b06
|
||||
&word r4s = $1b08
|
||||
&word r5s = $1b0a
|
||||
&word r6s = $1b0c
|
||||
&word r7s = $1b0e
|
||||
&word r8s = $1b10
|
||||
&word r9s = $1b12
|
||||
&word r10s = $1b14
|
||||
&word r11s = $1b16
|
||||
&word r12s = $1b18
|
||||
&word r13s = $1b1a
|
||||
&word r14s = $1b1c
|
||||
&word r15s = $1b1e
|
||||
&word r0s = $1be0
|
||||
&word r1s = $1be2
|
||||
&word r2s = $1be4
|
||||
&word r3s = $1be6
|
||||
&word r4s = $1be8
|
||||
&word r5s = $1bea
|
||||
&word r6s = $1bec
|
||||
&word r7s = $1bee
|
||||
&word r8s = $1bf0
|
||||
&word r9s = $1bf2
|
||||
&word r10s = $1bf4
|
||||
&word r11s = $1bf6
|
||||
&word r12s = $1bf8
|
||||
&word r13s = $1bfa
|
||||
&word r14s = $1bfc
|
||||
&word r15s = $1bfe
|
||||
|
||||
&ubyte r0L = $1b00
|
||||
&ubyte r1L = $1b02
|
||||
&ubyte r2L = $1b04
|
||||
&ubyte r3L = $1b06
|
||||
&ubyte r4L = $1b08
|
||||
&ubyte r5L = $1b0a
|
||||
&ubyte r6L = $1b0c
|
||||
&ubyte r7L = $1b0e
|
||||
&ubyte r8L = $1b10
|
||||
&ubyte r9L = $1b12
|
||||
&ubyte r10L = $1b14
|
||||
&ubyte r11L = $1b16
|
||||
&ubyte r12L = $1b18
|
||||
&ubyte r13L = $1b1a
|
||||
&ubyte r14L = $1b1c
|
||||
&ubyte r15L = $1b1e
|
||||
&ubyte r0L = $1be0
|
||||
&ubyte r1L = $1be2
|
||||
&ubyte r2L = $1be4
|
||||
&ubyte r3L = $1be6
|
||||
&ubyte r4L = $1be8
|
||||
&ubyte r5L = $1bea
|
||||
&ubyte r6L = $1bec
|
||||
&ubyte r7L = $1bee
|
||||
&ubyte r8L = $1bf0
|
||||
&ubyte r9L = $1bf2
|
||||
&ubyte r10L = $1bf4
|
||||
&ubyte r11L = $1bf6
|
||||
&ubyte r12L = $1bf8
|
||||
&ubyte r13L = $1bfa
|
||||
&ubyte r14L = $1bfc
|
||||
&ubyte r15L = $1bfe
|
||||
|
||||
&ubyte r0H = $1b01
|
||||
&ubyte r1H = $1b03
|
||||
&ubyte r2H = $1b05
|
||||
&ubyte r3H = $1b07
|
||||
&ubyte r4H = $1b09
|
||||
&ubyte r5H = $1b0b
|
||||
&ubyte r6H = $1b0d
|
||||
&ubyte r7H = $1b0f
|
||||
&ubyte r8H = $1b11
|
||||
&ubyte r9H = $1b13
|
||||
&ubyte r10H = $1b15
|
||||
&ubyte r11H = $1b17
|
||||
&ubyte r12H = $1b19
|
||||
&ubyte r13H = $1b1b
|
||||
&ubyte r14H = $1b1d
|
||||
&ubyte r15H = $1b1f
|
||||
&ubyte r0H = $1be1
|
||||
&ubyte r1H = $1be3
|
||||
&ubyte r2H = $1be5
|
||||
&ubyte r3H = $1be7
|
||||
&ubyte r4H = $1be9
|
||||
&ubyte r5H = $1beb
|
||||
&ubyte r6H = $1bed
|
||||
&ubyte r7H = $1bef
|
||||
&ubyte r8H = $1bf1
|
||||
&ubyte r9H = $1bf3
|
||||
&ubyte r10H = $1bf5
|
||||
&ubyte r11H = $1bf7
|
||||
&ubyte r12H = $1bf9
|
||||
&ubyte r13H = $1bfb
|
||||
&ubyte r14H = $1bfd
|
||||
&ubyte r15H = $1bff
|
||||
|
||||
&byte r0sL = $1b00
|
||||
&byte r1sL = $1b02
|
||||
&byte r2sL = $1b04
|
||||
&byte r3sL = $1b06
|
||||
&byte r4sL = $1b08
|
||||
&byte r5sL = $1b0a
|
||||
&byte r6sL = $1b0c
|
||||
&byte r7sL = $1b0e
|
||||
&byte r8sL = $1b10
|
||||
&byte r9sL = $1b12
|
||||
&byte r10sL = $1b14
|
||||
&byte r11sL = $1b16
|
||||
&byte r12sL = $1b18
|
||||
&byte r13sL = $1b1a
|
||||
&byte r14sL = $1b1c
|
||||
&byte r15sL = $1b1e
|
||||
&byte r0sL = $1be0
|
||||
&byte r1sL = $1be2
|
||||
&byte r2sL = $1be4
|
||||
&byte r3sL = $1be6
|
||||
&byte r4sL = $1be8
|
||||
&byte r5sL = $1bea
|
||||
&byte r6sL = $1bec
|
||||
&byte r7sL = $1bee
|
||||
&byte r8sL = $1bf0
|
||||
&byte r9sL = $1bf2
|
||||
&byte r10sL = $1bf4
|
||||
&byte r11sL = $1bf6
|
||||
&byte r12sL = $1bf8
|
||||
&byte r13sL = $1bfa
|
||||
&byte r14sL = $1bfc
|
||||
&byte r15sL = $1bfe
|
||||
|
||||
&byte r0sH = $1b01
|
||||
&byte r1sH = $1b03
|
||||
&byte r2sH = $1b05
|
||||
&byte r3sH = $1b07
|
||||
&byte r4sH = $1b09
|
||||
&byte r5sH = $1b0b
|
||||
&byte r6sH = $1b0d
|
||||
&byte r7sH = $1b0f
|
||||
&byte r8sH = $1b11
|
||||
&byte r9sH = $1b13
|
||||
&byte r10sH = $1b15
|
||||
&byte r11sH = $1b17
|
||||
&byte r12sH = $1b19
|
||||
&byte r13sH = $1b1b
|
||||
&byte r14sH = $1b1d
|
||||
&byte r15sH = $1b1f
|
||||
&byte r0sH = $1be1
|
||||
&byte r1sH = $1be3
|
||||
&byte r2sH = $1be5
|
||||
&byte r3sH = $1be7
|
||||
&byte r4sH = $1be9
|
||||
&byte r5sH = $1beb
|
||||
&byte r6sH = $1bed
|
||||
&byte r7sH = $1bef
|
||||
&byte r8sH = $1bf1
|
||||
&byte r9sH = $1bf3
|
||||
&byte r10sH = $1bf5
|
||||
&byte r11sH = $1bf7
|
||||
&byte r12sH = $1bf9
|
||||
&byte r13sH = $1bfb
|
||||
&byte r14sH = $1bfd
|
||||
&byte r15sH = $1bff
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
|
@ -97,13 +97,12 @@ sub uppercase() {
|
||||
c128.VM1 &= ~2
|
||||
}
|
||||
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left (bool alsocolors @ Pc) 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
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -133,17 +132,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dey
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_right (bool alsocolors @ Pc) 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
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -169,17 +166,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -205,17 +200,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -241,7 +234,6 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -266,10 +258,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@ -277,16 +268,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@ -302,16 +290,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@ -323,10 +308,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
@ -335,16 +319,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
@ -357,12 +338,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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
|
||||
@ -374,7 +354,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
@ -387,10 +367,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@ -398,17 +377,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@ -429,7 +405,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
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
|
||||
@ -583,15 +559,10 @@ _colormod sta $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr cbm.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,11 @@ FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
|
||||
|
||||
floats_store_reg .byte 0 ; temp storage
|
||||
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
|
||||
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers A, X, Y
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
@ -19,15 +17,12 @@ ub2float .proc
|
||||
jsr GIVAYF
|
||||
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
b2float .proc
|
||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers A, X, Y
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
@ -37,7 +32,7 @@ b2float .proc
|
||||
|
||||
uw2float .proc
|
||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
@ -48,7 +43,7 @@ uw2float .proc
|
||||
|
||||
w2float .proc
|
||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_W1
|
||||
@ -60,7 +55,7 @@ w2float .proc
|
||||
|
||||
cast_from_uw .proc
|
||||
; -- uword in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
@ -68,7 +63,7 @@ cast_from_uw .proc
|
||||
|
||||
cast_from_w .proc
|
||||
; -- word in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr GIVAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
@ -76,7 +71,7 @@ cast_from_w .proc
|
||||
|
||||
cast_from_ub .proc
|
||||
; -- ubyte in Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr FREADUY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
@ -84,169 +79,41 @@ cast_from_ub .proc
|
||||
|
||||
cast_from_b .proc
|
||||
; -- byte in A into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
cast_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast float at A/Y to uword into Y/A
|
||||
; clobbers X
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_uw_into_ya
|
||||
.pend
|
||||
|
||||
cast_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast float at A/Y to word into A/Y
|
||||
; clobbers X
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_w_into_ay
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast fac1 to uword into Y/A
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR ; into Y/A
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
; clobbers X
|
||||
jmp GETADR ; into Y/A
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast fac1 to word into A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr AYINT
|
||||
ldy floats.AYINT_facmo
|
||||
lda floats.AYINT_facmo+1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
stack_b2float .proc
|
||||
; -- b2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADSA
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_w2float .proc
|
||||
; -- w2float operating on the stack
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
lda P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_ub2float .proc
|
||||
; -- ub2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tay
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_uw2float .proc
|
||||
; -- uw2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_float2w .proc ; also used for float2b
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr AYINT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
lda floats.AYINT_facmo
|
||||
sta P8ESTACK_HI,x
|
||||
lda floats.AYINT_facmo+1
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_float2uw .proc ; also used for float2ub
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
sta P8ESTACK_HI,x
|
||||
tya
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
push_float .proc
|
||||
; ---- push mflpt5 in A/Y onto stack
|
||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float .proc
|
||||
; ---- pops mflpt5 from stack to memory A/Y
|
||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #4
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float_fac1 .proc
|
||||
; -- pops float from stack into FAC1
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
@ -272,25 +139,23 @@ copy_float .proc
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
dec_var_f .proc
|
||||
; -- subtract 1 from float pointed to by A/Y
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr MOVFM
|
||||
@ -299,23 +164,7 @@ dec_var_f .proc
|
||||
jsr FSUB
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
pop_2_floats_f2_in_fac1 .proc
|
||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jmp MOVFM
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
|
||||
@ -323,71 +172,9 @@ fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
|
||||
|
||||
push_fac1 .proc
|
||||
; -- push the float in FAC1 onto the stack
|
||||
stx P8ZP_SCRATCH_REG
|
||||
_internal ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
div_f .proc
|
||||
; -- push f1/f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FDIV
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
add_f .proc
|
||||
; -- push f1+f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FADD
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
sub_f .proc
|
||||
; -- push f1-f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FSUB
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
mul_f .proc
|
||||
; -- push f1*f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FMULT
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
neg_f .proc
|
||||
; -- toggle the sign bit on the stack
|
||||
lda P8ESTACK_HI+3,x
|
||||
eor #$80
|
||||
sta P8ESTACK_HI+3,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_less_f .proc
|
||||
; -- is the float in FAC1 < the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
beq +
|
||||
lda #0
|
||||
@ -397,10 +184,8 @@ var_fac1_less_f .proc
|
||||
.pend
|
||||
|
||||
var_fac1_lesseq_f .proc
|
||||
; -- is the float in FAC1 <= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 <= the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #255
|
||||
@ -412,10 +197,8 @@ var_fac1_lesseq_f .proc
|
||||
.pend
|
||||
|
||||
var_fac1_greater_f .proc
|
||||
; -- is the float in FAC1 > the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 > the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #1
|
||||
beq +
|
||||
lda #0
|
||||
@ -425,10 +208,8 @@ var_fac1_greater_f .proc
|
||||
.pend
|
||||
|
||||
var_fac1_greatereq_f .proc
|
||||
; -- is the float in FAC1 >= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 >= the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #1
|
||||
@ -439,17 +220,23 @@ var_fac1_greatereq_f .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_notequal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
var_fac1_equal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
and #1
|
||||
eor #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_notequal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
and #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_equal_f .proc
|
||||
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical?
|
||||
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical? Result in A
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
@ -478,51 +265,13 @@ _false lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
equal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack identical?
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
lda P8ESTACK_LO-3,x
|
||||
cmp P8ESTACK_LO,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bne _equals_false
|
||||
_equals_true lda #1
|
||||
_equals_store inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_equals_false lda #0
|
||||
beq _equals_store
|
||||
.pend
|
||||
|
||||
notequal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack different?
|
||||
jsr equal_f
|
||||
eor #1 ; invert the result
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_less_f .proc
|
||||
; -- is float in AY < float in P8ZP_SCRATCH_W2 ?
|
||||
; -- is float in AY < float in P8ZP_SCRATCH_W2 ? Result in A. Clobbers X.
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
lda #1
|
||||
@ -532,13 +281,11 @@ vars_less_f .proc
|
||||
.pend
|
||||
|
||||
vars_lesseq_f .proc
|
||||
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ?
|
||||
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ? Result in A. Clobbers X.
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
- lda #1
|
||||
@ -550,7 +297,7 @@ vars_lesseq_f .proc
|
||||
.pend
|
||||
|
||||
less_f .proc
|
||||
; -- is f1 < f2?
|
||||
; -- is f1 < f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
@ -559,7 +306,7 @@ less_f .proc
|
||||
|
||||
|
||||
lesseq_f .proc
|
||||
; -- is f1 <= f2?
|
||||
; -- is f1 <= f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
@ -569,7 +316,7 @@ lesseq_f .proc
|
||||
.pend
|
||||
|
||||
greater_f .proc
|
||||
; -- is f1 > f2?
|
||||
; -- is f1 > f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
@ -577,7 +324,7 @@ greater_f .proc
|
||||
.pend
|
||||
|
||||
greatereq_f .proc
|
||||
; -- is f1 >= f2?
|
||||
; -- is f1 >= f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
@ -586,32 +333,9 @@ greatereq_f .proc
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
compare_floats .proc
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVFM ; fac1 = flt1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_return_false lda #0
|
||||
_return_result sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_return_true lda #1
|
||||
bne _return_result
|
||||
.pend
|
||||
|
||||
set_array_float_from_fac1 .proc
|
||||
; -- set the float in FAC1 in the array (index in A, array in P8ZP_SCRATCH_W1)
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
@ -622,11 +346,8 @@ set_array_float_from_fac1 .proc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
iny
|
||||
+ stx floats_store_reg
|
||||
tax
|
||||
jsr MOVMF
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
+ tax
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
|
||||
@ -669,54 +390,59 @@ set_array_float .proc
|
||||
.pend
|
||||
|
||||
|
||||
equal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
beq _true
|
||||
bne _false
|
||||
_true lda #1
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_false lda #0
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
pushFAC1 .proc
|
||||
;-- push floating point in FAC onto the cpu stack
|
||||
; save return address
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
ldx #<floats.floats_temp_var
|
||||
ldy #>floats.floats_temp_var
|
||||
jsr floats.MOVMF
|
||||
lda floats.floats_temp_var
|
||||
pha
|
||||
lda floats.floats_temp_var+1
|
||||
pha
|
||||
lda floats.floats_temp_var+2
|
||||
pha
|
||||
lda floats.floats_temp_var+3
|
||||
pha
|
||||
lda floats.floats_temp_var+4
|
||||
pha
|
||||
; re-push return address
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
pha
|
||||
lda P8ZP_SCRATCH_W2
|
||||
pha
|
||||
rts
|
||||
.pend
|
||||
|
||||
notequal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
bne equal_zero._true
|
||||
beq equal_zero._false
|
||||
.pend
|
||||
popFAC1 .proc
|
||||
; -- pop floating point value from cpu stack into FAC1
|
||||
; save return address
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
pla
|
||||
sta floats.floats_temp_var+4
|
||||
pla
|
||||
sta floats.floats_temp_var+3
|
||||
pla
|
||||
sta floats.floats_temp_var+2
|
||||
pla
|
||||
sta floats.floats_temp_var+1
|
||||
pla
|
||||
sta floats.floats_temp_var
|
||||
lda #<floats.floats_temp_var
|
||||
ldy #>floats.floats_temp_var
|
||||
jsr floats.MOVFM
|
||||
; re-push return address
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
pha
|
||||
lda P8ZP_SCRATCH_W2
|
||||
pha
|
||||
rts
|
||||
.pend
|
||||
|
||||
greater_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
beq equal_zero._false
|
||||
bpl equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
|
||||
less_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
bmi equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
|
||||
greaterequal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
bpl equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
|
||||
lessequal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
beq equal_zero._true
|
||||
bmi equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
|
@ -159,12 +159,9 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
|
||||
sub rndf() -> float {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #1
|
||||
jsr FREADSA
|
||||
jsr RND ; rng into fac1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp RND ; rng into fac1
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,6 @@
|
||||
; --- floating point builtin functions
|
||||
|
||||
|
||||
func_sign_f_stack .proc
|
||||
jsr func_sign_f_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_f_into_A .proc
|
||||
jsr MOVFM
|
||||
jmp SIGN
|
||||
@ -146,17 +139,11 @@ func_all_f_stack .proc
|
||||
.pend
|
||||
|
||||
func_abs_f_into_FAC1 .proc
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
jsr ABS
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp ABS
|
||||
.pend
|
||||
|
||||
func_sqrt_into_FAC1 .proc
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
jsr SQR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp SQR
|
||||
.pend
|
||||
|
@ -185,7 +185,6 @@ graphics {
|
||||
lda length
|
||||
and #7
|
||||
sta separate_pixels
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
@ -210,8 +209,7 @@ _modified stx $ffff ; modified
|
||||
inc _modified+2
|
||||
+ dey
|
||||
bne _modified
|
||||
_zero ldx P8ZP_SCRATCH_REG
|
||||
|
||||
_zero
|
||||
ldy separate_pixels
|
||||
beq hline_zero2
|
||||
lda _modified+1
|
||||
|
@ -104,34 +104,26 @@ romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
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 {{
|
||||
txa
|
||||
pha
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr cbm.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -413,10 +405,6 @@ _irq_handler_init
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||
ldx #32
|
||||
cld
|
||||
rts
|
||||
|
||||
@ -742,111 +730,110 @@ cx16 {
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the C64 as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
; they are allocated at the bottom of the eval-stack (should be ample space unless
|
||||
; you're doing insane nesting of expressions...)
|
||||
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
|
||||
&uword r0 = $cf00
|
||||
&uword r1 = $cf02
|
||||
&uword r2 = $cf04
|
||||
&uword r3 = $cf06
|
||||
&uword r4 = $cf08
|
||||
&uword r5 = $cf0a
|
||||
&uword r6 = $cf0c
|
||||
&uword r7 = $cf0e
|
||||
&uword r8 = $cf10
|
||||
&uword r9 = $cf12
|
||||
&uword r10 = $cf14
|
||||
&uword r11 = $cf16
|
||||
&uword r12 = $cf18
|
||||
&uword r13 = $cf1a
|
||||
&uword r14 = $cf1c
|
||||
&uword r15 = $cf1e
|
||||
; (because there's no room for them in the zeropage in the default configuration)
|
||||
; Note that when using ZP options that free up more of the zeropage (such as %zeropage kernalsafe)
|
||||
; there might be enough space to put them there after all, and the compiler will change these addresses!
|
||||
&uword r0 = $cfe0
|
||||
&uword r1 = $cfe2
|
||||
&uword r2 = $cfe4
|
||||
&uword r3 = $cfe6
|
||||
&uword r4 = $cfe8
|
||||
&uword r5 = $cfea
|
||||
&uword r6 = $cfec
|
||||
&uword r7 = $cfee
|
||||
&uword r8 = $cff0
|
||||
&uword r9 = $cff2
|
||||
&uword r10 = $cff4
|
||||
&uword r11 = $cff6
|
||||
&uword r12 = $cff8
|
||||
&uword r13 = $cffa
|
||||
&uword r14 = $cffc
|
||||
&uword r15 = $cffe
|
||||
|
||||
&word r0s = $cf00
|
||||
&word r1s = $cf02
|
||||
&word r2s = $cf04
|
||||
&word r3s = $cf06
|
||||
&word r4s = $cf08
|
||||
&word r5s = $cf0a
|
||||
&word r6s = $cf0c
|
||||
&word r7s = $cf0e
|
||||
&word r8s = $cf10
|
||||
&word r9s = $cf12
|
||||
&word r10s = $cf14
|
||||
&word r11s = $cf16
|
||||
&word r12s = $cf18
|
||||
&word r13s = $cf1a
|
||||
&word r14s = $cf1c
|
||||
&word r15s = $cf1e
|
||||
&word r0s = $cfe0
|
||||
&word r1s = $cfe2
|
||||
&word r2s = $cfe4
|
||||
&word r3s = $cfe6
|
||||
&word r4s = $cfe8
|
||||
&word r5s = $cfea
|
||||
&word r6s = $cfec
|
||||
&word r7s = $cfee
|
||||
&word r8s = $cff0
|
||||
&word r9s = $cff2
|
||||
&word r10s = $cff4
|
||||
&word r11s = $cff6
|
||||
&word r12s = $cff8
|
||||
&word r13s = $cffa
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
&ubyte r0L = $cf00
|
||||
&ubyte r1L = $cf02
|
||||
&ubyte r2L = $cf04
|
||||
&ubyte r3L = $cf06
|
||||
&ubyte r4L = $cf08
|
||||
&ubyte r5L = $cf0a
|
||||
&ubyte r6L = $cf0c
|
||||
&ubyte r7L = $cf0e
|
||||
&ubyte r8L = $cf10
|
||||
&ubyte r9L = $cf12
|
||||
&ubyte r10L = $cf14
|
||||
&ubyte r11L = $cf16
|
||||
&ubyte r12L = $cf18
|
||||
&ubyte r13L = $cf1a
|
||||
&ubyte r14L = $cf1c
|
||||
&ubyte r15L = $cf1e
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
&ubyte r2L = $cfe4
|
||||
&ubyte r3L = $cfe6
|
||||
&ubyte r4L = $cfe8
|
||||
&ubyte r5L = $cfea
|
||||
&ubyte r6L = $cfec
|
||||
&ubyte r7L = $cfee
|
||||
&ubyte r8L = $cff0
|
||||
&ubyte r9L = $cff2
|
||||
&ubyte r10L = $cff4
|
||||
&ubyte r11L = $cff6
|
||||
&ubyte r12L = $cff8
|
||||
&ubyte r13L = $cffa
|
||||
&ubyte r14L = $cffc
|
||||
&ubyte r15L = $cffe
|
||||
|
||||
&ubyte r0H = $cf01
|
||||
&ubyte r1H = $cf03
|
||||
&ubyte r2H = $cf05
|
||||
&ubyte r3H = $cf07
|
||||
&ubyte r4H = $cf09
|
||||
&ubyte r5H = $cf0b
|
||||
&ubyte r6H = $cf0d
|
||||
&ubyte r7H = $cf0f
|
||||
&ubyte r8H = $cf11
|
||||
&ubyte r9H = $cf13
|
||||
&ubyte r10H = $cf15
|
||||
&ubyte r11H = $cf17
|
||||
&ubyte r12H = $cf19
|
||||
&ubyte r13H = $cf1b
|
||||
&ubyte r14H = $cf1d
|
||||
&ubyte r15H = $cf1f
|
||||
&ubyte r0H = $cfe1
|
||||
&ubyte r1H = $cfe3
|
||||
&ubyte r2H = $cfe5
|
||||
&ubyte r3H = $cfe7
|
||||
&ubyte r4H = $cfe9
|
||||
&ubyte r5H = $cfeb
|
||||
&ubyte r6H = $cfed
|
||||
&ubyte r7H = $cfef
|
||||
&ubyte r8H = $cff1
|
||||
&ubyte r9H = $cff3
|
||||
&ubyte r10H = $cff5
|
||||
&ubyte r11H = $cff7
|
||||
&ubyte r12H = $cff9
|
||||
&ubyte r13H = $cffb
|
||||
&ubyte r14H = $cffd
|
||||
&ubyte r15H = $cfff
|
||||
|
||||
&byte r0sL = $cf00
|
||||
&byte r1sL = $cf02
|
||||
&byte r2sL = $cf04
|
||||
&byte r3sL = $cf06
|
||||
&byte r4sL = $cf08
|
||||
&byte r5sL = $cf0a
|
||||
&byte r6sL = $cf0c
|
||||
&byte r7sL = $cf0e
|
||||
&byte r8sL = $cf10
|
||||
&byte r9sL = $cf12
|
||||
&byte r10sL = $cf14
|
||||
&byte r11sL = $cf16
|
||||
&byte r12sL = $cf18
|
||||
&byte r13sL = $cf1a
|
||||
&byte r14sL = $cf1c
|
||||
&byte r15sL = $cf1e
|
||||
&byte r0sL = $cfe0
|
||||
&byte r1sL = $cfe2
|
||||
&byte r2sL = $cfe4
|
||||
&byte r3sL = $cfe6
|
||||
&byte r4sL = $cfe8
|
||||
&byte r5sL = $cfea
|
||||
&byte r6sL = $cfec
|
||||
&byte r7sL = $cfee
|
||||
&byte r8sL = $cff0
|
||||
&byte r9sL = $cff2
|
||||
&byte r10sL = $cff4
|
||||
&byte r11sL = $cff6
|
||||
&byte r12sL = $cff8
|
||||
&byte r13sL = $cffa
|
||||
&byte r14sL = $cffc
|
||||
&byte r15sL = $cffe
|
||||
|
||||
&byte r0sH = $cf01
|
||||
&byte r1sH = $cf03
|
||||
&byte r2sH = $cf05
|
||||
&byte r3sH = $cf07
|
||||
&byte r4sH = $cf09
|
||||
&byte r5sH = $cf0b
|
||||
&byte r6sH = $cf0d
|
||||
&byte r7sH = $cf0f
|
||||
&byte r8sH = $cf11
|
||||
&byte r9sH = $cf13
|
||||
&byte r10sH = $cf15
|
||||
&byte r11sH = $cf17
|
||||
&byte r12sH = $cf19
|
||||
&byte r13sH = $cf1b
|
||||
&byte r14sH = $cf1d
|
||||
&byte r15sH = $cf1f
|
||||
&byte r0sH = $cfe1
|
||||
&byte r1sH = $cfe3
|
||||
&byte r2sH = $cfe5
|
||||
&byte r3sH = $cfe7
|
||||
&byte r4sH = $cfe9
|
||||
&byte r5sH = $cfeb
|
||||
&byte r6sH = $cfed
|
||||
&byte r7sH = $cfef
|
||||
&byte r8sH = $cff1
|
||||
&byte r9sH = $cff3
|
||||
&byte r10sH = $cff5
|
||||
&byte r11sH = $cff7
|
||||
&byte r12sH = $cff9
|
||||
&byte r13sH = $cffb
|
||||
&byte r14sH = $cffd
|
||||
&byte r15sH = $cfff
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
|
@ -96,13 +96,12 @@ sub uppercase() {
|
||||
c64.VMCSB &= ~2
|
||||
}
|
||||
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left (bool alsocolors @ Pc) 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
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -132,17 +131,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dey
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_right (bool alsocolors @ Pc) 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
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -168,17 +165,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -204,17 +199,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@ -240,7 +233,6 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -265,10 +257,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@ -276,16 +267,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@ -301,16 +289,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@ -322,10 +307,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
@ -334,16 +318,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
@ -356,12 +337,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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
|
||||
@ -373,7 +353,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
@ -386,10 +366,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
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 {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@ -397,17 +376,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@ -428,7 +404,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
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
|
||||
@ -582,15 +558,10 @@ _colormod sta $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr cbm.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -8,25 +8,22 @@ conv {
|
||||
|
||||
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
|
||||
|
||||
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
sty string_out
|
||||
sta string_out+1
|
||||
stx string_out+2
|
||||
lda #0
|
||||
sta string_out+3
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_B1
|
||||
jsr conv.ubyte2decimal
|
||||
@ -52,15 +49,13 @@ _output_byte_digits
|
||||
iny
|
||||
lda #0
|
||||
sta string_out,y
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub str_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the byte in A in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_B1
|
||||
cmp #0
|
||||
@ -75,7 +70,7 @@ asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ubhex (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in hex string form
|
||||
%asm {{
|
||||
jsr conv.ubyte2hex
|
||||
@ -87,7 +82,7 @@ asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ubbin (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ubbin (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in binary string form
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@ -106,7 +101,7 @@ _digit sta string_out,y
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uwbin (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub str_uwbin (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the uword in A/Y in binary string form
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_REG
|
||||
@ -145,10 +140,9 @@ asmsub str_uwhex (uword value @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub str_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@ -156,15 +150,13 @@ asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
beq +
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub str_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx #0
|
||||
_output_digits
|
||||
@ -182,7 +174,6 @@ _gotdigit sta string_out,x
|
||||
bne _gotdigit
|
||||
_end lda #0
|
||||
sta string_out,x
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
_allzero lda #'0'
|
||||
@ -192,12 +183,11 @@ _allzero lda #'0'
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_w (word value @ AY) clobbers(A,Y) {
|
||||
asmsub str_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl str_uw
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
lda #'-'
|
||||
sta string_out
|
||||
@ -700,10 +690,9 @@ asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
|
||||
asmsub ubyte2hex (ubyte value @A) clobbers(X) -> ubyte @A, ubyte @Y {
|
||||
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
and #$0f
|
||||
tax
|
||||
@ -715,7 +704,6 @@ asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
|
||||
lsr a
|
||||
tax
|
||||
lda _hex_digits,x
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
||||
|
@ -490,7 +490,6 @@ io_error:
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
lda headerless
|
||||
@ -501,7 +500,6 @@ io_error:
|
||||
+ lda #<P8ZP_SCRATCH_W1
|
||||
jsr cbm.SAVE
|
||||
+ php
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
plp
|
||||
}}
|
||||
|
||||
@ -549,7 +547,6 @@ io_error:
|
||||
secondary |= %00000010 ; activate cx16 kernal headerless load support
|
||||
cbm.SETLFS(1, drivenumber, secondary)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
ldx address_override
|
||||
ldy address_override+1
|
||||
@ -557,7 +554,7 @@ io_error:
|
||||
bcs +
|
||||
stx cx16.r1
|
||||
sty cx16.r1+1
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
+
|
||||
}}
|
||||
|
||||
cbm.CLRCHN()
|
||||
@ -609,14 +606,13 @@ io_error:
|
||||
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
||||
}
|
||||
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword address @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)
|
||||
%asm {{
|
||||
clc
|
||||
internal_vload:
|
||||
phx
|
||||
pha
|
||||
ldx drivenumber
|
||||
bcc +
|
||||
@ -644,13 +640,12 @@ internal_vload:
|
||||
+ jsr cbm.CLRCHN
|
||||
lda #1
|
||||
jsr cbm.CLOSE
|
||||
plx
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @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.
|
||||
|
@ -99,14 +99,11 @@ asmsub FREADSA (byte value @A) clobbers(A,X,Y) {
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
phx
|
||||
sty $c4 ; facmo ($64 on c128)
|
||||
sta $c5 ; facmo+1 ($65 on c128)
|
||||
ldx #$90
|
||||
sec
|
||||
jsr FLOATC
|
||||
plx
|
||||
rts
|
||||
jmp FLOATC
|
||||
}}
|
||||
}
|
||||
|
||||
@ -144,11 +141,8 @@ asmsub FREADUY (ubyte value @Y) {
|
||||
|
||||
sub rndf() -> float {
|
||||
%asm {{
|
||||
phx
|
||||
lda #1
|
||||
jsr RND_0
|
||||
plx
|
||||
rts
|
||||
jmp RND_0
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,6 @@
|
||||
; mode 6 = bitmap 640 x 480 x 4c
|
||||
; higher color dephts in highres are not supported due to lack of VRAM
|
||||
|
||||
; TODO remove the phx/plx pairs in non-stack compiler version
|
||||
|
||||
gfx2 {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
@ -223,7 +221,6 @@ _done
|
||||
position(x, y)
|
||||
%asm {{
|
||||
lda color
|
||||
phx
|
||||
ldx length+1
|
||||
beq +
|
||||
ldy #0
|
||||
@ -237,7 +234,7 @@ _done
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
+ plx
|
||||
+
|
||||
}}
|
||||
}
|
||||
6 -> {
|
||||
@ -262,7 +259,6 @@ _done
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
phx
|
||||
ldx x
|
||||
}}
|
||||
|
||||
@ -285,10 +281,6 @@ _done
|
||||
+ inx ; next pixel
|
||||
}}
|
||||
}
|
||||
|
||||
%asm {{
|
||||
plx
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -923,13 +915,12 @@ skip:
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, Y) {
|
||||
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, X, Y) {
|
||||
; -- sets the next bunch of pixels from a prepared array of bytes.
|
||||
; for 8 bpp screens this will plot 1 pixel per byte.
|
||||
; for 1 bpp screens it will plot 8 pixels at once (colors are the bit patterns per byte).
|
||||
; for 2 bpp screens it will plot 4 pixels at once (colors are the bit patterns per byte).
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r0+1
|
||||
@ -951,14 +942,13 @@ skip:
|
||||
iny
|
||||
dex
|
||||
bne -
|
||||
+ plx
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) {
|
||||
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) clobbers(X) {
|
||||
; this is only useful in 256 color mode where one pixel equals one byte value.
|
||||
%asm {{
|
||||
phx
|
||||
ldx #8
|
||||
- asl cx16.r0
|
||||
bcc +
|
||||
@ -967,7 +957,6 @@ skip:
|
||||
+ sty cx16.VERA_DATA0
|
||||
+ dex
|
||||
bne -
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -999,7 +988,6 @@ skip:
|
||||
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
|
||||
%asm {{
|
||||
; pre-shift the bits
|
||||
phx ; TODO remove in non-stack version
|
||||
lda text.x
|
||||
and #7
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@ -1019,7 +1007,6 @@ skip:
|
||||
iny
|
||||
cpy #8
|
||||
bne --
|
||||
plx ; TODO remove in non-stack version
|
||||
}}
|
||||
; left part of shifted char
|
||||
position2(x, y, true)
|
||||
@ -1086,7 +1073,6 @@ skip:
|
||||
position(x,y)
|
||||
y++
|
||||
%asm {{
|
||||
phx ; TODO remove in non-stack version
|
||||
ldx color
|
||||
lda cx16.VERA_DATA1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@ -1098,7 +1084,6 @@ skip:
|
||||
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
|
||||
+ dey
|
||||
bne -
|
||||
plx ; TODO remove in non-stack version
|
||||
}}
|
||||
}
|
||||
x+=8
|
||||
|
@ -53,25 +53,21 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
||||
|
||||
; ---- utility
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
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 {{
|
||||
phx
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
plx
|
||||
lda #0
|
||||
rts
|
||||
+ plx
|
||||
lda #1
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs.
|
||||
%asm {{
|
||||
phx
|
||||
php
|
||||
sei
|
||||
jsr cbm.RDTIM
|
||||
@ -81,7 +77,6 @@ asmsub RDTIM16() -> uword @AY {
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -454,15 +449,12 @@ asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub mouse_pos() -> ubyte @A {
|
||||
asmsub mouse_pos() clobbers(X) -> ubyte @A {
|
||||
; -- short wrapper around mouse_get() kernal routine:
|
||||
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status.
|
||||
%asm {{
|
||||
phx
|
||||
ldx #cx16.r0
|
||||
jsr cx16.mouse_get
|
||||
plx
|
||||
rts
|
||||
jmp cx16.mouse_get
|
||||
}}
|
||||
}
|
||||
|
||||
@ -500,7 +492,7 @@ inline asmsub getrambank() -> ubyte @A {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub numbanks() -> uword @AY {
|
||||
asmsub numbanks() clobbers(X) -> uword @AY {
|
||||
; -- Returns the number of available RAM banks according to the kernal (each bank is 8 Kb).
|
||||
; Note that the number of such banks can be 256 so a word is returned.
|
||||
; But just looking at the A register (the LSB of the result word) could suffice if you know that A=0 means 256 banks:
|
||||
@ -508,15 +500,13 @@ asmsub numbanks() -> uword @AY {
|
||||
; Kernal's MEMTOP routine reports 0 in this case but that doesn't mean 'zero banks', instead it means 256 banks,
|
||||
; as there is no X16 without at least 1 page of banked RAM. So this routine returns 256 instead of 0.
|
||||
%asm {{
|
||||
phx
|
||||
sec
|
||||
jsr cbm.MEMTOP
|
||||
ldy #0
|
||||
cmp #0
|
||||
bne +
|
||||
iny
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@ -956,10 +946,6 @@ _irq_handler_init
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||
ldx #32
|
||||
cld
|
||||
rts
|
||||
|
||||
@ -1079,19 +1065,17 @@ asmsub set_rasterline(uword line @AY) {
|
||||
void cx16.i2c_write_byte($42, $05, activity)
|
||||
}
|
||||
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
asmsub wait(uword jiffies @AY) clobbers(X) {
|
||||
; --- 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
|
||||
; note: this routine cannot be used from inside a irq handler
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
|
||||
_loop lda P8ZP_SCRATCH_W1
|
||||
ora P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
plx
|
||||
rts
|
||||
|
||||
+ sei
|
||||
|
@ -42,11 +42,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A, X) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
%asm {{
|
||||
sty _ly+1
|
||||
phx
|
||||
pha
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
@ -75,8 +74,7 @@ _ly ldy #1 ; modified
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
|
||||
set_vera_textmatrix_addresses:
|
||||
stz cx16.VERA_CTRL
|
||||
@ -90,11 +88,10 @@ set_vera_textmatrix_addresses:
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(X, Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
@ -116,16 +113,14 @@ _lx ldx #0 ; modified
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
asmsub clear_screencolors (ubyte color @ A) clobbers(X, Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
phx
|
||||
sta _la+1
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
@ -150,8 +145,7 @@ _la lda #0 ; modified
|
||||
sta cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@ -192,11 +186,10 @@ sub iso_off() {
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_left() clobbers(A, Y) {
|
||||
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 {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
@ -233,16 +226,14 @@ _lx ldx #0 ; modified
|
||||
|
||||
lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right() clobbers(A,Y) {
|
||||
asmsub scroll_right() clobbers(A,X,Y) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
@ -287,16 +278,14 @@ _lx ldx #0 ; modified
|
||||
|
||||
lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up() clobbers(A, Y) {
|
||||
asmsub scroll_up() clobbers(A, X, Y) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
@ -337,16 +326,14 @@ _nextline
|
||||
|
||||
+ lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down() clobbers(A, Y) {
|
||||
asmsub scroll_down() clobbers(A, X, Y) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
@ -393,7 +380,6 @@ _nextline
|
||||
|
||||
+ lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -418,10 +404,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
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 {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@ -429,16 +414,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@ -454,16 +436,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@ -475,10 +454,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
phx
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
@ -487,16 +465,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
@ -509,12 +484,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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
|
||||
@ -526,7 +500,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
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 {{
|
||||
@ -539,10 +513,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
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 {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@ -550,17 +523,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
plx
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@ -581,7 +551,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
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
|
||||
@ -704,12 +674,11 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, 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 {{
|
||||
phx
|
||||
lda column
|
||||
asl a
|
||||
tax
|
||||
@ -732,7 +701,6 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
and #$f0
|
||||
ora P8ZP_SCRATCH_B1
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -742,7 +710,6 @@ sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
; 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 {{
|
||||
phx
|
||||
lda column
|
||||
asl a
|
||||
tax
|
||||
@ -760,20 +727,14 @@ sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
inc cx16.VERA_ADDR_L
|
||||
lda colors
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
phx
|
||||
tax
|
||||
clc
|
||||
jsr cbm.PLOT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -455,13 +455,11 @@ io_error:
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<P8ZP_SCRATCH_W1
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
jsr cbm.SAVE
|
||||
php
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
plp
|
||||
}}
|
||||
|
||||
@ -488,7 +486,6 @@ io_error:
|
||||
secondary = 0
|
||||
cbm.SETLFS(1, drivenumber, secondary)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
ldx address_override
|
||||
ldy address_override+1
|
||||
@ -496,7 +493,7 @@ io_error:
|
||||
bcs +
|
||||
stx cx16.r1
|
||||
sty cx16.r1+1
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
+
|
||||
}}
|
||||
|
||||
cbm.CLRCHN()
|
||||
|
@ -5,7 +5,6 @@ floats {
|
||||
sub print_f(float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%asm {{
|
||||
stx floats_store_reg
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM ; load float into fac1
|
||||
@ -18,8 +17,7 @@ sub print_f(float value) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx floats_store_reg
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@ -44,10 +42,7 @@ sub sin(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr SIN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp SIN
|
||||
}}
|
||||
}
|
||||
|
||||
@ -56,9 +51,7 @@ sub cos(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr COS
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp COS
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -68,10 +61,7 @@ sub tan(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr TAN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp TAN
|
||||
}}
|
||||
}
|
||||
|
||||
@ -80,10 +70,7 @@ sub atan(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr ATN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp ATN
|
||||
}}
|
||||
}
|
||||
|
||||
@ -92,10 +79,7 @@ sub ln(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp LOG
|
||||
}}
|
||||
}
|
||||
|
||||
@ -104,15 +88,12 @@ sub log2(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
jsr MOVEF
|
||||
lda #<FL_LOG2_const
|
||||
ldy #>FL_LOG2_const
|
||||
jsr MOVFM
|
||||
jsr FDIVT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp FDIVT
|
||||
}}
|
||||
}
|
||||
|
||||
@ -122,12 +103,9 @@ sub rad(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_pi_div_180
|
||||
ldy #>_pi_div_180
|
||||
jsr FMULT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp FMULT
|
||||
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
||||
}}
|
||||
}
|
||||
@ -138,11 +116,9 @@ sub deg(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_one_over_pi_div_180
|
||||
ldy #>_one_over_pi_div_180
|
||||
jsr FMULT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp FMULT
|
||||
rts
|
||||
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
||||
}}
|
||||
@ -153,11 +129,8 @@ sub round(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FADDH
|
||||
jsr INT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp INT
|
||||
}}
|
||||
}
|
||||
|
||||
@ -166,10 +139,7 @@ sub floor(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr INT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp INT
|
||||
}}
|
||||
}
|
||||
|
||||
@ -179,7 +149,6 @@ sub ceil(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
@ -192,8 +161,7 @@ sub ceil(float value) -> float {
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@ -202,14 +170,11 @@ sub rndseedf(float seed) {
|
||||
seed = -seed ; make sure fp seed is always negative
|
||||
|
||||
%asm {{
|
||||
stx floats_store_reg
|
||||
lda #<seed
|
||||
ldy #>seed
|
||||
jsr MOVFM ; load float into fac1
|
||||
lda #-1
|
||||
jsr floats.RND
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
jmp floats.RND
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,6 @@
|
||||
;
|
||||
|
||||
|
||||
math_store_reg .byte 0 ; temporary storage
|
||||
|
||||
|
||||
multiply_bytes .proc
|
||||
; -- multiply 2 bytes A and Y, result as byte in A (signed or unsigned)
|
||||
sta P8ZP_SCRATCH_B1 ; num1
|
||||
@ -30,7 +27,6 @@ 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
|
||||
stx math_store_reg
|
||||
lda #0
|
||||
ldx #8
|
||||
lsr P8ZP_SCRATCH_B1
|
||||
@ -43,7 +39,6 @@ multiply_bytes_into_word .proc
|
||||
bne -
|
||||
tay
|
||||
lda P8ZP_SCRATCH_B1
|
||||
ldx math_store_reg
|
||||
rts
|
||||
.pend
|
||||
|
||||
@ -55,7 +50,6 @@ multiply_words .proc
|
||||
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
|
||||
mult16 lda #0
|
||||
sta result+2 ; clear upper bits of product
|
||||
@ -77,7 +71,6 @@ mult16 lda #0
|
||||
ror result
|
||||
dex
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
lda result
|
||||
ldy result+1
|
||||
rts
|
||||
@ -124,7 +117,6 @@ divmod_ub_asm .proc
|
||||
; division by zero will result in quotient = 255 and remainder = original number
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
stx math_store_reg
|
||||
|
||||
lda #0
|
||||
ldx #8
|
||||
@ -137,7 +129,6 @@ divmod_ub_asm .proc
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
ldx math_store_reg
|
||||
rts
|
||||
.pend
|
||||
|
||||
@ -197,7 +188,6 @@ result = dividend ;save memory by reusing divident to store the result
|
||||
|
||||
sta _divisor
|
||||
sty _divisor+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #0 ;preset remainder to 0
|
||||
sta remainder
|
||||
sta remainder+1
|
||||
@ -224,7 +214,6 @@ result = dividend ;save memory by reusing divident to store the result
|
||||
|
||||
lda result
|
||||
ldy result+1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_divisor .word 0
|
||||
.pend
|
||||
@ -257,501 +246,6 @@ b1=*+1
|
||||
randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
||||
|
||||
|
||||
; ----------- optimized multiplications (stack) : ---------
|
||||
stack_mul_byte_3 .proc
|
||||
; X + X*2
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_3 .proc
|
||||
; W*2 + W
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
stack_mul_byte_5 .proc
|
||||
; X*4 + X
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_5 .proc
|
||||
; W*4 + W
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
stack_mul_byte_6 .proc
|
||||
; (X*2 + X)*2
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_6 .proc
|
||||
; (W*2 + W)*2
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_7 .proc
|
||||
; X*8 - X
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_7 .proc
|
||||
; W*8 - W
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sbc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_9 .proc
|
||||
; X*8 + X
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_9 .proc
|
||||
; W*8 + W
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_10 .proc
|
||||
; (X*4 + X)*2
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_10 .proc
|
||||
; (W*4 + W)*2
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_11 .proc
|
||||
; (X*2 + X)*4 - X
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
; mul_word_11 is skipped (too much code)
|
||||
|
||||
stack_mul_byte_12 .proc
|
||||
; (X*2 + X)*4
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_12 .proc
|
||||
; (W*2 + W)*4
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_13 .proc
|
||||
; (X*2 + X)*4 + X
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
; mul_word_13 is skipped (too much code)
|
||||
|
||||
stack_mul_byte_14 .proc
|
||||
; (X*8 - X)*2
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
; mul_word_14 is skipped (too much code)
|
||||
|
||||
stack_mul_byte_15 .proc
|
||||
; X*16 - X
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_15 .proc
|
||||
; W*16 - W
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sbc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_20 .proc
|
||||
; (X*4 + X)*4
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_20 .proc
|
||||
; (W*4 + W)*4
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_25 .proc
|
||||
; (X*2 + X)*8 + X
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_25 .proc
|
||||
; W = (W*2 + W) *8 + W
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_40 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
and #7
|
||||
tay
|
||||
lda mul_byte_40._forties,y
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_40 .proc
|
||||
; (W*4 + W)*8
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda P8ESTACK_LO+1,x
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc P8ESTACK_HI+1,x
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol a
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_50 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
and #7
|
||||
tay
|
||||
lda mul_byte_50._fifties, y
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_50 .proc
|
||||
; W = W * 25 * 2
|
||||
jsr stack_mul_word_25
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_80 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
and #3
|
||||
tay
|
||||
lda mul_byte_80._eighties, y
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_80 .proc
|
||||
; W = W * 40 * 2
|
||||
jsr stack_mul_word_40
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_byte_100 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
and #3
|
||||
tay
|
||||
lda mul_byte_100._hundreds, y
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_100 .proc
|
||||
; W = W * 25 * 4
|
||||
jsr stack_mul_word_25
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl P8ESTACK_LO+1,x
|
||||
rol P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_320 .proc
|
||||
; stackW = stackLo * 256 + stackLo * 64 (stackHi doesn't matter)
|
||||
ldy P8ESTACK_LO+1,x
|
||||
lda #0
|
||||
sta P8ESTACK_HI+1,x
|
||||
tya
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
clc
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_640 .proc
|
||||
; stackW = (stackLo * 2 * 320) (stackHi doesn't matter)
|
||||
asl P8ESTACK_LO+1,x
|
||||
jmp stack_mul_word_320
|
||||
.pend
|
||||
|
||||
|
||||
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
||||
mul_byte_3 .proc
|
||||
; A = A + A*2
|
||||
@ -1252,250 +746,69 @@ mul_word_640 .proc
|
||||
; ----------- end optimized multiplications -----------
|
||||
|
||||
|
||||
; bit shifts.
|
||||
; anything below 3 is done inline. anything above 7 is done via other optimizations.
|
||||
|
||||
shift_left_w_7 .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_LO+1,x
|
||||
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
_shift6 asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
_shift5 asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
_shift4 asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
_shift3 asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
shift_left_w_6 .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_LO+1,x
|
||||
jmp shift_left_w_7._shift6
|
||||
.pend
|
||||
|
||||
shift_left_w_5 .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_LO+1,x
|
||||
jmp shift_left_w_7._shift5
|
||||
.pend
|
||||
|
||||
shift_left_w_4 .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_LO+1,x
|
||||
jmp shift_left_w_7._shift4
|
||||
.pend
|
||||
|
||||
shift_left_w_3 .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_LO+1,x
|
||||
jmp shift_left_w_7._shift3
|
||||
.pend
|
||||
|
||||
|
||||
shift_left_w .proc
|
||||
; -- variable number of shifts left
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne _shift
|
||||
rts
|
||||
_shift asl P8ESTACK_LO+1,x
|
||||
rol P8ESTACK_HI+1,x
|
||||
dey
|
||||
bne _shift
|
||||
rts
|
||||
.pend
|
||||
|
||||
shift_right_uw .proc
|
||||
; -- uword variable number of shifts right
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne _shift
|
||||
rts
|
||||
_shift lsr P8ESTACK_HI+1,x
|
||||
ror P8ESTACK_LO+1,x
|
||||
dey
|
||||
bne _shift
|
||||
rts
|
||||
.pend
|
||||
|
||||
shift_right_uw_7 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_HI+1,x
|
||||
|
||||
lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
_shift6 lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
_shift5 lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
_shift4 lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
_shift3 lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
|
||||
sta P8ESTACK_HI+1,x
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
shift_right_uw_6 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_HI+1,x
|
||||
jmp shift_right_uw_7._shift6
|
||||
.pend
|
||||
|
||||
shift_right_uw_5 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_HI+1,x
|
||||
jmp shift_right_uw_7._shift5
|
||||
.pend
|
||||
|
||||
shift_right_uw_4 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_HI+1,x
|
||||
jmp shift_right_uw_7._shift4
|
||||
.pend
|
||||
|
||||
shift_right_uw_3 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ESTACK_HI+1,x
|
||||
jmp shift_right_uw_7._shift3
|
||||
.pend
|
||||
|
||||
|
||||
shift_right_w_7 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
|
||||
asl a
|
||||
ror P8ZP_SCRATCH_W1+1
|
||||
ror P8ZP_SCRATCH_W1
|
||||
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
_shift6 asl a
|
||||
ror P8ZP_SCRATCH_W1+1
|
||||
ror P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
_shift5 asl a
|
||||
ror P8ZP_SCRATCH_W1+1
|
||||
ror P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
_shift4 asl a
|
||||
ror P8ZP_SCRATCH_W1+1
|
||||
ror P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
_shift3 asl a
|
||||
ror P8ZP_SCRATCH_W1+1
|
||||
ror P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
asl a
|
||||
ror P8ZP_SCRATCH_W1+1
|
||||
ror P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
asl a
|
||||
ror P8ZP_SCRATCH_W1+1
|
||||
ror P8ZP_SCRATCH_W1
|
||||
|
||||
lda P8ZP_SCRATCH_W1
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
shift_right_w_6 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
jmp shift_right_w_7._shift6
|
||||
.pend
|
||||
|
||||
shift_right_w_5 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
jmp shift_right_w_7._shift5
|
||||
.pend
|
||||
|
||||
shift_right_w_4 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
jmp shift_right_w_7._shift4
|
||||
.pend
|
||||
|
||||
shift_right_w_3 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
jmp shift_right_w_7._shift3
|
||||
.pend
|
||||
|
||||
|
||||
shift_right_w .proc
|
||||
; -- signed word variable number of shifts right
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne _shift
|
||||
rts
|
||||
_shift lda P8ESTACK_HI+1,x
|
||||
asl a
|
||||
ror P8ESTACK_HI+1,x
|
||||
ror P8ESTACK_LO+1,x
|
||||
dey
|
||||
bne _shift
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
; 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 (assume >0)
|
||||
; -- lsr signed byte in A times the value in Y (>1)
|
||||
cmp #0
|
||||
bmi _negative
|
||||
bpl lsr_ubyte_A
|
||||
- sec
|
||||
ror a
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
.pend
|
||||
|
||||
lsr_ubyte_A .proc
|
||||
; -- lsr unsigned byte in A times the value in Y (>1)
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
_negative lsr a
|
||||
ora #$80
|
||||
.pend
|
||||
|
||||
asl_byte_A .proc
|
||||
; -- asl any byte in A times the value in Y (>1)
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
lsr_word_AY .proc
|
||||
; -- lsr signed word in AY times the value in X (>1)
|
||||
cpy #0
|
||||
bpl lsr_uword_AY
|
||||
sty P8ZP_SCRATCH_B1
|
||||
_negative sec
|
||||
ror P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
dex
|
||||
bne _negative
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
.pend
|
||||
|
||||
lsr_uword_AY .proc
|
||||
; -- lsr unsigned word in AY times the value in X (>1)
|
||||
sty P8ZP_SCRATCH_B1
|
||||
- lsr P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
.pend
|
||||
|
||||
asl_word_AY .proc
|
||||
; -- asl any word in AY times the value in X (>1)
|
||||
sty P8ZP_SCRATCH_B1
|
||||
- asl a
|
||||
rol P8ZP_SCRATCH_B1
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
.pend
|
||||
|
||||
@ -1525,7 +838,6 @@ tempsq = P8ZP_SCRATCH_B1 ; temp byte for intermediate result
|
||||
|
||||
sta numberl
|
||||
sty numberh
|
||||
stx P8ZP_SCRATCH_REG
|
||||
|
||||
lda #$00 ; clear a
|
||||
sta squarel ; clear square low byte
|
||||
@ -1563,7 +875,6 @@ _nosqadd:
|
||||
|
||||
lda squarel
|
||||
ldy squareh
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
.pend
|
||||
|
@ -1,20 +1,6 @@
|
||||
; ---- builtin functions
|
||||
|
||||
|
||||
func_any_b_stack .proc
|
||||
jsr func_any_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_all_b_stack .proc
|
||||
jsr func_all_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_any_b_into_A .proc
|
||||
; -- any(array), array in P8ZP_SCRATCH_W1, num bytes in A
|
||||
sta _cmp_mod+1 ; self-modifying code
|
||||
@ -49,14 +35,6 @@ func_any_w_into_A .proc
|
||||
jmp func_any_b_into_A
|
||||
.pend
|
||||
|
||||
func_any_w_stack .proc
|
||||
asl a
|
||||
jsr func_any_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_all_w_into_A .proc
|
||||
; -- all(warray), array in P8ZP_SCRATCH_W1, num bytes in A
|
||||
asl a ; times 2 because of word
|
||||
@ -77,22 +55,6 @@ _cmp_mod cpy #255 ; modified
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_all_w_stack .proc
|
||||
jsr func_all_w_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_b_stack .proc
|
||||
; -- push abs(A) on stack (as unsigned word)
|
||||
jsr abs_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
stz P8ESTACK_HI,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_b_into_A .proc
|
||||
; -- A = abs(A)
|
||||
cmp #0
|
||||
@ -104,16 +66,6 @@ abs_b_into_A .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_w_stack .proc
|
||||
; -- push abs(AY) on stack (as word)
|
||||
jsr abs_w_into_AY
|
||||
sta P8ESTACK_LO,x
|
||||
tya
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_w_into_AY .proc
|
||||
; -- AY = abs(AY)
|
||||
cpy #0
|
||||
@ -142,13 +94,6 @@ _neg lda #-1
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_b_stack .proc
|
||||
jsr func_sign_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_ub_into_A .proc
|
||||
cmp #0
|
||||
bne _pos
|
||||
@ -157,13 +102,6 @@ _pos lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_ub_stack .proc
|
||||
jsr func_sign_ub_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_uw_into_A .proc
|
||||
cpy #0
|
||||
beq _possibly_zero
|
||||
@ -174,13 +112,6 @@ _possibly_zero cmp #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_uw_stack .proc
|
||||
jsr func_sign_uw_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_w_into_A .proc
|
||||
cpy #0
|
||||
beq _possibly_zero
|
||||
@ -195,26 +126,10 @@ _possibly_zero cmp #0
|
||||
.pend
|
||||
|
||||
|
||||
func_sign_w_stack .proc
|
||||
jsr func_sign_w_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sqrt16_stack .proc
|
||||
jsr func_sqrt16_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
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
|
||||
txa
|
||||
pha
|
||||
lda #0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
@ -237,8 +152,6 @@ func_sqrt16_into_A .proc
|
||||
rol P8ZP_SCRATCH_REG
|
||||
dex
|
||||
bne -
|
||||
pla
|
||||
tax
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
.pend
|
||||
|
@ -4,10 +4,8 @@
|
||||
|
||||
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
||||
|
||||
read_byte_from_address_on_stack .proc
|
||||
; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged)
|
||||
lda P8ESTACK_LO+1,x
|
||||
ldy P8ESTACK_HI+1,x
|
||||
|
||||
read_byte_from_address_in_AY .proc
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
@ -16,318 +14,16 @@ read_byte_from_address_on_stack .proc
|
||||
.pend
|
||||
|
||||
|
||||
write_byte_to_address_on_stack .proc
|
||||
; -- write the byte in A to the memory address on the top of the stack (stack remains unchanged)
|
||||
ldy P8ESTACK_LO+1,x
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy P8ESTACK_HI+1,x
|
||||
write_byte_X_to_address_in_AY .proc
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
txa
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
neg_b .proc
|
||||
lda #0
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
neg_w .proc
|
||||
sec
|
||||
lda #0
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda #0
|
||||
sbc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
inv_word .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
eor #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
eor #255
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitand_b .proc
|
||||
; -- bitwise and (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
and P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitor_b .proc
|
||||
; -- bitwise or (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
ora P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitxor_b .proc
|
||||
; -- bitwise xor (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
eor P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitand_w .proc
|
||||
; -- bitwise and (of 2 words)
|
||||
lda P8ESTACK_LO+2,x
|
||||
and P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
and P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitor_w .proc
|
||||
; -- bitwise or (of 2 words)
|
||||
lda P8ESTACK_LO+2,x
|
||||
ora P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitxor_w .proc
|
||||
; -- bitwise xor (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
eor P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
eor P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
add_w .proc
|
||||
; -- push word+word / uword+uword
|
||||
inx
|
||||
clc
|
||||
lda P8ESTACK_LO,x
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI,x
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
sub_w .proc
|
||||
; -- push word-word
|
||||
inx
|
||||
sec
|
||||
lda P8ESTACK_LO+1,x
|
||||
sbc P8ESTACK_LO,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
sbc P8ESTACK_HI,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte .proc
|
||||
; -- b*b->b (signed and unsigned)
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_LO+1,x
|
||||
jsr math.multiply_bytes
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word .proc
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO+1,x
|
||||
ldy P8ESTACK_HI+1,x
|
||||
jsr math.multiply_words
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
idiv_b .proc
|
||||
; signed division: use unsigned division and fix sign of result afterwards
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
eor P8ESTACK_LO+1,x
|
||||
php ; save sign of result
|
||||
lda P8ESTACK_LO,x
|
||||
bpl +
|
||||
eor #$ff
|
||||
sec
|
||||
adc #0 ; make num1 positive
|
||||
+ tay
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
bpl +
|
||||
eor #$ff
|
||||
sec
|
||||
adc #0 ; make num2 positive
|
||||
+ jsr math.divmod_ub_asm
|
||||
sta _remainder
|
||||
tya
|
||||
plp ; get sign of result
|
||||
bpl +
|
||||
eor #$ff
|
||||
sec
|
||||
adc #0 ; negate result
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_remainder .byte 0
|
||||
.pend
|
||||
|
||||
idiv_ub .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
lda P8ESTACK_LO+1,x
|
||||
jsr math.divmod_ub_asm
|
||||
tya
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
idiv_w .proc
|
||||
; signed division: use unsigned division and fix sign of result afterwards
|
||||
lda P8ESTACK_HI+2,x
|
||||
eor P8ESTACK_HI+1,x
|
||||
php ; save sign of result
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl +
|
||||
jsr neg_w ; make value positive
|
||||
+ inx
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl +
|
||||
jsr neg_w ; make value positive
|
||||
+ lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jsr math.divmod_uw_asm
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
sta P8ESTACK_HI+1,x
|
||||
plp
|
||||
bpl +
|
||||
jmp neg_w ; negate result
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
idiv_uw .proc
|
||||
inx
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jsr math.divmod_uw_asm
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
remainder_ub .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x ; right operand
|
||||
lda P8ESTACK_LO+1,x ; left operand
|
||||
jsr math.divmod_ub_asm
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
remainder_uw .proc
|
||||
inx
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jsr math.divmod_uw_asm
|
||||
lda P8ZP_SCRATCH_W2
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
equal_w .proc
|
||||
; -- are the two words on the stack identical?
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bne equal_b._equal_b_false
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bne equal_b._equal_b_false
|
||||
beq equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
notequal_b .proc
|
||||
; -- are the two bytes on the stack different?
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
beq equal_b._equal_b_false
|
||||
bne equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
notequal_w .proc
|
||||
; -- are the two words on the stack different?
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
beq notequal_b
|
||||
bne equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
less_ub .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcc equal_b._equal_b_true
|
||||
bcs equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
less_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_less_uw .proc
|
||||
; AY < P8ZP_SCRATCH_W2?
|
||||
cpy P8ZP_SCRATCH_W2+1
|
||||
@ -341,17 +37,6 @@ _true lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_uw .proc
|
||||
lda P8ESTACK_HI+2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bcc equal_b._equal_b_true
|
||||
bne equal_b._equal_b_false
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcc equal_b._equal_b_true
|
||||
bcs equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_less_w .proc
|
||||
; -- AY < P8ZP_SCRATCH_W2?
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
@ -366,48 +51,6 @@ _true lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_w .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
sbc P8ESTACK_HI+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
equal_b .proc
|
||||
; -- are the two bytes on the stack identical?
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bne _equal_b_false
|
||||
_equal_b_true lda #1
|
||||
_equal_b_store inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_equal_b_false lda #0
|
||||
beq _equal_b_store
|
||||
.pend
|
||||
|
||||
lesseq_ub .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
lesseq_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_lesseq_uw .proc
|
||||
; AY <= P8ZP_SCRATCH_W2?
|
||||
cpy P8ZP_SCRATCH_W2+1
|
||||
@ -424,17 +67,6 @@ _true lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
lesseq_uw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bcc equal_b._equal_b_false
|
||||
bne equal_b._equal_b_true
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_lesseq_w .proc
|
||||
; -- P8ZP_SCRATCH_W2 <= AY ? (note: order different from other routines)
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
@ -449,224 +81,6 @@ reg_lesseq_w .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
lesseq_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
sbc P8ESTACK_HI+2,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl equal_b._equal_b_true
|
||||
bmi equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_ub .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
beq equal_b._equal_b_false
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl equal_b._equal_b_true
|
||||
bmi equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_uw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bcc equal_b._equal_b_true
|
||||
bne equal_b._equal_b_false
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bcc equal_b._equal_b_true
|
||||
bcs equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
sbc P8ESTACK_HI+2,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_ub .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl equal_b._equal_b_true
|
||||
bmi equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_uw .proc
|
||||
lda P8ESTACK_HI+2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bcc equal_b._equal_b_false
|
||||
bne equal_b._equal_b_true
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_w .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
sbc P8ESTACK_HI+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_false
|
||||
bpl equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
|
||||
shiftleft_b .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne +
|
||||
rts
|
||||
+ lda P8ESTACK_LO+1,x
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
shiftright_b .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne +
|
||||
rts
|
||||
+ lda P8ESTACK_LO+1,x
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
equalzero_b .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
beq _true
|
||||
bne _false
|
||||
_true lda #1
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_false lda #0
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
equalzero_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
beq equalzero_b._true
|
||||
bne equalzero_b._false
|
||||
.pend
|
||||
|
||||
notequalzero_b .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
beq equalzero_b._false
|
||||
bne equalzero_b._true
|
||||
.pend
|
||||
|
||||
notequalzero_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
beq equalzero_b._false
|
||||
bne equalzero_b._true
|
||||
.pend
|
||||
|
||||
lesszero_b .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bmi equalzero_b._true
|
||||
jmp equalzero_b._false
|
||||
.pend
|
||||
|
||||
lesszero_w .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi equalzero_b._true
|
||||
jmp equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_ub .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne equalzero_b._true
|
||||
beq equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_sb .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
beq equalzero_b._false
|
||||
bpl equalzero_b._true
|
||||
bmi equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_uw .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
bne equalzero_b._true
|
||||
beq equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_sw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi equalzero_b._false
|
||||
ora P8ESTACK_LO+1,x
|
||||
beq equalzero_b._false
|
||||
bne equalzero_b._true
|
||||
.pend
|
||||
|
||||
lessequalzero_sb .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bmi equalzero_b._true
|
||||
beq equalzero_b._true
|
||||
bne equalzero_b._false
|
||||
.pend
|
||||
|
||||
lessequalzero_sw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi equalzero_b._true
|
||||
ora P8ESTACK_LO+1,x
|
||||
beq equalzero_b._true
|
||||
bne equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterequalzero_sb .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bpl equalzero_b._true
|
||||
bmi equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterequalzero_sw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl equalzero_b._true
|
||||
bmi equalzero_b._false
|
||||
.pend
|
||||
|
||||
memcopy16_up .proc
|
||||
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
|
||||
@ -996,21 +410,6 @@ _arg_s1 .word 0
|
||||
_arg_s2 .word 0
|
||||
.pend
|
||||
|
||||
strcmp_stack .proc
|
||||
; -- compare strings, both on stack.
|
||||
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jmp strcmp_mem
|
||||
.pend
|
||||
|
||||
|
||||
strcmp_mem .proc
|
||||
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
|
||||
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
|
||||
@ -1041,16 +440,6 @@ _return_minusone
|
||||
.pend
|
||||
|
||||
|
||||
sign_extend_stack_byte .proc
|
||||
; -- sign extend the (signed) byte on the stack to full 16 bits
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
strlen .proc
|
||||
; -- returns the number of bytes in the string in AY, in Y.
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@ -282,7 +282,6 @@ _done rts
|
||||
|
||||
str = P8ZP_SCRATCH_W1
|
||||
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta str
|
||||
sty str+1
|
||||
lda cx16.r0
|
||||
@ -294,7 +293,6 @@ str = P8ZP_SCRATCH_W1
|
||||
jsr _match
|
||||
lda #0
|
||||
rol a
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
; utility debug code to print the X (evalstack) and SP (cpu stack) registers.
|
||||
; utility debug code to print the SP (cpu stack pointer) register.
|
||||
|
||||
%import textio
|
||||
|
||||
@ -7,7 +7,6 @@ test_stack {
|
||||
|
||||
asmsub test() {
|
||||
%asm {{
|
||||
stx _saveX
|
||||
lda #13
|
||||
jsr txt.chrout
|
||||
lda #'-'
|
||||
@ -17,14 +16,6 @@ test_stack {
|
||||
bne -
|
||||
lda #13
|
||||
jsr txt.chrout
|
||||
lda #'x'
|
||||
jsr txt.chrout
|
||||
lda #'='
|
||||
jsr txt.chrout
|
||||
lda _saveX
|
||||
jsr txt.print_ub
|
||||
lda #' '
|
||||
jsr txt.chrout
|
||||
lda #'s'
|
||||
jsr txt.chrout
|
||||
lda #'p'
|
||||
@ -43,9 +34,7 @@ test_stack {
|
||||
bne -
|
||||
lda #13
|
||||
jsr txt.chrout
|
||||
ldx _saveX
|
||||
rts
|
||||
_saveX .byte 0
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package prog8
|
||||
import kotlinx.cli.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.code.core.CbmPrgLauncherType
|
||||
import prog8.code.core.toHex
|
||||
import prog8.code.target.*
|
||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||
import prog8.compiler.CompilationResult
|
||||
@ -39,16 +38,13 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
|
||||
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
|
||||
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
|
||||
val evalStackAddrString by cli.option(ArgType.String, fullName = "esa", description = "override the eval-stack base address (must be page aligned)")
|
||||
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
||||
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
|
||||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
||||
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
|
||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.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")
|
||||
@ -101,25 +97,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return runVm(moduleFiles.first())
|
||||
}
|
||||
|
||||
var evalStackAddr: UInt? = null
|
||||
if(evalStackAddrString!=null) {
|
||||
try {
|
||||
evalStackAddr = if (evalStackAddrString!!.startsWith("0x"))
|
||||
evalStackAddrString!!.substring(2).toUInt(16)
|
||||
else if (evalStackAddrString!!.startsWith("$"))
|
||||
evalStackAddrString!!.substring(1).toUInt(16)
|
||||
else
|
||||
evalStackAddrString!!.toUInt()
|
||||
} catch(nx: NumberFormatException) {
|
||||
System.err.println("invalid address for evalstack: $evalStackAddrString")
|
||||
return false
|
||||
}
|
||||
if(evalStackAddr !in 256u..65536u-512u || (evalStackAddr and 255u != 0u)) {
|
||||
System.err.println("invalid address for evalstack: ${evalStackAddr.toHex()}")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val processedSymbols = processSymbolDefs(symbolDefs) ?: return false
|
||||
|
||||
if(watchMode==true) {
|
||||
@ -134,15 +111,12 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val compilerArgs = CompilerArguments(
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
optimizeFloatExpressions == true,
|
||||
dontWriteAssembly != true,
|
||||
slowCodegenWarnings == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
compilationTarget!!,
|
||||
evalStackAddr,
|
||||
splitWordArrays == true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
@ -203,15 +177,12 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val compilerArgs = CompilerArguments(
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
optimizeFloatExpressions == true,
|
||||
dontWriteAssembly != true,
|
||||
slowCodegenWarnings == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
compilationTarget!!,
|
||||
evalStackAddr,
|
||||
splitWordArrays == true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
|
@ -6,10 +6,10 @@ package prog8.buildversion
|
||||
const val MAVEN_GROUP = "prog8"
|
||||
const val MAVEN_NAME = "compiler"
|
||||
const val VERSION = "9.2-SNAPSHOT"
|
||||
const val GIT_REVISION = 3964
|
||||
const val GIT_SHA = "aaa30e4a583fa7c8da61998cf7cbb2a60b52afb0"
|
||||
const val GIT_DATE = "2023-07-16T21:16:18Z"
|
||||
const val GIT_BRANCH = "master"
|
||||
const val BUILD_DATE = "2023-07-16T21:16:23Z"
|
||||
const val BUILD_UNIX_TIME = 1689542183583L
|
||||
const val GIT_REVISION = 3980
|
||||
const val GIT_SHA = "9f247901d484ca36d047cdcf77f5b905ba772f82"
|
||||
const val GIT_DATE = "2023-07-16T21:45:04Z"
|
||||
const val GIT_BRANCH = "remove_evalstack"
|
||||
const val BUILD_DATE = "2023-07-17T23:11:43Z"
|
||||
const val BUILD_UNIX_TIME = 1689635503506L
|
||||
const val DIRTY = 1
|
||||
|
@ -29,15 +29,12 @@ class CompilationResult(val compilerAst: Program, // deprecated, use codegenAs
|
||||
|
||||
class CompilerArguments(val filepath: Path,
|
||||
val optimize: Boolean,
|
||||
val optimizeFloatExpressions: Boolean,
|
||||
val writeAssembly: Boolean,
|
||||
val slowCodegenWarnings: Boolean,
|
||||
val quietAssembler: Boolean,
|
||||
val asmListfile: Boolean,
|
||||
val experimentalCodegen: Boolean,
|
||||
val varsHighBank: Int?,
|
||||
val compilationTarget: String,
|
||||
val evalStackBaseAddress: UInt?,
|
||||
val splitWordArrays: Boolean,
|
||||
val symbolDefs: Map<String, String>,
|
||||
val sourceDirs: List<String> = emptyList(),
|
||||
@ -49,8 +46,6 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
lateinit var program: Program
|
||||
lateinit var importedFiles: List<Path>
|
||||
|
||||
val optimizeFloatExpr = if(args.optimize) args.optimizeFloatExpressions else false
|
||||
|
||||
val compTarget =
|
||||
when(args.compilationTarget) {
|
||||
C64Target.NAME -> C64Target()
|
||||
@ -70,14 +65,11 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
compilationOptions = options
|
||||
|
||||
with(compilationOptions) {
|
||||
slowCodegenWarnings = args.slowCodegenWarnings
|
||||
optimize = args.optimize
|
||||
optimizeFloatExpressions = optimizeFloatExpr
|
||||
asmQuiet = args.quietAssembler
|
||||
asmListfile = args.asmListfile
|
||||
experimentalCodegen = args.experimentalCodegen
|
||||
varsHighBank = args.varsHighBank
|
||||
evalStackBaseAddress = args.evalStackBaseAddress
|
||||
splitWordArrays = args.splitWordArrays
|
||||
outputDir = args.outputDir.normalize()
|
||||
symbolDefs = args.symbolDefs
|
||||
@ -85,10 +77,6 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
program = programresult
|
||||
importedFiles = imported
|
||||
|
||||
if(compilationOptions.evalStackBaseAddress!=null) {
|
||||
compTarget.machine.overrideEvalStack(compilationOptions.evalStackBaseAddress!!)
|
||||
}
|
||||
|
||||
processAst(program, args.errors, compilationOptions)
|
||||
if (compilationOptions.optimize) {
|
||||
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
|
||||
@ -367,12 +355,11 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = program.simplifyExpressions(errors, compTarget)
|
||||
val optsDone2 = program.splitBinaryExpressions(compilerOptions)
|
||||
val optsDone3 = program.optimizeStatements(errors, functions, compilerOptions)
|
||||
val optsDone4 = program.inlineSubroutines(compilerOptions)
|
||||
val optsDone2 = program.optimizeStatements(errors, functions, compilerOptions)
|
||||
val optsDone3 = program.inlineSubroutines(compilerOptions)
|
||||
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
errors.report()
|
||||
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
|
||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||
break
|
||||
}
|
||||
val remover2 = UnusedCodeRemover(program, errors, compTarget)
|
||||
|
@ -27,7 +27,7 @@ internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationO
|
||||
boolRemover.visit(this)
|
||||
boolRemover.applyModifications()
|
||||
|
||||
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
|
||||
val fixer = BeforeAsmAstChanger(this, compilerOptions)
|
||||
fixer.visit(this)
|
||||
while (errors.noErrors() && fixer.applyModifications() > 0) {
|
||||
fixer.visit(this)
|
||||
@ -108,9 +108,6 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
||||
checker2.visit(this)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val transforms = AstOnetimeTransforms(this, options)
|
||||
transforms.visit(this)
|
||||
transforms.applyModifications()
|
||||
val lit2decl = LiteralsToAutoVars(this, options.compTarget, errors)
|
||||
lit2decl.visit(this)
|
||||
if(errors.noErrors())
|
||||
|
@ -1,62 +0,0 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
internal class AstOnetimeTransforms(private val program: Program, private val options: CompilationOptions) : AstWalker() {
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(parent !is VarDecl) {
|
||||
if(options.compTarget.name == VMTarget.NAME)
|
||||
return noModifications // vm codegen deals correctly with all cases
|
||||
// Don't replace the initializer value in a vardecl - this will be moved to a separate
|
||||
// assignment statement soon in after(VarDecl)
|
||||
return replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression, parent)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
// note: The CodeDesugarer already does something similar, but that is meant ONLY to take
|
||||
// into account the case where the index value is a word type.
|
||||
// The replacement here is to fix missing cases in the 6502 codegen. (VM codegen doesn't use this workaround)
|
||||
// TODO make the 6502 codegen better so this workaround can be removed
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||
if(parent is AssignTarget) {
|
||||
val assignment = parent.parent as? Assignment
|
||||
if(assignment?.value is NumericLiteral || assignment?.value is IdentifierReference) {
|
||||
// the codegen contains correct optimized code ONLY for a constant assignment, or direct variable assignment.
|
||||
return noModifications
|
||||
}
|
||||
// Other cases aren't covered correctly by the 6502 codegen, and there are a LOT of cases.
|
||||
// So rewrite assignment target pointervar[index] into @(pointervar+index)
|
||||
// (the 6502 codegen covers all cases correctly for a direct memory assignment).
|
||||
val indexer = arrayIndexedExpression.indexer
|
||||
val add: Expression =
|
||||
if(indexer.indexExpr.constValue(program)?.number==0.0)
|
||||
arrayIndexedExpression.arrayvar.copy()
|
||||
else
|
||||
BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexer.indexExpr, arrayIndexedExpression.position)
|
||||
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
||||
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,6 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
class AstPreprocessor(val program: Program,
|
||||
@ -25,15 +23,10 @@ class AstPreprocessor(val program: Program,
|
||||
relocateCx16VirtualRegisters(program, 0x0004u)
|
||||
}
|
||||
}
|
||||
else if(options.compTarget.name !in setOf(Cx16Target.NAME, VMTarget.NAME)) {
|
||||
relocateCx16VirtualRegisters(program, options.compTarget.machine.ESTACK_HI)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun relocateCx16VirtualRegisters(program: Program, baseAddress: UInt) {
|
||||
// reset the address of the virtual registers to be inside the evaluation stack.
|
||||
// (we don't do this on CommanderX16 itself as the registers have a fixed location in Zeropage there)
|
||||
val cx16block = program.allBlocks.single { it.name == "cx16" }
|
||||
val memVars = cx16block.statements
|
||||
.filterIsInstance<VarDecl>()
|
||||
|
@ -9,14 +9,10 @@ import prog8.ast.getTempVar
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
internal class BeforeAsmAstChanger(val program: Program,
|
||||
private val options: CompilationOptions,
|
||||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
internal class BeforeAsmAstChanger(val program: Program, private val options: CompilationOptions) : AstWalker() {
|
||||
|
||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||
throw InternalCompilerException("break should have been replaced by goto $breakStmt")
|
||||
@ -56,9 +52,6 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
&& !assignment.target.isIOAddress(options.compTarget.machine)) {
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
|
||||
if(binExpr!=null && binExpr.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
|
||||
return noModifications
|
||||
|
||||
if (binExpr != null && binExpr.operator !in ComparisonOperators) {
|
||||
if (binExpr.left !is BinaryExpression) {
|
||||
if (binExpr.right.referencesIdentifier(assignment.target.identifier!!.nameInSource)) {
|
||||
@ -222,12 +215,6 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization/check for Vm target
|
||||
val containingStatement = getContainingStatement(arrayIndexedExpression)
|
||||
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
|
||||
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
val index = arrayIndexedExpression.indexer.indexExpr
|
||||
if (index !is NumericLiteral && index !is IdentifierReference) {
|
||||
// replace complex indexing expression with a temp variable to hold the computed index first
|
||||
@ -238,42 +225,6 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun getComplexArrayIndexedExpressions(stmt: Statement): List<ArrayIndexedExpression> {
|
||||
|
||||
class Searcher : IAstVisitor {
|
||||
val complexArrayIndexedExpressions = mutableListOf<ArrayIndexedExpression>()
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
val ix = arrayIndexedExpression.indexer.indexExpr
|
||||
if(ix !is NumericLiteral && ix !is IdentifierReference)
|
||||
complexArrayIndexedExpressions.add(arrayIndexedExpression)
|
||||
}
|
||||
|
||||
override fun visit(branch: ConditionalBranch) {}
|
||||
|
||||
override fun visit(forLoop: ForLoop) {}
|
||||
|
||||
override fun visit(ifElse: IfElse) {
|
||||
ifElse.condition.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(untilLoop: UntilLoop) {
|
||||
untilLoop.condition.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
val searcher = Searcher()
|
||||
stmt.accept(searcher)
|
||||
return searcher.complexArrayIndexedExpressions
|
||||
}
|
||||
|
||||
private fun getContainingStatement(expression: Expression): Statement {
|
||||
var node: Node = expression
|
||||
while(node !is Statement)
|
||||
node = node.parent
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val statement = expr.containingStatement
|
||||
|
@ -11,7 +11,7 @@ import prog8tests.helpers.compileText
|
||||
|
||||
class TestAstChecks: FunSpec({
|
||||
|
||||
test("conditional expression w/float works even without tempvar to split it") {
|
||||
test("conditional expression w/float works") {
|
||||
val text = """
|
||||
%import floats
|
||||
main {
|
||||
|
@ -26,15 +26,12 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
val args = CompilerArguments(
|
||||
filepath,
|
||||
optimize,
|
||||
optimizeFloatExpressions = true,
|
||||
writeAssembly = true,
|
||||
slowCodegenWarnings = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHighBank = null,
|
||||
compilationTarget = target.name,
|
||||
evalStackBaseAddress = null,
|
||||
splitWordArrays = false,
|
||||
symbolDefs = emptyMap(),
|
||||
outputDir = outputDir
|
||||
|
@ -43,15 +43,12 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
val args = CompilerArguments(
|
||||
filepath = filePath,
|
||||
optimize = false,
|
||||
optimizeFloatExpressions = false,
|
||||
writeAssembly = true,
|
||||
slowCodegenWarnings = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHighBank = null,
|
||||
compilationTarget = Cx16Target.NAME,
|
||||
evalStackBaseAddress = null,
|
||||
splitWordArrays = false,
|
||||
symbolDefs = emptyMap(),
|
||||
sourceDirs,
|
||||
|
@ -120,36 +120,31 @@ class TestOptimization: FunSpec({
|
||||
// load_location = 12345
|
||||
// word llw
|
||||
// llw = 12345
|
||||
// cx16.r0 = load_location
|
||||
// cx16.r0 += 10000
|
||||
// cx16.r2 = load_location
|
||||
// cx16.r2 += 10000
|
||||
// cx16.r4 = load_location
|
||||
// cx16.r4 += 22
|
||||
// cx16.r5s = llw
|
||||
// cx16.r5s -= 1899
|
||||
// cx16.r7s = llw
|
||||
// cx16.r7s += 99
|
||||
// cx16.r0 = load_location + 10000
|
||||
// cx16.r2 = load_location + 10000
|
||||
// cx16.r4 = load_location + 22
|
||||
// cx16.r5s = llw - 1899
|
||||
// cx16.r7s = llw + 99
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 14
|
||||
stmts.size shouldBe 9
|
||||
|
||||
val addR0value = (stmts[5] as Assignment).value
|
||||
val addR0value = (stmts[4] as Assignment).value
|
||||
val binexpr0 = addR0value as BinaryExpression
|
||||
binexpr0.operator shouldBe "+"
|
||||
binexpr0.right shouldBe NumericLiteral(DataType.UWORD, 10000.0, Position.DUMMY)
|
||||
val addR2value = (stmts[7] as Assignment).value
|
||||
val addR2value = (stmts[5] as Assignment).value
|
||||
val binexpr2 = addR2value as BinaryExpression
|
||||
binexpr2.operator shouldBe "+"
|
||||
binexpr2.right shouldBe NumericLiteral(DataType.UWORD, 10000.0, Position.DUMMY)
|
||||
val addR4value = (stmts[9] as Assignment).value
|
||||
val addR4value = (stmts[6] as Assignment).value
|
||||
val binexpr4 = addR4value as BinaryExpression
|
||||
binexpr4.operator shouldBe "+"
|
||||
binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 22.0, Position.DUMMY)
|
||||
val subR5value = (stmts[11] as Assignment).value
|
||||
val subR5value = (stmts[7] as Assignment).value
|
||||
val binexpr5 = subR5value as BinaryExpression
|
||||
binexpr5.operator shouldBe "-"
|
||||
binexpr5.right shouldBe NumericLiteral(DataType.UWORD, 1899.0, Position.DUMMY)
|
||||
val subR7value = (stmts[13] as Assignment).value
|
||||
val subR7value = (stmts[8] as Assignment).value
|
||||
val binexpr7 = subR7value as BinaryExpression
|
||||
binexpr7.operator shouldBe "+"
|
||||
binexpr7.right shouldBe NumericLiteral(DataType.UWORD, 99.0, Position.DUMMY)
|
||||
@ -171,44 +166,34 @@ class TestOptimization: FunSpec({
|
||||
// expected:
|
||||
// word llw
|
||||
// llw = 300
|
||||
// cx16.r0s = llw
|
||||
// cx16.r0s *= 180
|
||||
// cx16.r1s = llw
|
||||
// cx16.r1s *= 180
|
||||
// cx16.r2s = llw
|
||||
// cx16.r2s /= 90
|
||||
// cx16.r3s = llw
|
||||
// cx16.r3s *= 5
|
||||
// cx16.r4s = llw
|
||||
// cx16.r4s *= 90
|
||||
// cx16.r4s /= 5
|
||||
// cx16.r0s = llw * 180
|
||||
// cx16.r1s = llw * 180
|
||||
// cx16.r2s = llw / 90
|
||||
// cx16.r3s = llw * 5
|
||||
// cx16.r4s = llw * 90 / 5
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 13
|
||||
stmts.size shouldBe 7
|
||||
|
||||
val mulR0Value = (stmts[3] as Assignment).value
|
||||
val mulR0Value = (stmts[2] as Assignment).value
|
||||
val binexpr0 = mulR0Value as BinaryExpression
|
||||
binexpr0.operator shouldBe "*"
|
||||
binexpr0.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY)
|
||||
val mulR1Value = (stmts[5] as Assignment).value
|
||||
val mulR1Value = (stmts[3] as Assignment).value
|
||||
val binexpr1 = mulR1Value as BinaryExpression
|
||||
binexpr1.operator shouldBe "*"
|
||||
binexpr1.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY)
|
||||
val divR2Value = (stmts[7] as Assignment).value
|
||||
val divR2Value = (stmts[4] as Assignment).value
|
||||
val binexpr2 = divR2Value as BinaryExpression
|
||||
binexpr2.operator shouldBe "/"
|
||||
binexpr2.right shouldBe NumericLiteral(DataType.UWORD, 90.0, Position.DUMMY)
|
||||
val mulR3Value = (stmts[9] as Assignment).value
|
||||
val mulR3Value = (stmts[5] as Assignment).value
|
||||
val binexpr3 = mulR3Value as BinaryExpression
|
||||
binexpr3.operator shouldBe "*"
|
||||
binexpr3.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY)
|
||||
val mulR4Value = (stmts[11] as Assignment).value
|
||||
val mulR4Value = (stmts[6] as Assignment).value
|
||||
val binexpr4 = mulR4Value as BinaryExpression
|
||||
binexpr4.operator shouldBe "*"
|
||||
binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 90.0, Position.DUMMY)
|
||||
val divR4Value = (stmts[12] as Assignment).value
|
||||
val binexpr4b = divR4Value as BinaryExpression
|
||||
binexpr4b.operator shouldBe "/"
|
||||
binexpr4b.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY)
|
||||
binexpr4.operator shouldBe "/"
|
||||
binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY)
|
||||
}
|
||||
|
||||
test("constantfolded and silently typecasted for initializervalues") {
|
||||
@ -377,14 +362,12 @@ class TestOptimization: FunSpec({
|
||||
uword z4
|
||||
z4 = 0
|
||||
ubyte z5
|
||||
z5 = z1
|
||||
z5 += 5
|
||||
z5 = z1 + 5
|
||||
ubyte z6
|
||||
z6 = z1
|
||||
z6 -= 5
|
||||
z6 = z1 - 5
|
||||
*/
|
||||
val statements = result.compilerAst.entrypoint.statements
|
||||
statements.size shouldBe 14
|
||||
statements.size shouldBe 12
|
||||
val z1decl = statements[0] as VarDecl
|
||||
val z1init = statements[1] as Assignment
|
||||
val z2decl = statements[2] as VarDecl
|
||||
@ -395,10 +378,8 @@ class TestOptimization: FunSpec({
|
||||
val z4init = statements[7] as Assignment
|
||||
val z5decl = statements[8] as VarDecl
|
||||
val z5init = statements[9] as Assignment
|
||||
val z5plus = statements[10] as Assignment
|
||||
val z6decl = statements[11] as VarDecl
|
||||
val z6init = statements[12] as Assignment
|
||||
val z6plus = statements[13] as Assignment
|
||||
val z6decl = statements[10] as VarDecl
|
||||
val z6init = statements[11] as Assignment
|
||||
|
||||
z1decl.name shouldBe "z1"
|
||||
z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
|
||||
@ -409,15 +390,11 @@ class TestOptimization: FunSpec({
|
||||
z4decl.name shouldBe "z4"
|
||||
z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
z5decl.name shouldBe "z5"
|
||||
(z5init.value as? IdentifierReference)?.nameInSource shouldBe listOf("z1")
|
||||
z5plus.isAugmentable shouldBe true
|
||||
(z5plus.value as BinaryExpression).operator shouldBe "+"
|
||||
(z5plus.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY)
|
||||
(z5init.value as BinaryExpression).operator shouldBe "+"
|
||||
(z5init.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY)
|
||||
z6decl.name shouldBe "z6"
|
||||
(z6init.value as? IdentifierReference)?.nameInSource shouldBe listOf("z1")
|
||||
z6plus.isAugmentable shouldBe true
|
||||
(z6plus.value as BinaryExpression).operator shouldBe "-"
|
||||
(z6plus.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY)
|
||||
(z6init.value as BinaryExpression).operator shouldBe "-"
|
||||
(z6init.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY)
|
||||
}
|
||||
|
||||
test("force_output option should work with optimizing memwrite assignment") {
|
||||
@ -435,7 +412,7 @@ class TestOptimization: FunSpec({
|
||||
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
stmts.size shouldBe 5
|
||||
val assign=stmts.last() as Assignment
|
||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||
}
|
||||
@ -452,7 +429,7 @@ class TestOptimization: FunSpec({
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
stmts.size shouldBe 5
|
||||
val assign=stmts.last() as Assignment
|
||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||
}
|
||||
@ -548,9 +525,9 @@ class TestOptimization: FunSpec({
|
||||
xx += 6
|
||||
*/
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 8
|
||||
stmts.size shouldBe 7
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 3
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 4
|
||||
}
|
||||
|
||||
test("only substitue assignments with 0 after a =0 initializer if it is the same variable") {
|
||||
|
@ -95,19 +95,16 @@ main {
|
||||
ubyte ub3
|
||||
ub3 = 1
|
||||
ubyte @shared bvalue
|
||||
bvalue = ub1
|
||||
bvalue ^= ub2
|
||||
bvalue ^= ub3
|
||||
bvalue ^= 1
|
||||
bvalue = ub1 ^ ub2 ^ ub3 ^ true
|
||||
bvalue = (((ub1^ub2)^ub3)^(ftrue(99)!=0))
|
||||
bvalue = ((ub1&ub2)&(ftrue(99)!=0))
|
||||
return
|
||||
*/
|
||||
stmts.size shouldBe 14
|
||||
val assignValue1 = (stmts[7] as Assignment).value as IdentifierReference
|
||||
val assignValue2 = (stmts[11] as Assignment).value as BinaryExpression
|
||||
val assignValue3 = (stmts[12] as Assignment).value as BinaryExpression
|
||||
assignValue1.nameInSource shouldBe listOf("ub1")
|
||||
stmts.size shouldBe 11
|
||||
val assignValue1 = (stmts[7] as Assignment).value as BinaryExpression
|
||||
val assignValue2 = (stmts[8] as Assignment).value as BinaryExpression
|
||||
val assignValue3 = (stmts[9] as Assignment).value as BinaryExpression
|
||||
assignValue1.operator shouldBe "^"
|
||||
assignValue2.operator shouldBe "^"
|
||||
assignValue3.operator shouldBe "&"
|
||||
val right2 = assignValue2.right as BinaryExpression
|
||||
|
@ -17,22 +17,18 @@ internal fun compileFile(
|
||||
outputDir: Path = prog8tests.helpers.outputDir,
|
||||
errors: IErrorReporter? = null,
|
||||
writeAssembly: Boolean = true,
|
||||
optFloatExpr: Boolean = true,
|
||||
) : CompilationResult? {
|
||||
val filepath = fileDir.resolve(fileName)
|
||||
assumeReadableFile(filepath)
|
||||
val args = CompilerArguments(
|
||||
filepath,
|
||||
optimize,
|
||||
optimizeFloatExpressions = optFloatExpr,
|
||||
writeAssembly = writeAssembly,
|
||||
slowCodegenWarnings = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
varsHighBank = null,
|
||||
platform.name,
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
outputDir = outputDir,
|
||||
errors = errors ?: ErrorReporterForTests(),
|
||||
@ -52,11 +48,10 @@ internal fun compileText(
|
||||
sourceText: String,
|
||||
errors: IErrorReporter? = null,
|
||||
writeAssembly: Boolean = true,
|
||||
optFloatExpr: Boolean = true,
|
||||
) : CompilationResult? {
|
||||
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
|
||||
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
|
||||
filePath.toFile().writeText(sourceText)
|
||||
return compileFile(platform, optimize, filePath.parent, filePath.name,
|
||||
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr)
|
||||
errors=errors, writeAssembly=writeAssembly)
|
||||
}
|
||||
|
@ -151,10 +151,6 @@ 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.
|
||||
|
||||
``-slowwarn``
|
||||
Shows debug warnings about slow or problematic assembly code generation.
|
||||
Ideally, the compiler should use as few stack based evaluations as possible.
|
||||
|
||||
``-quietasm``
|
||||
Don't print assembler output results.
|
||||
|
||||
@ -176,12 +172,6 @@ One or more .p8 module files
|
||||
Add this user-defined symbol directly to the beginning of the generated assembly file.
|
||||
Can be repeated to define multiple symbols.
|
||||
|
||||
``-esa <address>``
|
||||
Override the base address of the evaluation stack. Has to be page-aligned.
|
||||
You can specify an integer or hexadecimal address.
|
||||
When not compiling for the Commander X16 target, the location of the 16 virtual registers cx16.r0..r15
|
||||
is changed accordingly (to keep them in the same memory space as the evaluation stack).
|
||||
|
||||
``-varshigh <rambank>``
|
||||
Places the non-zeropage variables in a separate high memory area, instead of inside the program itself.
|
||||
This results in an increase of the amount of system ram available for the program
|
||||
|
@ -945,15 +945,12 @@ syscall (callnr), syscall1 (callnr, arg), syscall2 (callnr, arg1, arg2), syscall
|
||||
specific memory locations. So these builtin function calls are not useful yet except for
|
||||
experimentation in new code generation targets.
|
||||
|
||||
rsave, rsavex
|
||||
rsave
|
||||
Saves all registers including status (or only X) on the stack
|
||||
It's not needed to rsave()/rsavex() before an asm subroutine that clobbers the X register
|
||||
(which is used by prog8 as the internal evaluation stack pointer);
|
||||
the compiler will take care of this situation automatically.
|
||||
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* saved,
|
||||
but you can use ``cx16.save_virtual_registers()`` for that.
|
||||
|
||||
rrestore, rrestorex
|
||||
rrestore
|
||||
Restore all registers including status (or only X) back from the cpu hardware stack
|
||||
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* restored,
|
||||
but you can use ``cx16.restore_virtual_registers()`` for that.
|
||||
|
@ -56,35 +56,31 @@ Both systems have ways to alter the memory map and/or to switch memory banks, bu
|
||||
Footnotes for the Commander X16
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
*Golden Ram $0400 - $07FF*
|
||||
*reserved:* $0700 - $07FF (expression evaluation stack)
|
||||
|
||||
*free to use:* $0400 - $06FF
|
||||
*free to use.*
|
||||
|
||||
*Zero Page $0000 - $00FF*
|
||||
$00 and $01 are hardwired as Rom and Ram banking registers.
|
||||
|
||||
$02 - $21 are the 16 virtual cx16 registers R0-R15.
|
||||
|
||||
$22 - $7F are free to use, and Prog8 utilizes this to put variables in automatically.
|
||||
$22 - $7F are used by Prog8 to put variables in.
|
||||
|
||||
The top half of the ZP ($80-$FF) is reserved for use by the Kernal and Basic in normal operation.
|
||||
Zero page use by Prog8 can be manipulated with the ``%zeropage`` directive, various options
|
||||
may free up more locations for use by Prog8.
|
||||
may free up more locations for use by Prog8 or to reserve them for other things.
|
||||
|
||||
|
||||
Footnotes for the Commodore 64
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
*RAM $C000-$CFFF*
|
||||
*reserved:* $CF00 - $CFFF (expression evaluation stack)
|
||||
*this includes:* $CF00 - $CF20 for the 16 virtual cx16 registers R0-R15
|
||||
|
||||
*free to use:* $C000 - $CEFF
|
||||
*free to use:* $C000 - $CFDF
|
||||
*reserved:* $CFE0 - $CFFF for the 16 virtual cx16 registers R0-R15
|
||||
|
||||
*Zero Page $0000 - $00FF*
|
||||
Consider the full zero page to be reserved for use by the Kernal and Basic in normal operation.
|
||||
Zero page use by Prog8 can be manipulated with the ``%zeropage`` directive, various options
|
||||
may free up more locations for use by Prog8.
|
||||
may free up more locations for use by Prog8 or to reserve them for other things.
|
||||
|
||||
|
||||
Zero page usage by the Prog8 compiler
|
||||
@ -127,8 +123,6 @@ Directly Usable Registers
|
||||
|
||||
The hardware CPU registers are not directly accessible from regular Prog8 code.
|
||||
If you need to mess with them, you'll have to use inline assembly.
|
||||
Be extra wary of the ``X`` register because it is used as an evaluation stack pointer and
|
||||
changing its value you will destroy the evaluation stack and likely crash the program.
|
||||
|
||||
The status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
||||
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
||||
|
@ -44,28 +44,6 @@ All elements in scoped names such as ``main.routine.var1`` are prefixed so this
|
||||
}}
|
||||
|
||||
|
||||
Software stack for expression evaluation
|
||||
----------------------------------------
|
||||
|
||||
Prog8 uses a software stack to evaluate complex expressions that it can't calculate in-place or
|
||||
directly into the target variable, register, or memory location.
|
||||
|
||||
'software stack' means: seperated and not using the processor's hardware stack.
|
||||
|
||||
The software stack is implemented as follows:
|
||||
|
||||
- 2*128 bytes = 1 page of memory allocated for this, exact locations vary per machine target.
|
||||
For the C64 this page is at $cf00-$cfff.
|
||||
For the Commander X16 it is at $0700-$07ff (top of the "golden ram" area).
|
||||
This default location can be overridden using the `-esa` command line option.
|
||||
- these are the high and low bytes of the values on the stack (it's a 'split 16 bit word stack')
|
||||
- for byte values just the lsb page is used, for word values both pages
|
||||
- float values (5 bytes) are chopped up into 2 words and 1 byte on this stack.
|
||||
- the X register is permanently allocated to be the stack pointer in the software stack.
|
||||
- you can use the X register as long as you're not using the software stack.
|
||||
But you *must* make sure it is saved and restored after the code that modifies it,
|
||||
otherwise the evaluation stack gets corrupted.
|
||||
|
||||
Subroutine Calling Convention
|
||||
-----------------------------
|
||||
|
||||
@ -94,8 +72,7 @@ regular subroutines
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- subroutine parameters are just variables scoped to the subroutine.
|
||||
- the arguments passed in a call are evaluated (using the eval-stack if needed) and then
|
||||
copied into those variables.
|
||||
- the arguments passed in a call are evaluated and then copied into those variables.
|
||||
Using variables for this sometimes can seem inefficient but it's required to allow subroutines to work locally
|
||||
with their parameters and allow them to modify them as required, without changing the
|
||||
variables used in the call's arguments. If you want to get rid of this overhead you'll
|
||||
@ -118,40 +95,6 @@ doing something with that returnvalue. This can be on purpose if you're simply n
|
||||
Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case.
|
||||
|
||||
|
||||
The 6502 CPU's X-register: off-limits
|
||||
-------------------------------------
|
||||
|
||||
Prog8 uses the cpu's X-register as a pointer in its internal expression evaluation stack.
|
||||
When only writing code in Prog8, this is taken care of behind the scenes for you by the compiler.
|
||||
However when you are including or linking with assembly routines or Kernal/ROM calls that *do*
|
||||
use the X register (either clobbering it internally, or using it as a parameter, or return value register),
|
||||
those calls will destroy Prog8's stack pointer and this will result in invalid calculations.
|
||||
|
||||
You should avoid using the X register in your assembly code, or take preparations.
|
||||
If you make sure that the value of the X register is preserved before calling a routine
|
||||
that uses it, and restored when the routine is done, you'll be ok.
|
||||
|
||||
Routines that return a value in the X register can be called from Prog8 but the return value is
|
||||
inaccessible unless you write a short piece of inline assembly code to deal with it yourself, such as::
|
||||
|
||||
ubyte returnvalue
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG ; use 'phx/plx' if using 65c02 cpu
|
||||
ldx #10
|
||||
jsr routine_using_x
|
||||
stx returnvalue
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
}}
|
||||
; now use 'returnvalue' variable
|
||||
|
||||
Prog8 also provides some help to deal with this:
|
||||
|
||||
- you should use a ``clobbers(X)`` specification for asmsub routines that modify the X register; the compiler will preserve it for you automatically when such a routine is called
|
||||
- the ``rsavex()`` and ``rrestorex()`` builtin functions can preserve and restore the X register
|
||||
- the ``rsave()`` and ``rrestore()`` builtin functions can preserve and restore *all* registers (but this is very slow and overkill if you only need to save X)
|
||||
|
||||
|
||||
Compiler Internals
|
||||
------------------
|
||||
|
||||
|
@ -48,16 +48,6 @@ Libraries:
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||
|
||||
|
||||
Expressions: (see remove_evalstack branch):
|
||||
|
||||
- Once the evalstack-free expression codegen is in place, the Eval Stack can be removed from the compiler.
|
||||
Machinedefinition, .p8 and .asm library files, all routines operationg on estack, and everything saving/restoring the X register related to this stack.
|
||||
- Or rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code
|
||||
that, for instance, uses a fixed number of predetermined value 'variables'?
|
||||
The VM IL solves this already (by using unlimited registers) but that still lacks a translation to 6502.
|
||||
- this removes the need for the BinExprSplitter? (which is problematic and very limited now)
|
||||
and perhaps the assignment splitting in BeforeAsmAstChanger too
|
||||
|
||||
Optimizations:
|
||||
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
|
@ -63,9 +63,8 @@ main {
|
||||
cx16.GRAPH_put_next_char(c)
|
||||
}
|
||||
|
||||
asmsub print_number_gfx(ubyte num @ A) clobbers(A,Y) {
|
||||
asmsub print_number_gfx(ubyte num @ A) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
phx
|
||||
pha
|
||||
@ -81,9 +80,7 @@ main {
|
||||
beq _ones
|
||||
jsr cx16.GRAPH_put_char
|
||||
_ones pla
|
||||
jsr cx16.GRAPH_put_char
|
||||
plx
|
||||
rts
|
||||
jmp cx16.GRAPH_put_char
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -2,21 +2,24 @@
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
; The documentation for custom PS2 key handlers can be found here:
|
||||
; https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2002%20-%20Editor.md#custom-keyboard-scancode-handler
|
||||
; The documentation for custom key handlers can be found here:
|
||||
; https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2002%20-%20Editor.md#custom-keyboard-keynum-code-handler
|
||||
|
||||
main {
|
||||
|
||||
bool stop_program = false
|
||||
|
||||
sub start() {
|
||||
|
||||
txt.print("custom key handler test - press keys! esc to quit!\n")
|
||||
|
||||
sys.set_irqd()
|
||||
uword old_keyhdl = cx16.KEYHDL
|
||||
cx16.KEYHDL = &keyboard_scancode_handler
|
||||
cx16.KEYHDL = &keyboard_handler
|
||||
sys.clear_irqd()
|
||||
|
||||
while handle_keyboard_event() {
|
||||
while not stop_program {
|
||||
; wait
|
||||
}
|
||||
|
||||
sys.set_irqd()
|
||||
@ -24,55 +27,23 @@ main {
|
||||
sys.clear_irqd()
|
||||
}
|
||||
|
||||
; Keyboard handler communication variables.
|
||||
; these need to be in block scope instead of in a subroutine,
|
||||
; so that they won't get overwritten with initialization values every time.
|
||||
; The assembly keyboard handler will set these, prog8 will read them.
|
||||
bool @shared keyhdl_event ; is there a keyboard event to handle?
|
||||
ubyte @shared keyhdl_scancode
|
||||
|
||||
sub handle_keyboard_event() -> bool {
|
||||
; Potentially handle keyboard event.
|
||||
; Note that we do this from the program's main loop instead of
|
||||
; the actual keyboard handler routine itself.
|
||||
; The reason for this is documented below in the handler assembly routine.
|
||||
if not keyhdl_event
|
||||
return true
|
||||
keyhdl_event = false
|
||||
txt.print_ubhex(keyhdl_scancode, true)
|
||||
sub keyboard_handler(ubyte keynum) -> ubyte {
|
||||
; NOTE: this handler routine expects the keynum in A and return value in A
|
||||
; which is thankfully how prog8 translates this subroutine's calling convention.
|
||||
; NOTE: it may be better to store the keynum somewhere else and let the main program
|
||||
; loop figure out what to do with it, rather than putting it all in the handler routine
|
||||
txt.print_ubhex(keynum, true)
|
||||
txt.spc()
|
||||
if keyhdl_scancode & $80
|
||||
if keynum & $80
|
||||
txt.chrout('u')
|
||||
else
|
||||
txt.chrout('d')
|
||||
txt.nl()
|
||||
return keyhdl_scancode!=$6e ; escape breaks the loop
|
||||
}
|
||||
|
||||
asmsub keyboard_scancode_handler() {
|
||||
; NOTE that the keyboard handler is an asm subroutine.
|
||||
; Unfortunately is it not possible to use prog8 code or calls here,
|
||||
; because the X register gets overwritten here by the kernal.
|
||||
; Pog8 uses the X register internally (for the software eval stack).
|
||||
; So it is unsafe to call prog8 code from here because the evaluation stack pointer
|
||||
; will be invalid which produces undefined results.
|
||||
; So, instead, we store the various keyboard event bytes and signal
|
||||
; the main prog8 program that a keyboard event has occurred.
|
||||
; It then processes it independently from the assembly code here.
|
||||
;
|
||||
; Unfortunately this also means you cannot decide easily from that prog8 code
|
||||
; if the keyboard press should be consumed/ignored or put into the keyboard queue
|
||||
; (this is controlled by returning 0 or 1 in register A here)
|
||||
|
||||
%asm {{
|
||||
pha
|
||||
sta p8_keyhdl_scancode
|
||||
lda #1
|
||||
sta p8_keyhdl_event
|
||||
pla
|
||||
|
||||
lda #0 ; By setting A=0 we will eat this key event. leave A unchanged to pass it through.
|
||||
rts
|
||||
}}
|
||||
if keynum==$6e {
|
||||
; escape stops the program
|
||||
main.stop_program = true
|
||||
}
|
||||
return 0 ; By returning 0 (in A) we will eat this key event. Return the original keynum value to pass it through.
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ main {
|
||||
|
||||
sub start() {
|
||||
txt.print("calculating mandelbrot fractal...\n\n")
|
||||
cbm.SETTIM(0,0,0)
|
||||
|
||||
ubyte pixelx
|
||||
ubyte pixely
|
||||
@ -37,5 +38,10 @@ main {
|
||||
}
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
float duration = (cbm.RDTIM16() as float) / 60
|
||||
txt.print("\nfinished in ")
|
||||
floats.print_f(duration)
|
||||
txt.print(" seconds!\n")
|
||||
}
|
||||
}
|
||||
|
@ -33,11 +33,8 @@ class RequestParser : Take {
|
||||
val args = CompilerArguments(
|
||||
Path(a),
|
||||
optimize = true,
|
||||
optimizeFloatExpressions = false,
|
||||
writeAssembly = true,
|
||||
slowCodegenWarnings = true,
|
||||
compilationTarget = "c64",
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
quietAssembler = false,
|
||||
asmListfile = false,
|
||||
|
@ -88,7 +88,6 @@ class IRFileReader {
|
||||
val zpReserved = mutableListOf<UIntRange>()
|
||||
var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||
var optimize = true
|
||||
var evalStackBaseAddress: UInt? = null
|
||||
var outputDir = Path("")
|
||||
|
||||
if(text.isNotBlank()) {
|
||||
@ -109,7 +108,6 @@ class IRFileReader {
|
||||
"launcher" -> launcher = CbmPrgLauncherType.valueOf(value)
|
||||
"zeropage" -> zeropage = ZeropageType.valueOf(value)
|
||||
"loadAddress" -> loadAddress = parseIRValue(value).toUInt()
|
||||
"evalStackBaseAddress" -> evalStackBaseAddress = if(value=="") null else parseIRValue(value).toUInt()
|
||||
"zpReserved" -> {
|
||||
val (zpstart, zpend) = value.split(',')
|
||||
zpReserved.add(UIntRange(zpstart.toUInt(), zpend.toUInt()))
|
||||
@ -130,7 +128,6 @@ class IRFileReader {
|
||||
false,
|
||||
target,
|
||||
loadAddress,
|
||||
evalStackBaseAddress = evalStackBaseAddress,
|
||||
outputDir = outputDir,
|
||||
optimize = optimize
|
||||
)
|
||||
|
@ -110,7 +110,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
is IRInlineAsmChunk -> writeInlineAsm(chunk)
|
||||
is IRInlineBinaryChunk -> writeInlineBytes(chunk)
|
||||
is IRCodeChunk -> writeCodeChunk(chunk)
|
||||
else -> throw InternalCompilerException("invalid chunk")
|
||||
}
|
||||
}
|
||||
xml.writeEndElement()
|
||||
@ -171,7 +170,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
}
|
||||
xml.writeCharacters("loadAddress=${irProgram.options.loadAddress.toHex()}\n")
|
||||
xml.writeCharacters("optimize=${irProgram.options.optimize}\n")
|
||||
xml.writeCharacters("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress?.toHex() ?: ""}\n")
|
||||
xml.writeCharacters("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
|
||||
// other options not yet useful here?
|
||||
xml.writeEndElement()
|
||||
|
@ -168,7 +168,6 @@ class IRProgram(val name: String,
|
||||
}
|
||||
}
|
||||
is IRInlineBinaryChunk -> { }
|
||||
else -> throw AssemblyError("invalid chunk")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ output=PRG
|
||||
launcher=BASIC
|
||||
zeropage=KERNALSAFE
|
||||
loadAddress=$0000
|
||||
evalStackBaseAddress=
|
||||
</OPTIONS>
|
||||
|
||||
<ASMSYMBOLS>
|
||||
|
@ -1,6 +1,5 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.intermediate.FunctionCallArgs
|
||||
import prog8.intermediate.IRDataType
|
||||
import kotlin.math.*
|
||||
@ -484,7 +483,6 @@ object SysCalls {
|
||||
val result = floor(radians/2.0/PI*256.0)
|
||||
returnValue(callspec.returns!!, result, vm)
|
||||
}
|
||||
else -> throw AssemblyError("missing syscall ${call.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ class VmProgramLoader {
|
||||
|
||||
subroutines.forEach {
|
||||
it.value.chunks.forEach { chunk ->
|
||||
chunk.instructions.withIndex().forEach { (index, ins) ->
|
||||
chunk.instructions.withIndex().forEach { (_, ins) ->
|
||||
if(ins.opcode==Opcode.CALL) {
|
||||
val fcallspec = ins.fcallArgs!!
|
||||
val argsWithAddresses = fcallspec.arguments.map { arg ->
|
||||
|
Loading…
Reference in New Issue
Block a user