Get rid of any() and all() builtin functions.

Replaced by regular subroutines in the anyall module.
This commit is contained in:
Irmen de Jong 2024-07-06 18:49:03 +02:00
parent b10a8e728f
commit 484677b4b1
26 changed files with 223 additions and 507 deletions

View File

@ -67,7 +67,7 @@ What does Prog8 provide?
- conditional branches
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
- ``in`` expression for concise and efficient multi-value/containment check
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
- various powerful built-in libraries to do I/O, number conversions, graphics and more
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- inline assembly allows you to have full control when every cycle or byte matters

View File

@ -97,8 +97,6 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"divmod" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),

View File

@ -33,7 +33,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
@ -726,63 +725,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true, true)
}
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
val dt = fcall.args.single().type
val array = fcall.args[0] as PtIdentifier
when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A")
}
DataType.ARRAY_F -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr floats.func_${fcall.name}_f_into_A")
}
in SplitWordArrayTypes -> {
val numElements = (asmgen.symbolTable.lookup(array.name) as StStaticVariable).length
when(fcall.name) {
"any" -> {
// any(lsb-array) or any(msb-array)
val arrayName = asmgen.asmVariableName(array)
asmgen.out("""
lda #<${arrayName}_lsb
ldy #>${arrayName}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
""")
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
asmgen.out(" bne +") // shortcircuit
asmgen.out("""
pha
lda #<${arrayName}_msb
ldy #>${arrayName}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
""")
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
asmgen.out("""
sta P8ZP_SCRATCH_REG
pla
ora P8ZP_SCRATCH_REG
+""")
}
"all" -> {
TODO("split words all")
}
else -> throw AssemblyError("weird call")
}
}
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes, true)
}
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type

View File

@ -9,8 +9,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult {
return when(call.name) {
"any" -> funcAny(call)
"all" -> funcAll(call)
"abs__byte", "abs__word", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call)
@ -202,79 +200,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, dt, leftTr.resultReg, -1)
}
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val result = mutableListOf<IRCodeChunkBase>()
val lengthReg = codeGen.registers.nextFree()
if(arrayName.type in SplitWordArrayTypes) {
// any(lsb-array) or any(msb-array)
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
addToResult(result, trLsb, trLsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trLsb.resultReg)
val shortcircuitLabel = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = trLsb.resultReg, immediate = 0)
it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcircuitLabel)
it += IRInstruction(Opcode.PREPARECALL, immediate = 2)
}
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
addToResult(result, trMsb, trMsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trMsb.resultReg)
addInstr(result, IRInstruction(Opcode.ORR, IRDataType.BYTE, reg1=trLsb.resultReg, reg2=trMsb.resultReg), null)
result += IRCodeChunk(shortcircuitLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, trLsb.resultReg, -1)
}
val syscall =
when (arrayName.type) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ANY_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> IMSyscall.ANY_WORD
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type")
}
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength!! and 255), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
if(arrayName.type in SplitWordArrayTypes) {
// this is a bit complicated to calculate.... have to check all recombined (lsb,msb) words for $0000
TODO("all(split words $arrayName)")
}
val syscall =
when(arrayName.type) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ALL_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> IMSyscall.ALL_WORD
DataType.ARRAY_F -> IMSyscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type")
}
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength!! and 255), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>()

View File

@ -0,0 +1,81 @@
; any() and all() checks on arrays/memory buffers
%option no_symbol_prefixing, ignore_unused
anyall {
sub any(uword arrayptr, uword num_elements) -> bool {
; -- returns true if any byte in the array is not zero.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
for cx16.r0L in 0 to lsb(num_elements)-1 {
if cx16.r1[cx16.r0L]!=0
return true
}
return false
}
repeat num_elements {
if @(cx16.r1)!=0
return true
cx16.r1++
}
return false
}
sub all(uword arrayptr, uword num_elements) -> bool {
; -- returns true if all bytes in the array are not zero.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
for cx16.r0L in 0 to lsb(num_elements)-1 {
if cx16.r1[cx16.r0L]==0
return false
}
return true
}
repeat num_elements {
if @(cx16.r1)==0
return false
cx16.r1++
}
return true
}
sub anyw(uword arrayptr, uword num_elements) -> bool {
; -- returns true if any word in the array is not zero.
; doesn't work on @split arrays.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
repeat lsb(num_elements) {
if peekw(cx16.r1)!=0
return true
cx16.r1+=2
}
return false
}
repeat num_elements {
if peekw(cx16.r1)!=0
return true
cx16.r1+=2
}
return false
}
sub allw(uword arrayptr, uword num_elements) -> bool {
; -- returns true if all words in the array are not zero.
; doesn't work on @split arrays.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
repeat lsb(num_elements) {
if peekw(cx16.r1)==0
return false
cx16.r1+=2
}
return true
}
repeat num_elements {
if peekw(cx16.r1)==0
return false
cx16.r1+=2
}
return true
}
}

View File

@ -1,5 +1,7 @@
; experimental buffer data structures
%option no_symbol_prefixing, ignore_unused
smallringbuffer {
; -- A ringbuffer (FIFO queue) that occupies a single page in memory, containing 255 bytes maximum.
; You can store and retrieve words too.

View File

@ -16,26 +16,6 @@ a_times_5 .proc
rts
.pend
func_any_f_into_A .proc
jsr a_times_5
jmp prog8_lib.func_any_b_into_A
.pend
func_all_f_into_A .proc
jsr a_times_5
jmp prog8_lib.func_all_b_into_A
.pend
func_any_f_stack .proc
jsr a_times_5
jmp prog8_lib.func_any_b_stack
.pend
func_all_f_stack .proc
jsr a_times_5
jmp prog8_lib.func_all_b_stack
.pend
func_abs_f_into_FAC1 .proc
jsr MOVFM
jmp ABS

View File

@ -1,60 +1,5 @@
; ---- builtin functions
func_any_b_into_A .proc
; -- any(array), array in P8ZP_SCRATCH_W1, num bytes in A
sta _cmp_mod+1 ; self-modifying code
ldy #0
- lda (P8ZP_SCRATCH_W1),y
bne _got_any
iny
_cmp_mod cpy #255 ; modified
bne -
lda #0
rts
_got_any lda #1
rts
.pend
func_all_b_into_A .proc
; -- all(array), array in P8ZP_SCRATCH_W1, num bytes in A
sta _cmp_mod+1 ; self-modifying code
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq _got_not_all
iny
_cmp_mod cpy #255 ; modified
bne -
lda #1
_got_not_all rts
.pend
func_any_w_into_A .proc
asl a
jmp func_any_b_into_A
.pend
func_all_w_into_A .proc
; -- all(warray), array in P8ZP_SCRATCH_W1, num bytes in A
asl a ; times 2 because of word
sta _cmp_mod+1 ; self-modifying code
ldy #0
- lda (P8ZP_SCRATCH_W1),y
bne +
iny
lda (P8ZP_SCRATCH_W1),y
bne ++
lda #0
rts
+ iny
+ iny
_cmp_mod cpy #255 ; modified
bne -
lda #1
rts
.pend
abs_b_into_A .proc
; -- A = abs(A)
cmp #0

View File

@ -11,7 +11,7 @@ diskio {
%ir {{
loadm.w r65534,diskio.load.filenameptr
loadm.w r65535,diskio.load.address_override
syscall 61 (): r0.b
syscall 48 (): r0.b
returnr.b r0
}}
}
@ -157,7 +157,7 @@ diskio {
loadm.w r65533,diskio.save.filenameptr
loadm.w r65534,diskio.save.start_address
loadm.w r65535,diskio.save.savesize
syscall 58 (r65532.b, r65533.w, r65534.w, r65535.w): r0.b
syscall 45 (r65532.b, r65533.w, r65534.w, r65535.w): r0.b
returnr.b r0
}}
}
@ -169,7 +169,7 @@ diskio {
loadm.w r65533,diskio.save.filenameptr
loadm.w r65534,diskio.save.start_address
loadm.w r65535,diskio.save.savesize
syscall 58 (r65532.b, r65533.w, r65534.w, r65535.w): r0.b
syscall 45 (r65532.b, r65533.w, r65534.w, r65535.w): r0.b
returnr.b r0
}}
}
@ -184,7 +184,7 @@ diskio {
%ir {{
loadm.w r65534,diskio.load.filenameptr
loadm.w r65535,diskio.load.address_override
syscall 56 (r65534.w, r65535.w): r0.w
syscall 43 (r65534.w, r65535.w): r0.w
returnr.w r0
}}
}
@ -196,7 +196,7 @@ diskio {
%ir {{
loadm.w r65534,diskio.load_raw.filenameptr
loadm.w r65535,diskio.load_raw.start_address
syscall 57 (r65534.w, r65535.w): r0.w
syscall 44 (r65534.w, r65535.w): r0.w
returnr.w r0
}}
}
@ -205,7 +205,7 @@ diskio {
; -- delete a file on the drive
%ir {{
loadm.w r65535,diskio.delete.filenameptr
syscall 59 (r65535.w)
syscall 46 (r65535.w)
}}
}
@ -214,7 +214,7 @@ diskio {
%ir {{
loadm.w r65534,diskio.rename.oldfileptr
loadm.w r65535,diskio.rename.newfileptr
syscall 60 (r65534.w, r65535.w)
syscall 47 (r65534.w, r65535.w)
}}
}
}

View File

@ -13,7 +13,7 @@ sub print(float value) {
; ---- prints the floating point value (without a newline and no leading spaces).
%ir {{
loadm.f fr65535,floats.print.value
syscall 25 (fr65535.f)
syscall 15 (fr65535.f)
return
}}
}
@ -24,7 +24,7 @@ sub tostr(float value) -> str {
%ir {{
load.w r65535,floats.tostr.buffer
loadm.f fr65535,floats.tostr.value
syscall 47 (r65535.w, fr65535.f)
syscall 34 (r65535.w, fr65535.f)
load.w r0,floats.tostr.buffer
returnr.w r0
}}
@ -34,7 +34,7 @@ sub parse(str value) -> float {
; -- parse a string value of a number to float
%ir {{
loadm.w r65535,floats.parse.value
syscall 45 (r65535.w): fr0.f
syscall 32 (r65535.w): fr0.f
returnr.f fr0
}}
}
@ -147,7 +147,7 @@ sub ceil(float value) -> float {
sub rnd() -> float {
%ir {{
syscall 35 () : fr0.f
syscall 22 () : fr0.f
returnr.f fr0
}}
}
@ -155,7 +155,7 @@ sub rnd() -> float {
sub rndseed(float seed) {
%ir {{
loadm.f fr65535,floats.rndseed.seed
syscall 32 (fr65535.f)
syscall 19 (fr65535.f)
return
}}
}

View File

@ -164,14 +164,14 @@ math {
sub rnd() -> ubyte {
%ir {{
syscall 33 (): r0.b
syscall 20 (): r0.b
returnr.b r0
}}
}
sub rndw() -> uword {
%ir {{
syscall 34 (): r0.w
syscall 21 (): r0.w
returnr.w r0
}}
}
@ -199,7 +199,7 @@ math {
%ir {{
loadm.w r65534,math.rndseed.seed1
loadm.w r65535,math.rndseed.seed2
syscall 31 (r65534.w, r65535.w)
syscall 19 (r65534.w, r65535.w)
return
}}
}
@ -280,7 +280,7 @@ math {
loadm.b r65533,math.atan2.y1
loadm.b r65534,math.atan2.x2
loadm.b r65535,math.atan2.y2
syscall 44 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b
syscall 31 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b
returnr.b r0
}}
}
@ -294,7 +294,7 @@ math {
; - not all multiplications in the source code result in an actual multiplication call:
; some simpler multiplications will be optimized away into faster routines. These will not set the upper 16 bits at all!
%ir {{
syscall 46 (): r0.w
syscall 33 (): r0.w
returnr.w r0
}}
}

View File

@ -83,7 +83,7 @@ string {
%ir {{
loadm.w r65534,string.copy.source
loadm.w r65535,string.copy.target
syscall 52 (r65534.w, r65535.w): r0.b
syscall 39 (r65534.w, r65535.w): r0.b
returnr.b r0
}}
}
@ -103,7 +103,7 @@ string {
%ir {{
loadm.w r65534,string.compare.st1
loadm.w r65535,string.compare.st2
syscall 29 (r65534.w, r65535.w) : r0.b
syscall 16 (r65534.w, r65535.w) : r0.b
returnr.b r0
}}
}

View File

@ -34,7 +34,7 @@ sys {
%ir {{
loadm.w r65534,sys.internal_stringcopy.source
loadm.w r65535,sys.internal_stringcopy.tgt
syscall 52 (r65534.w, r65535.w): r0.b
syscall 39 (r65534.w, r65535.w): r0.b
}}
}
@ -43,7 +43,7 @@ sys {
loadm.w r65533,sys.memcopy.source
loadm.w r65534,sys.memcopy.tgt
loadm.w r65535,sys.memcopy.count
syscall 49 (r65533.w, r65534.w, r65535.w)
syscall 36 (r65533.w, r65534.w, r65535.w)
}}
}
@ -52,7 +52,7 @@ sys {
loadm.w r65533,sys.memset.mem
loadm.w r65534,sys.memset.numbytes
loadm.b r65535,sys.memset.value
syscall 50 (r65533.w, r65534.w, r65535.b)
syscall 37 (r65533.w, r65534.w, r65535.b)
}}
}
@ -61,7 +61,7 @@ sys {
loadm.w r65533,sys.memsetw.mem
loadm.w r65534,sys.memsetw.numwords
loadm.w r65535,sys.memsetw.value
syscall 51 (r65533.w, r65534.w, r65535.w)
syscall 38 (r65533.w, r65534.w, r65535.w)
}}
}
@ -128,7 +128,7 @@ sys {
%ir {{
loadm.w r65534,sys.gfx_getpixel.xx
loadm.w r65535,sys.gfx_getpixel.yy
syscall 30 (r65534.w, r65535.w): r0.b
syscall 17 (r65534.w, r65535.w): r0.b
returnr.b r0
}}
}

View File

@ -7,14 +7,14 @@ txt {
sub width() -> ubyte {
%ir {{
syscall 62 (): r0.w
syscall 49 (): r0.w
returnr.b r0
}}
}
sub height() -> ubyte {
%ir {{
syscall 62 (): r0.w
syscall 49 (): r0.w
msig.b r1,r0
returnr.b r1
}}

View File

@ -19,8 +19,6 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
"any" to { a, p, prg -> collectionArgBoolResult(a, p, prg) { array->array.any { it!=0.0 } } },
"all" to { a, p, prg -> collectionArgBoolResult(a, p, prg) { array->array.all { it!=0.0 } } },
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 8 and 255).toDouble()} },
"mkword" to ::builtinMkword,

View File

@ -20,23 +20,6 @@ import prog8tests.helpers.compileText
import kotlin.io.path.readText
class TestCompilerVirtual: FunSpec({
test("compile virtual: any all builtin funcs") {
val src = """
main {
sub start() {
uword[] words = [1111,2222,0,4444,3333]
bool result = all(words)
cx16.r0++
result = any(words)
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
}
test("compile virtual: array with pointers") {
val src = """
main {

View File

@ -85,7 +85,7 @@ Features
- Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
- ``when`` statement to avoid if-else chains
- ``in`` expression for concise and efficient multi-value/containment test
- Several powerful built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
- Several specialized built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
- Variable data types include signed and unsigned bytes and words, arrays, strings.
- Various powerful built-in libraries to do I/O, number conversions, graphics and more
- Floating point math is supported on select compiler targets.

View File

@ -73,12 +73,6 @@ divmod (dividend, divisor, quotient, remainder)
Array operations
^^^^^^^^^^^^^^^^
any (x)
true if any of the values in the array value x is 'true' (not zero), else false.
all (x)
true if all of the values in the array value x are 'true' (not zero), else false.
len (x)
Number of values in the array value x, or the number of characters in a string (excluding the 0-byte).
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof().
@ -369,6 +363,25 @@ sys (part of syslib)
Low-level function that should normally not be used.
anyall
------
Routines to check if any or all values in an array or memory buffer are not zero.
``any (arrayptr, num_elements)``
true if any of the byte values in the array is not zero, else false.
``all (arrayptr, num_elements)``
true if all of the byte values in the array are not zero, else false.
``anyw (arrayptr, num_elements)``
true if any of the word values in the array is not zero, else false.
Doesn't work on split arrays.
``allw (arrayptr, num_elements)``
true if all of the word values in the array are not zero, else false.
Doesn't work on split arrays.
conv
----
Routines to convert strings to numbers or vice versa.

View File

@ -1,8 +1,6 @@
TODO
====
Get rid of any() and all() as builtin functions? Replace them with regular subroutines in buffer.p8 for example?
See open issues on github.
IR: add SEC and CLC instructions in place of call to sys.set_carry() and sys.clear_carry(). (check more inline sub calls that should be a single instruction?)

View File

@ -1,20 +1,66 @@
%import textio
%import buffers
%zeropage basicsafe
%import anyall
%option no_sysinit
main {
byte[256] barray
word[128] warray
uword large_barray=memory("bytes", 1000, 0)
uword large_warray=memory("words", 1000, 0)
sub check() {
txt.print_bool(anyall.all(barray, 256))
txt.spc()
txt.print_bool(anyall.any(barray, 256))
txt.nl()
txt.print_bool(anyall.allw(warray, 128))
txt.spc()
txt.print_bool(anyall.anyw(warray, 128))
txt.nl()
txt.print_bool(anyall.all(large_barray, 1000))
txt.spc()
txt.print_bool(anyall.any(large_barray, 1000))
txt.nl()
txt.print_bool(anyall.allw(large_warray, 500))
txt.spc()
txt.print_bool(anyall.anyw(large_warray, 500))
txt.nl()
txt.nl()
}
sub start() {
smallringbuffer.init()
sys.memset(large_barray, 1000, 0)
sys.memset(large_warray, 1000, 0)
smallringbuffer.put(123)
txt.print_ub(smallringbuffer.get())
txt.nl()
check()
barray[250] = 99
warray[100] = $0100
large_barray[900] = 99
large_warray[900] = 99
check()
sys.memset(barray, 255, 1)
sys.memset(warray, 254, 1)
sys.memset(large_barray, 999, 1)
sys.memset(large_warray, 998, 1)
check()
barray[255]=1
warray[127]=1
@(large_barray+999)=1
@(large_warray+999)=1
check()
repeat {}
smallringbuffer.putw(12345)
txt.print_uw(smallringbuffer.getw())
txt.nl()
; smallringbuffer.init()
;
; smallringbuffer.put(123)
; txt.print_ub(smallringbuffer.get())
; txt.nl()
;
; smallringbuffer.putw(12345)
; txt.print_uw(smallringbuffer.getw())
; txt.nl()
}
}

View File

@ -5,19 +5,6 @@ package prog8.intermediate
// Note that in the VM these are translated into whatever the corresponding Syscall number in the VM is.
enum class IMSyscall(val number: Int) {
SORT_UBYTE(0x1000),
SORT_BYTE(0x1001),
SORT_UWORD(0x1002),
SORT_WORD(0x1003),
ANY_BYTE(0x1004),
ANY_WORD(0x1005),
ANY_FLOAT(0x1006),
ALL_BYTE(0x1007),
ALL_WORD(0x1008),
ALL_FLOAT(0x1009),
REVERSE_BYTES(0x100a),
REVERSE_WORDS(0x100b),
REVERSE_FLOATS(0x100c),
COMPARE_STRINGS(0x100d),
STRING_CONTAINS(0x100e),
BYTEARRAY_CONTAINS(0x100f),

View File

@ -14,7 +14,7 @@
<keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%option;%output;%zeropage;%zpallowed;%zpreserved;atascii:;cp437:;default:;iso16:;iso5:;iso:;petscii:;sc:" />
<keywords3 keywords="@nozp;@requirezp;@shared;@split;@zp;bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;all;any;call;callfar;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
<keywords4 keywords="abs;call;callfar;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
</highlighting>
<extensionMap>
<mapping ext="p8" />

View File

@ -27,7 +27,7 @@
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split requirezp nozp</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">abs all any call callfar clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
<Keywords name="Keywords4">abs call callfar clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>

View File

@ -10,7 +10,7 @@
syn keyword prog8BuiltInFunc sgn sqrtw
" Array functions
syn keyword prog8BuiltInFunc any all len
syn keyword prog8BuiltInFunc len
" Miscellaneous functions
syn keyword prog8BuiltInFunc cmp divmod lsb msb mkword min max peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex

View File

@ -25,54 +25,41 @@ SYSCALLS:
12 = decimal string to word (signed)
13 = wait ; wait certain amount of jiffies (1/60 sec)
14 = waitvsync ; wait on vsync
15 = sort_ubyte array
16 = sort_byte array
17 = sort_uword array
18 = sort_word array
19 = any_byte array
20 = any_word array
21 = any_float array
22 = all_byte array
23 = all_word array
24 = all_float array
25 = print_f (floating point value in fp reg 0)
26 = reverse_bytes array
27 = reverse_words array
28 = reverse_floats array
29 = compare strings
30 = gfx_getpixel ; get byte pixel value at coordinates r0.w/r1.w
31 = rndseed
32 = rndfseed
33 = RND
34 = RNDW
35 = RNDF
36 = STRING_CONTAINS
37 = BYTEARRAY_CONTAINS
38 = WORDARRAY_CONTAINS
39 = CLAMP_BYTE
40 = CLAMP_UBYTE
41 = CLAMP_WORD
42 = CLAMP_UWORD
43 = CLAMP_FLOAT
44 = ATAN
45 = str to float
46 = MUL16_LAST_UPPER
47 = float to str
48 = FLOATARRAY_CONTAINS
49 = memcopy
50 = memset
51 = memsetw
52 = stringcopy
53 = ARRAYCOPY_SPLITW_TO_NORMAL
54 = ARRAYCOPY_NORMAL_TO_SPLITW
55 = memcopy_small
56 = load
57 = load_raw
58 = save
59 = delete
60 = rename
61 = directory
62 = getconsolesize
15 = print_f (floating point value in fp reg 0)
16 = compare strings
17 = gfx_getpixel ; get byte pixel value at coordinates r0.w/r1.w
18 = rndseed
19 = rndfseed
20 = RND
21 = RNDW
22 = RNDF
23 = STRING_CONTAINS
24 = BYTEARRAY_CONTAINS
25 = WORDARRAY_CONTAINS
26 = CLAMP_BYTE
27 = CLAMP_UBYTE
28 = CLAMP_WORD
29 = CLAMP_UWORD
30 = CLAMP_FLOAT
31 = ATAN
32 = str to float
33 = MUL16_LAST_UPPER
34 = float to str
35 = FLOATARRAY_CONTAINS
36 = memcopy
37 = memset
38 = memsetw
39 = stringcopy
40 = ARRAYCOPY_SPLITW_TO_NORMAL
41 = ARRAYCOPY_NORMAL_TO_SPLITW
42 = memcopy_small
43 = load
44 = load_raw
45 = save
46 = delete
47 = rename
48 = directory
49 = getconsolesize
*/
enum class Syscall {
@ -91,20 +78,7 @@ enum class Syscall {
STR_TO_WORD,
WAIT,
WAITVSYNC,
SORT_UBYTE,
SORT_BYTE,
SORT_UWORD,
SORT_WORD,
ANY_BYTE,
ANY_WORD,
ANY_FLOAT,
ALL_BYTE,
ALL_WORD,
ALL_FLOAT,
PRINT_F,
REVERSE_BYTES,
REVERSE_WORDS,
REVERSE_FLOATS,
COMPARE_STRINGS,
GFX_GETPIXEL,
RNDSEED,
@ -243,149 +217,6 @@ object SysCalls {
Thread.sleep(time.toLong() * 1000/60)
}
Syscall.WAITVSYNC -> vm.waitvsync()
Syscall.SORT_UBYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setUB(address+index, value)
}
}
Syscall.SORT_BYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getSB(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setSB(address+index, value)
}
}
Syscall.SORT_UWORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setUW(address+index*2, value)
}
}
Syscall.SORT_WORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getSW(it)
}.sorted()
array.withIndex().forEach { (index, value)->
vm.memory.setSW(address+index*2, value)
}
}
Syscall.REVERSE_BYTES -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it)
}.reversed()
array.withIndex().forEach { (index, value)->
vm.memory.setUB(address+index, value)
}
}
Syscall.REVERSE_WORDS -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it)
}.reversed()
array.withIndex().forEach { (index, value)->
vm.memory.setUW(address+index*2, value)
}
}
Syscall.REVERSE_FLOATS -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map {
vm.memory.getFloat(it)
}.reversed()
array.withIndex().forEach { (index, value)->
vm.memory.setFloat(address+index*4, value)
}
}
Syscall.ANY_BYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256 else length
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-1, 1)
if(addresses.any { vm.memory.getUB(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ANY_WORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256*2 else length*2
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-2, 2)
if(addresses.any { vm.memory.getUW(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ANY_FLOAT -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + (if(length==0) 256*vm.machinedef.FLOAT_MEM_SIZE else length*vm.machinedef.FLOAT_MEM_SIZE)
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-vm.machinedef.FLOAT_MEM_SIZE, 4)
if(addresses.any { vm.memory.getFloat(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ALL_BYTE -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256 else length
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-1, 1)
if(addresses.all { vm.memory.getUB(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ALL_WORD -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + if(length==0) 256*2 else length*2
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-2, 2)
if(addresses.all { vm.memory.getUW(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.ALL_FLOAT -> {
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val endAddressExcl = address + (if(length==0) 256*vm.machinedef.FLOAT_MEM_SIZE else length*vm.machinedef.FLOAT_MEM_SIZE)
val addresses = IntProgression.fromClosedRange(address, endAddressExcl-vm.machinedef.FLOAT_MEM_SIZE, 4)
if(addresses.all { vm.memory.getFloat(it).toInt()!=0 })
returnValue(callspec.returns.single(), 1, vm)
else
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.PRINT_F -> {
val value = getArgValues(callspec.arguments, vm).single() as Double
if(value.toInt().toDouble()==value)

View File

@ -103,19 +103,6 @@ class VmProgramLoader {
if(ins.opcode == Opcode.SYSCALL) {
// convert IR Syscall to VM Syscall
val vmSyscall = when(ins.immediate!!) {
IMSyscall.SORT_UBYTE.number -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.number -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.number -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.number -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.number -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.number -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.number -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.number -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.number -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.number -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.number -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.number -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.number -> Syscall.REVERSE_FLOATS
IMSyscall.COMPARE_STRINGS.number -> Syscall.COMPARE_STRINGS
IMSyscall.STRING_CONTAINS.number -> Syscall.STRING_CONTAINS
IMSyscall.BYTEARRAY_CONTAINS.number -> Syscall.BYTEARRAY_CONTAINS