mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 01:49:22 +00:00
Merge branch 'master' into next_compositetypes
# Conflicts: # compiler/src/prog8/compiler/astprocessing/AstChecker.kt # compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt # compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt # compiler/test/TestSubroutines.kt
This commit is contained in:
commit
ba6db8f905
16
README.md
16
README.md
@ -54,27 +54,23 @@ What does Prog8 provide?
|
||||
------------------------
|
||||
|
||||
- all advantages of a higher level language over having to write assembly code manually
|
||||
- programs run very fast because compilation 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
|
||||
- modularity, symbol scoping, subroutines
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||
- floating point math is supported on certain targets
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- subroutines with input parameters and result values
|
||||
- high-level program optimizations
|
||||
- no need for forward declarations
|
||||
- small program boilerplate/compilersupport overhead
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- conditional branches
|
||||
- conditional branches that map 1:1 to cpu status flags
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
|
||||
- encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
|
||||
- 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
|
||||
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
|
@ -103,7 +103,7 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
is PtIdentifier -> parent.children[index] = node.prefix(parent, st)
|
||||
is PtFunctionCall -> throw AssemblyError("PtFunctionCall should be processed in their own list, last")
|
||||
is PtJump -> parent.children[index] = node.prefix(parent, st)
|
||||
is PtVariable -> parent.children[index] = node.prefix(st)
|
||||
is PtVariable -> parent.children[index] = node.prefix(parent, st)
|
||||
else -> throw AssemblyError("weird node to prefix $node")
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ private fun prefixScopedName(name: String, type: Char): String {
|
||||
return prefixed.joinToString(".")
|
||||
}
|
||||
|
||||
private fun PtVariable.prefix(st: SymbolTable): PtVariable {
|
||||
private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
name = prefixScopedName(name, 'v')
|
||||
if(value==null)
|
||||
return this
|
||||
@ -162,7 +162,9 @@ private fun PtVariable.prefix(st: SymbolTable): PtVariable {
|
||||
else -> throw AssemblyError("weird array value element $elt")
|
||||
}
|
||||
}
|
||||
PtVariable(name, type, zeropage, newValue, arraySize, position)
|
||||
val result = PtVariable(name, type, zeropage, newValue, arraySize, position)
|
||||
result.parent = parent
|
||||
result
|
||||
}
|
||||
else this
|
||||
}
|
||||
|
@ -393,11 +393,35 @@ internal class ConstantIdentifierReplacer(
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
// convert a range expression that is assigned to an array, to an array literal instead.
|
||||
val range = assignment.value as? RangeExpression
|
||||
if(range!=null) {
|
||||
val targetDatatype = assignment.target.inferType(program)
|
||||
if(targetDatatype.isArray) {
|
||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOrUndef(),
|
||||
ZeropageWish.DONTCARE, null, "dummy", emptyList(),
|
||||
assignment.value, false, false, Position.DUMMY)
|
||||
val replaceValue = createConstArrayInitializerValue(decl)
|
||||
if(replaceValue!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun createConstArrayInitializerValue(decl: VarDecl): ArrayLiteral? {
|
||||
|
||||
if(decl.type==VarDeclType.MEMORY)
|
||||
return null // memory mapped arrays can never have an initializer value other than the address where they're mapped.
|
||||
|
||||
val rangeSize=(decl.value as? RangeExpression)?.size()
|
||||
if(rangeSize!=null && rangeSize>65535) {
|
||||
errors.err("range size overflow", decl.value!!.position)
|
||||
return null
|
||||
}
|
||||
|
||||
// convert the initializer range expression from a range or int, to an actual array.
|
||||
// this is to allow initialization of arrays with a single value like ubyte[10] array = 42
|
||||
val dt = decl.datatype
|
||||
|
@ -757,6 +757,11 @@ internal_vload:
|
||||
}}
|
||||
}
|
||||
|
||||
; note: There is no vsave_raw() routine because the Kernal doesn't have a VSAVE routine.
|
||||
; You'll have to write your own loop that reads vram data and use
|
||||
; cbm.CHROUT or cx16.MCIOUT to write it to an open output file.
|
||||
|
||||
|
||||
sub chdir(str path) {
|
||||
; -- change current directory.
|
||||
list_filename[0] = 'c'
|
||||
|
@ -16,7 +16,7 @@ import kotlin.io.path.Path
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
* Semantic analysis.
|
||||
* Semantic analysis and error reporting.
|
||||
*/
|
||||
internal class AstChecker(private val program: Program,
|
||||
private val errors: IErrorReporter,
|
||||
@ -666,7 +666,7 @@ internal class AstChecker(private val program: Program,
|
||||
if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(targetDatatype.getOrUndef(),
|
||||
checkAssignmentCompatible(assignTarget, targetDatatype.getOrUndef(),
|
||||
sourceDatatype.getOrUndef(), assignment.value)
|
||||
}
|
||||
}
|
||||
@ -697,6 +697,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
fun err(msg: String) = errors.err(msg, decl.position)
|
||||
fun valueerr(msg: String) = errors.err(msg, decl.value?.position ?: decl.position)
|
||||
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
if(decl.value?.referencesIdentifier(listOf(decl.name)) == true || decl.arraysize?.indexExpr?.referencesIdentifier(listOf(decl.name)) == true)
|
||||
@ -717,11 +718,11 @@ internal class AstChecker(private val program: Program,
|
||||
if(decl.type== VarDeclType.MEMORY)
|
||||
err("memory mapped array must have a size specification")
|
||||
if(decl.value==null) {
|
||||
err("array variable is missing a size specification or an initialization value")
|
||||
valueerr("array variable is missing a size specification or an initialization value")
|
||||
return
|
||||
}
|
||||
if(decl.value is NumericLiteral) {
|
||||
err("unsized array declaration cannot use a single literal initialization value")
|
||||
valueerr("unsized array declaration cannot use a single literal initialization value")
|
||||
return
|
||||
}
|
||||
if(decl.value is RangeExpression)
|
||||
@ -747,7 +748,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
else -> {
|
||||
if(decl.type==VarDeclType.CONST) {
|
||||
err("const declaration needs a compile-time constant initializer value")
|
||||
valueerr("const declaration needs a compile-time constant initializer value")
|
||||
super.visit(decl)
|
||||
return
|
||||
}
|
||||
@ -778,10 +779,10 @@ internal class AstChecker(private val program: Program,
|
||||
val numvalue = decl.value as? NumericLiteral
|
||||
if(numvalue!=null) {
|
||||
if (!numvalue.type.isInteger || numvalue.number.toInt() < 0 || numvalue.number.toInt() > 65535) {
|
||||
err("memory address must be valid integer 0..\$ffff")
|
||||
valueerr("memory address must be valid integer 0..\$ffff")
|
||||
}
|
||||
} else {
|
||||
err("value of memory mapped variable can only be a constant, maybe use an address pointer type instead?")
|
||||
valueerr("value of memory mapped variable can only be a constant, maybe use an address pointer type instead?")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -793,10 +794,10 @@ internal class AstChecker(private val program: Program,
|
||||
if(decl.isArray) {
|
||||
val eltDt = decl.datatype.elementType()
|
||||
if(!(iDt istype eltDt))
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
valueerr("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
} else {
|
||||
if(!(iDt.isBool && decl.datatype.isUnsignedByte || iDt issimpletype BaseDataType.UBYTE && decl.datatype.isBool))
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
valueerr("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -845,7 +846,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(decl.type==VarDeclType.MEMORY)
|
||||
err("strings can't be memory mapped")
|
||||
else
|
||||
err("string var must be initialized with a string literal")
|
||||
valueerr("string var must be initialized with a string literal")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1008,9 +1009,11 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
if(array.parent is Assignment) {
|
||||
val arraydt = array.inferType(program)
|
||||
val assignTarget = (array.parent as Assignment).target
|
||||
if(!assignTarget.inferType(program).isArray)
|
||||
errors.err("cannot assign array to a non-array variable", assignTarget.position)
|
||||
val targetDt = assignTarget.inferType(program)
|
||||
if(arraydt!=targetDt)
|
||||
errors.err("value has incompatible type ($arraydt) for the variable ($targetDt)", array.position)
|
||||
}
|
||||
super.visit(array)
|
||||
}
|
||||
@ -1624,7 +1627,7 @@ internal class AstChecker(private val program: Program,
|
||||
return err("boolean array length must be 1-256")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return err("array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return true
|
||||
}
|
||||
return err("invalid boolean array size, must be 1-256")
|
||||
@ -1643,7 +1646,7 @@ internal class AstChecker(private val program: Program,
|
||||
return err("byte array length must be 1-256")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return err("array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return true
|
||||
}
|
||||
return err("invalid byte array size, must be 1-256")
|
||||
@ -1663,7 +1666,7 @@ internal class AstChecker(private val program: Program,
|
||||
return err("array length must be 1-$maxLength")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return err("array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return true
|
||||
}
|
||||
return err("invalid array size, must be 1-$maxLength")
|
||||
@ -1682,7 +1685,7 @@ internal class AstChecker(private val program: Program,
|
||||
return err("float array length must be 1-51")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return err("array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
} else
|
||||
return err("invalid float array size, must be 1-51")
|
||||
|
||||
@ -1751,7 +1754,7 @@ internal class AstChecker(private val program: Program,
|
||||
return true
|
||||
}
|
||||
|
||||
private fun checkArrayValues(value: ArrayLiteral, type: DataType): Boolean {
|
||||
private fun checkArrayValues(value: ArrayLiteral, targetDt: DataType): Boolean {
|
||||
val array = value.value.map {
|
||||
when (it) {
|
||||
is NumericLiteral -> it.number.toInt()
|
||||
@ -1763,41 +1766,52 @@ internal class AstChecker(private val program: Program,
|
||||
if(cast==null || !cast.isValid)
|
||||
-9999999
|
||||
else
|
||||
cast.valueOrZero().number.toInt()
|
||||
cast.valueOrZero().number
|
||||
}
|
||||
else -> -9999999
|
||||
}
|
||||
}
|
||||
val correct: Boolean
|
||||
when {
|
||||
type.isUnsignedByteArray -> {
|
||||
targetDt.isUnsignedByteArray -> {
|
||||
correct = array.all { it in 0..255 }
|
||||
}
|
||||
type.isSignedByteArray -> {
|
||||
targetDt.isSignedByteArray -> {
|
||||
correct = array.all { it in -128..127 }
|
||||
}
|
||||
type.isUnsignedWordArray || type.isSplitUnsignedWordArray -> {
|
||||
targetDt.isUnsignedWordArray || targetDt.isSplitUnsignedWordArray -> {
|
||||
correct = array.all { (it in 0..65535) }
|
||||
}
|
||||
type.isSignedWordArray || type.isSplitSignedWordArray -> {
|
||||
targetDt.isSignedWordArray || targetDt.isSplitSignedWordArray -> {
|
||||
correct = array.all { it in -32768..32767 }
|
||||
}
|
||||
type.isBoolArray -> {
|
||||
targetDt.isBoolArray -> {
|
||||
correct = array.all { it==0 || it==1 }
|
||||
}
|
||||
type.isFloatArray -> correct = true
|
||||
else -> throw FatalAstException("invalid array type $type")
|
||||
targetDt.isFloatArray -> correct = true
|
||||
else -> throw FatalAstException("invalid type $targetDt")
|
||||
}
|
||||
if (!correct)
|
||||
errors.err("array value out of range for type $type", value.position)
|
||||
errors.err("array element out of range for type $targetDt", value.position)
|
||||
return correct
|
||||
}
|
||||
|
||||
private fun checkAssignmentCompatible(targetDatatype: DataType,
|
||||
private fun checkAssignmentCompatible(target: AssignTarget,
|
||||
targetDatatype: DataType,
|
||||
sourceDatatype: DataType,
|
||||
sourceValue: Expression) : Boolean {
|
||||
val position = sourceValue.position
|
||||
|
||||
if(sourceValue is ArrayLiteral && targetDatatype.isArray) {
|
||||
val vardecl=target.identifier?.targetVarDecl(program)
|
||||
val targetSize = vardecl?.arraysize?.constIndex()
|
||||
if(targetSize!=null) {
|
||||
if(sourceValue.value.size != targetSize) {
|
||||
errors.err("array size mismatch (expecting $targetSize, got ${sourceValue.value.size})", sourceValue.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sourceValue is RangeExpression) {
|
||||
errors.err("can't assign a range value to something else", position)
|
||||
return false
|
||||
@ -1817,8 +1831,12 @@ internal class AstChecker(private val program: Program,
|
||||
targetDatatype.isFloat -> sourceDatatype.isNumeric
|
||||
targetDatatype.isString -> sourceDatatype.isString
|
||||
else -> {
|
||||
errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
||||
false
|
||||
if(targetDatatype.isArray && sourceValue is ArrayLiteral)
|
||||
true // assigning array literal to an array variable is allowed, size and type are checked elsewhere
|
||||
else {
|
||||
errors.err("cannot assign this value to variable of type $targetDatatype", position)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1839,6 +1857,9 @@ internal class AstChecker(private val program: Program,
|
||||
else if(targetDatatype.isUnsignedWord && sourceDatatype.isPassByRef) {
|
||||
// this is allowed: a pass-by-reference datatype into a uword (pointer value).
|
||||
}
|
||||
else if(sourceDatatype.isArray && targetDatatype.isArray) {
|
||||
// this is allowed (assigning array to array)
|
||||
}
|
||||
else if(sourceDatatype.isBool && !targetDatatype.isBool) {
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
|
@ -258,18 +258,17 @@ internal class StatementReorderer(
|
||||
if(assign.value is ArrayLiteral) {
|
||||
return // invalid assignment of literals will be reported elsewhere
|
||||
}
|
||||
|
||||
if(assign.value !is IdentifierReference) {
|
||||
errors.err("invalid array value to assign to other array", assign.value.position)
|
||||
return
|
||||
return // invalid assignment value will be reported elsewhere
|
||||
}
|
||||
|
||||
val sourceIdent = assign.value as IdentifierReference
|
||||
val sourceVar = sourceIdent.targetVarDecl(program)!!
|
||||
if(!sourceVar.isArray) {
|
||||
errors.err("value must be an array", sourceIdent.position)
|
||||
} else {
|
||||
if (sourceVar.arraysize!!.constIndex() != targetVar.arraysize!!.constIndex())
|
||||
errors.err("element count mismatch", assign.position)
|
||||
errors.err("array size mismatch (expecting ${targetVar.arraysize!!.constIndex()}, got ${sourceVar.arraysize!!.constIndex()})", assign.value.position)
|
||||
val sourceEltDt = sourceVar.datatype.elementType()
|
||||
val targetEltDt = targetVar.datatype.elementType()
|
||||
if (!sourceEltDt.equalsSize(targetEltDt)) {
|
||||
|
@ -135,8 +135,9 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
if(isMultiComparisonRecurse(leftBinExpr1)) {
|
||||
val elementType = needle.inferType(program).getOrElse { throw FatalAstException("invalid needle dt") }
|
||||
require(elementType.isNumericOrBool)
|
||||
val elementIType = needle.inferType(program)
|
||||
if(elementIType.isUnknown) return noModifications
|
||||
val elementType = elementIType.getOrUndef()
|
||||
if(values.size==2 || values.size==3 && (elementType.isUnsignedByte || elementType.isUnsignedWord)) {
|
||||
val numbers = values.map{it.number}.toSet()
|
||||
if(numbers == setOf(0.0, 1.0)) {
|
||||
|
@ -127,7 +127,6 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"cxlogo",
|
||||
"diskspeed",
|
||||
"fileseek",
|
||||
"highresbitmap",
|
||||
"kefrenbars",
|
||||
"keyboardhandler",
|
||||
"life",
|
||||
@ -170,8 +169,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"dirlist",
|
||||
"fibonacci",
|
||||
"fractal-tree",
|
||||
"line-circle-gfx",
|
||||
"line-circle-txt",
|
||||
"maze",
|
||||
"mandelbrot",
|
||||
"mandelbrot-gfx",
|
||||
@ -179,7 +176,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"primes",
|
||||
"queens",
|
||||
"screencodes",
|
||||
"sincos",
|
||||
"swirl",
|
||||
"swirl-float",
|
||||
"tehtriz",
|
||||
|
@ -34,7 +34,7 @@ class TestSubroutines: FunSpec({
|
||||
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "type mismatch, was: str expected: ubyte"
|
||||
errors.errors[1] shouldContain "initialisation value has incompatible type"
|
||||
errors.errors[1] shouldContain "value has incompatible type"
|
||||
}
|
||||
|
||||
test("stringParameter") {
|
||||
|
@ -4,6 +4,7 @@ import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.code.ast.PtBuiltinFunctionCall
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
@ -327,5 +328,80 @@ main {
|
||||
errors.errors[1] shouldContain "out of bounds"
|
||||
errors.errors[2] shouldContain "out of bounds"
|
||||
}
|
||||
|
||||
test("array assignments should check for number of elements and element type correctness") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte[] array = 1 to 4
|
||||
ubyte[] array2 = [1,2,3,4]
|
||||
str[] names = ["apple", "banana", "tomato"]
|
||||
|
||||
array = [10,11,12,13] ; ok!
|
||||
array = 20 to 23 ; ok!
|
||||
names = ["x1", "x2", "x3"] ; ok!
|
||||
|
||||
ubyte[] array3 = [1,2,3,4000] ; error: element type
|
||||
array = 10 to 15 ; error: array size
|
||||
array = 1000 to 1003 ; error: element type
|
||||
names = ["x1", "x2", "x3", "x4"] ; error: array size
|
||||
names = [1.1, 2.2, 3.3, 4.4] ; error: array size AND element type
|
||||
names = [1.1, 2.2, 999999.9] ; error: element type
|
||||
names = [1.1, 2.2, 9.9] ; error: element type
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = true, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 8
|
||||
errors.errors[0] shouldContain "incompatible type"
|
||||
errors.errors[1] shouldContain "array size mismatch"
|
||||
errors.errors[2] shouldContain "array element out of range"
|
||||
errors.errors[3] shouldContain "array size mismatch"
|
||||
errors.errors[4] shouldContain "array size mismatch"
|
||||
errors.errors[5] shouldContain "value has incompatible type"
|
||||
errors.errors[6] shouldContain "value has incompatible type"
|
||||
errors.errors[7] shouldContain "value has incompatible type"
|
||||
}
|
||||
|
||||
test("array assignments should work via array copy call") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte[] array = [1,2,3]
|
||||
ubyte[3] array2
|
||||
float[] flarray = [1.1, 2.2, 3.3]
|
||||
float[3] flarray2
|
||||
word[] warray = [-2222,42,3333]
|
||||
word[3] warray2
|
||||
str[] names = ["apple", "banana", "tomato"]
|
||||
str[3] names2
|
||||
|
||||
; 8 array assignments -> 8 arraycopies:
|
||||
array = [8,7,6]
|
||||
array = array2
|
||||
flarray = [99.9, 88.8, 77.7]
|
||||
flarray = flarray2
|
||||
warray = [4444,5555,6666]
|
||||
warray = warray2
|
||||
names = ["x1", "x2", "x3"]
|
||||
names = names2
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
|
||||
val x = result.codegenAst!!.entrypoint()!!
|
||||
(x.children[12] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
(x.children[13] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
(x.children[14] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
(x.children[15] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
(x.children[16] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
(x.children[17] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
(x.children[18] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
(x.children[19] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -110,8 +110,8 @@ class TestVariables: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "initialisation value has incompatible type"
|
||||
errors.errors[1] shouldContain "initialisation value has incompatible type"
|
||||
errors.errors[0] shouldContain "value has incompatible type"
|
||||
errors.errors[1] shouldContain "value has incompatible type"
|
||||
}
|
||||
|
||||
test("initialization of boolean array with single value") {
|
||||
|
@ -34,6 +34,8 @@ Data types
|
||||
You'll have to add explicit casts to increase the size of the value if required.
|
||||
For example when adding two byte variables having values 100 and 200, the result won't be 300, because that doesn't fit in a byte. It will be 44.
|
||||
You'll have to cast one or both of the *operands* to a word type first if you want to accomodate the actual result value of 300.
|
||||
- Arrays and strings have a limited size and the allocated size never changes
|
||||
- Arrays and strings are mutable
|
||||
|
||||
|
||||
Variables
|
||||
@ -85,10 +87,10 @@ Foreign function interface (external/ROM calls)
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
- Prog8 contains many compiler optimizations to generate efficent code, but also lacks many optimizations that modern compilers do have.
|
||||
- Prog8 contains many compiler optimizations to generate efficient code, but also lacks many optimizations that modern compilers do have.
|
||||
While empirical evidence shows that Prog8 generates more efficent code than some C compilers that also target the same 8 bit systems,
|
||||
it still is limited in how sophisticated the optimizations are that it performs on your code.
|
||||
- For time critical code, it may be worth it to inspect the generated assembly code to see if you could write things differently
|
||||
the optimizations it makes on your code aren't super sophisticated.
|
||||
- For time critical code, it may be worth it to inspect the generated assembly code to see if you can write things differently
|
||||
to help the compiler generate more efficient code (or even replace it with hand written inline assembly altogether).
|
||||
For example, if you repeat an expression multiple times it will be evaluated every time, so maybe you should store it
|
||||
in a variable instead and reuse that variable::
|
||||
|
@ -88,30 +88,28 @@ Features
|
||||
- provides a convenient and fast edit/compile/run cycle by being able to directly launch
|
||||
the compiled program in an emulator and provide debugging information to this emulator.
|
||||
- the language looks like a mix of Python and C so should be quite easy to learn
|
||||
- Modular programming, scoping via modules, code blocks, and subroutines. No need for forward declarations.
|
||||
- Provide high level programming constructs but at the same time stay close to the metal;
|
||||
- Modular programming, scoping via module source files, code blocks, and subroutines. No need for forward declarations.
|
||||
- Provides high level programming constructs but at the same time stay close to the metal;
|
||||
still able to directly use memory addresses and ROM subroutines,
|
||||
and inline assembly to have full control when every register, cycle or byte matters
|
||||
- Subroutines with parameters and return values of various types
|
||||
- Complex nested expressions are possible
|
||||
- Variables are all allocated statically, no memory allocator overhead
|
||||
- Variables are all allocated statically, no memory allocation overhead
|
||||
- Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
|
||||
- ``when`` statement to avoid if-else chains
|
||||
- ``in`` expression for concise and efficient multi-value/containment test
|
||||
- Several specialized built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
- Variable data types include signed and unsigned bytes and words, arrays, strings.
|
||||
- Various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||
- Floating point math is supported on select compiler targets.
|
||||
- Floating point math is supported on certain compiler targets.
|
||||
- Easy and highly efficient integration with external subroutines and ROM routines on the target systems.
|
||||
- Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents.
|
||||
- Encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
|
||||
- Identifiers can contain Unicode Letters, so ``knäckebröd``, ``приблизительно``, ``見せしめ`` and ``π`` are all valid identifiers.
|
||||
- Advanced code optimizations to make the resulting program smaller and faster
|
||||
- Programs can be restarted after exiting (i.e. run them multiple times without having to reload everything), due to automatic variable (re)initializations.
|
||||
- Supports the sixteen 'virtual' 16-bit registers R0 to R15 as defined on the Commander X16. These are also available on the other compilation targets!
|
||||
- Supports the sixteen 'virtual' 16-bit registers R0 to R15 as defined on the Commander X16. You can look at them as general purpose global variables. These are also available on the other compilation targets!
|
||||
- On the Commander X16: Support for low level system features such as Vera Fx, which includes 16x16 bits multiplication in hardware and fast memory copy and fill.
|
||||
- Many library routines are available across compiler targets. This means that as long as you only use standard Kernal
|
||||
and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines (just change the compilation target flag).
|
||||
and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines by just changing the compilation target flag.
|
||||
|
||||
|
||||
Code example
|
||||
|
@ -312,9 +312,9 @@ Note that the various keywords for the data type and variable type (``byte``, ``
|
||||
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
||||
for instance.
|
||||
|
||||
It's possible to assign an array to another array; this will overwrite all elements in the target
|
||||
array with those in the source array. The number and types of elements have to match for this to work!
|
||||
For large arrays this is a slow operation because every element is copied over. It should probably be avoided.
|
||||
It is possible to assign an array (variable or array literal) to another array; this will overwrite all elements in the target
|
||||
array with those in the source array. The number of elements in the arrays and the data types have to match.
|
||||
For large arrays this is a slow operation because all values are copied over.
|
||||
|
||||
Using the ``in`` operator you can easily check if a value is present in an array,
|
||||
example: ``if choice in [1,2,3,4] {....}``
|
||||
|
@ -10,6 +10,11 @@ Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEva
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- remove support for array variable initialization with a single value, just require explicitly creating the value array [42] * 10 (which is what the compiler now does for you implicitly)
|
||||
- should the array-to-array assignment support be removed and instead require an explicit copy function call? What prog8_lib_arraycopy() now does.
|
||||
- should we add a cleararray builtin function that can efficiently set every element in the array to the given value
|
||||
|
||||
- improve detection that a variable is not read before being written so that initializing it to zero can be omitted (only happens now if a vardecl is immediately followed by a for loop for instance)
|
||||
- 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
|
||||
- Can we support signed % (remainder) somehow?
|
||||
- Don't add "random" rts to %asm blocks but instead give a warning about it? (but this breaks existing behavior that others already depend on... command line switch? block directive?)
|
||||
|
@ -1,7 +1,6 @@
|
||||
%import syslib
|
||||
%import textio
|
||||
%import math
|
||||
%import test_stack
|
||||
|
||||
|
||||
spritedata $2000 {
|
||||
@ -97,8 +96,6 @@ main {
|
||||
txt.print(" jiffies/fr = ")
|
||||
txt.print_ub(60/cbm.TIME_LO)
|
||||
txt.print(" fps")
|
||||
|
||||
; test_stack.test()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
%import syslib
|
||||
%import test_stack
|
||||
%import textio
|
||||
%import math
|
||||
|
||||
@ -35,8 +34,6 @@ main {
|
||||
txt.print_ub(60/cbm.TIME_LO)
|
||||
txt.print(" fps")
|
||||
cbm.TIME_LO=0
|
||||
|
||||
; test_stack.test()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
%import syslib
|
||||
%import textio
|
||||
%import math
|
||||
%import test_stack
|
||||
|
||||
|
||||
main {
|
||||
@ -68,9 +67,6 @@ waitkey:
|
||||
}
|
||||
|
||||
drawScore()
|
||||
|
||||
; txt.plot(0,0)
|
||||
; test_stack.test()
|
||||
}
|
||||
|
||||
ubyte key
|
||||
|
@ -1,6 +1,5 @@
|
||||
%import floats
|
||||
%import textio
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
@ -37,8 +36,6 @@ main {
|
||||
txt.print(" jiffies/fr = ")
|
||||
txt.print_ub(60/jiffies)
|
||||
txt.print(" fps")
|
||||
|
||||
;test_stack.test()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,6 @@ main {
|
||||
anglex += 317
|
||||
angley -= 505
|
||||
anglez += 452
|
||||
|
||||
; test_stack.test()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,85 +0,0 @@
|
||||
%import monogfx
|
||||
%import floats
|
||||
%import textio
|
||||
%import math
|
||||
%zeropage dontuse
|
||||
|
||||
main {
|
||||
|
||||
sub start () {
|
||||
monogfx.text_charset(3)
|
||||
|
||||
test_monochrome()
|
||||
|
||||
monogfx.textmode()
|
||||
txt.print("done!\n")
|
||||
}
|
||||
|
||||
sub test_monochrome() {
|
||||
monogfx.hires()
|
||||
uword yy
|
||||
uword xx
|
||||
word ww
|
||||
|
||||
yy = 20
|
||||
xx = 20
|
||||
monogfx.drawmode(monogfx.MODE_NORMAL)
|
||||
monogfx.rect(xx, yy, 250, 80, true)
|
||||
monogfx.drawmode(monogfx.MODE_STIPPLE)
|
||||
monogfx.fillrect(xx+2, yy+2, 250-4, 80-4, true)
|
||||
monogfx.drawmode(monogfx.MODE_NORMAL)
|
||||
monogfx.fillrect(xx+20, yy+20, 200, 30, true)
|
||||
monogfx.rect(xx+21, yy+21, 200-2, 30-2, false)
|
||||
|
||||
monogfx.text(xx+30, yy+32, false, sc:"High Res Bitmap Example")
|
||||
|
||||
; monogfx.drawmode(monogfx.MODE_STIPPLE)
|
||||
monogfx.horizontal_line(10, 240, 620, true)
|
||||
monogfx.vertical_line(320, 10, 460, true)
|
||||
monogfx.text(320, 242, true, sc:"0,0")
|
||||
monogfx.text(322, 10, true, sc:"Y-axis")
|
||||
monogfx.text(590, 242, true, sc:"X-axis")
|
||||
for ww in -10 to 10 {
|
||||
xx = (ww*30) + 320 as uword
|
||||
monogfx.vertical_line(xx, 239, 3, true)
|
||||
}
|
||||
for ww in -7 to 7 {
|
||||
yy = (ww*30) + 240 as uword
|
||||
monogfx.horizontal_line(319, yy, 3, true)
|
||||
}
|
||||
|
||||
monogfx.drawmode(monogfx.MODE_NORMAL)
|
||||
float y_f
|
||||
for ww in -600 to 600 {
|
||||
y_f = floats.sin(ww as float / 60.0)*150
|
||||
monogfx.plot(ww/2 + 320 as uword, (y_f + 240) as uword, true)
|
||||
}
|
||||
monogfx.text(480, 100, true, sc:"sin(x)")
|
||||
|
||||
for ww in -300 to 300 {
|
||||
y_f = floats.cos(ww as float/30.0)*60 - (ww as float)/1.7
|
||||
monogfx.plot(ww + 320 as uword, (y_f + 240) as uword, true)
|
||||
}
|
||||
monogfx.text(80, 420, true, sc:"cos(x)+x")
|
||||
|
||||
sys.wait(3*60)
|
||||
|
||||
monogfx.circle(320, 240, 220, true)
|
||||
monogfx.circle(320, 240, 210, true)
|
||||
monogfx.circle(320, 240, 200, true)
|
||||
monogfx.circle(320, 240, 190, true)
|
||||
monogfx.drawmode(monogfx.MODE_STIPPLE)
|
||||
monogfx.disc(320, 240, 140, true)
|
||||
monogfx.drawmode(monogfx.MODE_NORMAL)
|
||||
monogfx.disc(320, 240, 90, true)
|
||||
monogfx.disc(320, 240, 40, false)
|
||||
|
||||
sys.wait(2*60)
|
||||
|
||||
monogfx.drawmode(monogfx.MODE_INVERT)
|
||||
repeat 255
|
||||
monogfx.line(math.rndw() % 640, math.rndw() % 480, math.rndw() % 640, math.rndw() % 480, true)
|
||||
|
||||
sys.wait(1*60)
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@
|
||||
%import syslib
|
||||
%import textio
|
||||
%import math
|
||||
%import test_stack
|
||||
%import psg
|
||||
|
||||
main {
|
||||
@ -90,9 +89,6 @@ waitkey:
|
||||
}
|
||||
|
||||
drawScore()
|
||||
|
||||
; txt.plot(0,0)
|
||||
; test_stack.test()
|
||||
}
|
||||
|
||||
ubyte key
|
||||
|
@ -1,7 +1,6 @@
|
||||
%import textio
|
||||
%import diskio
|
||||
%zeropage basicsafe
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
@ -53,7 +52,5 @@ main {
|
||||
} else {
|
||||
txt.print("error\n")
|
||||
}
|
||||
|
||||
; test_stack.test()
|
||||
}
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
%import graphics
|
||||
%import test_stack
|
||||
%import math
|
||||
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
graphics.enable_bitmap_mode()
|
||||
|
||||
draw_lines()
|
||||
draw_circles()
|
||||
draw_rects()
|
||||
|
||||
; graphics.disable_bitmap_mode()
|
||||
; test_stack.test()
|
||||
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
|
||||
sub draw_rects() {
|
||||
graphics.rect(220,10,80,10)
|
||||
graphics.rect(20,180,80,10)
|
||||
graphics.fillrect(220,30,80,10)
|
||||
graphics.fillrect(20,160,80,10)
|
||||
}
|
||||
|
||||
|
||||
sub draw_circles() {
|
||||
ubyte xx
|
||||
for xx in 3 to 7 {
|
||||
graphics.circle(xx*50-100, 10+xx*16, (xx+6)*4)
|
||||
graphics.disc(xx*50-100, 10+xx*16, (xx+6)*2)
|
||||
}
|
||||
}
|
||||
|
||||
sub draw_lines() {
|
||||
ubyte i
|
||||
for i in 0 to 255 step 4 {
|
||||
uword x1 = ((graphics.WIDTH-256)/2 as uword) + math.sin8u(i)
|
||||
uword y1 = (graphics.HEIGHT-128)/2 + math.cos8u(i)/2
|
||||
uword x2 = ((graphics.WIDTH-64)/2 as uword) + math.sin8u(i)/4
|
||||
uword y2 = (graphics.HEIGHT-64)/2 + math.cos8u(i)/4
|
||||
graphics.line(x1, lsb(y1), x2, lsb(y2))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
%import textio
|
||||
%import syslib
|
||||
%zeropage basicsafe
|
||||
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
txt.print("rectangles\nand circle\ndrawing.\n")
|
||||
|
||||
ubyte r
|
||||
for r in 3 to 12 step 3 {
|
||||
circle(20, 12, r)
|
||||
}
|
||||
|
||||
txt.print("enter for disc:")
|
||||
void cbm.CHRIN()
|
||||
txt.nl()
|
||||
txt.clear_screen()
|
||||
disc(20, 12, 12)
|
||||
|
||||
txt.print("enter for rectangles:")
|
||||
void cbm.CHRIN()
|
||||
txt.nl()
|
||||
txt.clear_screen()
|
||||
|
||||
rect(4, 8, 37, 23, false)
|
||||
rect(20, 12, 30, 20, true)
|
||||
rect(10, 10, 10, 10, false)
|
||||
rect(6, 0, 16, 20, true)
|
||||
|
||||
|
||||
sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, bool fill) {
|
||||
ubyte x
|
||||
ubyte y
|
||||
if fill {
|
||||
for y in y1 to y2 {
|
||||
for x in x1 to x2 {
|
||||
txt.setcc(x, y, 42, x+y)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for x in x1 to x2 {
|
||||
txt.setcc(x, y1, 42, 8)
|
||||
txt.setcc(x, y2, 42, 8)
|
||||
}
|
||||
if y2>y1 {
|
||||
for y in y1+1 to y2-1 {
|
||||
txt.setcc(x1, y, 42, 7)
|
||||
txt.setcc(x2, y, 42, 7)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub circle(ubyte xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Midpoint algorithm
|
||||
ubyte x = radius
|
||||
ubyte y = 0
|
||||
byte decisionOver2 = 1-x as byte
|
||||
|
||||
while x>=y {
|
||||
txt.setcc(xcenter + x, ycenter + y, 81, 1)
|
||||
txt.setcc(xcenter - x, ycenter + y, 81, 2)
|
||||
txt.setcc(xcenter + x, ycenter - y, 81, 3)
|
||||
txt.setcc(xcenter - x, ycenter - y, 81, 4)
|
||||
txt.setcc(xcenter + y, ycenter + x, 81, 5)
|
||||
txt.setcc(xcenter - y, ycenter + x, 81, 6)
|
||||
txt.setcc(xcenter + y, ycenter - x, 81, 7)
|
||||
txt.setcc(xcenter - y, ycenter - x, 81, 8)
|
||||
y++
|
||||
if decisionOver2>=0 {
|
||||
x--
|
||||
decisionOver2 -= 2*x
|
||||
}
|
||||
decisionOver2 += 2*y
|
||||
decisionOver2++
|
||||
}
|
||||
}
|
||||
|
||||
sub disc(ubyte cx, ubyte cy, ubyte radius) {
|
||||
; Midpoint algorithm, filled
|
||||
; NOTE: because of the symmetry drawing, some horizontal spans will be drawn multiple times.
|
||||
; because this is only a very low res (text tiles) disc, it's not worth optimizing that.
|
||||
ubyte x = radius
|
||||
ubyte y = 0
|
||||
byte decisionOver2 = 1-x as byte
|
||||
ubyte xx
|
||||
|
||||
while x>=y {
|
||||
xx = cx-x
|
||||
repeat 2*x+1 {
|
||||
txt.setcc(xx, cy + y, 81, 11)
|
||||
txt.setcc(xx, cy - y, 81, 12)
|
||||
xx++
|
||||
}
|
||||
xx = cx-y
|
||||
repeat 2*y+1 {
|
||||
txt.setcc(xx, cy + x, 81, 13)
|
||||
txt.setcc(xx, cy - x, 81, 14)
|
||||
xx++
|
||||
}
|
||||
y++
|
||||
if decisionOver2>=0 {
|
||||
x--
|
||||
decisionOver2 -= 2*x
|
||||
}
|
||||
decisionOver2 += 2*y
|
||||
decisionOver2++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
%import graphics
|
||||
%import floats
|
||||
%import test_stack
|
||||
%zeropage floatsafe
|
||||
|
||||
; Draw a mandelbrot in graphics mode (the image will be 256 x 200 pixels).
|
||||
@ -45,9 +44,6 @@ main {
|
||||
}
|
||||
}
|
||||
|
||||
; graphics.disable_bitmap_mode()
|
||||
; test_stack.test()
|
||||
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
@ -45,7 +44,5 @@ main {
|
||||
txt.print("finished in ")
|
||||
floats.print(duration)
|
||||
txt.print(" seconds!\n")
|
||||
|
||||
; test_stack.test()
|
||||
}
|
||||
}
|
||||
|
@ -1,113 +0,0 @@
|
||||
%import graphics
|
||||
%import math
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
; Draw sine and cosine graphs. The sine and cosine functions are table lookups
|
||||
; where the tables are generated by 64tass list functions.
|
||||
|
||||
; This uses the graphics library which works on multiple targets (C64, CX16)
|
||||
|
||||
main {
|
||||
const uword width = 320
|
||||
const ubyte height = 200
|
||||
|
||||
sub start() {
|
||||
graphics.enable_bitmap_mode()
|
||||
|
||||
sincos255()
|
||||
sys.wait(60)
|
||||
|
||||
graphics.clear_screen(1, 0)
|
||||
|
||||
sincos180()
|
||||
sys.wait(60)
|
||||
|
||||
graphics.clear_screen(1, 0)
|
||||
circles()
|
||||
sys.wait(60)
|
||||
|
||||
graphics.disable_bitmap_mode()
|
||||
txt.print("done\n")
|
||||
}
|
||||
|
||||
sub sincos255() {
|
||||
graphics.line(256,0,256,height-1)
|
||||
|
||||
ubyte pixelyb
|
||||
ubyte pixelxb
|
||||
byte pixelys
|
||||
|
||||
; unsigned
|
||||
for pixelxb in 0 to 255 {
|
||||
pixelyb = math.cos8u(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
pixelyb = math.sin8u(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
}
|
||||
|
||||
; signed
|
||||
for pixelxb in 0 to 255 {
|
||||
pixelys = math.cos8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
pixelys = math.sin8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
}
|
||||
}
|
||||
|
||||
sub sincos180() {
|
||||
graphics.line(180,0,180,height-1)
|
||||
|
||||
ubyte pixelyb
|
||||
ubyte pixelxb
|
||||
byte pixelys
|
||||
|
||||
; signed
|
||||
for pixelxb in 0 to 179 {
|
||||
pixelyb = math.cosr8u(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
pixelyb = math.sinr8u(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelyb+20)
|
||||
}
|
||||
|
||||
; unsigned
|
||||
for pixelxb in 0 to 179 {
|
||||
pixelys = math.cosr8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
pixelys = math.sinr8(pixelxb) / 2
|
||||
graphics.plot(pixelxb, pixelys as ubyte + 90)
|
||||
}
|
||||
}
|
||||
|
||||
sub circles() {
|
||||
ubyte pixelyb
|
||||
uword pixelxw
|
||||
ubyte r
|
||||
|
||||
; circle with "degrees" from 0 to 255
|
||||
for r in 0 to 255 {
|
||||
pixelxw = (math.sin8(r)/2 as word) + 80 as uword
|
||||
pixelyb = (math.cos8(r)/2 as uword + height/2) as ubyte
|
||||
graphics.plot(pixelxw, pixelyb)
|
||||
}
|
||||
|
||||
for r in 0 to 255 {
|
||||
pixelxw = math.sin8u(r)/2
|
||||
pixelyb = math.cos8u(r)/2
|
||||
graphics.plot(pixelxw + 16, pixelyb+50)
|
||||
}
|
||||
|
||||
; circle with half-degrees from 0 to 179 (=full degrees 0..358 with steps of 2 degrees)
|
||||
for r in 0 to 179 {
|
||||
pixelxw = (math.sinr8(r) as word /2 + 220) as uword
|
||||
pixelyb = (math.cosr8(r)/2 + height/2) as ubyte
|
||||
graphics.plot(pixelxw, pixelyb)
|
||||
}
|
||||
|
||||
for r in 0 to 179 {
|
||||
pixelxw = math.sinr8u(r)/2
|
||||
pixelyb = math.cosr8u(r)/2
|
||||
graphics.plot(pixelxw + 156, pixelyb+50)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +1,25 @@
|
||||
%import palette
|
||||
%import textio
|
||||
%option no_sysinit
|
||||
%option no_sysinit, enable_floats
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
repeat 4 {
|
||||
for cx16.r0L in 0 to 15 {
|
||||
txt.color2(cx16.r0L, cx16.r0L)
|
||||
txt.spc()
|
||||
txt.spc()
|
||||
txt.spc()
|
||||
txt.spc()
|
||||
}
|
||||
txt.nl()
|
||||
}
|
||||
bool changed
|
||||
uword[] colors = [
|
||||
$f00, $800, $200, $000,
|
||||
$f0f, $80f, $20f, $00f
|
||||
]
|
||||
do {
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
changed = palette.fade_step_colors(0, 8, colors)
|
||||
} until not changed
|
||||
ubyte[] array = [1,2,3]
|
||||
ubyte[3] array2
|
||||
float[] flarray = [1.1, 2.2, 3.3]
|
||||
float[3] flarray2
|
||||
word[] warray = [-2222,42,3333]
|
||||
word[3] warray2
|
||||
str[] names = ["apple", "banana", "tomato"]
|
||||
str[3] names2
|
||||
|
||||
sys.wait(60)
|
||||
changed = false
|
||||
do {
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
changed = palette.fade_step_multi(0, 8, $fff)
|
||||
} until not changed
|
||||
sys.wait(60)
|
||||
; 8 array assignments -> 8 arraycopies:
|
||||
array = [8,7,6]
|
||||
array = array2
|
||||
flarray = [99.9, 88.8, 77.7]
|
||||
flarray = flarray2
|
||||
warray = [4444,5555,6666]
|
||||
warray = warray2
|
||||
names = ["x1", "x2", "x3"]
|
||||
names = names2
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user