Compare commits

..

14 Commits

Author SHA1 Message Date
Irmen de Jong
ec728bad52 fix certain float array assignment, fixes #193 2025-10-30 01:45:25 +01:00
Irmen de Jong
a5e827e40f implement some more long array operations 2025-10-30 01:20:59 +01:00
Irmen de Jong
34061c5a63 implement inplace bitwise operations on long struct fields 2025-10-28 23:44:34 +01:00
Irmen de Jong
9c2bcab4a5 fix more ptr/long issues 2025-10-28 23:08:12 +01:00
Irmen de Jong
2f07f41664 detect self-referencing aliases 2025-10-23 21:24:14 +02:00
Irmen de Jong
7027304597 put some upper bounds on more internal compiler loops 2025-10-23 20:37:44 +02:00
Irmen de Jong
ee75333891 implement more long assignments through pointers 2025-10-21 22:55:46 +02:00
Irmen de Jong
6a70fb0480 deal with invalid sqrt results (negative argument), allow sqrt of longs (like floats) 2025-10-21 21:01:03 +02:00
Irmen de Jong
ebc738b132 'hack' to allow unsigned long constants such as $ffffffff to be assigned to longs without casts 2025-10-20 00:33:40 +02:00
Irmen de Jong
e5939be0bd some more long operations implemented 2025-10-19 23:03:17 +02:00
Irmen de Jong
0c0affd1bf cx16 diskio.load_size() now returns full long size. Fixes #184 2025-10-19 21:18:46 +02:00
Irmen de Jong
c7158fd968 implement more long operations on struct fields 2025-10-17 01:55:11 +02:00
Irmen de Jong
de2f3f0d0d tweak TODO messages for longs 2025-10-16 23:20:34 +02:00
Irmen de Jong
c0286e3349 added txt.size() to return text screen width and height in 1 call. Also fixed txt.width/height on the C128 (where SCREEN reports 1 less for them...) 2025-10-16 00:33:14 +02:00
44 changed files with 1165 additions and 265 deletions

View File

@@ -58,9 +58,9 @@ What does Prog8 provide?
- all advantages of a higher level language over having to write assembly code manually
- programs run very fast because it's compiled to native machine code
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- compiled code is very small; much smaller than equivalent C code compiled with CC65, and usually runs faster as well
- modularity, symbol scoping, subroutines. No need for forward declarations.
- various data types other than just bytes (16-bit words, floats, strings)
- various data types other than just bytes (16-bit words, long integers, floats, strings)
- Structs and typed pointers
- floating point math is supported on certain targets
- access to most Kernal ROM routines as external subroutine definitions you can call normally
@@ -82,7 +82,7 @@ What does Prog8 provide?
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
- encode strings and characters into petscii or screencodes or even other encodings
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
- 50 Kb of available program RAM size on the C64 by default (41 Kb on the C128) because Basic ROM is banked out by default
*Rapid edit-compile-run-debug cycle:*

View File

@@ -112,6 +112,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sqrt__long" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.LONG)),
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),

View File

@@ -758,6 +758,15 @@ class AsmGen6502Internal (
CpuRegister.Y -> out(" tay")
}
}
expr.type.isLong -> {
assignExpressionToRegister(expr.index, RegisterOrPair.A)
out(" asl a | asl a")
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
expr.type.isFloat -> {
if(options.compTarget.FLOAT_MEM_SIZE != 5)
TODO("support float size other than 5 ${expr.position}")
@@ -844,7 +853,7 @@ class AsmGen6502Internal (
return
}
TargetStorageKind.POINTER -> {
TODO("assign to pointer ${target.position}")
pointerGen.assignByte(PtrTarget(target), 0)
return
}
else -> { }
@@ -854,7 +863,10 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
}
target.datatype.isPointer -> TODO("assign expression to pointer ${target.position}")
target.datatype.isPointer -> {
assignExpressionToRegister(value, RegisterOrPair.AX)
pointerGen.assignWordReg(PtrTarget(target), RegisterOrPair.AX)
}
target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment(
@@ -866,7 +878,7 @@ class AsmGen6502Internal (
}
target.datatype.isLong -> {
if(value is PtNumber) {
val hex = value.asConstInteger()!!.toString(16).padStart(8, '0')
val hex = value.asConstInteger()!!.toLongHex()
when(target.kind) {
TargetStorageKind.VARIABLE -> {
out("""
@@ -882,7 +894,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> pointerGen.assignLong(target.pointer!!, value.number.toInt())
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtIdentifier && value.type.isLong) {
@@ -905,7 +917,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> pointerGen.assignLongVar(target.pointer!!, asmSymbolName(value))
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtTypeCast && value.type.isLong) {
@@ -920,7 +932,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign typecasted long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value.value.type.isWord) {
@@ -945,9 +957,7 @@ class AsmGen6502Internal (
sta cx16.$startreg+1""")
signExtendLongVariable("cx16.$startreg", value.value.type.base)
}
TargetStorageKind.POINTER -> {
throw AssemblyError("assign typecasted long into pointer ${target.position}")
}
TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else throw AssemblyError("weird casted type")
@@ -956,6 +966,8 @@ class AsmGen6502Internal (
}
} else if(target.kind == TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, true)
} else if(target.kind == TargetStorageKind.VARIABLE) {
assignExpressionToVariable(value, target.asmVarname, target.datatype)
} else {
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
}
@@ -1426,7 +1438,10 @@ $repeatLabel""")
sta $asmvar+3""")
}
BaseDataType.UWORD -> {
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
if(isTargetCpu(CpuType.CPU65C02))
out(" stz $asmvar+2 | stz $asmvar+3")
else
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
}
BaseDataType.WORD -> {
out("""
@@ -2109,6 +2124,22 @@ $repeatLabel""")
}
}
internal fun loadIndirectLongIntoR14R15(zpPtrVar: String, offset: UByte) {
out("""
ldy #$offset
lda ($zpPtrVar),y
sta cx16.r14
iny
lda ($zpPtrVar),y
sta cx16.r14+1
iny
lda ($zpPtrVar),y
sta cx16.r14+2
iny
lda ($zpPtrVar),y
sta cx16.r14+3""")
}
internal fun storeIndirectByte(byte: Int, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
out(" lda #$byte | ldy #$offset | sta ($zpPtrVar),y")
@@ -2245,7 +2276,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #$offset
@@ -2312,7 +2343,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #0
@@ -2497,4 +2528,8 @@ internal class SubroutineExtraAsmInfo {
var usedFloatEvalResultVar2 = false
val extraVars = mutableListOf<Triple<BaseDataType, String, UInt?>>()
}
}
internal fun Double.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')
internal fun Int.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')

View File

@@ -40,7 +40,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(fcall, resultRegister)
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
"divmod__uword" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall)
@@ -416,7 +416,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(arg2.type.isLong) {
if(arg1 is PtIdentifier && arg2 is PtNumber) {
val var1 = asmgen.asmVariableName(arg1)
val hex = arg2.number.toUInt().toString(16).padStart(8, '0')
val hex = arg2.number.toLongHex()
asmgen.out("""
sec
lda $var1
@@ -538,7 +538,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -557,7 +557,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -577,7 +577,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
sta $variable+3
+""")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -620,7 +620,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -641,7 +641,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+1 | ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -652,7 +652,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+3 | ror $variable+2 | ror $variable+1 | ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -684,7 +684,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -703,7 +703,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -721,7 +721,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
inc $variable
+""")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -763,7 +763,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -784,7 +784,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -795,7 +795,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1 | rol $variable+2 | rol $variable+3")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")

View File

@@ -1406,7 +1406,7 @@ _jump jmp (${target.asmLabel})
if(left is PtIdentifier) {
val leftvar = asmgen.asmVariableName(left)
if(constRight!=null) {
val hex = constRight.toUInt().toString(16).padStart(8, '0')
val hex = constRight.toLongHex()
asmgen.out("""
lda $leftvar
cmp #$${hex.substring(6,8)}
@@ -1437,7 +1437,7 @@ _jump jmp (${target.asmLabel})
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
if(constRight!=null) {
val hex = constRight.toUInt().toString(16).padStart(8, '0')
val hex = constRight.toLongHex()
asmgen.out("""
lda cx16.r14
cmp #$${hex.substring(6,8)}
@@ -2113,4 +2113,5 @@ _jump jmp (${target.asmLabel})
else -> throw AssemblyError("expected comparison operator")
}
}
}
}

View File

@@ -368,7 +368,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
private fun translateLongExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if L==number
// TODO reuse code from ifElse?
val hex = number.toUInt().toString(16).padStart(8, '0')
val hex = number.toLongHex()
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
@@ -386,7 +386,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
cmp #$${hex.substring(6, 8)}
@@ -424,7 +424,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
cmp $varname2
@@ -493,7 +493,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
ora cx16.r14+1
@@ -518,7 +518,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
beq $falseLabel""")
} else {
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
ora cx16.r14+1

View File

@@ -1044,7 +1044,7 @@ internal class ProgramAndVarsGen(
}
dt.isLongArray -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(8, '0')
val hexnum = number.absoluteValue.toLongHex()
if(number>=0)
"$$hexnum"
else

View File

@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
import kotlin.math.log2
@@ -201,7 +202,7 @@ internal class AssignmentAsmGen(
assignRegisterByte(tgt, CpuRegister.A, false, false)
}
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
when(assign.source.kind) {
SourceStorageKind.LITERALBOOLEAN -> {
// simple case: assign a constant boolean (0 or 1)
@@ -294,6 +295,19 @@ internal class AssignmentAsmGen(
asmgen.out(" lda $arrayVarName+$indexValue | ldy $arrayVarName+$indexValue+1")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
elementDt.isLong -> {
// it's not an expression so no need to preserve R14-R15
asmgen.out("""
lda $arrayVarName+$indexValue
sta cx16.r14
lda $arrayVarName+$indexValue+1
sta cx16.r14+1
lda $arrayVarName+$indexValue+2
sta cx16.r14+2
lda $arrayVarName+$indexValue+3
sta cx16.r14+3""")
assignRegisterLong(assign.target, RegisterOrPair.R14R15_32)
}
elementDt.isFloat -> {
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue)")
assignFloatFromAY(assign.target)
@@ -313,6 +327,20 @@ internal class AssignmentAsmGen(
asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y")
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
}
elementDt.isLong -> {
// it's not an expression so no need to preserve R14-R15
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
asmgen.out("""
lda $arrayVarName,y
sta cx16.r14
lda $arrayVarName+1,y
sta cx16.r14+1
lda $arrayVarName+2,y
sta cx16.r14+2
lda $arrayVarName+3,y
sta cx16.r14+3""")
assignRegisterLong(assign.target, RegisterOrPair.R14R15_32)
}
elementDt.isFloat -> {
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A)
asmgen.out("""
@@ -787,8 +815,12 @@ internal class AssignmentAsmGen(
return if(translatedOk)
true
else
else {
if(expr.type.isLong && expr.operator in "*/%")
TODO("long multiplication or division ${expr.position}")
anyExprGen.assignAnyExpressionUsingStack(expr, assign)
}
}
private fun optimizedComparison(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
@@ -1741,49 +1773,9 @@ internal class AssignmentAsmGen(
val left = expr.left
val right = expr.right
when(right) {
is PtIdentifier -> {
if(target.kind == TargetStorageKind.VARIABLE) {
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out("""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}""")
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out(
"""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<cx16.$startreg
ldy #>cx16.$startreg"""
)
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else {
TODO("add/subtract long into ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
}
}
is PtNumber -> {
asmgen.assignExpressionTo(left, target)
val hex = right.number.toInt().toString(16).padStart(8, '0')
val hex = right.number.toLongHex()
if (target.kind == TargetStorageKind.VARIABLE) {
if (expr.operator == "+") {
asmgen.out("""
@@ -1851,10 +1843,90 @@ internal class AssignmentAsmGen(
sta cx16.$startreg+3""")
}
return true
} else if(target.kind==TargetStorageKind.POINTER) {
// not an expression, no need to save R14/R15
assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, target.datatype.isSigned)
pointergen.assignLongVar(target.pointer!!, "cx16.r14")
return true
} else {
TODO("add/subtract long const into ${target.kind} ${target.position} - use simple expressions and temporary variables for now")
}
}
is PtIdentifier -> {
if(target.kind == TargetStorageKind.VARIABLE) {
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out("""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}""")
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out(
"""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<cx16.$startreg
ldy #>cx16.$startreg"""
)
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else {
// isn't an expression, so no need to preserve R12-R15
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R12R13_32, left.type.isSigned)
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R14R15_32, left.type.isSigned)
if(expr.operator=="+") {
asmgen.out("""
clc
lda cx16.r12
adc cx16.r14
sta cx16.r12
lda cx16.r12+1
adc cx16.r14+1
sta cx16.r12+1
lda cx16.r12+2
adc cx16.r14+2
sta cx16.r12+2
lda cx16.r12+3
adc cx16.r14+3
sta cx16.r12+3""")
} else {
asmgen.out("""
sec
lda cx16.r12
sbc cx16.r14
sta cx16.r12
lda cx16.r12+1
sbc cx16.r14+1
sta cx16.r12+1
lda cx16.r12+2
sbc cx16.r14+2
sta cx16.r12+2
lda cx16.r12+3
sbc cx16.r14+3
sta cx16.r12+3""")
}
asmgen.assignRegister(RegisterOrPair.R12R13_32, target)
return true
}
}
else -> {
val targetreg = target.register
if(targetreg==RegisterOrPair.R14R15_32)
@@ -2617,7 +2689,7 @@ $endLabel""")
}
else -> {}
}
} else if(valueDt.isUnsignedWord) {
} else if(valueDt.isWord) {
when(target.register) {
RegisterOrPair.A,
RegisterOrPair.X,
@@ -2636,7 +2708,38 @@ $endLabel""")
// 'cast' uword into a 16 bits register, just assign it
return assignExpressionToRegister(value, target.register!!, targetDt.isSigned)
}
in combinedLongRegisters -> TODO("assign wprd to long reg ${value.position}")
RegisterOrPair.R0R1_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R0, false)
asmgen.signExtendLongVariable("cx16.r0", valueDt.base)
}
RegisterOrPair.R2R3_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R2, false)
asmgen.signExtendLongVariable("cx16.r2", valueDt.base)
}
RegisterOrPair.R4R5_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R4, false)
asmgen.signExtendLongVariable("cx16.r4", valueDt.base)
}
RegisterOrPair.R6R7_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R6, false)
asmgen.signExtendLongVariable("cx16.r6", valueDt.base)
}
RegisterOrPair.R8R9_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R8, false)
asmgen.signExtendLongVariable("cx16.r8", valueDt.base)
}
RegisterOrPair.R10R11_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R10, false)
asmgen.signExtendLongVariable("cx16.r10", valueDt.base)
}
RegisterOrPair.R12R13_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R12, false)
asmgen.signExtendLongVariable("cx16.r12", valueDt.base)
}
RegisterOrPair.R14R15_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R14, false)
asmgen.signExtendLongVariable("cx16.r14", valueDt.base)
}
else -> {}
}
}
@@ -2672,7 +2775,7 @@ $endLabel""")
// long to word, just take the lsw
assignCastViaLswFunc(value, target)
} else
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker")
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker ${value.position}")
}
}
}
@@ -2812,7 +2915,8 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName")
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("weird type")
BaseDataType.LONG -> TODO("cast float to long")
else -> throw AssemblyError("weird type $targetDt")
}
}
@@ -2842,6 +2946,7 @@ $endLabel""")
else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2878,6 +2983,7 @@ $endLabel""")
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
@@ -2911,6 +3017,7 @@ $endLabel""")
BaseDataType.WORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2950,6 +3057,7 @@ $endLabel""")
BaseDataType.UWORD, BaseDataType.POINTER -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2979,6 +3087,7 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
BaseDataType.LONG -> TODO("cast float to long")
else -> throw AssemblyError("weird type")
}
}
@@ -3026,6 +3135,7 @@ $endLabel""")
else
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
st${regs.toString().lowercase()} $targetAsmVarName
@@ -3073,6 +3183,7 @@ $endLabel""")
asmgen.signExtendAYlsb(sourceDt)
asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
asmgen.signExtendLongVariable(targetAsmVarName, sourceDt)
@@ -3108,6 +3219,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs")
}
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3146,6 +3258,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs")
}
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3479,7 +3592,7 @@ $endLabel""")
else -> throw AssemblyError("wrong dt ${target.position}")
}
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLongVar(target.pointer!!, varName)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -3662,11 +3775,11 @@ $endLabel""")
pointergen.assignFloatAY(IndexedPtrTarget(target))
return
}
asmgen.out(" pha")
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.A)
asmgen.assignExpressionToVariable(target.array.index, "P8ZP_SCRATCH_REG", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" pla")
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out("""
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
@@ -3674,6 +3787,7 @@ $endLabel""")
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_REG
jsr floats.set_array_float""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
@@ -3984,7 +4098,18 @@ $endLabel""")
else throw AssemblyError("only combined vreg allowed as long target ${target.position}")
}
TargetStorageKind.ARRAY -> {
TODO("assign 32 bits int into array ${target.position}")
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
val arrayVarName = asmgen.asmSymbolName(target.array.variable!!)
val startreg = pairedRegisters.startregname()
asmgen.out("""
lda cx16.$startreg
sta $arrayVarName,y
lda cx16.$startreg+1
sta $arrayVarName+1,y
lda cx16.$startreg+2
sta $arrayVarName+2,y
lda cx16.$startreg+3
sta $arrayVarName+3,y""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> {
@@ -4004,7 +4129,10 @@ $endLabel""")
sta cx16.$targetStartReg+3""")
}
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> {
val startreg = pairedRegisters.startregname()
pointergen.assignLongVar(target.pointer!!, "cx16.$startreg")
}
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -4499,7 +4627,7 @@ $endLabel""")
stz $startreg+2
stz $startreg+3""")
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLong(target.pointer!!, 0)
TargetStorageKind.VOID -> { /* do nothing */ }
}
return
@@ -4514,7 +4642,7 @@ $endLabel""")
asmgen.out(" lda #$$hexbyte | sta ${target.asmVarname}+$offset")
}
}
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
store(hex.substring(6,8), 0)
store(hex.substring(4,6), 1)
store(hex.substring(2,4), 2)
@@ -4527,7 +4655,7 @@ $endLabel""")
return
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
asmgen.out("""
lda #$${hex.substring(6,8)}
sta ${target.asmVarname},y
@@ -4542,7 +4670,7 @@ $endLabel""")
TargetStorageKind.REGISTER -> {
require(target.register in combinedLongRegisters)
val regstart = target.register!!.startregname()
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
asmgen.out("""
lda #$${hex.substring(6,8)}
sta cx16.$regstart
@@ -4553,7 +4681,7 @@ $endLabel""")
lda #$${hex.take(2)}
sta cx16.$regstart+3""")
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLong(target.pointer!!, long)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -5309,7 +5437,7 @@ $endLabel""")
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register negate ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> TODO("long negate via pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}

View File

@@ -4,6 +4,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
@@ -859,7 +860,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
inc $variable+3
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
clc
lda $variable
@@ -921,7 +922,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
dec $variable+3
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
sec
lda $variable
@@ -943,7 +944,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<<" -> if (value > 0) inplaceLongShiftLeft()
">>" -> if (value > 0) inplaceLongShiftRight()
"|" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
ora #$${hex.substring(6,8)}
@@ -959,7 +960,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"&" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
and #$${hex.substring(6,8)}
@@ -975,7 +976,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"^" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
eor #$${hex.substring(6,8)}

View File

@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
import kotlin.math.log2
@@ -248,8 +249,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
asmgen.loadIndirectFloat(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else if(value.type.isLong)
TODO("load long ${value.position}")
else if(value.type.isLong) {
asmgen.loadIndirectLongIntoR14R15(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.R14R15_32, target)
}
else
throw AssemblyError("weird dt ${value.type} in pointer deref assignment ${target.position}")
}
@@ -298,29 +301,34 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
"<<" -> {
if(target.dt.isByte) inplaceByteShiftLeft(target, value)
else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
else if(target.dt.isLong) TODO("inplace long << ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
">>" -> {
if(target.dt.isByte) inplaceByteShiftRight(target, value)
else if(target.dt.isWord) inplaceWordShiftRight(target, value)
else if(target.dt.isLong) TODO("inplace long >> ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
"&", "and" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
else if(target.dt.isWord) inplaceWordAnd(target, value)
else if(target.dt.isLong) inplaceLongAnd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"|", "or" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteOr(target, value)
else if(target.dt.isWord) inplaceWordOr(target, value)
else if(target.dt.isLong) inplaceLongOr(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"^", "xor" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteXor(target, value)
else if(target.dt.isWord) inplaceWordXor(target, value)
else if(target.dt.isLong) inplaceLongXor(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
@@ -454,6 +462,42 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
TODO("array ptr assign long var ${target.position}")
}
internal fun assignLongVar(pointer: PtPointerDeref, varName: String) {
val (ptrVar, offset) = deref(pointer)
asmgen.out("""
ldy #$offset
lda $varName
sta ($ptrVar),y
iny
lda $varName+1
sta ($ptrVar),y
iny
lda $varName+2
sta ($ptrVar),y
iny
lda $varName+3
sta ($ptrVar),y""")
}
internal fun assignLong(pointer: PtPointerDeref, long: Int) {
val (ptrVar, offset) = deref(pointer)
val hex = long.toLongHex()
asmgen.out("""
ldy #$offset
lda #$${hex.substring(6,8)}
sta ($ptrVar),y
iny
lda #$${hex.substring(4, 6)}
sta ($ptrVar),y
iny
lda #$${hex.substring(2, 4)}
sta ($ptrVar),y
iny
lda #$${hex.take(2)}
sta ($ptrVar),y""")
}
internal fun operatorDereference(binExpr: PtBinaryExpression): Triple<String, UByte, DataType> {
// the only case we support here is: a.b.c[i] . value
// returns the ZP var to use as a pointer, and a Y register offset (which can be zero), and finally the datatype of the field
@@ -948,19 +992,141 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
private fun inplaceLongAdd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long add with ${value.number} ${target.position}")
SourceStorageKind.VARIABLE -> TODO("inplace long add with ${value.asmVarname} ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("inplace long add with ${value.expression} ${target.position}")
SourceStorageKind.LITERALNUMBER -> {
val hex = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc #$${hex.substring(6, 8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc ${value.asmVarname}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
// it's not an expression so no need to preserve R14:R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+3
sta ($zpPtrVar),y""")
}
else -> TODO("inplace long add with ${value.kind} ${target.position}")
}
}
private fun inplaceLongSub(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long sub with ${value.number} ${target.position}")
SourceStorageKind.VARIABLE -> TODO("inplace long sub with ${value.asmVarname} ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("inplace long sub with ${value.expression} ${target.position}")
SourceStorageKind.LITERALNUMBER -> {
val hex = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc #$${hex.substring(6, 8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc ${value.asmVarname}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
// it's not an expression so no need to preserve R14:R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+3
sta ($zpPtrVar),y""")
}
else -> TODO("inplace long sub with ${value.kind} ${target.position}")
}
}
@@ -1621,7 +1787,220 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
}
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
private fun inplaceLongAnd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long &= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceLongOr(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long |= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceLongXor(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long ^= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
internal fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
// val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
// assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
@@ -1692,7 +2071,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
pha""")
}
in combinedLongRegisters -> {
TODO("save on stack long register pair")
TODO("save on stack long register pair - do we really want to do this?")
}
else -> asmgen.saveRegisterStack(regs.asCpuRegister(), false)
}
@@ -1727,7 +2106,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
sta $regname""")
}
in combinedLongRegisters -> {
TODO("restore from stack long register")
TODO("restore from stack long register - do we really want to do this?")
}
else -> asmgen.restoreRegisterStack(regs.asCpuRegister(), false)
}

View File

@@ -17,7 +17,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
"sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(call)
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore

View File

@@ -244,8 +244,16 @@ class VarConstantValueTypeAdjuster(
dt.isUnsignedByte -> "sqrt__ubyte"
dt.isUnsignedWord -> "sqrt__uword"
dt.isFloat -> "sqrt__float"
dt.isLong -> {
val value = functionCallExpr.args[0].constValue(program)?.number
if(value!=null && value<0) {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications
}
"sqrt__long"
}
else -> {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
errors.err("expected numeric argument", functionCallExpr.args[0].position)
return noModifications
}
}

View File

@@ -406,6 +406,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
%asm {{
jsr cbm.SCREEN
inx
txa
rts
}}
@@ -415,11 +416,22 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
%asm {{
jsr cbm.SCREEN
iny
tya
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr cbm.SCREEN
inx
iny
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN

View File

@@ -424,6 +424,14 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN

View File

@@ -818,8 +818,13 @@ io_error:
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb are truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> long {
&long banksize = &cx16.r12
banksize = cx16.getrambank() - startbank
banksize <<= 13 ; * 8192
banksize += endaddress
banksize -= startaddress
return banksize
}
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {

View File

@@ -615,6 +615,13 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN

View File

@@ -236,6 +236,17 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr width
tax
jsr height
tay
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN

View File

@@ -21,6 +21,11 @@ sub height() -> ubyte {
}}
}
sub size() -> ubyte, ubyte {
; -- returns the text screen width and height (number of columns and rows)
return width(), height()
}
sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H"
%ir {{

View File

@@ -20,7 +20,19 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
"sqrt__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) {
val value=it.toDouble()
if(value<0)
throw CannotEvaluateException("sqrt", "argument cannot be negative")
else
sqrt(value)
} },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) {
if(it<0)
throw CannotEvaluateException("sqrt", "argument cannot be negative")
else
sqrt(it)
} },
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
"lsb__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
"lsw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 65535).toDouble() } },
@@ -71,11 +83,14 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position)
} else {
if(constval.type!=BaseDataType.UBYTE && constval.type!=BaseDataType.UWORD)
if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position)
}
val integer = constval.number.toInt()
return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position)
val result = function(integer)
if(result.isNaN())
throw CannotEvaluateException("built-in function", "result is NaN $position")
return NumericLiteral.optimalInteger(result.toInt(), args[0].position)
}
private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Double): NumericLiteral {
@@ -84,8 +99,10 @@ private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, p
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type != BaseDataType.FLOAT)
throw SyntaxError("built-in function requires one float argument", position)
return NumericLiteral(BaseDataType.FLOAT, function(constval.number), args[0].position)
val result = function(constval.number)
if(result.isNaN())
throw CannotEvaluateException("built-in function", "result is NaN $position")
return NumericLiteral(BaseDataType.FLOAT, result, args[0].position)
}
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteral {

View File

@@ -34,8 +34,14 @@ internal fun Program.reorderStatements(options: CompilationOptions, errors: IErr
internal fun Program.changeNotExpressionAndIfComparisonExpr(errors: IErrorReporter, target: ICompilationTarget) {
val changer = NotExpressionAndIfComparisonExprChanger(this, errors, target)
changer.visit(this)
while(errors.noErrors() && changer.applyModifications()>0) {
changer.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && changer.applyModifications() > 0)
changer.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("changeNotExpressionAndIfComparisonExpr() is looping endlessly")
}
}
@@ -93,8 +99,15 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
val desugar = CodeDesugarer(this, options.compTarget, errors)
desugar.visit(this)
while(errors.noErrors() && desugar.applyModifications()>0)
desugar.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && desugar.applyModifications() > 0)
desugar.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("desugaring() is looping endlessly")
}
}
internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter, options: CompilationOptions) {
@@ -121,16 +134,30 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
if(errors.noErrors()) {
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
lit2decl.visit(this)
while(errors.noErrors() && lit2decl.applyModifications()>0)
lit2decl.visit(this)
for(numCycles in 0..1000) {
if(errors.noErrors() && lit2decl.applyModifications() > 0)
lit2decl.visit(this)
else
break
if(numCycles==1000) {
throw InternalCompilerException("checkIdentifiers() is looping endlessly - check for circular aliases")
}
}
}
}
internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
val process = VariousCleanups(this, errors, options)
process.visit(this)
while(errors.noErrors() && process.applyModifications()>0) {
process.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && process.applyModifications() > 0)
process.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("variousCleanups() is looping endlessly")
}
}

View File

@@ -44,6 +44,9 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
override fun visit(alias: Alias) {
if(alias.target.targetStatement(program.builtinFunctions)==null)
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
if(alias.alias == alias.target.nameInSource.first())
errors.err("alias references itself", alias.position)
}
override fun visit(block: Block) {

View File

@@ -132,7 +132,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
expr))
}
if(rightCv!=null && rightCv.number<0) {
val value = if(leftDt.isBytes) 256+rightCv.number else if(leftDt.isWords) 65536+rightCv.number else 0xffffffffL+rightCv.number
val value = if(leftDt.isBytes) 256+rightCv.number else if(leftDt.isWords) 65536+rightCv.number else (0x100000000L+rightCv.number).toLong().toInt().toDouble()
return listOf(IAstModification.ReplaceNode(
expr.right,
NumericLiteral(leftDt.getOrUndef().base, value, expr.right.position),

View File

@@ -662,6 +662,50 @@ main {
err[0] shouldContain("15:16: incompatible value type, can only assign uword or correct pointer")
}
xtest("pointer uword assignments with cast") {
val src="""
main {
struct Node1 {
ubyte type
word ww
}
struct Node2 {
ubyte type
^^ubyte text
}
sub start() {
^^Node1 @shared next
^^Node2 @shared this
^^ubyte ubptr
ubptr = next as ^^ubyte
ubptr = 12345
ubptr = cx16.r0
ubptr = next as uword ; TODO fix type error; the cast should succeed
this.text = next as ^^ubyte
this.text = 12345
this.text = cx16.r0
this.text = next as uword ; TODO fix type error; the cast should succeed
}
}"""
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
xtest("const pointer") {
val src="""
main {
sub start() {
^^uword @shared ptr = 7000
const ^^uword cptr = 8000 ; TODO fix type error; loses pointer
ptr^^ = 12345
cptr^^ = 12345
}
}"""
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("unknown field") {
val src="""
main {
@@ -1258,7 +1302,7 @@ main {
errors.errors[3] shouldContain "assigning this value to struct instance not supported"
}
xtest("assigning struct instances") {
test("assigning struct instances") {
val src="""
main {
sub start() {
@@ -1273,16 +1317,26 @@ main {
lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11)
lp2[2] = lp1^^ ; memcopy(lp1, lp2 + 22, 11)
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above) TODO fix astchecker to allow this case
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above)
lp2^^ = lp1[2] ; memcopy(lp1 + 22, lp2, 11)
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above) TODO fix astchecker to allow this case
lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above)
;;; lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 99
st.size shouldBe 11
val memcopy1 = st[5] as FunctionCallStatement
val memcopy2 = st[6] as FunctionCallStatement
val memcopy3 = st[7] as FunctionCallStatement
val memcopy4 = st[8] as FunctionCallStatement
val memcopy5 = st[9] as FunctionCallStatement
memcopy1.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy2.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy3.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy4.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy5.target.nameInSource shouldBe listOf("sys", "memcopy")
}
test("a.b.c[i]^^.value as expression where pointer is struct") {
@@ -2548,4 +2602,24 @@ main {
be6.right shouldBe instanceOf<IdentifierReference>()
(a7.value as? ArrayIndexedExpression)?.indexer?.constIndex() shouldBe 0
}
test("long pointer assignments") {
val src="""
main {
sub start() {
long lvar = 999999
^^long lptr = 5000
pokel(5000, 11223344)
lptr^^ = 0
lptr^^ = lvar
lptr^^ = 82348234+lvar
lvar = lptr^^+1111111
}
}"""
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
})

View File

@@ -17,13 +17,14 @@ import prog8.code.core.BaseDataType
import prog8.code.core.Position
import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
class TestConst: FunSpec({
val outputDir = tempdir().toPath()
test("const folding multiple scenarios +/-") {
val source = """
main {
@@ -224,7 +225,7 @@ class TestConst: FunSpec({
}
test("const pointer variable indexing works") {
val src="""
val src = """
main {
sub start() {
const uword pointer=$1000
@@ -233,11 +234,11 @@ main {
}
}
"""
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
compileText(C64Target(), optimize = false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("advanced const folding of known library functions") {
val src="""
val src = """
%import floats
%import math
%import strings
@@ -260,7 +261,7 @@ main {
}
test("const address-of memory mapped arrays") {
val src= """
val src = """
main {
sub start() {
&uword[30] @nosplit wb = $2000
@@ -277,10 +278,10 @@ main {
st.size shouldBe 7
((st[0] as VarDecl).value as NumericLiteral).number shouldBe 0x2000
((st[1] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00
((st[2] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00+2*30
((st[2] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
((st[3] as Assignment).value as NumericLiteral).number shouldBe 0x9e00
((st[4] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30
((st[5] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30
((st[4] as Assignment).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
((st[5] as Assignment).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
}
test("address of a memory mapped variable") {
@@ -296,7 +297,7 @@ main {
&uword[20] @shared @nosplit wa = HIGH_MEMORY_START
}
}"""
val result = compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true)!!
val result = compileText(Cx16Target(), optimize = false, src, outputDir, writeAssembly = true)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 7
val arrayDeclV = (st[2] as VarDecl).value
@@ -306,7 +307,7 @@ main {
}
test("address of a const uword pointer array expression") {
val src= """
val src = """
main {
sub start() {
const uword buffer = 2000
@@ -328,7 +329,7 @@ main {
}
test("out of range const byte and word give correct error") {
var src="""
var src = """
main {
sub start() {
const byte MIN_BYTE = -129
@@ -339,7 +340,7 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain "out of range"
errors.errors[1] shouldContain "out of range"
@@ -348,7 +349,7 @@ main {
}
test("out of range var byte and word give correct error") {
var src="""
var src = """
main {
sub start() {
byte @shared v_MIN_BYTE = -129
@@ -359,7 +360,7 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 8
errors.errors[0] shouldContain "out of range"
errors.errors[2] shouldContain "out of range"
@@ -368,7 +369,7 @@ main {
}
test("out of range const byte and word no errors with explicit cast if possible") {
var src="""
var src = """
main {
sub start() {
const byte MIN_BYTE = -129 as byte ; still error
@@ -379,16 +380,16 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain(":4:31: const declaration needs a compile-time constant")
errors.errors[1] shouldContain(":4:32: no cast available")
errors.errors[2] shouldContain(":5:31: const declaration needs a compile-time constant")
errors.errors[3] shouldContain(":5:32: no cast available")
errors.errors[0] shouldContain (":4:31: const declaration needs a compile-time constant")
errors.errors[1] shouldContain (":4:32: no cast available")
errors.errors[2] shouldContain (":5:31: const declaration needs a compile-time constant")
errors.errors[3] shouldContain (":5:32: no cast available")
}
test("out of range var byte and word no errors with explicit cast if possible") {
var src="""
var src = """
main {
sub start() {
byte @shared v_min_byte2 = -129 as byte ; still error
@@ -399,14 +400,14 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain(":4:37: no cast available")
errors.errors[1] shouldContain(":5:37: no cast available")
errors.errors[0] shouldContain (":4:37: no cast available")
errors.errors[1] shouldContain (":5:37: no cast available")
}
test("const evaluation of signed bitwise operations") {
val src="""
val src = """
main {
sub start() {
byte @shared a = -1
@@ -455,7 +456,7 @@ main {
}
test("const long with small values") {
val src="""
val src = """
main {
sub start() {
const long notkaputt = 42
@@ -464,4 +465,34 @@ main {
}"""
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("const long with large unsigned long values should be converted to signed longs") {
val src = $$"""
main {
sub start() {
long @shared l1 = $e1fa84c6
long @shared l2 = -1
long @shared l3 = $ffffffff
long @shared l4 = $7fffffff
l1 ^= -1
l2 ^= $ffffffff
l3 ^= $7fffffff
}
}"""
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 12
val a = st.filterIsInstance<Assignment>()
(a[0].value as NumericLiteral).number shouldBe -503675706.0
(a[1].value as NumericLiteral).number shouldBe -1.0
(a[2].value as NumericLiteral).number shouldBe -1.0
(a[3].value as NumericLiteral).number shouldBe 0x7fffffffL.toDouble()
((a[4].value as BinaryExpression).right as NumericLiteral).number shouldBe -1.0
((a[5].value as BinaryExpression).right as NumericLiteral).number shouldBe -1.0
((a[6].value as BinaryExpression).right as NumericLiteral).number shouldBe 0x7fffffffL.toDouble()
}
})

View File

@@ -126,6 +126,24 @@ txt {
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
}
test("infinite alias loop detected") {
val src="""
main {
sub start() {
alias vv = vv
alias xx = xx.yy
alias zz = mm
alias mm = zz
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "references itself"
errors.errors[1] shouldContain "undefined symbol: xx.yy"
errors.errors[2] shouldContain "references itself"
}
test("wrong alias gives correct error") {
val src="""
main {

View File

@@ -385,6 +385,13 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
else -> throw FatalAstException(terminal.text)
}
// TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast
if(integer.second.isLong && integer.first > Integer.MAX_VALUE) {
val signedLong = integer.first.toLong().toInt()
return NumericLiteral(integer.second, signedLong.toDouble(), ctx.toPosition())
}
return NumericLiteral(integer.second, integer.first, ctx.toPosition())
}

View File

@@ -825,6 +825,7 @@ txt {
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers (A,Y)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
uppercase ()
waitkey () -> ubyte @A

View File

@@ -938,6 +938,7 @@ txt {
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers (A,Y)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
uppercase ()
waitkey () -> ubyte @A

View File

@@ -240,7 +240,7 @@ diskio {
list_filenames (str pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte
load (str filenameptr, uword address_override) -> uword
load_raw (str filenameptr, uword startaddress) -> uword
load_size (ubyte startbank, uword startaddress, uword endaddress) -> uword
load_size (ubyte startbank, uword startaddress, uword endaddress) -> long
loadlib (str libnameptr, uword libaddress) -> uword
mkdir (str name)
read4hex () -> uword
@@ -1422,6 +1422,7 @@ txt {
setcc2 (ubyte col, ubyte row, ubyte character, ubyte colors)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A)
setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers (A)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
t256c (bool enable)
uppercase ()

View File

@@ -540,6 +540,7 @@ txt {
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
setclr (ubyte col, ubyte row, ubyte color)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
uppercase ()
waitkey () -> ubyte @A

View File

@@ -559,6 +559,7 @@ txt {
rvs_off ()
rvs_on ()
setchr (ubyte col, ubyte row, ubyte char)
size () -> ubyte, ubyte
spc ()
uppercase ()
width () -> ubyte

View File

@@ -16,11 +16,12 @@ Then you can choose a few ways to get a compiler:
#. download a recent "fat-jar" (called something like "prog8c-all.jar") from `the releases on Github <https://github.com/irmen/prog8/releases>`_
#. run the compiler with "java -jar prog8c.jar" to see how you can use it (use the correct name and version of the jar file you've downloaded).
**Or, install via a Package Manager:**
**Or, install via a Package Manager (takes care of dependencies for you):**
Arch Linux:
Currently, it's available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and compatible systems.
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_.
Currently, it's available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and derivative systems.
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_. There should be no need to install aything else as
it can automatically pull in the required dependencies.
This package, alongside the compiler itself, also globally installs syntax highlighting for ``vim`` and ``nano``.
In order to run compiler, you can type ``prog8c``. The usage of those commands is exactly the same as with the ``java -jar`` method.

View File

@@ -69,7 +69,7 @@ Language Features
- it is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
- the compiled programs run very fast, because compilation to highly efficient native machine code.
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- compiled code is very compact; it is much smaller and usually also runs faster than equivalent C code compiled with CC65
- provides a convenient and fast edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- the language looks like a mix of Python and C so should be quite easy to learn
@@ -78,7 +78,7 @@ Language Features
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Variables are all allocated statically, no memory allocation overhead
- Variable data types include signed and unsigned bytes and words, arrays, strings.
- Variable data types include signed and unsigned bytes and words, long integers, floats, arrays, and strings.
- Structs and typed pointers
- Tight control over Zeropage usage
- Programs can be restarted after exiting (i.e. run them multiple times without having to reload everything), due to automatic variable (re)initializations.

View File

@@ -58,9 +58,9 @@ sgn (x)
Get the sign of the value (integer or floating point).
The result is a byte: -1, 0 or 1 (negative, zero, positive).
sqrt (w)
sqrt (x)
Returns the square root of the number.
Supports unsigned integer (result is ubyte) and floating point numbers.
Accepts unsigned integer (result is ubyte), long (result is uword, but this may not be implemented on all targets), and floating point numbers.
To do the reverse - squaring a number - just write ``x*x``.
divmod (dividend, divisor, quotient, remainder)

View File

@@ -918,6 +918,7 @@ You can also reference identifiers defined elsewhere in your code.
of precision though and gives an error if you may be losing a floating point result.
.. _arithmetic:
Arithmetic and Logical expressions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -955,6 +956,7 @@ and ``(true or false) and false`` is false instead of true.
byte @shared b = 44
w = (b as word)*55
w = b*(55 as word)
w = b * $0037
Operators
@@ -1236,10 +1238,8 @@ Otherwise the compiler will warn you about discarding the result of the call.
Multiple return values
^^^^^^^^^^^^^^^^^^^^^^
Subroutines can return more than one value.
For example, ``asmsub`` routines (implemented in assembly code) or ``extsub`` routines
(referencing an external routine in ROM or elsewhere in RAM) can return multiple values spread
across different registers, and even the CPU's status register flags for boolean values.
Normal subroutines can also return multiple values.
``asmsub`` and ``extsub`` routines return their multiple values spread across different registers,
and can also efficiently use the CPU's status register flags for boolean returnvalues.
You have to "multi assign" all return values of the subroutine call to something:
write the assignment targets as a comma separated list, where the element's order corresponds
to the order of the return values declared in the subroutine's signature.

View File

@@ -1,33 +1,23 @@
TODO
====
- implement the TODO in assignPointerDerefExpression()
- implement inplaceLongAdd/Sub in PointerAssignmentGen to make this compile::
- fix/check github issues.
- redo the benchmark-c tests with final 12.0 release version.
struct Node {
long x
}
^^Node np = 20000
np.x += cx16.r0
STRUCTS and TYPED POINTERS
--------------------------
- implement the remaining TODO's in PointerAssignmentsGen.
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- optimize the float copying in assignIndexedPointer() (also word and long?)
- optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment)
- implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
- support @nosplit pointer arrays?
- support pointer to pointer?
- support for typed function pointers? (&routine could be typed by default as well then)
- really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
- (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- struct/ptr: implement the remaining TODO's in PointerAssignmentsGen.
- struct/ptr: optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- struct/ptr: optimize the float copying in assignIndexedPointer() (also word and long?)
- struct/ptr: optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment)
- struct/ptr: implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
- struct/ptr: support @nosplit pointer arrays?
- struct/ptr: support pointer to pointer?
- struct/ptr: support for typed function pointers? (&routine could be typed by default as well then)
- struct/ptr: really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
- struct/ptr: (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
- make memory mapped variables support more constant expressions such as: &uword MyHigh = &mylong1+2
- fix the line, cols in Position, sometimes they count from 0 sometimes from 1, should both always be 1-based (is this the reason some source lines end up missing in the IR file?)
@@ -51,6 +41,7 @@ Future Things and Ideas
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
- detect circular aliases and print proper error message for them
- Improve register load order in subroutine call args assignments:
in certain situations (need examples!), the "wrong" order of evaluation of function call arguments is done which results
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
@@ -185,6 +176,8 @@ Optimizations
- optimize optimizedBitwiseExpr() for const and variable operands to not assign needlessly to R0-R3.
- optimize inplacemodificationLongWithLiteralval() for more shift values such as 8, 16, 24 etc but take sign bit into account!
- optimize simple cases in funcPeekL and funcPokeL
- bind types in the Ast much sooner than the simplifiedAst creation, so that we maybe could get rid of InferredType ?
- longvar = lptr^^ now goes via temporary registers, optimize this to avoid using temps. Also check lptr^^ = lvar.
- Port benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up.
- Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappeared. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again?
- in Identifier: use typedarray of strings instead of listOf? Other places?

View File

@@ -228,9 +228,10 @@ value datatype
-2147483647 .. 2147483647 long (there is no unsigned long right now)
========================= =================
Numeric expressions usually 'stay within their type' unless a cast is used, see :ref:`arithmetic`.
If the number fits in a byte but you really require it as a word value, you'll have to explicitly cast it: ``60 as uword``
or you can use the full word hexadecimal notation ``$003c``. This is useful in expressions where you want a calcuation
to be done on word values, and don't want to explicitly have to cast everything all the time. For instance::
or you can use the full word hexadecimal notation ``$003c``. This is useful in expressions where you want a calculation
to be done on word values, and don't want to explicitly have to cast everything all the time. For instance::
ubyte column
uword offset = column * 64 ; does (column * 64) as uword, wrong result?
@@ -246,7 +247,7 @@ to be done on word values, and don't want to explicitly have to cast everything
operations should work fine. Notably absent for now are multiplication and division of longs.
There is no unsigned long type at the moment, but you can sometimes simply treat the signed
long value as an unsigned 32 bits value just fine.
Operations on long integers take a lot of instructions on 8 bit cpu's so code that uses them
Operations on long integers take a lot of instructions on 8 bit CPUs so code that uses them
a lot will be much slower than when you restrict yourself to 8 or 16 bit values. Use long values sparingly.
.. danger::

View File

@@ -412,20 +412,28 @@ asmsub waitkey() -> ubyte @A {
asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
; TODO
%asm {{
lda #0
lda #DEFAULT_WIDTH
rts
}}
}
asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
; TODO
%asm {{
lda #0
lda #DEFAULT_HEIGHT
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
ldx #DEFAULT_WIDTH
ldy #DEFAULT_HEIGHT
rts
}}
}
}

View File

@@ -417,7 +417,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ X) {
asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
%asm {{
lda DEFAULT_WIDTH
lda #DEFAULT_WIDTH
rts
}}
}
@@ -425,11 +425,21 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
%asm {{
lda DEFAULT_HEIGHT
lda #DEFAULT_HEIGHT
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
ldx #DEFAULT_HEIGHT
ldy #DEFAULT_HEIGHT
rts
}}
}
; TODO: jmp to cbm.CHRIN?
asmsub waitkey() -> ubyte @A {
%asm {{

View File

@@ -12,26 +12,26 @@ main{
}
^^Country[] countries = [
^^Country:["Indonesia", 285.72, 1904],
^^Country:["Congo", 112.83, 2344],
^^Country:["Vietnam", 101.60, 331],
^^Country:["United States", 347.28, 9372],
^^Country:["Iran", 92.42, 1648],
^^Country:["Turkey", 87.69, 783],
^^Country:["Brazil", 212.81, 8515],
^^Country:["Bangladesh", 175.69, 147],
^^Country:["Germany", 84.08, 357],
^^Country:["Japan", 123.10, 377],
^^Country:["India", 1463.87, 3287],
^^Country:["China", 1416.10, 9596],
^^Country:["Philippines", 116.79, 300],
^^Country:["Russia", 143.99, 17098],
^^Country:["Pakistan", 255.22, 881],
^^Country:["Nigeria", 237.53, 923],
^^Country:["Ethiopia", 135.47, 1104],
^^Country:["Mexico", 131.95, 1964],
^^Country:["Thailand", 71.62, 513],
^^Country:["Egypt", 118.37, 1002],
["Indonesia", 285.72, 1904],
["Congo", 112.83, 2344],
["Vietnam", 101.60, 331],
["United States", 347.28, 9372],
["Iran", 92.42, 1648],
["Turkey", 87.69, 783],
["Brazil", 212.81, 8515],
["Bangladesh", 175.69, 147],
["Germany", 84.08, 357],
["Japan", 123.10, 377],
["India", 1463.87, 3287],
["China", 1416.10, 9596],
["Philippines", 116.79, 300],
["Russia", 143.99, 17098],
["Pakistan", 255.22, 881],
["Nigeria", 237.53, 923],
["Ethiopia", 135.47, 1104],
["Mexico", 131.95, 1964],
["Thailand", 71.62, 513],
["Egypt", 118.37, 1002],
]
sub start() {

View File

@@ -1,41 +1,135 @@
;%zeropage basicsafe
;%import textio
;
;main {
; sub start() {
; ^^uword @shared ptr = 7000
; const ^^uword cptr = 8000 ; TODO fix type error; loses pointer
; ptr^^ = 12345
; cptr^^ = 12345
; txt.print_uw(peekw(7000))
; txt.spc()
; txt.print_uw(peekw(8000))
; }
;}
; TYPE CAST CRASH:
;%zeropage basicsafe
;%import strings
;%import textio
;
;main {
; struct Line {
; ^^Line prev
; ^^Line next
; ^^ubyte text
; }
; uword buffer = memory("buffer", 100*sizeof(Line), 1)
; ^^Line next = buffer
;
; sub start() {
; ^^Line line = next
; next += 1
; line.text = next as ^^ubyte ; TODO fix crash here
; next = (next as uword) + 81
; txt.print_uwhex(buffer, true) txt.nl()
; txt.print_uwhex(next, true) txt.nl()
; txt.print_uwhex(line, true) txt.nl()
; txt.print_uwhex(line.text, true) txt.nl()
; }
;}
%import floats
%import textio
%import math
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
long @shared lv, lv2
bytetest()
wordtest()
longtest()
floattest() ; TODO fix invalid 6502 code gen /crash
}
lv = $11111111
lv2 = $55555555
sub bytetest() {
ubyte[] foo = [11, 22, 33]
ubyte i
txt.print("before:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_ub(foo[i])
}
txt.nl()
foo[2] = foo[1]
foo[1] = foo[0]
foo[0] = 0
txt.print(" after:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_ub(foo[i])
}
txt.nl()
}
lv = lv | lv2 ^ 999999
sub wordtest() {
uword[] foo = [1111, 2222, 3333]
ubyte i
txt.print("before:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_uw(foo[i])
}
txt.nl()
foo[2] = foo[1]
foo[1] = foo[0]
foo[0] = 0
txt.print(" after:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_uw(foo[i])
}
txt.nl()
}
txt.print_ulhex(lv, true) ; $555b177b
sub longtest() {
long[] foo = [111111, 222222, 333333]
ubyte i
txt.print("before:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_l(foo[i])
}
txt.nl()
foo[2] = foo[1]
foo[1] = foo[0]
foo[0] = 0
txt.print(" after:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_l(foo[i])
}
txt.nl()
}
; long @shared lv1 = $12345678
;
; txt.print_ubhex(msb(lv1), true)
; txt.spc()
; txt.print_ubhex(lsb(lv1), true)
; txt.spc()
; txt.print_ubhex(lv1 as ubyte, true)
; txt.nl()
; txt.print_uwhex(msw(lv1), true)
; txt.spc()
; txt.print_uwhex(lsw(lv1), true)
; txt.nl()
; ; TODO support long+1 / -1 expressions....
; cx16.r4 = msw(lv1-1)
; cx16.r5 = lsw(lv1-1)
; txt.print_uwhex(cx16.r4, true)
; txt.print_uwhex(cx16.r5, true)
; txt.nl()
;
; txt.print_ubhex((lv1-1) as ubyte, true)
; txt.nl()
sub floattest() {
float[] foo = [1.1, 2.2, 3.3]
ubyte i
txt.print("before:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_f(foo[i])
}
txt.nl()
foo[2] = foo[1]
foo[1] = foo[0]
foo[0] = 0
txt.print(" after:")
for i in 0 to 2 {
txt.chrout(' ')
txt.print_f(foo[i])
}
txt.nl()
}
}

View File

@@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.configuration-cache=false
kotlin.code.style=official
version=12.0-BETA5
version=12.0-SNAPSHOT

View File

@@ -167,7 +167,7 @@ modr reg1, reg2 - remainder (modulo) of unsigned div
mod reg1, value - remainder (modulo) of unsigned division reg1 %= value note: division by zero yields max signed int $ff/$ffff
divmodr reg1, reg2 - unsigned division reg1/reg2, storing division and remainder on value stack (so need to be POPped off)
divmod reg1, value - unsigned division reg1/value, storing division and remainder on value stack (so need to be POPped off)
sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be l.1, .w or .b, result type in reg1 is .w or .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
square reg1, reg2 - reg1 is the square of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
sgn reg1, reg2 - reg1.b is the sign of reg2 (or fpreg1, if sgn.f) (0.b, 1.b or -1.b)
cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction)
@@ -1102,6 +1102,8 @@ data class IRInstruction(
if(type==IRDataType.LONG) {
if(opcode==Opcode.SGN)
return IRDataType.BYTE
if(opcode==Opcode.SQRT)
return IRDataType.WORD
}
if(opcode==Opcode.JUMPI || opcode==Opcode.CALLI || opcode==Opcode.STOREZI || opcode==Opcode.LSIGW || opcode==Opcode.MSIGW)
return IRDataType.WORD

View File

@@ -583,7 +583,7 @@ class VirtualMachine(irProgram: IRProgram) {
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
val value = memory.getSL(pointer.toInt())
registers.setSL(i.reg1!!, value)
statusbitsNZ(value.toInt(), i.type!!)
statusbitsNZ(value, i.type!!)
}
IRDataType.FLOAT -> {
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!)
@@ -1444,7 +1444,12 @@ class VirtualMachine(irProgram: IRProgram) {
when(i.type!!) {
IRDataType.BYTE -> registers.setUB(i.reg1!!, sqrt(registers.getUB(i.reg2!!).toDouble()).toInt().toUByte())
IRDataType.WORD -> registers.setUB(i.reg1!!, sqrt(registers.getUW(i.reg2!!).toDouble()).toInt().toUByte())
IRDataType.LONG -> TODO("long sqrt")
IRDataType.LONG -> {
val value = registers.getSL(i.reg2!!)
if(value<0)
throw IllegalArgumentException("sqrt of negative long $value reg=${i.reg2}")
registers.setSL(i.reg1!!, sqrt(value.toDouble()).toInt())
}
IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, sqrt(registers.getFloat(i.fpReg2!!)))
}
nextPc()
@@ -2526,7 +2531,7 @@ class VirtualMachine(irProgram: IRProgram) {
newStatusCarry = (orig and 0x80000000u) != 0u
val rotated: UInt = (if (useCarry) {
val carry = if (statusCarry) 1u else 0u
(orig.toUInt().rotateLeft(1) or carry)
(orig.rotateLeft(1) or carry)
} else
orig.rotateLeft(1))
registers.setSL(i.reg1!!, rotated.toInt())
@@ -2648,7 +2653,7 @@ class VirtualMachine(irProgram: IRProgram) {
val lsb = registers.getUB(i.reg3!!)
val value = ((msb.toInt() shl 8) or lsb.toInt())
registers.setUW(i.reg1!!, value.toUShort())
statusbitsNZ(value.toInt(), i.type!!)
statusbitsNZ(value, i.type!!)
}
IRDataType.WORD -> {
val msw = registers.getUW(i.reg2!!)
@@ -2959,15 +2964,18 @@ class VirtualMachine(irProgram: IRProgram) {
}
private fun multiplyAnyLongSigned(reg1: Int, reg2: Int) {
TODO("multiplyAnyLongSigned - multiplication and division of long numbers not yet supported, use floats or words")
val result = registers.getSL(reg1) * registers.getSL(reg2)
registers.setSL(reg1, result)
}
private fun multiplyConstLongSigned(reg1: Int, value: Int) {
TODO("multiplyConstLongSigned - multiplication and division of long numbers not yet supported, use floats or words")
val result = registers.getSL(reg1) * value
registers.setSL(reg1, result)
}
private fun multiplyAnyLongSignedInplace(reg1: Int, address: Int) {
TODO("multiplyAnyLongSignedInplace - multiplication and division of long numbers not yet supported, use floats or words")
val result = memory.getSL(address) * registers.getSL(reg1)
memory.setSL(address, result)
}
private fun divModLongSigned(operator: String, reg1: Int, reg2: Int) {