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 - 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 - 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. - 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 - Structs and typed pointers
- floating point math is supported on certain targets - floating point math is supported on certain targets
- access to most Kernal ROM routines as external subroutine definitions you can call normally - 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) - 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 - 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 - 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:* *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" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)), "sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)), "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)), "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" 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)), "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") 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 -> { expr.type.isFloat -> {
if(options.compTarget.FLOAT_MEM_SIZE != 5) if(options.compTarget.FLOAT_MEM_SIZE != 5)
TODO("support float size other than 5 ${expr.position}") TODO("support float size other than 5 ${expr.position}")
@@ -844,7 +853,7 @@ class AsmGen6502Internal (
return return
} }
TargetStorageKind.POINTER -> { TargetStorageKind.POINTER -> {
TODO("assign to pointer ${target.position}") pointerGen.assignByte(PtrTarget(target), 0)
return return
} }
else -> { } else -> { }
@@ -854,7 +863,10 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A) assignExpressionToRegister(value, RegisterOrPair.A)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false) 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 -> { target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY) assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment( translateNormalAssignment(
@@ -866,7 +878,7 @@ class AsmGen6502Internal (
} }
target.datatype.isLong -> { target.datatype.isLong -> {
if(value is PtNumber) { if(value is PtNumber) {
val hex = value.asConstInteger()!!.toString(16).padStart(8, '0') val hex = value.asConstInteger()!!.toLongHex()
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
out(""" out("""
@@ -882,7 +894,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true) 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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} else if(value is PtIdentifier && value.type.isLong) { } else if(value is PtIdentifier && value.type.isLong) {
@@ -905,7 +917,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true) 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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} else if(value is PtTypeCast && value.type.isLong) { } 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.ARRAY -> TODO("assign typecasted long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true) 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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} else if(value.value.type.isWord) { } else if(value.value.type.isWord) {
@@ -945,9 +957,7 @@ class AsmGen6502Internal (
sta cx16.$startreg+1""") sta cx16.$startreg+1""")
signExtendLongVariable("cx16.$startreg", value.value.type.base) signExtendLongVariable("cx16.$startreg", value.value.type.base)
} }
TargetStorageKind.POINTER -> { TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
throw AssemblyError("assign typecasted long into pointer ${target.position}")
}
TargetStorageKind.VOID -> { /* do nothing */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} else throw AssemblyError("weird casted type") } else throw AssemblyError("weird casted type")
@@ -956,6 +966,8 @@ class AsmGen6502Internal (
} }
} else if(target.kind == TargetStorageKind.REGISTER) { } else if(target.kind == TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, true) assignExpressionToRegister(value, target.register!!, true)
} else if(target.kind == TargetStorageKind.VARIABLE) {
assignExpressionToVariable(value, target.asmVarname, target.datatype)
} else { } else {
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now") TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
} }
@@ -1426,6 +1438,9 @@ $repeatLabel""")
sta $asmvar+3""") sta $asmvar+3""")
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
if(isTargetCpu(CpuType.CPU65C02))
out(" stz $asmvar+2 | stz $asmvar+3")
else
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3") out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
} }
BaseDataType.WORD -> { BaseDataType.WORD -> {
@@ -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) { internal fun storeIndirectByte(byte: Int, zpPtrVar: String, offset: UByte) {
if (offset > 0u) { if (offset > 0u) {
out(" lda #$byte | ldy #$offset | sta ($zpPtrVar),y") out(" lda #$byte | ldy #$offset | sta ($zpPtrVar),y")
@@ -2245,7 +2276,7 @@ $repeatLabel""")
} }
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD) val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out(""" out("""
lda $regname lda $regname
ldy #$offset ldy #$offset
@@ -2312,7 +2343,7 @@ $repeatLabel""")
} }
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD) val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out(""" out("""
lda $regname lda $regname
ldy #0 ldy #0
@@ -2498,3 +2529,7 @@ internal class SubroutineExtraAsmInfo {
val extraVars = mutableListOf<Triple<BaseDataType, String, UInt?>>() 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) "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) "abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"sgn" -> funcSgn(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__ubyte" -> funcDivmod(fcall)
"divmod__uword" -> funcDivmodW(fcall) "divmod__uword" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
@@ -416,7 +416,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(arg2.type.isLong) { if(arg2.type.isLong) {
if(arg1 is PtIdentifier && arg2 is PtNumber) { if(arg1 is PtIdentifier && arg2 is PtNumber) {
val var1 = asmgen.asmVariableName(arg1) val var1 = asmgen.asmVariableName(arg1)
val hex = arg2.number.toUInt().toString(16).padStart(8, '0') val hex = arg2.number.toLongHex()
asmgen.out(""" asmgen.out("""
sec sec
lda $var1 lda $var1
@@ -538,7 +538,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable") asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
@@ -557,7 +557,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ") 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 -> { BaseDataType.LONG -> {
@@ -577,7 +577,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
sta $variable+3 sta $variable+3
+""") +""")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
@@ -620,7 +620,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable") asmgen.out(" ror $variable")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
@@ -641,7 +641,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+1 | ror $variable") asmgen.out(" ror $variable+1 | ror $variable")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
BaseDataType.LONG -> { BaseDataType.LONG -> {
@@ -652,7 +652,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+3 | ror $variable+2 | ror $variable+1 | ror $variable") 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") else -> throw AssemblyError("weird type")
@@ -684,7 +684,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable") asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
@@ -703,7 +703,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ") asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
BaseDataType.LONG -> { BaseDataType.LONG -> {
@@ -721,7 +721,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
inc $variable inc $variable
+""") +""")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
@@ -763,7 +763,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable") asmgen.out(" rol $variable")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
@@ -784,7 +784,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1") asmgen.out(" rol $variable | rol $variable+1")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird node")
} }
} }
BaseDataType.LONG -> { BaseDataType.LONG -> {
@@ -795,7 +795,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1 | rol $variable+2 | rol $variable+3") 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") else -> throw AssemblyError("weird type")

View File

@@ -1406,7 +1406,7 @@ _jump jmp (${target.asmLabel})
if(left is PtIdentifier) { if(left is PtIdentifier) {
val leftvar = asmgen.asmVariableName(left) val leftvar = asmgen.asmVariableName(left)
if(constRight!=null) { if(constRight!=null) {
val hex = constRight.toUInt().toString(16).padStart(8, '0') val hex = constRight.toLongHex()
asmgen.out(""" asmgen.out("""
lda $leftvar lda $leftvar
cmp #$${hex.substring(6,8)} 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... // 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) asmgen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
if(constRight!=null) { if(constRight!=null) {
val hex = constRight.toUInt().toString(16).padStart(8, '0') val hex = constRight.toLongHex()
asmgen.out(""" asmgen.out("""
lda cx16.r14 lda cx16.r14
cmp #$${hex.substring(6,8)} cmp #$${hex.substring(6,8)}
@@ -2114,3 +2114,4 @@ _jump jmp (${target.asmLabel})
} }
} }
} }

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
import kotlin.math.log2 import kotlin.math.log2
@@ -201,7 +202,7 @@ internal class AssignmentAsmGen(
assignRegisterByte(tgt, CpuRegister.A, false, false) assignRegisterByte(tgt, CpuRegister.A, false, false)
} }
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) { internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
when(assign.source.kind) { when(assign.source.kind) {
SourceStorageKind.LITERALBOOLEAN -> { SourceStorageKind.LITERALBOOLEAN -> {
// simple case: assign a constant boolean (0 or 1) // 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") asmgen.out(" lda $arrayVarName+$indexValue | ldy $arrayVarName+$indexValue+1")
assignRegisterpairWord(assign.target, RegisterOrPair.AY) 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 -> { elementDt.isFloat -> {
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue)") asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue)")
assignFloatFromAY(assign.target) assignFloatFromAY(assign.target)
@@ -313,6 +327,20 @@ internal class AssignmentAsmGen(
asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y") asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y")
assignRegisterpairWord(assign.target, RegisterOrPair.AX) 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 -> { elementDt.isFloat -> {
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A) asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A)
asmgen.out(""" asmgen.out("""
@@ -787,9 +815,13 @@ internal class AssignmentAsmGen(
return if(translatedOk) return if(translatedOk)
true true
else else {
if(expr.type.isLong && expr.operator in "*/%")
TODO("long multiplication or division ${expr.position}")
anyExprGen.assignAnyExpressionUsingStack(expr, assign) anyExprGen.assignAnyExpressionUsingStack(expr, assign)
} }
}
private fun optimizedComparison(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { private fun optimizedComparison(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
if(expr.right.asConstInteger() == 0) { if(expr.right.asConstInteger() == 0) {
@@ -1741,6 +1773,85 @@ internal class AssignmentAsmGen(
val left = expr.left val left = expr.left
val right = expr.right val right = expr.right
when(right) { when(right) {
is PtNumber -> {
asmgen.assignExpressionTo(left, target)
val hex = right.number.toLongHex()
if (target.kind == TargetStorageKind.VARIABLE) {
if (expr.operator == "+") {
asmgen.out("""
lda ${target.asmVarname}
clc
adc #$${hex.substring(6,8)}
sta ${target.asmVarname}
lda ${target.asmVarname}+1
adc #$${hex.substring(4, 6)}
sta ${target.asmVarname}+1
lda ${target.asmVarname}+2
adc #$${hex.substring(2, 4)}
sta ${target.asmVarname}+2
lda ${target.asmVarname}+3
adc #$${hex.take(2)}
sta ${target.asmVarname}+3""")
} else {
asmgen.out("""
lda ${target.asmVarname}
sec
sbc #$${hex.substring(6, 8)}
sta ${target.asmVarname}
lda ${target.asmVarname}+1
sbc #$${hex.substring(4, 6)}
sta ${target.asmVarname}+1
lda ${target.asmVarname}+2
sbc #$${hex.substring(2, 4)}
sta ${target.asmVarname}+2
lda ${target.asmVarname}+3
sbc #$${hex.take(2)}
sta ${target.asmVarname}+3""")
}
return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
if(expr.operator=="+") {
asmgen.out("""
lda cx16.$startreg
clc
adc #$${hex.substring(6,8)}
sta cx16.$startreg
lda cx16.$startreg+1
adc #$${hex.substring(4, 6)}
sta cx16.$startreg+1
lda cx16.$startreg+2
adc #$${hex.substring(2, 4)}
sta cx16.$startreg+2
lda cx16.$startreg+3
adc #$${hex.take(2)}
sta cx16.$startreg+3""")
} else {
asmgen.out("""
lda cx16.$startreg
sec
sbc #$${hex.substring(6,8)}
sta cx16.$startreg
lda cx16.$startreg+1
sbc #$${hex.substring(4, 6)}
sta cx16.$startreg+1
lda cx16.$startreg+2
sbc #$${hex.substring(2, 4)}
sta cx16.$startreg+2
lda cx16.$startreg+3
sbc #$${hex.take(2)}
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 -> { is PtIdentifier -> {
if(target.kind == TargetStorageKind.VARIABLE) { if(target.kind == TargetStorageKind.VARIABLE) {
asmgen.assignExpressionTo(left, target) asmgen.assignExpressionTo(left, target)
@@ -1778,81 +1889,42 @@ internal class AssignmentAsmGen(
} }
return true return true
} else { } else {
TODO("add/subtract long into ${target.kind} at ${target.position} - use simple expressions and temporary variables for now") // 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)
is PtNumber -> {
asmgen.assignExpressionTo(left, target)
val hex = right.number.toInt().toString(16).padStart(8, '0')
if (target.kind == TargetStorageKind.VARIABLE) {
if(expr.operator=="+") { if(expr.operator=="+") {
asmgen.out(""" asmgen.out("""
lda ${target.asmVarname}
clc clc
adc #$${hex.substring(6,8)} lda cx16.r12
sta ${target.asmVarname} adc cx16.r14
lda ${target.asmVarname}+1 sta cx16.r12
adc #$${hex.substring(4, 6)} lda cx16.r12+1
sta ${target.asmVarname}+1 adc cx16.r14+1
lda ${target.asmVarname}+2 sta cx16.r12+1
adc #$${hex.substring(2, 4)} lda cx16.r12+2
sta ${target.asmVarname}+2 adc cx16.r14+2
lda ${target.asmVarname}+3 sta cx16.r12+2
adc #$${hex.take(2)} lda cx16.r12+3
sta ${target.asmVarname}+3""") adc cx16.r14+3
sta cx16.r12+3""")
} else { } else {
asmgen.out(""" asmgen.out("""
lda ${target.asmVarname}
sec sec
sbc #$${hex.substring(6, 8)} lda cx16.r12
sta ${target.asmVarname} sbc cx16.r14
lda ${target.asmVarname}+1 sta cx16.r12
sbc #$${hex.substring(4, 6)} lda cx16.r12+1
sta ${target.asmVarname}+1 sbc cx16.r14+1
lda ${target.asmVarname}+2 sta cx16.r12+1
sbc #$${hex.substring(2, 4)} lda cx16.r12+2
sta ${target.asmVarname}+2 sbc cx16.r14+2
lda ${target.asmVarname}+3 sta cx16.r12+2
sbc #$${hex.take(2)} lda cx16.r12+3
sta ${target.asmVarname}+3""") sbc cx16.r14+3
sta cx16.r12+3""")
} }
asmgen.assignRegister(RegisterOrPair.R12R13_32, target)
return true return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
if(expr.operator=="+") {
asmgen.out("""
lda cx16.$startreg
clc
adc #$${hex.substring(6,8)}
sta cx16.$startreg
lda cx16.$startreg+1
adc #$${hex.substring(4, 6)}
sta cx16.$startreg+1
lda cx16.$startreg+2
adc #$${hex.substring(2, 4)}
sta cx16.$startreg+2
lda cx16.$startreg+3
adc #$${hex.take(2)}
sta cx16.$startreg+3""")
} else {
asmgen.out("""
lda cx16.$startreg
sec
sbc #$${hex.substring(6,8)}
sta cx16.$startreg
lda cx16.$startreg+1
sbc #$${hex.substring(4, 6)}
sta cx16.$startreg+1
lda cx16.$startreg+2
sbc #$${hex.substring(2, 4)}
sta cx16.$startreg+2
lda cx16.$startreg+3
sbc #$${hex.take(2)}
sta cx16.$startreg+3""")
}
return true
} else {
TODO("add/subtract long const into ${target.kind} ${target.position} - use simple expressions and temporary variables for now")
} }
} }
else -> { else -> {
@@ -2617,7 +2689,7 @@ $endLabel""")
} }
else -> {} else -> {}
} }
} else if(valueDt.isUnsignedWord) { } else if(valueDt.isWord) {
when(target.register) { when(target.register) {
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
@@ -2636,7 +2708,38 @@ $endLabel""")
// 'cast' uword into a 16 bits register, just assign it // 'cast' uword into a 16 bits register, just assign it
return assignExpressionToRegister(value, target.register!!, targetDt.isSigned) 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 -> {} else -> {}
} }
} }
@@ -2672,7 +2775,7 @@ $endLabel""")
// long to word, just take the lsw // long to word, just take the lsw
assignCastViaLswFunc(value, target) assignCastViaLswFunc(value, target)
} else } 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.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.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") 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 else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
asmgen.out(""" asmgen.out("""
lda $sourceAsmVarName lda $sourceAsmVarName
@@ -2878,6 +2983,7 @@ $endLabel""")
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE) asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE) asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
@@ -2911,6 +3017,7 @@ $endLabel""")
BaseDataType.WORD -> { BaseDataType.WORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
asmgen.out(""" asmgen.out("""
lda $sourceAsmVarName lda $sourceAsmVarName
@@ -2950,6 +3057,7 @@ $endLabel""")
BaseDataType.UWORD, BaseDataType.POINTER -> { BaseDataType.UWORD, BaseDataType.POINTER -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
asmgen.out(""" asmgen.out("""
lda $sourceAsmVarName lda $sourceAsmVarName
@@ -2979,6 +3087,7 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName") 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.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.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") else -> throw AssemblyError("weird type")
} }
} }
@@ -3026,6 +3135,7 @@ $endLabel""")
else else
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1") asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
asmgen.out(""" asmgen.out("""
st${regs.toString().lowercase()} $targetAsmVarName st${regs.toString().lowercase()} $targetAsmVarName
@@ -3073,6 +3183,7 @@ $endLabel""")
asmgen.signExtendAYlsb(sourceDt) asmgen.signExtendAYlsb(sourceDt)
asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1") asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName") asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
asmgen.signExtendLongVariable(targetAsmVarName, sourceDt) asmgen.signExtendLongVariable(targetAsmVarName, sourceDt)
@@ -3108,6 +3219,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs") else -> throw AssemblyError("non-word regs")
} }
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
when(regs) { when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1") RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3146,6 +3258,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs") else -> throw AssemblyError("non-word regs")
} }
} }
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> { BaseDataType.LONG -> {
when(regs) { when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1") RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3479,7 +3592,7 @@ $endLabel""")
else -> throw AssemblyError("wrong dt ${target.position}") 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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} }
@@ -3662,11 +3775,11 @@ $endLabel""")
pointergen.assignFloatAY(IndexedPtrTarget(target)) pointergen.assignFloatAY(IndexedPtrTarget(target))
return return
} }
asmgen.out(" pha") asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, 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.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" pla") asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(""" asmgen.out("""
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
@@ -3674,6 +3787,7 @@ $endLabel""")
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_REG
jsr floats.set_array_float""") jsr floats.set_array_float""")
} }
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte") 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}") else throw AssemblyError("only combined vreg allowed as long target ${target.position}")
} }
TargetStorageKind.ARRAY -> { 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.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
@@ -4004,7 +4129,10 @@ $endLabel""")
sta cx16.$targetStartReg+3""") 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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} }
@@ -4499,7 +4627,7 @@ $endLabel""")
stz $startreg+2 stz $startreg+2
stz $startreg+3""") 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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
return return
@@ -4514,7 +4642,7 @@ $endLabel""")
asmgen.out(" lda #$$hexbyte | sta ${target.asmVarname}+$offset") 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(6,8), 0)
store(hex.substring(4,6), 1) store(hex.substring(4,6), 1)
store(hex.substring(2,4), 2) store(hex.substring(2,4), 2)
@@ -4527,7 +4655,7 @@ $endLabel""")
return return
} }
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
val hex = long.toUInt().toString(16).padStart(8, '0') val hex = long.toLongHex()
asmgen.out(""" asmgen.out("""
lda #$${hex.substring(6,8)} lda #$${hex.substring(6,8)}
sta ${target.asmVarname},y sta ${target.asmVarname},y
@@ -4542,7 +4670,7 @@ $endLabel""")
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
require(target.register in combinedLongRegisters) require(target.register in combinedLongRegisters)
val regstart = target.register!!.startregname() val regstart = target.register!!.startregname()
val hex = long.toUInt().toString(16).padStart(8, '0') val hex = long.toLongHex()
asmgen.out(""" asmgen.out("""
lda #$${hex.substring(6,8)} lda #$${hex.substring(6,8)}
sta cx16.$regstart sta cx16.$regstart
@@ -4553,7 +4681,7 @@ $endLabel""")
lda #$${hex.take(2)} lda #$${hex.take(2)}
sta cx16.$regstart+3""") 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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} }
@@ -5309,7 +5437,7 @@ $endLabel""")
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}") TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register negate ${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 */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} }

View File

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

View File

@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
import kotlin.math.log2 import kotlin.math.log2
@@ -248,8 +249,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
asmgen.loadIndirectFloat(zpPtrVar, offset) asmgen.loadIndirectFloat(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.FAC1, target) asmgen.assignRegister(RegisterOrPair.FAC1, target)
} }
else if(value.type.isLong) else if(value.type.isLong) {
TODO("load long ${value.position}") asmgen.loadIndirectLongIntoR14R15(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.R14R15_32, target)
}
else else
throw AssemblyError("weird dt ${value.type} in pointer deref assignment ${target.position}") 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) if(target.dt.isByte) inplaceByteShiftLeft(target, value)
else if(target.dt.isWord) inplaceWordShiftLeft(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}") else throw AssemblyError("weird dt ${target.position}")
} }
">>" -> { ">>" -> {
if(target.dt.isByte) inplaceByteShiftRight(target, value) if(target.dt.isByte) inplaceByteShiftRight(target, value)
else if(target.dt.isWord) inplaceWordShiftRight(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}") else throw AssemblyError("weird dt ${target.position}")
} }
"&", "and" -> { "&", "and" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here // 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) if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
else if(target.dt.isWord) inplaceWordAnd(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}") else throw AssemblyError("weird dt ${target.dt} ${target.position}")
} }
"|", "or" -> { "|", "or" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here // 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) if(target.dt.isByteOrBool) inplaceByteOr(target, value)
else if(target.dt.isWord) inplaceWordOr(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}") else throw AssemblyError("weird dt ${target.dt} ${target.position}")
} }
"^", "xor" -> { "^", "xor" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here // 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) if(target.dt.isByteOrBool) inplaceByteXor(target, value)
else if(target.dt.isWord) inplaceWordXor(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("weird dt ${target.dt} ${target.position}")
} }
else -> throw AssemblyError("invalid operator for in-place modification $operator") 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}") 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> { internal fun operatorDereference(binExpr: PtBinaryExpression): Triple<String, UByte, DataType> {
// the only case we support here is: a.b.c[i] . value // 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 // 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) { private fun inplaceLongAdd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long add with ${value.number} ${target.position}") SourceStorageKind.LITERALNUMBER -> {
SourceStorageKind.VARIABLE -> TODO("inplace long add with ${value.asmVarname} ${target.position}") val hex = value.number!!.number.toLongHex()
SourceStorageKind.EXPRESSION -> TODO("inplace long add with ${value.expression} ${target.position}") 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}") else -> TODO("inplace long add with ${value.kind} ${target.position}")
} }
} }
private fun inplaceLongSub(target: PtrTarget, value: AsmAssignSource) { private fun inplaceLongSub(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long sub with ${value.number} ${target.position}") SourceStorageKind.LITERALNUMBER -> {
SourceStorageKind.VARIABLE -> TODO("inplace long sub with ${value.asmVarname} ${target.position}") val hex = value.number!!.number.toLongHex()
SourceStorageKind.EXPRESSION -> TODO("inplace long sub with ${value.expression} ${target.position}") 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}") 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}") 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") // val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
// assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index) // assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
@@ -1692,7 +2071,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
pha""") pha""")
} }
in combinedLongRegisters -> { 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) else -> asmgen.saveRegisterStack(regs.asCpuRegister(), false)
} }
@@ -1727,7 +2106,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
sta $regname""") sta $regname""")
} }
in combinedLongRegisters -> { 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) 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) "abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call) "cmp" -> funcCmp(call)
"sgn" -> funcSgn(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__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD) "divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore "rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore

View File

@@ -244,10 +244,18 @@ class VarConstantValueTypeAdjuster(
dt.isUnsignedByte -> "sqrt__ubyte" dt.isUnsignedByte -> "sqrt__ubyte"
dt.isUnsignedWord -> "sqrt__uword" dt.isUnsignedWord -> "sqrt__uword"
dt.isFloat -> "sqrt__float" dt.isFloat -> "sqrt__float"
else -> { 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) errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications return noModifications
} }
"sqrt__long"
}
else -> {
errors.err("expected numeric argument", functionCallExpr.args[0].position)
return noModifications
}
} }
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference}, return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position), IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),

View File

@@ -406,6 +406,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
jsr cbm.SCREEN jsr cbm.SCREEN
inx
txa txa
rts rts
}} }}
@@ -415,11 +416,22 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
jsr cbm.SCREEN jsr cbm.SCREEN
iny
tya tya
rts 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 { asmsub waitkey() -> ubyte @A {
%asm {{ %asm {{
- jsr cbm.GETIN - 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 { asmsub waitkey() -> ubyte @A {
%asm {{ %asm {{
- jsr cbm.GETIN - 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): ; 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) ; Calculates the number of bytes loaded (files > 64Kb are truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword { sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> long {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress &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 { 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 { asmsub waitkey() -> ubyte @A {
%asm {{ %asm {{
- jsr cbm.GETIN - 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 { asmsub waitkey() -> ubyte @A {
%asm {{ %asm {{
- jsr cbm.GETIN - 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() { sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H" str @shared sequence = "\x1b[2J\x1B[H"
%ir {{ %ir {{

View File

@@ -20,7 +20,19 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"sgn" to ::builtinSgn, "sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } }, "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__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" 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() } }, "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() } }, "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) if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position) throw SyntaxError("built-in function requires one integer argument", position)
} else { } else {
if(constval.type!=BaseDataType.UBYTE && constval.type!=BaseDataType.UWORD) if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position) throw SyntaxError("built-in function requires one integer argument", position)
} }
val integer = constval.number.toInt() 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 { 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() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type != BaseDataType.FLOAT) if(constval.type != BaseDataType.FLOAT)
throw SyntaxError("built-in function requires one float argument", position) throw SyntaxError("built-in function requires one float argument", position)
val result = function(constval.number)
return NumericLiteral(BaseDataType.FLOAT, function(constval.number), args[0].position) 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 { 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) { internal fun Program.changeNotExpressionAndIfComparisonExpr(errors: IErrorReporter, target: ICompilationTarget) {
val changer = NotExpressionAndIfComparisonExprChanger(this, errors, target) val changer = NotExpressionAndIfComparisonExprChanger(this, errors, target)
changer.visit(this) changer.visit(this)
while(errors.noErrors() && changer.applyModifications()>0) { for(numCycles in 0..2000) {
if (errors.noErrors() && changer.applyModifications() > 0)
changer.visit(this) 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) { fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
val desugar = CodeDesugarer(this, options.compTarget, errors) val desugar = CodeDesugarer(this, options.compTarget, errors)
desugar.visit(this) desugar.visit(this)
while(errors.noErrors() && desugar.applyModifications()>0) for(numCycles in 0..2000) {
if (errors.noErrors() && desugar.applyModifications() > 0)
desugar.visit(this) desugar.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("desugaring() is looping endlessly")
}
} }
internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter, options: CompilationOptions) { internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter, options: CompilationOptions) {
@@ -121,16 +134,30 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
if(errors.noErrors()) { if(errors.noErrors()) {
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors) val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
lit2decl.visit(this) lit2decl.visit(this)
while(errors.noErrors() && lit2decl.applyModifications()>0) for(numCycles in 0..1000) {
if(errors.noErrors() && lit2decl.applyModifications() > 0)
lit2decl.visit(this) 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) { internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
val process = VariousCleanups(this, errors, options) val process = VariousCleanups(this, errors, options)
process.visit(this) process.visit(this)
while(errors.noErrors() && process.applyModifications()>0) { for(numCycles in 0..2000) {
if (errors.noErrors() && process.applyModifications() > 0)
process.visit(this) 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) { override fun visit(alias: Alias) {
if(alias.target.targetStatement(program.builtinFunctions)==null) if(alias.target.targetStatement(program.builtinFunctions)==null)
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position) 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) { override fun visit(block: Block) {

View File

@@ -132,7 +132,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
expr)) expr))
} }
if(rightCv!=null && rightCv.number<0) { 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( return listOf(IAstModification.ReplaceNode(
expr.right, expr.right,
NumericLiteral(leftDt.getOrUndef().base, value, expr.right.position), 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") 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") { test("unknown field") {
val src=""" val src="""
main { main {
@@ -1258,7 +1302,7 @@ main {
errors.errors[3] shouldContain "assigning this value to struct instance not supported" errors.errors[3] shouldContain "assigning this value to struct instance not supported"
} }
xtest("assigning struct instances") { test("assigning struct instances") {
val src=""" val src="""
main { main {
sub start() { sub start() {
@@ -1273,16 +1317,26 @@ main {
lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11) lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11)
lp2[2] = lp1^^ ; memcopy(lp1, lp2 + 22, 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)
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above) 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 ;;; lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
} }
}""" }"""
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)!! val result = compileText(VMTarget(), false, src, outputDir, errors=errors)!!
val st = result.compilerAst.entrypoint.statements 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") { test("a.b.c[i]^^.value as expression where pointer is struct") {
@@ -2548,4 +2602,24 @@ main {
be6.right shouldBe instanceOf<IdentifierReference>() be6.right shouldBe instanceOf<IdentifierReference>()
(a7.value as? ArrayIndexedExpression)?.indexer?.constIndex() shouldBe 0 (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,6 +17,7 @@ import prog8.code.core.BaseDataType
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.Cx16Target import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
@@ -464,4 +465,34 @@ main {
}""" }"""
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null 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 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") { test("wrong alias gives correct error") {
val src=""" val src="""
main { main {

View File

@@ -385,6 +385,13 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2) BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
else -> throw FatalAstException(terminal.text) 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()) return NumericLiteral(integer.second, integer.first, ctx.toPosition())
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -559,6 +559,7 @@ txt {
rvs_off () rvs_off ()
rvs_on () rvs_on ()
setchr (ubyte col, ubyte row, ubyte char) setchr (ubyte col, ubyte row, ubyte char)
size () -> ubyte, ubyte
spc () spc ()
uppercase () uppercase ()
width () -> ubyte 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>`_ #. 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). #. 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: Arch Linux:
Currently, it's available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and compatible systems. 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>`_. 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``. 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. 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, ...) - 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. - 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 - 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 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 - 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, still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters and inline assembly to have full control when every register, cycle or byte matters
- Variables are all allocated statically, no memory allocation overhead - 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 - Structs and typed pointers
- Tight control over Zeropage usage - 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. - 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). Get the sign of the value (integer or floating point).
The result is a byte: -1, 0 or 1 (negative, zero, positive). The result is a byte: -1, 0 or 1 (negative, zero, positive).
sqrt (w) sqrt (x)
Returns the square root of the number. 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``. To do the reverse - squaring a number - just write ``x*x``.
divmod (dividend, divisor, quotient, remainder) 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. of precision though and gives an error if you may be losing a floating point result.
.. _arithmetic:
Arithmetic and Logical expressions Arithmetic and Logical expressions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -955,6 +956,7 @@ and ``(true or false) and false`` is false instead of true.
byte @shared b = 44 byte @shared b = 44
w = (b as word)*55 w = (b as word)*55
w = b*(55 as word) w = b*(55 as word)
w = b * $0037
Operators Operators
@@ -1236,10 +1238,8 @@ Otherwise the compiler will warn you about discarding the result of the call.
Multiple return values Multiple return values
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
Subroutines can return more than one value. Subroutines can return more than one value.
For example, ``asmsub`` routines (implemented in assembly code) or ``extsub`` routines ``asmsub`` and ``extsub`` routines return their multiple values spread across different registers,
(referencing an external routine in ROM or elsewhere in RAM) can return multiple values spread and can also efficiently use the CPU's status register flags for boolean returnvalues.
across different registers, and even the CPU's status register flags for boolean values.
Normal subroutines can also return multiple values.
You have to "multi assign" all return values of the subroutine call to something: 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 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. to the order of the return values declared in the subroutine's signature.

View File

@@ -1,33 +1,23 @@
TODO TODO
==== ====
- implement the TODO in assignPointerDerefExpression() - fix/check github issues.
- implement inplaceLongAdd/Sub in PointerAssignmentGen to make this compile:: - 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 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 $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 - 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?) - 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 - 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 - 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>'? - 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: - 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 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!) 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 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 inplacemodificationLongWithLiteralval() for more shift values such as 8, 16, 24 etc but take sign bit into account!
- optimize simple cases in funcPeekL and funcPokeL - 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. - 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? - 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? - in Identifier: use typedarray of strings instead of listOf? Other places?

View File

@@ -228,8 +228,9 @@ value datatype
-2147483647 .. 2147483647 long (there is no unsigned long right now) -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`` 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 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:: to be done on word values, and don't want to explicitly have to cast everything all the time. For instance::
ubyte column ubyte column
@@ -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. 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 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. 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. a lot will be much slower than when you restrict yourself to 8 or 16 bit values. Use long values sparingly.
.. danger:: .. danger::

View File

@@ -412,20 +412,28 @@ asmsub waitkey() -> ubyte @A {
asmsub width() clobbers(X,Y) -> ubyte @A { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
; TODO
%asm {{ %asm {{
lda #0 lda #DEFAULT_WIDTH
rts rts
}} }}
} }
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
; TODO
%asm {{ %asm {{
lda #0 lda #DEFAULT_HEIGHT
rts 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 { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
lda DEFAULT_WIDTH lda #DEFAULT_WIDTH
rts rts
}} }}
} }
@@ -425,11 +425,21 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
lda DEFAULT_HEIGHT lda #DEFAULT_HEIGHT
rts 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? ; TODO: jmp to cbm.CHRIN?
asmsub waitkey() -> ubyte @A { asmsub waitkey() -> ubyte @A {
%asm {{ %asm {{

View File

@@ -12,26 +12,26 @@ main{
} }
^^Country[] countries = [ ^^Country[] countries = [
^^Country:["Indonesia", 285.72, 1904], ["Indonesia", 285.72, 1904],
^^Country:["Congo", 112.83, 2344], ["Congo", 112.83, 2344],
^^Country:["Vietnam", 101.60, 331], ["Vietnam", 101.60, 331],
^^Country:["United States", 347.28, 9372], ["United States", 347.28, 9372],
^^Country:["Iran", 92.42, 1648], ["Iran", 92.42, 1648],
^^Country:["Turkey", 87.69, 783], ["Turkey", 87.69, 783],
^^Country:["Brazil", 212.81, 8515], ["Brazil", 212.81, 8515],
^^Country:["Bangladesh", 175.69, 147], ["Bangladesh", 175.69, 147],
^^Country:["Germany", 84.08, 357], ["Germany", 84.08, 357],
^^Country:["Japan", 123.10, 377], ["Japan", 123.10, 377],
^^Country:["India", 1463.87, 3287], ["India", 1463.87, 3287],
^^Country:["China", 1416.10, 9596], ["China", 1416.10, 9596],
^^Country:["Philippines", 116.79, 300], ["Philippines", 116.79, 300],
^^Country:["Russia", 143.99, 17098], ["Russia", 143.99, 17098],
^^Country:["Pakistan", 255.22, 881], ["Pakistan", 255.22, 881],
^^Country:["Nigeria", 237.53, 923], ["Nigeria", 237.53, 923],
^^Country:["Ethiopia", 135.47, 1104], ["Ethiopia", 135.47, 1104],
^^Country:["Mexico", 131.95, 1964], ["Mexico", 131.95, 1964],
^^Country:["Thailand", 71.62, 513], ["Thailand", 71.62, 513],
^^Country:["Egypt", 118.37, 1002], ["Egypt", 118.37, 1002],
] ]
sub start() { 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 textio
%import math
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit
main { main {
sub start() { sub start() {
long @shared lv, lv2 bytetest()
wordtest()
longtest()
floattest() ; TODO fix invalid 6502 code gen /crash
}
lv = $11111111 sub bytetest() {
lv2 = $55555555 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()
}
sub floattest() {
; long @shared lv1 = $12345678 float[] foo = [1.1, 2.2, 3.3]
; ubyte i
; txt.print_ubhex(msb(lv1), true) txt.print("before:")
; txt.spc() for i in 0 to 2 {
; txt.print_ubhex(lsb(lv1), true) txt.chrout(' ')
; txt.spc() txt.print_f(foo[i])
; txt.print_ubhex(lv1 as ubyte, true) }
; txt.nl() txt.nl()
; txt.print_uwhex(msw(lv1), true) foo[2] = foo[1]
; txt.spc() foo[1] = foo[0]
; txt.print_uwhex(lsw(lv1), true) foo[0] = 0
; txt.nl() txt.print(" after:")
for i in 0 to 2 {
; ; TODO support long+1 / -1 expressions.... txt.chrout(' ')
; cx16.r4 = msw(lv1-1) txt.print_f(foo[i])
; cx16.r5 = lsw(lv1-1) }
; txt.print_uwhex(cx16.r4, true) txt.nl()
; txt.print_uwhex(cx16.r5, true)
; txt.nl()
;
; txt.print_ubhex((lv1-1) as ubyte, true)
; txt.nl()
} }
} }

View File

@@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.configuration-cache=false org.gradle.configuration-cache=false
kotlin.code.style=official 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 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) 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) 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) 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) 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) 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(type==IRDataType.LONG) {
if(opcode==Opcode.SGN) if(opcode==Opcode.SGN)
return IRDataType.BYTE 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) if(opcode==Opcode.JUMPI || opcode==Opcode.CALLI || opcode==Opcode.STOREZI || opcode==Opcode.LSIGW || opcode==Opcode.MSIGW)
return IRDataType.WORD return IRDataType.WORD

View File

@@ -583,7 +583,7 @@ class VirtualMachine(irProgram: IRProgram) {
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!) val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
val value = memory.getSL(pointer.toInt()) val value = memory.getSL(pointer.toInt())
registers.setSL(i.reg1!!, value) registers.setSL(i.reg1!!, value)
statusbitsNZ(value.toInt(), i.type!!) statusbitsNZ(value, i.type!!)
} }
IRDataType.FLOAT -> { IRDataType.FLOAT -> {
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!) val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!)
@@ -1444,7 +1444,12 @@ class VirtualMachine(irProgram: IRProgram) {
when(i.type!!) { when(i.type!!) {
IRDataType.BYTE -> registers.setUB(i.reg1!!, sqrt(registers.getUB(i.reg2!!).toDouble()).toInt().toUByte()) 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.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!!))) IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, sqrt(registers.getFloat(i.fpReg2!!)))
} }
nextPc() nextPc()
@@ -2526,7 +2531,7 @@ class VirtualMachine(irProgram: IRProgram) {
newStatusCarry = (orig and 0x80000000u) != 0u newStatusCarry = (orig and 0x80000000u) != 0u
val rotated: UInt = (if (useCarry) { val rotated: UInt = (if (useCarry) {
val carry = if (statusCarry) 1u else 0u val carry = if (statusCarry) 1u else 0u
(orig.toUInt().rotateLeft(1) or carry) (orig.rotateLeft(1) or carry)
} else } else
orig.rotateLeft(1)) orig.rotateLeft(1))
registers.setSL(i.reg1!!, rotated.toInt()) registers.setSL(i.reg1!!, rotated.toInt())
@@ -2648,7 +2653,7 @@ class VirtualMachine(irProgram: IRProgram) {
val lsb = registers.getUB(i.reg3!!) val lsb = registers.getUB(i.reg3!!)
val value = ((msb.toInt() shl 8) or lsb.toInt()) val value = ((msb.toInt() shl 8) or lsb.toInt())
registers.setUW(i.reg1!!, value.toUShort()) registers.setUW(i.reg1!!, value.toUShort())
statusbitsNZ(value.toInt(), i.type!!) statusbitsNZ(value, i.type!!)
} }
IRDataType.WORD -> { IRDataType.WORD -> {
val msw = registers.getUW(i.reg2!!) val msw = registers.getUW(i.reg2!!)
@@ -2959,15 +2964,18 @@ class VirtualMachine(irProgram: IRProgram) {
} }
private fun multiplyAnyLongSigned(reg1: Int, reg2: Int) { 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) { 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) { 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) { private fun divModLongSigned(operator: String, reg1: Int, reg2: Int) {