mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c86c0912f8 | ||
|
|
268b0c9365 | ||
|
|
099fe280ba | ||
|
|
f786f60e9c | ||
|
|
f40e1eb1f2 | ||
|
|
8b9da65357 | ||
|
|
2cbbe0d48a | ||
|
|
b6e1fb3ba8 | ||
|
|
bdccffbb8e | ||
|
|
5a85474712 | ||
|
|
f50899c6fa | ||
|
|
4daa909f32 | ||
|
|
4555edf369 | ||
|
|
529ea5bf58 | ||
|
|
fe011de934 | ||
|
|
0653d430a7 | ||
|
|
a587f6e9a0 | ||
|
|
3850e1dbb5 | ||
|
|
91cde072e0 | ||
|
|
2ca4aed566 | ||
|
|
5071da6784 | ||
|
|
4c1e2f3110 | ||
|
|
2727a4dcb3 | ||
|
|
126d4c69e6 |
No files matched your search
@@ -59,6 +59,7 @@ What does Prog8 provide?
|
||||
- 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 on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
- 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
|
||||
- high-level program optimizations
|
||||
|
||||
@@ -42,6 +42,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
||||
}
|
||||
|
||||
val allVariables: Collection<StStaticVariable> by lazy {
|
||||
// can't be done with a generic function because those don't support local recursive functions yet
|
||||
val vars = mutableListOf<StStaticVariable>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
@@ -56,6 +57,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
||||
}
|
||||
|
||||
val allMemMappedVariables: Collection<StMemVar> by lazy {
|
||||
// can't be done with a generic function because those don't support local recursive functions yet
|
||||
val vars = mutableListOf<StMemVar>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
@@ -70,6 +72,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
||||
}
|
||||
|
||||
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
||||
// can't be done with a generic function because those don't support local recursive functions yet
|
||||
val vars = mutableListOf<StMemorySlab>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
@@ -113,7 +116,7 @@ enum class StNodeType {
|
||||
|
||||
open class StNode(val name: String,
|
||||
val type: StNodeType,
|
||||
val astNode: PtNode,
|
||||
val astNode: PtNode?,
|
||||
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||
) {
|
||||
|
||||
@@ -187,7 +190,7 @@ class StStaticVariable(name: String,
|
||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||
val zpwish: ZeropageWish, // used in the variable allocator
|
||||
val align: Int,
|
||||
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
|
||||
astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) {
|
||||
|
||||
var initializationNumericValue: Double? = null
|
||||
private set
|
||||
@@ -229,7 +232,7 @@ class StStaticVariable(name: String,
|
||||
}
|
||||
|
||||
|
||||
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode) :
|
||||
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode?) :
|
||||
StNode(name, StNodeType.CONSTANT, astNode)
|
||||
|
||||
|
||||
@@ -237,7 +240,7 @@ class StMemVar(name: String,
|
||||
val dt: DataType,
|
||||
val address: UInt,
|
||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||
astNode: PtNode) :
|
||||
astNode: PtNode?) :
|
||||
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||
|
||||
init{
|
||||
@@ -251,7 +254,7 @@ class StMemorySlab(
|
||||
name: String,
|
||||
val size: UInt,
|
||||
val align: UInt,
|
||||
astNode: PtNode
|
||||
astNode: PtNode?
|
||||
):
|
||||
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
||||
|
||||
|
||||
@@ -55,43 +55,18 @@ val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
|
||||
|
||||
sealed class SubType(val dt: BaseDataType) {
|
||||
companion object {
|
||||
private val types by lazy {
|
||||
// lazy because of static initialization order
|
||||
mapOf(
|
||||
BaseDataType.UBYTE to SubUnsignedByte,
|
||||
BaseDataType.BYTE to SubSignedByte,
|
||||
BaseDataType.UWORD to SubUnsignedWord,
|
||||
BaseDataType.WORD to SubSignedWord,
|
||||
BaseDataType.FLOAT to SubFloat,
|
||||
BaseDataType.BOOL to SubBool
|
||||
)}
|
||||
|
||||
fun forDt(dt: BaseDataType) = types.getValue(dt)
|
||||
}
|
||||
}
|
||||
|
||||
data object SubUnsignedByte: SubType(BaseDataType.UBYTE)
|
||||
data object SubSignedByte: SubType(BaseDataType.BYTE)
|
||||
data object SubUnsignedWord: SubType(BaseDataType.UWORD)
|
||||
data object SubSignedWord: SubType(BaseDataType.WORD)
|
||||
data object SubBool: SubType(BaseDataType.BOOL)
|
||||
data object SubFloat: SubType(BaseDataType.FLOAT)
|
||||
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||
|
||||
init {
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub.dt == BaseDataType.UWORD || sub.dt == BaseDataType.WORD)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
else if(base==BaseDataType.STR)
|
||||
require(sub?.dt==BaseDataType.UBYTE) { "STR subtype should be ubyte" }
|
||||
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
else
|
||||
require(sub == null)
|
||||
require(sub == null) { "only string and array base types can have a subtype"}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -111,7 +86,7 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, SubUnsignedByte),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
)
|
||||
|
||||
@@ -119,10 +94,14 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && elementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, SubType.forDt(actualElementDt))
|
||||
else
|
||||
DataType(BaseDataType.ARRAY, SubType.forDt(actualElementDt))
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
else
|
||||
throw NoSuchElementException("invalid element dt "+elementDt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,26 +112,26 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
|
||||
fun elementType(): DataType =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!.dt)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
SubBool -> "bool[]"
|
||||
SubFloat -> "float[]"
|
||||
SubSignedByte -> "byte[]"
|
||||
SubSignedWord -> "word[]"
|
||||
SubUnsignedByte -> "ubyte[]"
|
||||
SubUnsignedWord -> "uword[]"
|
||||
null -> throw IllegalArgumentException("invalid sub type")
|
||||
BaseDataType.BOOL -> "bool[]"
|
||||
BaseDataType.FLOAT -> "float[]"
|
||||
BaseDataType.BYTE -> "byte[]"
|
||||
BaseDataType.WORD -> "word[]"
|
||||
BaseDataType.UBYTE -> "ubyte[]"
|
||||
BaseDataType.UWORD -> "uword[]"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
SubSignedWord -> "word[] (split)"
|
||||
SubUnsignedWord -> "uword[] (split)"
|
||||
BaseDataType.WORD -> "word[] (split)"
|
||||
BaseDataType.UWORD -> "uword[] (split)"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
@@ -170,19 +149,19 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
SubUnsignedByte -> "ubyte["
|
||||
SubUnsignedWord -> "@nosplit uword["
|
||||
SubBool -> "bool["
|
||||
SubSignedByte -> "byte["
|
||||
SubSignedWord -> "@nosplit word["
|
||||
SubFloat -> "float["
|
||||
null -> throw IllegalArgumentException("invalid sub type")
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
BaseDataType.UWORD -> "@nosplit uword["
|
||||
BaseDataType.BOOL -> "bool["
|
||||
BaseDataType.BYTE -> "byte["
|
||||
BaseDataType.WORD -> "@nosplit word["
|
||||
BaseDataType.FLOAT -> "float["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
SubUnsignedWord -> "uword["
|
||||
SubSignedWord -> "word["
|
||||
BaseDataType.UWORD -> "uword["
|
||||
BaseDataType.WORD -> "word["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
@@ -228,22 +207,22 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isBoolArray = base.isArray && sub?.dt == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub?.dt == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && sub?.dt == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && (sub?.dt == BaseDataType.UWORD || sub?.dt == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && sub?.dt == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && sub?.dt == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && sub?.dt == BaseDataType.FLOAT
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
val isFloat = base == BaseDataType.FLOAT
|
||||
val isLong = base == BaseDataType.LONG
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE))
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||
val isSplitWordArray = base.isSplitWordArray
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.WORD
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
|
||||
@@ -2,5 +2,14 @@ package prog8.code.core
|
||||
|
||||
interface IMemSizer {
|
||||
fun memorySize(dt: DataType, numElements: Int?): Int
|
||||
fun memorySize(dt: SubType): Int
|
||||
|
||||
fun memorySize(dt: BaseDataType): Int {
|
||||
if(dt.isPassByRef)
|
||||
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size
|
||||
try {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
} catch (x: NoSuchElementException) {
|
||||
throw IllegalArgumentException(x.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
|
||||
return bittestCall
|
||||
}
|
||||
|
||||
fun isAndByteCondition(condition: PtBinaryExpression?): Triple<PtBinaryExpression, PtIdentifier, Int>? {
|
||||
fun isAndByteConditionForBRK(condition: PtBinaryExpression?): Triple<PtBinaryExpression, PtIdentifier, Int>? {
|
||||
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
|
||||
if (condition.right.asConstInteger() == 0) {
|
||||
val and = condition.left as? PtBinaryExpression
|
||||
@@ -119,7 +119,7 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if(node is PtIfElse) {
|
||||
val condition = node.condition as? PtBinaryExpression
|
||||
val check = isAndByteCondition(condition)
|
||||
val check = isAndByteConditionForBRK(condition)
|
||||
if(check!=null) {
|
||||
val (and, variable, bitmask) = check
|
||||
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
|
||||
@@ -137,7 +137,7 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
|
||||
}
|
||||
if (node is PtIfExpression) {
|
||||
val condition = node.condition as? PtBinaryExpression
|
||||
val check = isAndByteCondition(condition)
|
||||
val check = isAndByteConditionForBRK(condition)
|
||||
if(check!=null) {
|
||||
val (and, variable, bitmask) = check
|
||||
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
|
||||
|
||||
@@ -2,18 +2,32 @@ package prog8.code.source
|
||||
|
||||
import prog8.code.core.Position
|
||||
import java.nio.file.Path
|
||||
import java.util.TreeMap
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// Resource caching "filesystem".
|
||||
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
|
||||
|
||||
object ImportFileSystem {
|
||||
|
||||
fun expandTilde(path: String): String = if (path.startsWith("~")) {
|
||||
val userHome = System.getProperty("user.home")
|
||||
userHome + path.drop(1)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
|
||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||
|
||||
fun getFile(path: Path): SourceCode {
|
||||
val cached = cache[path.toString()]
|
||||
if (cached != null) return cached
|
||||
val file = SourceCode.File(path)
|
||||
cache[path.toString()] = file
|
||||
val normalized = path.absolute().normalize()
|
||||
val cached = cache[normalized.toString()]
|
||||
if (cached != null)
|
||||
return cached
|
||||
val file = SourceCode.File(normalized)
|
||||
cache[normalized.toString()] = file
|
||||
return file
|
||||
}
|
||||
|
||||
@@ -34,11 +48,11 @@ object ImportFileSystem {
|
||||
val cached = cache[position.file]
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
val path = Path(position.file).toAbsolutePath().normalize()
|
||||
val path = Path(position.file).absolute().normalize()
|
||||
val cached2 = cache[path.toString()]
|
||||
if(cached2 != null)
|
||||
return getLine(cached2, position.line)
|
||||
throw NoSuchElementException("cannot get source line $position")
|
||||
throw NoSuchElementException("cannot get source line $position, with path $path")
|
||||
}
|
||||
|
||||
private fun getLine(code: SourceCode, lineIndex: Int): String {
|
||||
@@ -55,6 +69,6 @@ object ImportFileSystem {
|
||||
|
||||
private class LineSpan(val start: Int, val end: Int)
|
||||
|
||||
private val cache = mutableMapOf<String, SourceCode>()
|
||||
private val cache = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER)
|
||||
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.text.Normalizer
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.readText
|
||||
|
||||
/**
|
||||
@@ -52,8 +53,8 @@ sealed class SourceCode {
|
||||
*/
|
||||
private const val LIBRARYFILEPREFIX = "library:"
|
||||
private const val STRINGSOURCEPREFIX = "string:"
|
||||
val curdir: Path = Path(".").toAbsolutePath()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
||||
val curdir: Path = Path(".").absolute()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.absolute())
|
||||
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||
|
||||
@@ -15,23 +15,26 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
}
|
||||
}
|
||||
@@ -15,23 +15,26 @@ class Neo6502Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
}
|
||||
}
|
||||
@@ -14,23 +14,26 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@ class C128MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xff00u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
@@ -20,7 +20,7 @@ class C64MachineDefinition: IMachineDefinition {
|
||||
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = 0xcfffu
|
||||
override val BSSHIGHRAM_END = 0xcfdfu
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
|
||||
@@ -3,28 +3,31 @@ package prog8.code.target.cbm
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.SubType
|
||||
|
||||
|
||||
internal object CbmMemorySizer: IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * Mflpt5.FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> Mflpt5.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import prog8.code.source.SourceCode
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.writeLines
|
||||
|
||||
|
||||
@@ -59,7 +60,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
}
|
||||
is PtFunctionCall -> {
|
||||
val stNode = st.lookup(node.name)!!
|
||||
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
functionCallsToPrefix += node.parent to index
|
||||
}
|
||||
@@ -70,7 +71,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
lookupName = lookupName.dropLast(4)
|
||||
}
|
||||
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node")
|
||||
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
}
|
||||
@@ -1083,8 +1084,8 @@ $repeatLabel""")
|
||||
val sourcePath = Path(incbin.definingBlock()!!.source.origin)
|
||||
val includedPath = sourcePath.resolveSibling(incbin.file)
|
||||
val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file*
|
||||
.toAbsolutePath()
|
||||
.relativize(includedPath.toAbsolutePath())
|
||||
.absolute()
|
||||
.relativize(includedPath.absolute())
|
||||
.normalize() // avoid assembler warnings (-Wportable; only some, not all)
|
||||
.toString().replace('\\', '/')
|
||||
out(" .binary \"$pathForAssembler\" $offset $length")
|
||||
@@ -1268,7 +1269,7 @@ $repeatLabel""")
|
||||
val node = stScope.astNode
|
||||
if(node is PtSubroutineParameter)
|
||||
return node
|
||||
return node.definingSub()?.parameters?.singleOrNull { it.name===name }
|
||||
return node!!.definingSub()?.parameters?.singleOrNull { it.name===name }
|
||||
}
|
||||
|
||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||
|
||||
@@ -644,32 +644,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
}
|
||||
is PtNumber -> {
|
||||
val num = (fcall.args[0] as PtNumber).number + if(msb) 1 else 0
|
||||
val mem = PtMemoryByte(fcall.position)
|
||||
mem.add(PtNumber(BaseDataType.UBYTE, num, fcall.position))
|
||||
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
val addrof = fcall.args[0] as PtAddressOf
|
||||
if(addrof.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
} else {
|
||||
val mem = PtMemoryByte(fcall.position)
|
||||
if(addrof.isFromArrayElement)
|
||||
TODO("address-of arrayelement")
|
||||
if(msb) {
|
||||
val address = PtBinaryExpression("+", DataType.forDt(BaseDataType.UWORD), addrof.position)
|
||||
address.add(addrof)
|
||||
address.add(PtNumber(address.type.base, 1.0, addrof.position))
|
||||
mem.add(address)
|
||||
} else {
|
||||
mem.add(addrof)
|
||||
}
|
||||
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
|
||||
}
|
||||
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
val elementSize: Int
|
||||
|
||||
@@ -808,11 +808,11 @@ _jump jmp (${target.asmLabel})
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
} else {
|
||||
var varname = asmgen.asmVariableName(value.identifier)
|
||||
if(value.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
} else {
|
||||
asmgen.out(" lda #>${asmgen.asmVariableName(value.identifier)}")
|
||||
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
|
||||
}
|
||||
asmgen.out(" lda #>$varname")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -1607,13 +1607,13 @@ _jump jmp (${target.asmLabel})
|
||||
if(left.isFromArrayElement)
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
if(left.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varname = left.identifier.name
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
left.identifier.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -1659,13 +1659,13 @@ _jump jmp (${target.asmLabel})
|
||||
if(left.isFromArrayElement)
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
if(left.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varname = left.identifier.name
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
left.identifier.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
||||
@@ -60,7 +60,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
result.fold(
|
||||
@@ -68,7 +68,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
numVariablesAllocatedInZP++
|
||||
},
|
||||
failure = {
|
||||
errors.err(it.message!!, variable.astNode.position)
|
||||
errors.err(it.message!!, variable.astNode?.position ?: Position.DUMMY)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -79,7 +79,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||
@@ -99,7 +99,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
variable.scopedName,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||
|
||||
@@ -1341,13 +1341,12 @@ internal class AssignmentAsmGen(
|
||||
|
||||
when (right) {
|
||||
is PtAddressOf -> {
|
||||
val symbol = asmgen.asmVariableName(right.identifier)
|
||||
var symbol = asmgen.asmVariableName(right.identifier)
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $symbol at ${right.position}")
|
||||
} else {
|
||||
if(right.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
return true
|
||||
symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb"
|
||||
}
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
|
||||
if(expr.operator=="+")
|
||||
@@ -2579,7 +2578,8 @@ $endLabel""")
|
||||
|
||||
private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, arrayDt: DataType?, arrayIndexExpr: PtExpression?) {
|
||||
if(arrayIndexExpr!=null) {
|
||||
require(arrayDt?.isSplitWordArray!=true)
|
||||
if(arrayDt?.isSplitWordArray==true)
|
||||
TODO("address of element of a split word array")
|
||||
val constIndex = arrayIndexExpr.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if (arrayDt?.isUnsignedWord==true) {
|
||||
@@ -2930,14 +2930,12 @@ $endLabel""")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(target.array!!.splitWords)
|
||||
TODO("assign into split words ${target.position}")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
val scaledIdx = program.memsizer.memorySize(target.datatype, target.constArrayIndexValue!!.toInt())
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
@@ -2979,19 +2977,33 @@ $endLabel""")
|
||||
""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(wordtarget.array!!.splitWords)
|
||||
TODO("assign byte into split words ${wordtarget.position}")
|
||||
if (wordtarget.constArrayIndexValue!=null) {
|
||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.signExtendAYlsb(BaseDataType.BYTE)
|
||||
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
if(wordtarget.array!!.splitWords) {
|
||||
// signed byte, we must sign-extend
|
||||
if (wordtarget.constArrayIndexValue!=null) {
|
||||
val scaledIdx = wordtarget.constArrayIndexValue!!
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}_lsb+$scaledIdx")
|
||||
asmgen.signExtendAYlsb(BaseDataType.BYTE)
|
||||
asmgen.out(" sty ${wordtarget.asmVarname}_msb+$scaledIdx")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X)
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}_msb,x")
|
||||
asmgen.signExtendAYlsb(BaseDataType.BYTE)
|
||||
asmgen.out(" tya | sta ${wordtarget.asmVarname}_msb,x")
|
||||
}
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X)
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.signExtendAYlsb(BaseDataType.BYTE)
|
||||
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
|
||||
if (wordtarget.constArrayIndexValue != null) {
|
||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.signExtendAYlsb(BaseDataType.BYTE)
|
||||
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X)
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.signExtendAYlsb(BaseDataType.BYTE)
|
||||
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
@@ -3700,8 +3712,7 @@ $endLabel""")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(target.array!!.splitWords)
|
||||
TODO("assign into split words ${target.position}")
|
||||
require(!target.array!!.splitWords)
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
val indexValue = target.constArrayIndexValue!!
|
||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
||||
|
||||
@@ -7,7 +7,7 @@ internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when(dt.sub?.dt) {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
@@ -21,8 +21,8 @@ internal object DummyMemsizer : IMemSizer {
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
override fun memorySize(dt: BaseDataType): Int {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -904,10 +904,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
return null // TODO("inplace split word array +")
|
||||
return null // TODO("inplace split word array -")
|
||||
}
|
||||
}
|
||||
return null // TODO("inplace split word array +")
|
||||
return null // TODO("inplace split word array -")
|
||||
}
|
||||
|
||||
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
|
||||
@@ -133,7 +133,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
if(expr.isFromArrayElement) {
|
||||
require(!expr.identifier.type.isSplitWordArray)
|
||||
if(expr.identifier.type.isSplitWordArray)
|
||||
TODO("address of element of a split word array")
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
|
||||
val indexTr2 = translateExpression(expr.arrayIndexExpr!!)
|
||||
addToResult(result, indexTr2, indexTr2.resultReg, -1)
|
||||
|
||||
@@ -25,7 +25,7 @@ class IRCodeGen(
|
||||
verifyNameScoping(program, symbolTable)
|
||||
changeGlobalVarInits(symbolTable)
|
||||
|
||||
val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable)
|
||||
val irSymbolTable = IRSymbolTable.fromAstSymboltable(symbolTable)
|
||||
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||
|
||||
// collect global variables initializers
|
||||
@@ -477,9 +477,8 @@ class IRCodeGen(
|
||||
}
|
||||
else -> {
|
||||
// iterate over regular array
|
||||
val element = iterable.type.sub!!
|
||||
val elementDt = element.dt
|
||||
val elementSize = program.memsizer.memorySize(element)
|
||||
val elementDt = iterable.type.sub!!
|
||||
val elementSize = program.memsizer.memorySize(elementDt)
|
||||
val lengthBytes = iterableLength!! * elementSize
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
@@ -487,7 +486,7 @@ class IRCodeGen(
|
||||
it += IRInstruction(Opcode.STOREM, irType(DataType.forDt(elementDt)), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
result += addConstIntToReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(lengthBytes!=256) {
|
||||
// for length 256, the compare is actually against 0, which doesn't require a separate CMP instruction
|
||||
@@ -640,7 +639,7 @@ class IRCodeGen(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun addConstReg(dt: IRDataType, reg: Int, value: Int): IRCodeChunk {
|
||||
private fun addConstIntToReg(dt: IRDataType, reg: Int, value: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
when(value) {
|
||||
0 -> { /* do nothing */ }
|
||||
@@ -1349,7 +1348,7 @@ class IRCodeGen(
|
||||
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null || !condition.left.type.isFloat) {
|
||||
return ifWithElse_IntegerCond(ifElse)
|
||||
return ifElse_IntegerCond(ifElse)
|
||||
}
|
||||
|
||||
// we assume only a binary expression can contain a floating point.
|
||||
@@ -1409,7 +1408,7 @@ class IRCodeGen(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
|
||||
private fun ifElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
|
||||
|
||||
@@ -5,7 +5,7 @@ internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
@@ -19,8 +19,8 @@ internal object DummyMemsizer : IMemSizer {
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
override fun memorySize(dt: BaseDataType): Int {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.ExpressionError
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ExpressionError
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.BaseDataType
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.maySwapOperandOrder
|
||||
import prog8.ast.statements.*
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.UndefinedSymbolError
|
||||
import prog8.ast.UndefinedSymbolError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
@@ -438,7 +438,7 @@ internal class ConstantIdentifierReplacer(
|
||||
} else {
|
||||
require(rangeType.sub!=null)
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(rangeType.sub!!.dt, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
constRange.map { NumericLiteral(rangeType.sub!!, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.ast.statements.IfElse
|
||||
|
||||
@@ -367,6 +367,20 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
||||
@@ -97,7 +97,7 @@ extsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C
|
||||
extsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
extsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; get current window dimensions into X (columns) and Y (rows) NOTE: changed behavior compared to VIC/C64/PET SCREEN() routine!
|
||||
extsub $FFED = SCRORG() -> ubyte @ X, ubyte @ Y ; get current window dimensions into X (columns) and Y (rows) NOTE: changed behavior compared to VIC/C64/PET SCREEN() routine!
|
||||
extsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
extsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ Y, ubyte @ X ; read/set position of cursor on screen (Y=column, X=row). Also see txt.plot
|
||||
extsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 compatible ROM kernal routines ----
|
||||
@@ -918,6 +918,20 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
||||
@@ -99,7 +99,7 @@ extsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A
|
||||
extsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
extsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
extsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; get size of text screen into X (columns) and Y (rows)
|
||||
extsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
extsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ Y, ubyte @ X ; read/set position of cursor on screen (Y=column, X=row). Also see txt.plot
|
||||
extsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
|
||||
@@ -934,6 +934,20 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
; Cooperative multitasking / Coroutines
|
||||
; EXPERIMENTAL LIBRARY: Api may change or it may be removed completely in a future version!
|
||||
|
||||
; Achieves cooperative multitasking among a list of tasks each calling yield() to pass control to the next.
|
||||
; Uses cpu stack return address juggling to cycle between different tasks.
|
||||
;
|
||||
; Features:
|
||||
; - can have a dynamic number of active tasks (max 64), when a task ends it is automatically removed from the task list.
|
||||
; - you can add new tasks, while the rest is already running. Just not yet from inside IRQ handlers!
|
||||
; - tasks are regular subroutines but have to call yield() to pass control to the next task (round-robin)
|
||||
; - yield() returns the registered userdata value for that task, so a single subroutine could be used as multiple tasks on different userdata
|
||||
; BUT!! in that case, the subroutine cannot have any variables of its own that keep state, because they're shared across the multiple tasks!
|
||||
; (if a subroutine is just inserted as a task exactly ONCE, it's okay to use normal variables for state, because nobody will share them)
|
||||
; - you can kill a task (if you know it's id...)
|
||||
; - when all tasks are finished the run() call will also return.
|
||||
; - tasks can't push anything on the cpu stack before calling yield() - that will cause chaos.
|
||||
; - this library is not (yet) usable from IRQ handlers. Don't do it. It will end badly. (can't manipulate the task list simultaneously)
|
||||
;
|
||||
; Difference from IRQ handlers:
|
||||
; - you can have many tasks instead of only 2 (main program + irq handler)
|
||||
; - it's not tied to any IRQ setup, and will run as fast as the tasks themselves allow
|
||||
; - tasks fully control the switch to the next task; there is no preemptive switching
|
||||
; - tasks will need to save/restore their own state, maybe by useing the userdata (pointer?) and/or task id for that.
|
||||
;
|
||||
; USAGE:
|
||||
; - call add(taskaddress) to add a new task. It returns the task id.
|
||||
; - call run(supervisor) to start executing all tasks until none are left. Pass 0 or a pointer to a 'supervisor' routine.
|
||||
; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute.
|
||||
; It has then to return a boolean: true=next task is to be executed, false=skip the task this time.
|
||||
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
|
||||
; - call current() to get the current task id.
|
||||
; - call kill(taskid) to kill a task by id.
|
||||
; - call killall() to kill all tasks.
|
||||
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
|
||||
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS = 64
|
||||
uword[MAX_TASKS] tasklist
|
||||
uword[MAX_TASKS] userdatas
|
||||
uword[MAX_TASKS] returnaddresses
|
||||
ubyte active_task
|
||||
uword supervisor
|
||||
|
||||
sub add(uword taskaddress, uword userdata) -> ubyte {
|
||||
; find the next empty slot in the tasklist and stick it there
|
||||
; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id!
|
||||
; also returns the success in the Carry flag (carry set=success, carry clear = task was not added)
|
||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||
if tasklist[cx16.r0L] == 0 {
|
||||
tasklist[cx16.r0L] = taskaddress
|
||||
userdatas[cx16.r0L] = userdata
|
||||
returnaddresses[cx16.r0L] = 0
|
||||
sys.set_carry()
|
||||
return cx16.r0L
|
||||
}
|
||||
}
|
||||
; no space for new task
|
||||
sys.clear_carry()
|
||||
return 255
|
||||
}
|
||||
|
||||
sub killall() {
|
||||
; kill all existing tasks
|
||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||
kill(cx16.r0L)
|
||||
}
|
||||
}
|
||||
|
||||
sub run(uword supervisor_routine) {
|
||||
supervisor = supervisor_routine
|
||||
for active_task in 0 to len(tasklist)-1 {
|
||||
if tasklist[active_task]!=0 {
|
||||
; activate the termination handler and start the first task
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(&termination)
|
||||
goto tasklist[active_task]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub yield() -> uword {
|
||||
; Store the return address of the yielding task,
|
||||
; and continue with the next one instead (round-robin)
|
||||
; Returns the associated userdata value
|
||||
uword task_start, task_continue
|
||||
returnaddresses[active_task] = sys.popw()
|
||||
|
||||
resume_with_next_task:
|
||||
if not next_task() {
|
||||
void sys.popw() ; remove return to the termination handler
|
||||
return 0 ; exiting here will now actually return from the start() call back to the calling program :)
|
||||
}
|
||||
|
||||
if supervisor!=0 {
|
||||
if lsb(call(supervisor))==0
|
||||
goto resume_with_next_task
|
||||
}
|
||||
|
||||
if task_continue==0 {
|
||||
; fetch start address of next task.
|
||||
; address on the stack must be pushed in reverse byte order
|
||||
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(task_start)
|
||||
} else
|
||||
sys.pushw(task_continue)
|
||||
|
||||
; returning from yield then continues with the next coroutine
|
||||
return userdatas[active_task]
|
||||
|
||||
sub next_task() -> bool {
|
||||
; search through the task list for the next active task
|
||||
repeat len(tasklist) {
|
||||
active_task++
|
||||
if active_task==len(returnaddresses)
|
||||
active_task=0
|
||||
task_start = tasklist[active_task]
|
||||
if task_start!=0 {
|
||||
task_continue = returnaddresses[active_task]
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false ; no task
|
||||
}
|
||||
}
|
||||
|
||||
sub kill(ubyte taskid) {
|
||||
tasklist[taskid] = 0
|
||||
returnaddresses[taskid] = 0
|
||||
}
|
||||
|
||||
sub current() -> ubyte {
|
||||
return active_task
|
||||
}
|
||||
|
||||
sub termination() {
|
||||
; a task has terminated. wipe it from the list.
|
||||
; this is an internal routine
|
||||
kill(active_task)
|
||||
; reactivate this termination handler
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(&termination)
|
||||
goto coroutines.yield.resume_with_next_task
|
||||
}
|
||||
}
|
||||
@@ -104,15 +104,20 @@ io_error:
|
||||
}
|
||||
|
||||
sub diskname() -> uword {
|
||||
; returns disk label name or 0 if error
|
||||
cbm.SETNAM(3, "$")
|
||||
; -- Returns pointer to disk name string or 0 if failure.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, 0)
|
||||
ubyte status = 1
|
||||
void cbm.OPEN() ; open 12,8,0,"$=c"
|
||||
bool okay = false
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
|
||||
void cbm.CHRIN()
|
||||
if cbm.READST()!=0
|
||||
goto io_error
|
||||
|
||||
while cbm.CHRIN()!='"' {
|
||||
; skip up to entry name
|
||||
}
|
||||
@@ -130,14 +135,14 @@ io_error:
|
||||
}
|
||||
cx16.r0++
|
||||
}
|
||||
status = cbm.READST()
|
||||
okay = true
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN()
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if status!=0 and status & $40 == 0
|
||||
return 0
|
||||
return list_filename
|
||||
if okay
|
||||
return &list_filename
|
||||
return 0
|
||||
}
|
||||
|
||||
; internal variables for the iterative file lister / loader
|
||||
|
||||
@@ -588,6 +588,7 @@ gfx_hires {
|
||||
; Non-recursive scanline flood fill.
|
||||
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
|
||||
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
|
||||
; Also see https://lodev.org/cgtutor/floodfill.html
|
||||
word @zp xx = x as word
|
||||
word @zp yy = y as word
|
||||
word x1
|
||||
|
||||
@@ -640,6 +640,7 @@ gfx_lores {
|
||||
; Non-recursive scanline flood fill.
|
||||
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
|
||||
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
|
||||
; Also see https://lodev.org/cgtutor/floodfill.html
|
||||
word @zp xx = x as word
|
||||
word @zp yy = y as word
|
||||
word x1
|
||||
|
||||
@@ -739,6 +739,7 @@ invert:
|
||||
; Non-recursive scanline flood fill.
|
||||
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
|
||||
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
|
||||
; Also see https://lodev.org/cgtutor/floodfill.html
|
||||
word @zp xx = x as word
|
||||
word @zp yy = y as word
|
||||
word x1
|
||||
|
||||
@@ -81,7 +81,7 @@ extsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A
|
||||
extsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
extsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
extsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; get size of text screen into X (columns) and Y (rows)
|
||||
extsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Also see txt.plot
|
||||
extsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ Y, ubyte @ X ; read/set position of cursor on screen (Y=column, X=row). Also see txt.plot
|
||||
extsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- utility
|
||||
@@ -1848,6 +1848,18 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
phy
|
||||
phx
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
||||
@@ -666,4 +666,12 @@ asmsub print_lit(str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
sub t256c(bool enable) {
|
||||
; set 256 color tile mode on or off
|
||||
if enable
|
||||
cx16.VERA_L1_CONFIG |= 8
|
||||
else
|
||||
cx16.VERA_L1_CONFIG &= ~8
|
||||
}
|
||||
|
||||
}
|
||||
@@ -300,6 +300,20 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
||||
@@ -465,6 +465,20 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
||||
@@ -225,13 +225,13 @@ asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
jsr home
|
||||
cpy #0
|
||||
beq +
|
||||
- lda #17
|
||||
- lda #29
|
||||
jsr chrout
|
||||
dey
|
||||
bne -
|
||||
+ cpx #0
|
||||
beq +
|
||||
- lda #29
|
||||
- lda #17
|
||||
jsr chrout
|
||||
dex
|
||||
bne -
|
||||
|
||||
@@ -92,11 +92,13 @@ io_error:
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
|
||||
void cbm.CHRIN()
|
||||
if cbm.READST()!=0
|
||||
goto io_error
|
||||
|
||||
while cbm.CHRIN()!='"' {
|
||||
; skip up to entry name
|
||||
}
|
||||
if cbm.READST()!=0
|
||||
goto io_error
|
||||
|
||||
cx16.r0 = &list_filename
|
||||
repeat {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
coroutines {
|
||||
; the VM doesn't support coroutines because the flow of control is radically different than how the 6502 CPU does it.
|
||||
}
|
||||
@@ -409,6 +409,7 @@ monogfx {
|
||||
; Non-recursive scanline flood fill.
|
||||
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
|
||||
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
|
||||
; Also see https://lodev.org/cgtutor/floodfill.html
|
||||
word @zp xx = x as word
|
||||
word @zp yy = y as word
|
||||
word x1
|
||||
|
||||
@@ -190,6 +190,14 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub push_returnaddress(uword w) {
|
||||
; note: this actually doesn't do anything useful on the VM because the code execution doesn't use the simulated cpu stack
|
||||
%ir {{
|
||||
loadm.w r65535,sys.pushw.w
|
||||
push.w r65535
|
||||
}}
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
@@ -233,8 +241,8 @@ sys {
|
||||
|
||||
cx16 {
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the VirtualMachine as well but their location in memory is different
|
||||
; the sixteen virtual 16-bit registers that the Commander X16 has defined in the zeropage
|
||||
; they are on the VirtualMachine as well, but their location in memory is different
|
||||
&uword r0 = $ff02
|
||||
&uword r1 = $ff04
|
||||
&uword r2 = $ff06
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package prog8
|
||||
|
||||
import kotlinx.cli.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.AstException
|
||||
import prog8.code.core.CbmPrgLauncherType
|
||||
import prog8.code.source.ImportFileSystem
|
||||
import prog8.code.target.CompilationTargets
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.getCompilationTargetByName
|
||||
@@ -95,8 +96,8 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
val srcdirs = sourceDirs.toMutableList()
|
||||
if(srcdirs.firstOrNull()!=".")
|
||||
val srcdirs = sourceDirs.map { ImportFileSystem.expandTilde(it) }.toMutableList()
|
||||
if(sourceDirs.firstOrNull()!=".")
|
||||
srcdirs.add(0, ".")
|
||||
|
||||
if(startVm==null) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.AstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.code.core.*
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package prog8.compiler
|
||||
|
||||
import com.github.michaelbull.result.onFailure
|
||||
import prog8.ast.AstException
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.printProgram
|
||||
|
||||
@@ -3,7 +3,7 @@ package prog8.compiler
|
||||
import com.github.michaelbull.result.*
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.ast.statements.DirectiveArg
|
||||
import prog8.code.core.IErrorReporter
|
||||
@@ -135,7 +135,7 @@ class ModuleImporter(private val program: Program,
|
||||
if (importingModule == null) { // <=> imported from library module
|
||||
sourcePaths
|
||||
} else {
|
||||
val pathFromImportingModule = (Path(importingModule.position.file).parent ?: Path("")).absolute()
|
||||
val pathFromImportingModule = (Path(importingModule.position.file).parent ?: Path("")).absolute().normalize()
|
||||
listOf(pathFromImportingModule) + sourcePaths
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
@@ -533,7 +533,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("identifiers cannot start with an underscore", p.position)
|
||||
|
||||
if(p.type.isPassByRef && !p.type.isString && !p.type.isUnsignedByteArray) {
|
||||
errors.err("this pass-by-reference type can't be used as a parameter type. Instead, use an uword to receive the address, or access the variable from the outer scope directly.", p.position)
|
||||
errors.err("this pass-by-reference type can't be used as a parameter type. Instead, use just 'uword' to receive the address, or maybe don't pass the value via a parameter but access it directly.", p.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -642,7 +642,7 @@ internal class AstChecker(private val program: Program,
|
||||
fcallTarget.returntypes.zip(targets).withIndex().forEach { (index, p) ->
|
||||
val (returnType, target) = p
|
||||
val targetDt = target.inferType(program).getOrUndef()
|
||||
if (!target.void && !(returnType isAssignableTo targetDt))
|
||||
if (!target.void && returnType != targetDt)
|
||||
errors.err("can't assign returnvalue #${index + 1} to corresponding target; $returnType vs $targetDt", target.position)
|
||||
}
|
||||
}
|
||||
@@ -703,7 +703,12 @@ internal class AstChecker(private val program: Program,
|
||||
if (variable!=null) {
|
||||
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
|
||||
errors.err("invalid pointer-of operand type",addressOf.position)
|
||||
|
||||
if(addressOf.arrayIndex!=null && variable.datatype.isSplitWordArray) {
|
||||
errors.err("cannot take the adress of a word element that is in a split-word array", addressOf.position)
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(addressOf)
|
||||
}
|
||||
|
||||
@@ -1467,18 +1472,18 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
|
||||
// in-place modification, can't be done on literals
|
||||
// in-place modification, can be done on specific types of arguments only (variables, array elements)
|
||||
if(funcName[0]=="setlsb" || funcName[0]=="setmsb") {
|
||||
val firstArg = functionCallStatement.args[0]
|
||||
if(firstArg !is IdentifierReference && firstArg !is ArrayIndexedExpression)
|
||||
errors.err("invalid argument to a in-place modifying function", firstArg.position)
|
||||
errors.err("this function can only act on an identifier or array element", firstArg.position)
|
||||
} else if(funcName[0]=="divmod" || funcName[0].startsWith("divmod__")) {
|
||||
val thirdArg = functionCallStatement.args[2]
|
||||
val fourthArg = functionCallStatement.args[3]
|
||||
if(thirdArg !is IdentifierReference && thirdArg !is ArrayIndexedExpression)
|
||||
errors.err("invalid argument to a in-place modifying function", thirdArg.position)
|
||||
errors.err("this function can only act on an identifier or array element", thirdArg.position)
|
||||
if(fourthArg !is IdentifierReference && fourthArg !is ArrayIndexedExpression)
|
||||
errors.err("invalid argument to a in-place modifying function", fourthArg.position)
|
||||
errors.err("this function can only act on an identifier or array element", fourthArg.position)
|
||||
} else {
|
||||
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead })
|
||||
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
||||
|
||||
@@ -3,8 +3,8 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.findParentNode
|
||||
import prog8.ast.statements.*
|
||||
@@ -198,7 +198,7 @@ class AstPreprocessor(val program: Program,
|
||||
options.dontSplitWordArrays && decl.datatype.isSplitWordArray
|
||||
|
||||
private fun makeUnSplitArray(decl: VarDecl): Iterable<IAstModification> {
|
||||
val splitDt = DataType.arrayFor(decl.datatype.sub!!.dt, false)
|
||||
val splitDt = DataType.arrayFor(decl.datatype.sub!!, false)
|
||||
val newDecl = VarDecl(
|
||||
decl.type, decl.origin, splitDt, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, emptyList(),
|
||||
decl.value?.copy(), decl.sharedWithAsm, decl.alignment, false, decl.position
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
import com.github.michaelbull.result.mapError
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.ast.*
|
||||
@@ -15,6 +15,7 @@ import prog8.code.source.SourceCode
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
import java.io.File
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
|
||||
@@ -257,10 +258,10 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
val offset: UInt? = if(directive.args.size>=2) directive.args[1].int!! else null
|
||||
val length: UInt? = if(directive.args.size>=3) directive.args[2].int!! else null
|
||||
val abspath = if(File(filename).isFile) {
|
||||
Path(filename).toAbsolutePath()
|
||||
Path(filename).absolute()
|
||||
} else {
|
||||
val sourcePath = Path(directive.definingModule.source.origin)
|
||||
sourcePath.resolveSibling(filename).toAbsolutePath()
|
||||
sourcePath.resolveSibling(filename).absolute()
|
||||
}
|
||||
if(abspath.toFile().isFile)
|
||||
PtIncludeBinary(abspath, offset, length, directive.position)
|
||||
@@ -743,8 +744,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
if(type.isSplitWordArray) {
|
||||
// ranges are never a split word array!
|
||||
when(type.sub) {
|
||||
is SubSignedWord -> type = DataType.arrayFor(BaseDataType.WORD, false)
|
||||
is SubUnsignedWord -> type = DataType.arrayFor(BaseDataType.UWORD, false)
|
||||
BaseDataType.WORD -> type = DataType.arrayFor(BaseDataType.WORD, false)
|
||||
BaseDataType.UWORD -> type = DataType.arrayFor(BaseDataType.UWORD, false)
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
val identifier = node.target as? PtIdentifier
|
||||
if (identifier != null) {
|
||||
val stNode = st.lookup(identifier.name)!!
|
||||
val targetSub = stNode.astNode.definingSub()
|
||||
val targetSub = stNode.astNode!!.definingSub()
|
||||
if (targetSub != node.definingSub())
|
||||
jumpsAndCallsToAugment.add(node)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
|
||||
@@ -3,7 +3,7 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
@@ -264,7 +264,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
} else if(targetDt==BaseDataType.BOOL) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, BaseDataType.BOOL, call as Node)
|
||||
} else if(argDt isAssignableTo DataType.forDt(targetDt)) {
|
||||
} else if(!targetDt.isIterable && argDt isAssignableTo DataType.forDt(targetDt)) {
|
||||
if(!argDt.isString || targetDt!=BaseDataType.UWORD)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.ast.PtFunctionCall
|
||||
import prog8.code.ast.PtTypeCast
|
||||
|
||||
@@ -4,7 +4,7 @@ import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
|
||||
@@ -61,7 +61,7 @@ private fun prepareTestFiles(source: String, optimize: Boolean, target: ICompila
|
||||
}
|
||||
val filepath = searchIn.asSequence()
|
||||
.map { it.resolve("$source.p8") }
|
||||
.map { it.normalize().absolute() }
|
||||
.map { it.absolute().normalize() }
|
||||
.map { workingDir.relativize(it) }
|
||||
.first { it.exists() }
|
||||
val displayName = "${examplesDir.relativize(filepath.absolute())}: ${target.name}, optimize=$optimize"
|
||||
@@ -172,7 +172,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
listOf(
|
||||
"animals",
|
||||
"balls",
|
||||
"coroutines",
|
||||
"cube3d",
|
||||
"cube3d-float",
|
||||
"cube3d-gfx",
|
||||
@@ -182,6 +181,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"maze",
|
||||
"mandelbrot",
|
||||
"mandelbrot-gfx",
|
||||
"multitasking",
|
||||
"numbergame",
|
||||
"primes",
|
||||
"queens",
|
||||
|
||||
@@ -11,6 +11,7 @@ import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Label
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8tests.helpers.*
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.name
|
||||
|
||||
|
||||
@@ -97,7 +98,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
|
||||
|
||||
// the bug we're testing for (#54) was hidden if outputDir == workingDir
|
||||
withClue("sanity check: workingDir and outputDir should not be the same folder") {
|
||||
outputDir.normalize().toAbsolutePath() shouldNotBe workingDir.normalize().toAbsolutePath()
|
||||
outputDir.absolute().normalize() shouldNotBe workingDir.absolute().normalize()
|
||||
}
|
||||
|
||||
withClue("argument to assembler directive .binary " +
|
||||
|
||||
@@ -23,6 +23,8 @@ class TestLaunchEmu: FunSpec({
|
||||
</VARIABLESNOINIT>
|
||||
<VARIABLESWITHINIT>
|
||||
</VARIABLESWITHINIT>
|
||||
<CONSTANTS>
|
||||
</CONSTANTS>
|
||||
|
||||
<MEMORYMAPPEDVARIABLES>
|
||||
</MEMORYMAPPEDVARIABLES>
|
||||
|
||||
+14
-19
@@ -253,32 +253,27 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
|
||||
context("memsizer") {
|
||||
withData(VMTarget(), AtariTarget(), C64Target(), PETTarget(), AtariTarget(), C128Target()) { target ->
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(SubType.forDt(BaseDataType.UNDEFINED))
|
||||
withData(VMTarget(), AtariTarget(), C64Target(), PETTarget(), AtariTarget(), C128Target(), Neo6502Target()) { target ->
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(BaseDataType.UNDEFINED)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(SubType.forDt(BaseDataType.LONG))
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(BaseDataType.LONG)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(SubType.forDt(BaseDataType.STR))
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(SubType.forDt(BaseDataType.ARRAY))
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(SubType.forDt(BaseDataType.ARRAY_SPLITW))
|
||||
}
|
||||
target.memorySize(SubType.forDt(BaseDataType.BOOL)) shouldBe 1
|
||||
target.memorySize(SubType.forDt(BaseDataType.BYTE)) shouldBe 1
|
||||
target.memorySize(SubType.forDt(BaseDataType.WORD)) shouldBe 2
|
||||
target.memorySize(SubType.forDt(BaseDataType.FLOAT)) shouldBe target.machine.FLOAT_MEM_SIZE
|
||||
target.memorySize(BaseDataType.BOOL) shouldBe 1
|
||||
target.memorySize(BaseDataType.BYTE) shouldBe 1
|
||||
target.memorySize(BaseDataType.WORD) shouldBe 2
|
||||
target.memorySize(BaseDataType.FLOAT) shouldBe target.machine.FLOAT_MEM_SIZE
|
||||
|
||||
target.memorySize(DataType.forDt(BaseDataType.UNDEFINED), null) shouldBe 2
|
||||
target.memorySize(DataType.forDt(BaseDataType.BOOL), null) shouldBe 1
|
||||
target.memorySize(DataType.forDt(BaseDataType.WORD), null) shouldBe 2
|
||||
target.memorySize(DataType.forDt(BaseDataType.FLOAT), null) shouldBe target.machine.FLOAT_MEM_SIZE
|
||||
|
||||
target.memorySize(DataType.forDt(BaseDataType.STR), null) shouldBe 2
|
||||
target.memorySize(DataType.forDt(BaseDataType.STR), 50) shouldBe 50
|
||||
target.memorySize(BaseDataType.STR) shouldBe 2
|
||||
target.memorySize(BaseDataType.ARRAY) shouldBe 2
|
||||
target.memorySize(BaseDataType.ARRAY_SPLITW) shouldBe 2
|
||||
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.BOOL), 10) shouldBe 10
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.BYTE), 10) shouldBe 10
|
||||
|
||||
@@ -5,7 +5,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.ast.base.ExpressionError
|
||||
import prog8.ast.ExpressionError
|
||||
import prog8.ast.expressions.ArrayLiteral
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestSymbolTable: FunSpec({
|
||||
st.type shouldBe StNodeType.GLOBAL
|
||||
st.children shouldBe mutableMapOf()
|
||||
st.astNode shouldBeSameInstanceAs astNode
|
||||
st.astNode.position shouldBe Position.DUMMY
|
||||
st.astNode!!.position shouldBe Position.DUMMY
|
||||
}
|
||||
|
||||
test("symboltable flatten") {
|
||||
|
||||
@@ -383,4 +383,65 @@ main {
|
||||
errors.warnings.size shouldBe 0
|
||||
errors.errors[0] shouldContain "missing &"
|
||||
}
|
||||
|
||||
test("can't use uword[] as a parameter type give clear error") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
uword[] @nosplit array = [1111,2222]
|
||||
funcw1(array)
|
||||
funcw1([1111,2222])
|
||||
funcw2(array)
|
||||
funcw2([1111,2222])
|
||||
funcb([11,22])
|
||||
}
|
||||
|
||||
sub funcw1(uword[] ptr) {
|
||||
; error
|
||||
}
|
||||
|
||||
sub funcw2(uword ptr) {
|
||||
; ok
|
||||
}
|
||||
|
||||
sub funcb(ubyte[] ptr) {
|
||||
; ok
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 3
|
||||
errors.warnings.size shouldBe 0
|
||||
errors.errors[0] shouldContain ":5:16: argument 1 type mismatch"
|
||||
errors.errors[1] shouldContain ":6:16: argument 1 type mismatch"
|
||||
errors.errors[2] shouldContain ":12:16: this pass-by-reference type can't be used as a parameter type"
|
||||
}
|
||||
|
||||
test("proper type checking for multi-value assigns") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
bool bb
|
||||
ubyte ub
|
||||
uword uw
|
||||
uw, void = thing2()
|
||||
uw, bb = thing2()
|
||||
uw, ub = thing2()
|
||||
}
|
||||
|
||||
asmsub thing2() -> ubyte @A, bool @Pc {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors[0] shouldContain "can't assign returnvalue #1 to corresponding target; ubyte vs uword"
|
||||
errors.errors[1] shouldContain "can't assign returnvalue #1 to corresponding target; ubyte vs uword"
|
||||
errors.errors[2] shouldContain "can't assign returnvalue #1 to corresponding target; ubyte vs uword"
|
||||
errors.errors[3] shouldContain "can't assign returnvalue #2 to corresponding target; bool vs ubyte"
|
||||
}
|
||||
})
|
||||
@@ -103,7 +103,7 @@ class TestProg8Parser: FunSpec( {
|
||||
val nlUnix = "\n"
|
||||
val nlMac = "\r"
|
||||
|
||||
//parseModule(Paths.get("test", "fixtures", "mac_newlines.p8").toAbsolutePath())
|
||||
//parseModule(Paths.get("test", "fixtures", "mac_newlines.p8").absolute())
|
||||
|
||||
// a good mix of all kinds of newlines:
|
||||
val srcText =
|
||||
|
||||
@@ -15,7 +15,6 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.SubType
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
@@ -846,29 +845,7 @@ main {
|
||||
errors.errors[2] shouldEndWith "cannot assign to 'void', perhaps a void function call was intended"
|
||||
}
|
||||
|
||||
test("datatype subtype consistencies") {
|
||||
shouldThrow<NoSuchElementException> {
|
||||
SubType.forDt(BaseDataType.STR)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
SubType.forDt(BaseDataType.UNDEFINED)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
SubType.forDt(BaseDataType.ARRAY_SPLITW)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
SubType.forDt(BaseDataType.ARRAY)
|
||||
}
|
||||
SubType.forDt(BaseDataType.FLOAT).dt shouldBe BaseDataType.FLOAT
|
||||
}
|
||||
|
||||
test("datatype consistencies") {
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.forDt(BaseDataType.ARRAY)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.forDt(BaseDataType.ARRAY_SPLITW)
|
||||
}
|
||||
DataType.forDt(BaseDataType.UNDEFINED).isUndefined shouldBe true
|
||||
DataType.forDt(BaseDataType.LONG).isLong shouldBe true
|
||||
DataType.forDt(BaseDataType.WORD).isWord shouldBe true
|
||||
@@ -876,6 +853,19 @@ main {
|
||||
DataType.forDt(BaseDataType.BYTE).isByte shouldBe true
|
||||
DataType.forDt(BaseDataType.UBYTE).isByte shouldBe true
|
||||
|
||||
DataType.arrayFor(BaseDataType.UBYTE, true).isUnsignedByteArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.FLOAT).isFloatArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isUnsignedWordArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isSplitWordArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD, false).isSplitWordArray shouldBe false
|
||||
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.forDt(BaseDataType.ARRAY)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.forDt(BaseDataType.ARRAY_SPLITW)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.arrayFor(BaseDataType.ARRAY)
|
||||
}
|
||||
@@ -885,12 +875,6 @@ main {
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.arrayFor(BaseDataType.UNDEFINED)
|
||||
}
|
||||
DataType.arrayFor(BaseDataType.UBYTE, true).isUnsignedByteArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.FLOAT).isFloatArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isUnsignedWordArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isSplitWordArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD, false).isSplitWordArray shouldBe false
|
||||
}
|
||||
|
||||
test("array of strings becomes array of uword pointers") {
|
||||
|
||||
@@ -353,6 +353,7 @@ main {
|
||||
sub start() {
|
||||
sys.push(11)
|
||||
sys.pushw(2222)
|
||||
sys.push_returnaddress(3333)
|
||||
cx16.r2++
|
||||
cx16.r1 = sys.popw()
|
||||
cx16.r0L = sys.pop()
|
||||
@@ -363,6 +364,7 @@ main {
|
||||
val assembly = assemblyFile.readText()
|
||||
assembly shouldContain "inlined routine follows: push"
|
||||
assembly shouldContain "inlined routine follows: pushw"
|
||||
assembly shouldContain "inlined routine follows: push_returnaddress"
|
||||
assembly shouldContain "inlined routine follows: pop"
|
||||
assembly shouldContain "inlined routine follows: popw"
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
@@ -38,8 +38,8 @@ internal object DummyMemsizer : IMemSizer {
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
override fun memorySize(dt: BaseDataType): Int {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ internal object DummyCompilationTarget : ICompilationTarget {
|
||||
throw NotImplementedError("dummy")
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
override fun memorySize(dt: BaseDataType): Int {
|
||||
throw NotImplementedError("dummy")
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
@@ -385,6 +384,4 @@ fun defaultZero(dt: BaseDataType, position: Position) = when(dt) {
|
||||
else -> throw FatalAstException("can only determine default zero value for a numeric type")
|
||||
}
|
||||
|
||||
fun defaultZero(dt: SubType, position: Position) = defaultZero(dt.dt, position)
|
||||
|
||||
fun defaultZero(idt: InferredTypes.InferredType, position: Position) = defaultZero(idt.getOrUndef().base, position)
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8.ast.base
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.code.core.Position
|
||||
@@ -1,6 +1,5 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.statements.*
|
||||
|
||||
@@ -3,8 +3,8 @@ package prog8.ast.antlr
|
||||
import org.antlr.v4.runtime.ParserRuleContext
|
||||
import org.antlr.v4.runtime.Token
|
||||
import org.antlr.v4.runtime.tree.TerminalNode
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.ast.expressions
|
||||
|
||||
import prog8.ast.ExpressionError
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ExpressionError
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
|
||||
@@ -83,7 +83,7 @@ object InferredTypes {
|
||||
type.isString -> InferredType.known(BaseDataType.STR)
|
||||
type.isLong -> InferredType.known(BaseDataType.LONG)
|
||||
type.isSplitWordArray -> {
|
||||
when(type.sub?.dt) {
|
||||
when(type.sub) {
|
||||
BaseDataType.UWORD -> InferredType.known(DataType.arrayFor(BaseDataType.UWORD, true))
|
||||
BaseDataType.WORD -> InferredType.known(DataType.arrayFor(BaseDataType.WORD, true))
|
||||
BaseDataType.STR -> InferredType.known(DataType.arrayFor(BaseDataType.STR, true))
|
||||
@@ -91,7 +91,7 @@ object InferredTypes {
|
||||
}
|
||||
}
|
||||
type.isArray -> {
|
||||
InferredType.known(DataType.arrayFor(type.sub!!.dt, false))
|
||||
InferredType.known(DataType.arrayFor(type.sub!!, false))
|
||||
}
|
||||
else -> throw IllegalArgumentException("invalid type")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.ast.statements
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
@@ -285,8 +284,8 @@ class VarDecl(val type: VarDeclType,
|
||||
if(arrayDt.isSplitWordArray) {
|
||||
// autovars for array literals are NOT stored as a split word array!
|
||||
when(arrayDt.sub) {
|
||||
is SubSignedWord -> arrayDt = DataType.arrayFor(BaseDataType.WORD, false)
|
||||
is SubUnsignedWord -> arrayDt = DataType.arrayFor(BaseDataType.UWORD, false)
|
||||
BaseDataType.WORD -> arrayDt = DataType.arrayFor(BaseDataType.WORD, false)
|
||||
BaseDataType.UWORD -> arrayDt = DataType.arrayFor(BaseDataType.UWORD, false)
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.ast.walk
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.AssociativeOperators
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
%import buffers
|
||||
%import compression
|
||||
%import coroutines
|
||||
%import conv
|
||||
%import cx16logo
|
||||
%import math
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
%import coroutines
|
||||
%import cx16logo
|
||||
%import diskio
|
||||
;;%import floats
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
%import coroutines
|
||||
%import cx16logo
|
||||
%import diskio
|
||||
%import floats
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
%import bmx
|
||||
%import compression
|
||||
%import conv
|
||||
%import coroutines
|
||||
%import cx16logo
|
||||
%import diskio
|
||||
%import emudbg
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
%import coroutines
|
||||
%import math
|
||||
%import prog8_lib
|
||||
%import strings
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
%import coroutines
|
||||
%import cx16logo
|
||||
%import math
|
||||
%import prog8_lib
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-atari.p8
|
||||
@@ -102,11 +102,31 @@ conv {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: coroutines
|
||||
-------------------------------
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
add (uword taskaddress, uword userdata) -> ubyte
|
||||
current () -> ubyte
|
||||
kill (ubyte taskid)
|
||||
killall ()
|
||||
run (uword supervisor_routine)
|
||||
termination ()
|
||||
yield () -> uword
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: cx16logo
|
||||
-----------------------------
|
||||
|
||||
cx16logo {
|
||||
@nosplit uword[] logo_lines
|
||||
uword[] logo_lines
|
||||
logo ()
|
||||
logo_at (ubyte column, ubyte row)
|
||||
}
|
||||
@@ -242,6 +262,7 @@ sys {
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
push_returnaddress (uword address @XY)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
reset_system ()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c128.p8
|
||||
@@ -102,11 +102,31 @@ conv {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: coroutines
|
||||
-------------------------------
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
add (uword taskaddress, uword userdata) -> ubyte
|
||||
current () -> ubyte
|
||||
kill (ubyte taskid)
|
||||
killall ()
|
||||
run (uword supervisor_routine)
|
||||
termination ()
|
||||
yield () -> uword
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: cx16logo
|
||||
-----------------------------
|
||||
|
||||
cx16logo {
|
||||
@nosplit uword[] logo_lines
|
||||
uword[] logo_lines
|
||||
logo ()
|
||||
logo_at (ubyte column, ubyte row)
|
||||
}
|
||||
@@ -300,7 +320,7 @@ cbm {
|
||||
MEMBOT (uword address @XY, bool dir @Pc) -> uword @XY = $ff9c
|
||||
MEMTOP (uword address @XY, bool dir @Pc) -> uword @XY = $ff99
|
||||
OPEN () -> clobbers (X,Y) -> bool @Pc, ubyte @A = $ffc0
|
||||
PLOT (ubyte col @Y, ubyte row @X, bool dir @Pc) -> clobbers (A) -> ubyte @X, ubyte @Y = $fff0
|
||||
PLOT (ubyte col @Y, ubyte row @X, bool dir @Pc) -> clobbers (A) -> ubyte @Y, ubyte @X = $fff0
|
||||
RAMTAS () -> clobbers (A,X,Y) = $ff87
|
||||
RDTIM () -> ubyte @A, ubyte @X, ubyte @Y = $ffde
|
||||
RDTIM16 () -> clobbers (X) -> uword @AY
|
||||
@@ -507,6 +527,7 @@ sys {
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
push_returnaddress (uword address @XY)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
reset_system ()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c64.p8
|
||||
@@ -102,11 +102,31 @@ conv {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: coroutines
|
||||
-------------------------------
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
add (uword taskaddress, uword userdata) -> ubyte
|
||||
current () -> ubyte
|
||||
kill (ubyte taskid)
|
||||
killall ()
|
||||
run (uword supervisor_routine)
|
||||
termination ()
|
||||
yield () -> uword
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: cx16logo
|
||||
-----------------------------
|
||||
|
||||
cx16logo {
|
||||
@nosplit uword[] logo_lines
|
||||
uword[] logo_lines
|
||||
logo ()
|
||||
logo_at (ubyte column, ubyte row)
|
||||
}
|
||||
@@ -429,7 +449,7 @@ cbm {
|
||||
MEMBOT (uword address @XY, bool dir @Pc) -> uword @XY = $ff9c
|
||||
MEMTOP (uword address @XY, bool dir @Pc) -> uword @XY = $ff99
|
||||
OPEN () -> clobbers (X,Y) -> bool @Pc, ubyte @A = $ffc0
|
||||
PLOT (ubyte col @Y, ubyte row @X, bool dir @Pc) -> clobbers (A) -> ubyte @X, ubyte @Y = $fff0
|
||||
PLOT (ubyte col @Y, ubyte row @X, bool dir @Pc) -> clobbers (A) -> ubyte @Y, ubyte @X = $fff0
|
||||
RAMTAS () -> clobbers (A,X,Y) = $ff87
|
||||
RDTIM () -> ubyte @A, ubyte @X, ubyte @Y = $ffde
|
||||
RDTIM16 () -> clobbers (X) -> uword @AY
|
||||
@@ -633,6 +653,7 @@ sys {
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
push_returnaddress (uword address @XY)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
reset_system ()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-cx16.p8
|
||||
@@ -140,11 +140,31 @@ conv {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: coroutines
|
||||
-------------------------------
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
add (uword taskaddress, uword userdata) -> ubyte
|
||||
current () -> ubyte
|
||||
kill (ubyte taskid)
|
||||
killall ()
|
||||
run (uword supervisor_routine)
|
||||
termination ()
|
||||
yield () -> uword
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: cx16logo
|
||||
-----------------------------
|
||||
|
||||
cx16logo {
|
||||
@nosplit uword[] logo_lines
|
||||
uword[] logo_lines
|
||||
logo ()
|
||||
logo_at (ubyte column, ubyte row)
|
||||
}
|
||||
@@ -713,7 +733,7 @@ cbm {
|
||||
MEMBOT (uword address @XY, bool dir @Pc) -> uword @XY = $ff9c
|
||||
MEMTOP (uword address @XY, bool dir @Pc) -> uword @XY, ubyte @A = $ff99
|
||||
OPEN () -> clobbers (X,Y) -> bool @Pc, ubyte @A = $ffc0
|
||||
PLOT (ubyte col @Y, ubyte row @X, bool dir @Pc) -> clobbers (A) -> ubyte @X, ubyte @Y = $fff0
|
||||
PLOT (ubyte col @Y, ubyte row @X, bool dir @Pc) -> clobbers (A) -> ubyte @Y, ubyte @X = $fff0
|
||||
RAMTAS () -> clobbers (A,X,Y) = $ff87
|
||||
RDTIM () -> ubyte @A, ubyte @X, ubyte @Y = $ffde
|
||||
RDTIM16 () -> clobbers (X) -> uword @AY
|
||||
@@ -1186,6 +1206,7 @@ sys {
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
push_returnaddress (uword address @XY)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
reset_system ()
|
||||
@@ -1277,6 +1298,7 @@ txt {
|
||||
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) -> clobbers (A)
|
||||
setclr (ubyte col @X, ubyte row @Y, ubyte color @A) -> clobbers (A)
|
||||
spc ()
|
||||
t256c (bool enable)
|
||||
uppercase ()
|
||||
waitkey () -> ubyte @A
|
||||
width () -> clobbers (X,Y) -> ubyte @A
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-neo.p8
|
||||
@@ -102,6 +102,26 @@ conv {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: coroutines
|
||||
-------------------------------
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
add (uword taskaddress, uword userdata) -> ubyte
|
||||
current () -> ubyte
|
||||
kill (ubyte taskid)
|
||||
killall ()
|
||||
run (uword supervisor_routine)
|
||||
termination ()
|
||||
yield () -> uword
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: math
|
||||
-------------------------
|
||||
|
||||
@@ -226,6 +246,7 @@ sys {
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
push_returnaddress (uword address @XY)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
reset_system ()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-pet32.p8
|
||||
@@ -102,11 +102,31 @@ conv {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: coroutines
|
||||
-------------------------------
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
add (uword taskaddress, uword userdata) -> ubyte
|
||||
current () -> ubyte
|
||||
kill (ubyte taskid)
|
||||
killall ()
|
||||
run (uword supervisor_routine)
|
||||
termination ()
|
||||
yield () -> uword
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: cx16logo
|
||||
-----------------------------
|
||||
|
||||
cx16logo {
|
||||
@nosplit uword[] logo_lines
|
||||
uword[] logo_lines
|
||||
logo ()
|
||||
logo_at (ubyte column, ubyte row)
|
||||
}
|
||||
@@ -260,6 +280,7 @@ sys {
|
||||
progend () -> uword @AY
|
||||
progstart () -> uword @AY
|
||||
push (ubyte value @A)
|
||||
push_returnaddress (uword address @XY)
|
||||
pushw (uword value @AY)
|
||||
read_flags () -> ubyte @A
|
||||
reset_system ()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-virtual.p8
|
||||
@@ -99,7 +99,7 @@ LIBRARY MODULE NAME: cx16logo
|
||||
-----------------------------
|
||||
|
||||
cx16logo {
|
||||
@nosplit uword[] logo_lines
|
||||
uword[] logo_lines
|
||||
logo ()
|
||||
logo_at (ubyte column, ubyte row)
|
||||
}
|
||||
@@ -344,6 +344,7 @@ sys {
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte b)
|
||||
push_returnaddress (uword w)
|
||||
pushw (uword w)
|
||||
read_flags () -> ubyte
|
||||
reset_system ()
|
||||
|
||||
@@ -73,8 +73,8 @@ Subroutines
|
||||
variables declared in their parent subroutine(s).
|
||||
- Everything in prog8 is publicly accessible from everywhere else (via fully scoped names) - there is no notion of private or public symbol accessibility.
|
||||
- Because there is no callstack for subroutine arguments, it becomes very easy to manipulate the return address that *does* get pushed on the stack by the cpu.
|
||||
With only a little bit of code it is possible to implement a simple cooperative multitasking system that runs multiple tasks simultaneously. See the "coroutines" example.
|
||||
Each task is a subroutine and it simply has its state stored in its statically allocated variables so it can resume after a yield, without doing anything special.
|
||||
With only a little bit of code it is possible to implement a simple cooperative multitasking system that runs multiple tasks simultaneously. See the "multitasking" example,
|
||||
which uses the "coroutines" library. Each task is a subroutine and it simply has its state stored in the statically allocated variables so it can resume after yielding, without doing anything special.
|
||||
|
||||
Pointers
|
||||
--------
|
||||
|
||||
@@ -102,7 +102,7 @@ Features
|
||||
- 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
|
||||
- Floating point math is supported on certain compiler targets.
|
||||
- Easy and highly efficient integration with external subroutines and ROM routines on the target systems.
|
||||
- Provides access to most Kernal ROM routines as external subroutine definitions you can call normally.
|
||||
- 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)
|
||||
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
|
||||
|
||||
@@ -221,13 +221,6 @@ callfar2 (bank, address, argA, argX, argY, argCarry) -> uword
|
||||
Identical to ``callfar``, except here you can give arguments not only for AY,
|
||||
but for each of the A, X and Y registers (each an ubyte) and the Carry status bit as well (a boolean).
|
||||
|
||||
syscall (callnr), syscall1 (callnr, arg), syscall2 (callnr, arg1, arg2), syscall3 (callnr, arg1, arg2, arg3)
|
||||
Functions for doing a system call on targets that support this. Currently no actual target
|
||||
uses this though except, possibly, the experimental code generation target!
|
||||
The regular 6502 based compiler targets just use a subroutine call to asmsub Kernal routines at
|
||||
specific memory locations. So these builtin function calls are not useful yet except for
|
||||
experimentation in new code generation targets.
|
||||
|
||||
rsave
|
||||
Saves all registers including status (or only X) on the stack
|
||||
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* saved,
|
||||
@@ -369,6 +362,38 @@ Read the `conv source code <https://github.com/irmen/prog8/tree/master/compiler/
|
||||
to see what's in there.
|
||||
|
||||
|
||||
coroutines (experimental)
|
||||
-------------------------
|
||||
Provides a system to make cooperative multitasking programs via coroutines.
|
||||
A 'coroutine' is a subroutine whose execution you can pause and resume.
|
||||
This library handles the voodoo for you to switch between such coroutines transparently,
|
||||
so it can seem that your program is executing many subroutines at the same time.
|
||||
|
||||
API is experimental and may change or disappear in a future version.
|
||||
|
||||
Read the `coroutines source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/coroutines.p8>`_
|
||||
to see what's in there. And look at the ``multitasking`` example to see how it can be used.
|
||||
Here is a minimal example (if the library gets more stable, better docs will be written here)::
|
||||
|
||||
%import coroutine
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
coroutines.killall()
|
||||
coroutines.add(&some_task, 1111)
|
||||
; ... add more tasks here or later
|
||||
coroutines.run(0)
|
||||
}
|
||||
|
||||
sub some_task() {
|
||||
repeat 100 {
|
||||
uword userdata = coroutines.yield()
|
||||
; ... do something...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cx16
|
||||
----
|
||||
This is available on *all targets*, it is always imported as part of syslib.
|
||||
@@ -1079,13 +1104,19 @@ sys (part of syslib)
|
||||
|
||||
``pushw (value)``
|
||||
pushes a 16-bit word value on the CPU hardware stack. Low-level function that should normally not be used.
|
||||
Don't assume anything about the order in which the bytes are pushed - popw will make sense of them again.
|
||||
|
||||
``push_returnaddress (address)``
|
||||
pushes a 16 bit memory address on the CPU hardware stack in the same byte order as a JSR instruction would,
|
||||
which means the next RTS instruction will jump to that address instead.you
|
||||
You cannot use pushw() for this because the bytes pushed by JSR are different
|
||||
|
||||
``pop ()``
|
||||
pops a byte value off the CPU hardware stack and returns it.
|
||||
Low-level function that should normally not be used.
|
||||
|
||||
``popw ()``
|
||||
pops a 16-bit word value off the CPU hardware stack and returns it.
|
||||
pops a 16-bit word value off the CPU hardware stack that was pushed before by pushw, and returns it.
|
||||
Low-level function that should normally not be used.
|
||||
|
||||
|
||||
|
||||
@@ -127,18 +127,23 @@ So the normal IRQ vector can still run and will be when the program is started!
|
||||
CPU
|
||||
===
|
||||
|
||||
Directly Usable Registers
|
||||
-------------------------
|
||||
Directly Accessible Registers
|
||||
-----------------------------
|
||||
|
||||
The hardware CPU registers are not directly accessible from regular Prog8 code.
|
||||
If you need to mess with them, you'll have to use inline assembly.
|
||||
The hardware CPU registers (A, X, Y) are not directly accessible from regular Prog8 code.
|
||||
If you need to work with them, you'll have to use some inline assembly with ``%asm``.
|
||||
Or, if they are required to have a value as arguments to some external kernal or library assembly routine,
|
||||
just use a normal subroutine call to an ``extsub`` that correctly specifies what registers go where.
|
||||
The compiler will then take care of loading the arguments into the required registers and returning
|
||||
any response value(s) back to the prog8 code.
|
||||
|
||||
The status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
||||
The status register (P) carry flag and interrupt disable flag *can* be written via a couple of special
|
||||
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
||||
and read via the ``read_flags()`` function.
|
||||
and read via the ``read_flags()`` function. With the special status branch statements like ``if_cc``,
|
||||
``if_cs`` etc you can branch directly on the status of the flags.
|
||||
|
||||
The 16 'virtual' 16-bit registers that are defined on the Commander X16 machine are not real hardware
|
||||
registers and are just 16 memory-mapped word values that you *can* access directly.
|
||||
registers and are just 16 memory-mapped word values that you *can* access directly from everywhere.
|
||||
|
||||
|
||||
IRQ Handling
|
||||
|
||||
+16
-3
@@ -1,6 +1,7 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- add paypal donation button as well?
|
||||
- announce prog8 on the 6502.org site?
|
||||
|
||||
...
|
||||
@@ -9,8 +10,19 @@ TODO
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- why is this code so much larger with charpos variable, than with just using cx16.r0L ?
|
||||
ubyte charpos
|
||||
for charpos in DIALOG_TOPX+1 to DIALOG_TOPX+30 {
|
||||
txt.setchr(charpos, cx16.r1L, txt.getchr(charpos, cx16.r1L) ^ 128)
|
||||
}
|
||||
|
||||
- support &, &< and &> on array elements from split word arrays too not just the array as a whole (to get rid of the error "&< is only valid on array variables"
|
||||
and "cannot take the adress of a word element that is in a split-word array" and the TODOS "address of element of a split word array")
|
||||
- after that: fix leftover asmgen split word array todo's
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
- 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
|
||||
|
||||
- don't do BIT instruction tests via makeBittestCall() fake builtin function. Just do it in the code generator when it encounters the correct bitwise and sequence. (also IR)
|
||||
- Compiling Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.
|
||||
Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
|
||||
Add a -library $xxxx command line option (and/or some directive) to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?
|
||||
@@ -20,11 +32,11 @@ Future Things and Ideas
|
||||
- [problematic due to using 64tass:] better support for building library programs, where unused .proc are NOT deleted from the assembly.
|
||||
Perhaps replace all uses of .proc/.pend/.endproc by .block/.bend will fix that with a compiler flag?
|
||||
But all library code written in asm uses .proc already..... (textual search/replace when writing the actual asm?)
|
||||
Maybe propose a patch to 64tass itself that will treat .proc as .block ?
|
||||
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
|
||||
|
||||
- Allow normal subroutines to return multiple values as well (just as asmsubs already can)
|
||||
- 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)
|
||||
- Fix missing cases where regular & has to return the start of the split array in memory whatever byte comes first. Search TODO("address of split word array")
|
||||
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
|
||||
- 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
|
||||
@@ -43,8 +55,8 @@ Future Things and Ideas
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers?
|
||||
- constants are not retained in the IR file, they should. (need to be able to make asm labels from them eventually)
|
||||
- fix TODO("IR rol/ror on split words array")
|
||||
- fix "<< in array" / ">> in array"
|
||||
- implement missing operators in AssignmentGen (array shifts etc)
|
||||
- support %align on code chunks
|
||||
- fix call() return value handling
|
||||
@@ -61,6 +73,7 @@ IR/VM
|
||||
- getting it in shape for code generation...
|
||||
- make optimizeBitTest work for IR too to use the BIT instruction?
|
||||
- make sure that a 6502 codegen based off the IR, still generates BIT instructions when testing bit 7 or 6 of a byte var.
|
||||
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers? maybe not, this requires hundreds of lines to change
|
||||
|
||||
|
||||
Libraries
|
||||
|
||||
@@ -168,7 +168,7 @@ type identifier type storage size example var declara
|
||||
``uword[]`` unsigned word array depends on value ``uword[] myvar = [1, 2, 3, 4]``
|
||||
``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]``
|
||||
``bool[]`` boolean array depends on value ``bool[] myvar = [true, false, true]`` note: consider using bit flags in a byte or word instead to save space
|
||||
``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]``
|
||||
``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]`` note: equivalent to a uword array.
|
||||
``str`` string (PETSCII) varies ``str myvar = "hello."``
|
||||
implicitly terminated by a 0-byte
|
||||
=============== ======================= ================= =========================================
|
||||
@@ -326,11 +326,14 @@ Unlike array variables, negative indexing for pointer variables does *not* mean
|
||||
Instead, it simply addresses memory that lies *before* the pointer variable.
|
||||
See also :ref:`pointervars`
|
||||
|
||||
**LSB/MSB split word arrays:**
|
||||
**LSB/MSB split word and str arrays:**
|
||||
|
||||
As an optimization, (u)word arrays are split by the compiler in memory as two separate arrays,
|
||||
As an optimization, (u)word arrays and str arrays are split by the compiler in memory as two separate arrays,
|
||||
one with the LSBs and one with the MSBs of the word values. This is more efficient to access by the 6502 cpu.
|
||||
It also enables a maximum length of 256 for word arrays, where normally it would have been 128.
|
||||
It also allows a maximum length of 256 for word arrays, where normally it would have been 128.
|
||||
|
||||
For normal prog8 array indexing, the compiler takes care of the distiction for you under water.
|
||||
*But for assembly code, or code that otherwise accesses the array elements directly, you have to be aware of the distinction from 'normal' arrays.*
|
||||
In the assembly code, the array is generated as two byte arrays namely ``name_lsb`` and ``name_msb``, immediately following eachother in memory.
|
||||
|
||||
The ``@split`` tag can be added to the variable declaration to *always* split the array even when the command line option -dontsplitarrays is set
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
|
||||
; Cooperative multitasking / Coroutines
|
||||
;
|
||||
; Uses cpu stack return address juggling to cycle between the different tasks when they call yield().
|
||||
; Super simplistic implementation here to just show the core idea.
|
||||
;
|
||||
; To make it actually useful, we probably have to:
|
||||
; - make the list of tasks dynamic;
|
||||
; - allow tasks to finish, allow new tasks to be added
|
||||
; - return a unique value (pointer that you had to provide when adding the task to the list?)
|
||||
; from yield() that the subroutine could use to access unique state,
|
||||
; because right now a single task == a single subroutine; right now you cannot re-use a subroutine to run
|
||||
; the same task multiple times for different things.
|
||||
|
||||
|
||||
%import textio
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.print("cooperative multitasking / coroutines\n\n")
|
||||
txt.print("here are 4 subroutines that each run\nan endless loop bouncing a digit around.")
|
||||
coroutines.start()
|
||||
}
|
||||
}
|
||||
|
||||
coroutines {
|
||||
uword[] tasklist = [&task1, &task2, &task3, &task4, &vsynctask]
|
||||
|
||||
sub start() {
|
||||
goto tasklist[0]
|
||||
}
|
||||
|
||||
uword[len(tasklist)] returnaddresses
|
||||
ubyte active_task
|
||||
|
||||
sub yield() {
|
||||
; store the return address of the yielding task,
|
||||
; and continue with the next one instead (round-robin)
|
||||
returnaddresses[active_task] = sys.popw()
|
||||
active_task++
|
||||
if active_task==len(returnaddresses)
|
||||
active_task=0
|
||||
cx16.r0 = returnaddresses[active_task]
|
||||
if cx16.r0==0 {
|
||||
; fetch start address of next task.
|
||||
; address on the stack must be pushed in reverse byte order
|
||||
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
|
||||
cx16.r0 = tasklist[active_task]-1
|
||||
sys.push(cx16.r0H)
|
||||
sys.push(cx16.r0L)
|
||||
} else
|
||||
sys.pushw(cx16.r0)
|
||||
|
||||
; returning from yield then continues with the next coroutine
|
||||
}
|
||||
|
||||
sub task1() {
|
||||
const ubyte x = 5
|
||||
ubyte y
|
||||
repeat {
|
||||
for y in 10 to 24 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
yield()
|
||||
}
|
||||
for y in 24 downto 10 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub task2() {
|
||||
const ubyte x = 10
|
||||
ubyte y
|
||||
repeat {
|
||||
for y in 5 to 18 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
yield()
|
||||
}
|
||||
for y in 18 downto 5 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub task3() {
|
||||
ubyte x
|
||||
const ubyte y = 10
|
||||
repeat {
|
||||
for x in 14 to 38 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
yield()
|
||||
}
|
||||
for x in 38 downto 14 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub task4() {
|
||||
ubyte x
|
||||
const ubyte y = 14
|
||||
repeat {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub vsynctask() {
|
||||
repeat {
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
yield()
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,10 +101,10 @@ adpcm {
|
||||
}
|
||||
|
||||
sub decode_nibble_second(ubyte @zp nibble) {
|
||||
; Decoder for nibbles for the second channel.
|
||||
; this is the hotspot of the decoder algorithm!
|
||||
; Decoder for a single nibble for the second channel. (value of 'nibble' needs to be strictly 0-15 !)
|
||||
; This is the hotspot of the decoder algorithm!
|
||||
; Note that the generated assembly from this is pretty efficient,
|
||||
; rewriting it by hand in asm seems to improve it only 5-10%
|
||||
; rewriting it by hand in asm seems to improve it only ~10%.
|
||||
cx16.r0s = 0 ; difference
|
||||
if nibble & %0100 !=0
|
||||
cx16.r0s += pstep_2
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,172 @@
|
||||
; Cooperative Multitasking example.
|
||||
; Can be compiled for different targets (except virtual).
|
||||
|
||||
%import coroutines
|
||||
%import textio
|
||||
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.print("cooperative multitasking / coroutines\n\n")
|
||||
txt.print("here are couple of routines that each\nrun a few loops bouncing a digit around.\n")
|
||||
txt.print("press any key to add a new counter task.")
|
||||
|
||||
coroutines.killall()
|
||||
void coroutines.add(&task1, sc:'1')
|
||||
void coroutines.add(&task2, sc:'2')
|
||||
void coroutines.add(&task3, sc:'3')
|
||||
void coroutines.add(&task4, sc:'4')
|
||||
void coroutines.add(&keyhandler, 0)
|
||||
void coroutines.add(&delaytask, 0)
|
||||
coroutines.run(&supervisor) ; can also use 0 if you don't need a supervisor routine
|
||||
; the supervisor is called every time a task switch is about to occur - and you can control that somewhat.
|
||||
; if that control is not needed you could also just add a "system" routine as a regular task,
|
||||
; much like the keyhandler and delay tasks above.
|
||||
|
||||
; we will end up here if there are no more tasks to run (doesn't happen in this example)
|
||||
txt.print("we're all done!\n")
|
||||
}
|
||||
|
||||
sub keyhandler() {
|
||||
repeat {
|
||||
ubyte key = cbm.GETIN2()
|
||||
if key!=0 {
|
||||
ubyte taskid = coroutines.add(&countertask, key)
|
||||
counters[taskid] = 222
|
||||
}
|
||||
void coroutines.yield()
|
||||
}
|
||||
}
|
||||
|
||||
sub supervisor() -> bool {
|
||||
; you can call coroutines.current() to get the id of the next task to run.
|
||||
; (or just read the active_task variable)
|
||||
; return true to execute that task, or false to skip it this time.
|
||||
; or do something else....?
|
||||
return true
|
||||
}
|
||||
|
||||
ubyte[coroutines.MAX_TASKS] counters
|
||||
|
||||
sub countertask() {
|
||||
repeat {
|
||||
uword userdata = coroutines.yield() ; yield and obtain our userdata
|
||||
ubyte tid = coroutines.current() ; what task are we?
|
||||
counters[tid]-- ; our counter is in the array, it cannot be kept in a local variable (shared state)
|
||||
|
||||
txt.plot(15, 10 + tid)
|
||||
if counters[tid] == 0 {
|
||||
txt.print(" ")
|
||||
return ; done, exit the task
|
||||
} else {
|
||||
txt.chrout(lsb(userdata))
|
||||
txt.chrout(':')
|
||||
txt.print_uw(counters[tid])
|
||||
txt.spc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub task1() {
|
||||
const ubyte x = 5
|
||||
ubyte y
|
||||
repeat 3 {
|
||||
for y in 10 to 24 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
void coroutines.yield()
|
||||
}
|
||||
for y in 24 downto 10 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
void coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(x, 10, sc:' ')
|
||||
}
|
||||
|
||||
sub task2() {
|
||||
const ubyte x = 10
|
||||
ubyte y
|
||||
repeat 2 {
|
||||
for y in 9 to 22 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
void coroutines.yield()
|
||||
}
|
||||
for y in 22 downto 9 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
void coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(x, 9, sc:' ')
|
||||
|
||||
; add a new task dynamically
|
||||
void coroutines.add(&task5, 0)
|
||||
}
|
||||
|
||||
sub task3() {
|
||||
ubyte x
|
||||
const ubyte y = 10
|
||||
repeat 4 {
|
||||
for x in 14 to 38 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
void coroutines.yield()
|
||||
}
|
||||
for x in 38 downto 14 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
void coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(14, y, sc:' ')
|
||||
}
|
||||
|
||||
sub task4() {
|
||||
ubyte x
|
||||
const ubyte y = 14
|
||||
repeat 4 {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
void coroutines.yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
void coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(15, y, sc:' ')
|
||||
}
|
||||
|
||||
sub task5() {
|
||||
ubyte x
|
||||
const ubyte y = 16
|
||||
repeat 4 {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'5')
|
||||
void coroutines.yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'5')
|
||||
void coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(15, y, sc:' ')
|
||||
}
|
||||
|
||||
sub delaytask() {
|
||||
repeat 200 {
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
void coroutines.yield()
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-22
@@ -1,31 +1,22 @@
|
||||
%import textio
|
||||
%import math
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
str input = iso:"the quick brown fox jumps over the lazy dog"
|
||||
bool bb
|
||||
ubyte ub
|
||||
uword uw
|
||||
uw, void = thing2()
|
||||
uw, bb = thing2()
|
||||
uw, ub = thing2()
|
||||
}
|
||||
|
||||
|
||||
txt.print_uwhex(math.crc16(input, len(input)), true)
|
||||
txt.nl()
|
||||
|
||||
math.crc32(input, len(input))
|
||||
|
||||
txt.print_uwhex(cx16.r15, true)
|
||||
txt.print_uwhex(cx16.r14, false)
|
||||
txt.nl()
|
||||
|
||||
math.crc32_start()
|
||||
for cx16.r0L in input
|
||||
math.crc32_update(cx16.r0L)
|
||||
uword hiw,low
|
||||
hiw,low = math.crc32_end_result()
|
||||
txt.print_uwhex(hiw, true)
|
||||
txt.print_uwhex(low, false)
|
||||
txt.nl()
|
||||
asmsub thing2() -> ubyte @A, bool @Pc {
|
||||
%asm {{
|
||||
lda #$aa
|
||||
clc
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user