diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 9bbbcd8df..266e93a15 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -43,7 +43,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, "abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope) "any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope) - "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope) "sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope) "rol" -> funcRol(fcall) "rol2" -> funcRol2(fcall) @@ -687,28 +686,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } - private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { - when(func.name) { - "rnd" -> { - if(resultToStack) - asmgen.out(" jsr prog8_lib.func_rnd_stack") - else { - asmgen.out(" jsr math.randbyte") - assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A) - } - } - "rndw" -> { - if(resultToStack) - asmgen.out(" jsr prog8_lib.func_rndw_stack") - else { - asmgen.out(" jsr math.randword") - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY) - } - } - else -> throw AssemblyError("wrong func") - } - } - private fun funcPokeW(fcall: IFunctionCall) { when(val addrExpr = fcall.args[0]) { is NumericLiteral -> { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 96a8ae60e..45a87c6f5 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -852,7 +852,8 @@ internal class AssignmentAsmGen(private val program: Program, fun assignViaExprEval(addressExpression: Expression) { asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null) asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2") - assignRegisterByte(target, CpuRegister.A) + asmgen.out(" ldy #0") + assignRegisterpairWord(target, RegisterOrPair.AY) } when (value.addressExpression) { @@ -1972,18 +1973,10 @@ internal class AssignmentAsmGen(private val program: Program, } internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) { - // we make an exception in the type check for assigning something to a cx16 virtual register, or a register pair - // these will be correctly typecasted from a byte to a word value - if(target.register !in Cx16VirtualRegisters && - target.register!=RegisterOrPair.AX && target.register!=RegisterOrPair.AY && target.register!=RegisterOrPair.XY) { - if(target.kind== TargetStorageKind.VARIABLE) { - val parts = target.asmVarname.split('.') - if (parts.size != 2 || parts[0] != "cx16") - require(target.datatype in ByteDatatypes) - } else { - require(target.datatype in ByteDatatypes) - } - } + // we make an exception in the type check for assigning something to a register pair AX, AY or XY + // these will be correctly typecasted from a byte to a word value here + if(target.register !in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) + require(target.datatype in ByteDatatypes) when(target.kind) { TargetStorageKind.VARIABLE -> { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 438276e98..8ba69bb1f 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -4,7 +4,6 @@ import prog8.code.StStaticVariable import prog8.code.ast.* import prog8.code.core.AssemblyError import prog8.code.core.DataType -import prog8.code.core.Position import prog8.intermediate.* @@ -26,8 +25,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "rsavex", "rrestore", "rrestorex" -> emptyList() // vm doesn't have registers to save/restore - "rnd" -> funcRnd(resultRegister, call.position) - "rndw" -> funcRndw(resultRegister, call.position) "callfar" -> throw AssemblyError("callfar() is for cx16 target only") "callrom" -> throw AssemblyError("callrom() is for cx16 target only") "msb" -> funcMsb(call, resultRegister) @@ -361,18 +358,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe return result } - private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunks { - val code = IRCodeChunk(null, position, null) - code += IRInstruction(Opcode.RND, IRDataType.BYTE, reg1=resultRegister) - return listOf(code) - } - - private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunks { - val code = IRCodeChunk(null, position, null) - code += IRInstruction(Opcode.RND, IRDataType.WORD, reg1=resultRegister) - return listOf(code) - } - private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks { val name = (call.args[0] as PtString).value val code = IRCodeChunk(null, call.position, null) diff --git a/compiler/res/prog8lib/c128/floats.p8 b/compiler/res/prog8lib/c128/floats.p8 index 6c6da35b0..ed6dd3b22 100644 --- a/compiler/res/prog8lib/c128/floats.p8 +++ b/compiler/res/prog8lib/c128/floats.p8 @@ -56,7 +56,7 @@ romsub $af4b = ROUND() clobbers(A,X,Y) ; round fac1 romsub $af4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1) romsub $af51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive romsub $af54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than -romsub $af57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator NOTE: special cx16 setup required, use RND() stub instead!! +romsub $af57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator romsub $af5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2 romsub $af5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2 romsub $af60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead) @@ -151,6 +151,32 @@ asmsub FREADUY (ubyte value @Y) { &uword AYINT_facmo = $66 ; $66/$67 contain result of AYINT +sub rndf() -> float { + %asm {{ + stx P8ZP_SCRATCH_REG + lda #1 + jsr RND_0 + ldx P8ZP_SCRATCH_REG + rts + }} +} + +asmsub rndseedf(ubyte s1 @A, ubyte s2 @X, ubyte s3 @Y) clobbers(X) { + %asm {{ + sta _tmpseed + stx _tmpseed+1 + sty _tmpseed+2 + stx _tmpseed+3 + sty _tmpseed+4 + lda #<_tmpseed + ldy #>_tmpseed + jsr MOVFM + lda #-1 + jmp RND_0 +_tmpseed .byte 0,0,0,0,0 + }} +} + %asminclude "library:c128/floats.asm" %asminclude "library:c64/floats_funcs.asm" diff --git a/compiler/res/prog8lib/c64/floats.p8 b/compiler/res/prog8lib/c64/floats.p8 index a5f2f12ad..dfb681a5d 100644 --- a/compiler/res/prog8lib/c64/floats.p8 +++ b/compiler/res/prog8lib/c64/floats.p8 @@ -171,6 +171,29 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY { &uword AYINT_facmo = $64 ; $64/$65 contain result of AYINT +sub rndf() -> float { + %asm {{ + stx P8ZP_SCRATCH_REG + lda #1 + jsr FREADSA + jsr RND ; rng into fac1 + ldx P8ZP_SCRATCH_REG + rts + }} +} + +asmsub rndseedf(ubyte s1 @A, ubyte s2 @X, ubyte s3 @Y) clobbers(X) { + %asm {{ + pha + tya + ora #128 ; make sure the seed is negative + tay + pla + jsr FREADS24AXY + jmp RND + }} +} + %asminclude "library:c64/floats.asm" %asminclude "library:c64/floats_funcs.asm" diff --git a/compiler/res/prog8lib/cx16/floats.p8 b/compiler/res/prog8lib/cx16/floats.p8 index 8bbc2c622..4791e5156 100644 --- a/compiler/res/prog8lib/cx16/floats.p8 +++ b/compiler/res/prog8lib/cx16/floats.p8 @@ -57,7 +57,7 @@ romsub $fe4b = ROUND() clobbers(A,X,Y) ; round fac1 romsub $fe4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1) romsub $fe51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive romsub $fe54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than -romsub $fe57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator NOTE: special cx16 setup required, use RND() stub instead!! +romsub $fe57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator NOTE: incompatible with C64's RND routine romsub $fe5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2 romsub $fe5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2 romsub $fe60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead) @@ -147,17 +147,31 @@ asmsub FREADUY (ubyte value @Y) { }} } -asmsub RND() clobbers(A,X,Y) { + +&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT + +sub rndf() -> float { %asm {{ - lda #0 - php - jsr cx16.entropy_get - plp - jmp RND_0 + phx + lda #1 + jsr RND_0 + plx + rts + }} +} + +asmsub rndseedf(ubyte s1 @A, ubyte s2 @X, ubyte s3 @Y) clobbers(X) { + %asm {{ + sta P8ZP_SCRATCH_REG + lda #0 + php + lda P8ZP_SCRATCH_REG + ora #32 ; not sure why this is needed but without it the seed is not consistent + plp ; Z=N=0 + jmp floats.RND_0 }} } -&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT %asminclude "library:c64/floats.asm" %asminclude "library:c64/floats_funcs.asm" diff --git a/compiler/res/prog8lib/floats_functions.p8 b/compiler/res/prog8lib/floats_functions.p8 index a6a7860bc..bc2655e82 100644 --- a/compiler/res/prog8lib/floats_functions.p8 +++ b/compiler/res/prog8lib/floats_functions.p8 @@ -221,15 +221,4 @@ sub ceil(float value) -> float { }} } -sub rndf() -> float { - %asm {{ - stx P8ZP_SCRATCH_REG - lda #1 - jsr FREADSA - jsr RND ; rng into fac1 - ldx P8ZP_SCRATCH_REG - rts - }} -} - } diff --git a/compiler/res/prog8lib/math.asm b/compiler/res/prog8lib/math.asm index 51d4b9963..957c86ae2 100644 --- a/compiler/res/prog8lib/math.asm +++ b/compiler/res/prog8lib/math.asm @@ -229,60 +229,32 @@ _divisor .word 0 .pend -randseed .proc - ; -- reset the random seeds for the byte and word random generators - ; arguments: uword seed in A/Y clobbers A - ; (default starting values are: A=$2c Y=$9e) - sta randword._seed - sty randword._seed+1 - clc - adc #14 - sta randbyte._seed - rts - .pend - - -randbyte .proc - ; -- 8 bit pseudo random number generator into A (by just reusing randword) - jmp randword - .pend - randword .proc ; -- 16 bit pseudo random number generator into AY - - ; rand64k ;Factors of 65535: 3 5 17 257 - lda sr1+1 - asl a - asl a - eor sr1+1 - asl a - eor sr1+1 - asl a - asl a - eor sr1+1 - asl a - rol sr1 ;shift this left, "random" bit comes from low - rol sr1+1 - ; rand32k ;Factors of 32767: 7 31 151 are independent and can be combined - lda sr2+1 - asl a - eor sr2+1 - asl a - asl a - ror sr2 ;shift this right, random bit comes from high - nicer when eor with sr1 - rol sr2+1 - lda sr1+1 ;can be left out - eor sr2+1 ;if you dont use - tay ;y as suggested - lda sr1 ;mix up lowbytes of SR1 - eor sr2 ;and SR2 to combine both + ; default seed = $00c2 $1137 + ; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit + inc x1 + clc +x1=*+1 + lda #$00 ;x1 +c1=*+1 + eor #$c2 ;c1 +a1=*+1 + eor #$11 ;a1 + sta a1 +b1=*+1 + adc #$37 ;b1 + sta b1 + lsr a + eor a1 + adc c1 + sta c1 + ldy b1 rts - -sr1 .word $a55a -sr2 .word $7653 - .pend +randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword) + ; ----------- optimized multiplications (stack) : --------- stack_mul_byte_3 .proc diff --git a/compiler/res/prog8lib/math.p8 b/compiler/res/prog8lib/math.p8 index 7564760d2..259a09dc6 100644 --- a/compiler/res/prog8lib/math.p8 +++ b/compiler/res/prog8lib/math.p8 @@ -71,4 +71,28 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0))) }} } + asmsub rnd() -> ubyte @A { + %asm {{ + jmp math.randbyte + }} + } + + asmsub rndw() -> uword @AY { + %asm {{ + jmp math.randword + }} + } + + asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) { + ; -- set new pseudo RNG's seed values. Defaults are: $00c2, $1137 + %asm {{ + sta math.randword.x1 + sty math.randword.c1 + lda cx16.r0L + sta math.randword.a1 + lda cx16.r0H + sta math.randword.b1 + rts + }} + } } diff --git a/compiler/res/prog8lib/prog8_funcs.asm b/compiler/res/prog8lib/prog8_funcs.asm index 1d7c2f26f..5728d0bc1 100644 --- a/compiler/res/prog8lib/prog8_funcs.asm +++ b/compiler/res/prog8lib/prog8_funcs.asm @@ -244,24 +244,6 @@ func_sqrt16_into_A .proc rts .pend -func_rnd_stack .proc - ; -- put a random ubyte on the estack - jsr math.randbyte - sta P8ESTACK_LO,x - dex - rts - .pend - -func_rndw_stack .proc - ; -- put a random uword on the estack - jsr math.randword - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - func_sort_ub .proc ; 8bit unsigned sort diff --git a/compiler/res/prog8lib/virtual/floats.p8 b/compiler/res/prog8lib/virtual/floats.p8 index 1dcd61d63..ce9edf992 100644 --- a/compiler/res/prog8lib/virtual/floats.p8 +++ b/compiler/res/prog8lib/virtual/floats.p8 @@ -126,8 +126,18 @@ sub ceil(float value) -> float { sub rndf() -> float { %ir {{ - rnd.f fr0 + syscall 35 return }} } + +sub rndseedf(ubyte s1, ubyte s2, ubyte s3) { + %ir {{ + loadm.b r0,floats.rndseedf.s1 + loadm.b r1,floats.rndseedf.s2 + loadm.b r2,floats.rndseedf.s3 + syscall 32 + }} +} + } diff --git a/compiler/res/prog8lib/virtual/math.p8 b/compiler/res/prog8lib/virtual/math.p8 index 317402ea5..9a752c3ff 100644 --- a/compiler/res/prog8lib/virtual/math.p8 +++ b/compiler/res/prog8lib/virtual/math.p8 @@ -159,4 +159,27 @@ math { return costab[radians] as byte } + sub rnd() -> ubyte { + %ir {{ + syscall 33 + return + }} + } + + sub rndw() -> uword { + %ir {{ + syscall 34 + return + }} + } + + sub rndseed(uword seed1, uword seed2) { + ; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653. + %ir {{ + loadm.w r0,math.rndseed.seed1 + loadm.w r1,math.rndseed.seed2 + syscall 31 + return + }} + } } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 7518bd6d2..0aeb80c5d 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1005,7 +1005,7 @@ internal class AstChecker(private val program: Program, // It's not (yet) possible to handle these multiple return values because assignments // are only to a single unique target at the same time. // EXCEPTION: - // if the asmsub returns multiple values and one of them is via a status register bit, + // if the asmsub returns multiple values and one of them is via a status register bit (such as carry), // it *is* possible to handle them by just actually assigning the register value and // dealing with the status bit as just being that, the status bit after the call. val (returnRegisters, _) = stmt.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null } diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index 523cc40f7..92c6af7cc 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -150,6 +150,13 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter, override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement) private fun visitFunctionCall(call: IFunctionCall) { + if(call.target.nameInSource==listOf("rnd") || call.target.nameInSource==listOf("rndw")) { + val target = call.target.targetStatement(program) + if(target==null) { + errors.err("rnd() and rndw() builtin functions have been moved into the math module", call.position) + return + } + } when (val target = call.target.targetStatement(program)) { is Subroutine -> { val expectedNumberOfArgs: Int = target.parameters.size diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index f72627916..011306334 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -91,6 +91,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe if(target.asmReturnvaluesRegisters.size>1) { // multiple return values will NOT work inside an expression. // they MIGHT work in a regular assignment or just a function call statement. + // EXCEPTION: + // if the asmsub returns multiple values and one of them is via a status register bit (such as carry), + // it *is* possible to handle them by just actually assigning the register value and + // dealing with the status bit as just being that, the status bit after the call. val parent = if(call is Statement) call.parent else if(call is Expression) call.parent else null if (call !is FunctionCallStatement) { val checkParent = @@ -99,7 +103,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe else parent if (checkParent !is Assignment && checkParent !is VarDecl) { - return Pair("can't use subroutine call that returns multiple return values here", call.position) + val (returnRegisters, _) = target.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null } + if (returnRegisters.size>1) { + return Pair("can't use subroutine call that returns multiple return values here", call.position) + } } } } diff --git a/compiler/test/TestBuiltinFunctions.kt b/compiler/test/TestBuiltinFunctions.kt index f59699599..f7fc4fa36 100644 --- a/compiler/test/TestBuiltinFunctions.kt +++ b/compiler/test/TestBuiltinFunctions.kt @@ -44,18 +44,18 @@ class TestBuiltinFunctions: FunSpec({ conv.returns.reg shouldBe RegisterOrPair.A } - test("not-pure func with fixed type") { - val func = BuiltinFunctions.getValue("rnd") - func.name shouldBe "rnd" - func.parameters.size shouldBe 0 + test("not-pure func with varying result value type") { + val func = BuiltinFunctions.getValue("cmp") + func.name shouldBe "cmp" + func.parameters.size shouldBe 2 func.pure shouldBe false - func.returnType shouldBe DataType.UBYTE + func.returnType shouldBe null - val conv = func.callConvention(emptyList()) - conv.params.size shouldBe 0 - conv.returns.dt shouldBe DataType.UBYTE + val conv = func.callConvention(listOf(DataType.UWORD, DataType.UWORD)) + conv.params.size shouldBe 2 + conv.returns.dt shouldBe null conv.returns.floatFac1 shouldBe false - conv.returns.reg shouldBe RegisterOrPair.A + conv.returns.reg shouldBe null } test("func without return type") { diff --git a/compiler/test/TestSubroutines.kt b/compiler/test/TestSubroutines.kt index 07d24ac81..cf4cc3c30 100644 --- a/compiler/test/TestSubroutines.kt +++ b/compiler/test/TestSubroutines.kt @@ -258,8 +258,8 @@ class TestSubroutines: FunSpec({ sub start() { label() label(1) - void rnd() - void rnd(1) + void cmp(22,44) + void cmp(11) } } """ diff --git a/compiler/test/TestTypecasts.kt b/compiler/test/TestTypecasts.kt index 939a55e6b..32d8ccc42 100644 --- a/compiler/test/TestTypecasts.kt +++ b/compiler/test/TestTypecasts.kt @@ -919,4 +919,19 @@ main { }""" compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null } + + test("memory reads byte into word variable") { + val text = """ + main { + sub start() { + uword @shared ww + uword address = $1000 + ww = @(address+100) + ww = @(address+1000) + cx16.r0 = @(address+100) + cx16.r0 = @(address+1000) + } + }""" + compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null + } }) diff --git a/compiler/test/arithmetic/builtins.p8 b/compiler/test/arithmetic/builtins.p8 index dc44f6870..f8b6737c6 100644 --- a/compiler/test/arithmetic/builtins.p8 +++ b/compiler/test/arithmetic/builtins.p8 @@ -2,6 +2,7 @@ %import floats %import string %import syslib +%import math %import test_stack %zeropage basicsafe @@ -281,17 +282,17 @@ main { txt.nl() - ub = rnd() + ub = math.rnd() txt.print_ub(ub) txt.nl() - ub = zero+rnd()*1+zero + ub = zero+math.rnd()*1+zero txt.print_ub(ub) txt.nl() - uw = rndw() + uw = math.rndw() txt.print_uw(uw) txt.nl() - uw = zero+rndw()*1+zero + uw = zero+math.rndw()*1+zero txt.print_uw(uw) txt.nl() diff --git a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt b/compilerAst/src/prog8/compiler/BuiltinFunctions.kt index d8139549e..353f2fc9f 100644 --- a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt +++ b/compilerAst/src/prog8/compiler/BuiltinFunctions.kt @@ -119,8 +119,6 @@ private val functionSignatures: List = listOf( FSignature("rsavex" , false, emptyList(), null), FSignature("rrestore" , false, emptyList(), null), FSignature("rrestorex" , false, emptyList(), null), - FSignature("rnd" , false, emptyList(), DataType.UBYTE), - FSignature("rndw" , false, emptyList(), DataType.UWORD), FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), diff --git a/docs/source/index.rst b/docs/source/index.rst index c1bc165be..84e1331bf 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -71,7 +71,7 @@ Language features - Floating point math also supported if the target system provides floating point library routines (C64 and Cx16 both do). - 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. - High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting. -- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``sort`` and ``reverse`` +- Many built-in functions, such as ``sin``, ``cos``, ``abs``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``sort`` and ``reverse`` - Programs can be run multiple times without reloading because of automatic variable (re)initializations. - Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the other machines. - If you only use standard kernal and core prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compilation target flag)! diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 858416ea5..66b9d9853 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -257,7 +257,14 @@ tan(x) Tangent. rndf() - returns a pseudo-random float between 0.0 and 1.0 + returns the next random float between 0.0 and 1.0 from the Pseudo RNG sequence. + +rndseedf(ubyte s1, ubyte s2, ubyte s3) + Sets a new seed for the float pseudo-RNG sequence. The seed consists of a three byte value. + Do not use zeros for the seed! + + .. attention:: + The rndseedf and maybe the rndf routines may change a bit in the future. graphics @@ -276,12 +283,22 @@ Use the ``gfx2`` library if you want full-screen graphics or non-monochrome draw math ---- -Low level math routines. You should not normally have to bother with this directly. -The compiler needs it to implement most of the math operations in your programs. +Low level integer math routines (which you usually don't have to bother with directly, but they are used by the compiler internally). +Pseudo-Random number generators (byte and word). +Various 8-bit integer trig functions that use lookup tables to quickly calculate sine and cosines. +Usually a custom lookup table is the way to go if your application needs these, +but perhaps the provided ones can be of service too. -However there's a bunch of 8-bit integer trig functions in here too that use lookup tables -to quickly calculate sine and cosines. Usually a custom lookup table is the way to go if your -application needs this, but perhaps the provided ones can be of service too: + +rnd() + Returns next random byte 0-255 from the pseudo-RNG sequence. + +rndw() + Returns next random word 0-65535 from the pseudo-RNG sequence. + +rndseed(uword seed1, uword seed2) + Sets a new seed for the pseudo-RNG sequence (both rnd and rndw). The seed consists of two words. + Do not use zeros for the seed! sin8u(x) Fast 8-bit ubyte sine of angle 0..255, result is in range 0..255 diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 8b4de1021..c7525302e 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -514,16 +514,26 @@ if statements Conditional execution means that the flow of execution changes based on certiain conditions, rather than having fixed gotos or subroutine calls:: - if aa>4 goto overflow + if xx==5 { + yy = 99 + zz = 42 + } else { + aa = 3 + bb = 9 + } - if xx==3 yy = 4 - if xx==3 yy = 4 else aa = 2 + if xx==5 + yy = 42 + else if xx==6 + yy = 43 + else + yy = 44 - if xx==5 { - yy = 99 - } else { - aa = 3 - } + if aa>4 goto some_label + + if xx==3 yy = 4 + + if xx==3 yy = 4 else aa = 2 Conditional jumps (``if condition goto label``) are compiled using 6502's branching instructions (such as ``bne`` and ``bcc``) so @@ -848,12 +858,6 @@ popw(value) pops a 16-bit word value off the CPU hardware stack into the given variable. Only variables can be used. Lowlevel function that should normally not be used. -rnd() - returns a pseudo-random byte from 0..255 - -rndw() - returns a pseudo-random word from 0..65535 - rol(x) Rotate the bits in x (byte or word) one position to the left. This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag, diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 4f708359a..cb185e5f6 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -783,19 +783,23 @@ Note: to do an indirect *JSR* to a routine with a varying address, you can use t (which is not very efficient) or you have to write a small piece of inline assembly. -Conditional execution -^^^^^^^^^^^^^^^^^^^^^ +if statements +^^^^^^^^^^^^^ With the 'if' / 'else' statement you can execute code depending on the value of a condition:: if [else ] -where can be just a single statement for instance just a ``goto``, or it can be a block such as this:: +If is just a single statement, for instance just a ``goto`` or a single assignment, +it's possible to just write the statement without any curly braces. +However if is a block of multiple statements, you'll have to enclose it in curly braces:: if { + } else if { + } else { - + } @@ -807,7 +811,7 @@ itself defines on what status register bit it should branch on:: if_XX [else ] -where can be just a single statement for instance just a ``goto``, or it can be a block such as this:: +where can be just a single statement or a block again:: if_XX { diff --git a/examples/balloonflight.p8 b/examples/balloonflight.p8 index a2a8edf29..2ed090114 100644 --- a/examples/balloonflight.p8 +++ b/examples/balloonflight.p8 @@ -1,5 +1,6 @@ %import syslib %import textio +%import math %import test_stack %zeropage basicsafe @@ -41,7 +42,7 @@ main { active_height-- upwards = false } else { - target_height = 8 + rnd() % 16 + target_height = 8 + math.rnd() % 16 if upwards mountain = 233 else @@ -56,7 +57,7 @@ main { txt.scroll_left(true) ; float the balloon - if rnd() & %10000 + if math.rnd() & %10000 c64.SPXY[1] ++ else c64.SPXY[1] -- @@ -70,10 +71,10 @@ main { txt.setcc(39, yy, 160, 8) ; draw mountain } - yy = rnd() + yy = math.rnd() if yy > 100 { ; draw a star - txt.setcc(39, yy % (active_height-1), '.', rnd()) + txt.setcc(39, yy % (active_height-1), '.', math.rnd()) } if yy > 200 { @@ -84,7 +85,7 @@ main { tree = 88 else if yy & %00100000 != 0 tree = 65 - if rnd() > 130 + if math.rnd() > 130 treecolor = 13 txt.setcc(39, active_height, tree, treecolor) } diff --git a/examples/balls.p8 b/examples/balls.p8 index fb5f51a40..14081c0af 100644 --- a/examples/balls.p8 +++ b/examples/balls.p8 @@ -1,4 +1,5 @@ %import textio +%import math %zeropage basicsafe ; Note: this program is compatible with C64 and CX16. @@ -22,11 +23,11 @@ main { ; Setup Starting Ball Positions ubyte lp for lp in 0 to ballCount-1 { - BX[lp] = rnd() % txt.DEFAULT_WIDTH - BY[lp] = rnd() % txt.DEFAULT_HEIGHT - BC[lp] = rnd() & 15 - DX[lp] = rnd() & 1 - DY[lp] = rnd() & 1 + BX[lp] = math.rnd() % txt.DEFAULT_WIDTH + BY[lp] = math.rnd() % txt.DEFAULT_HEIGHT + BC[lp] = math.rnd() & 15 + DX[lp] = math.rnd() & 1 + DY[lp] = math.rnd() & 1 } ; display balls diff --git a/examples/cx16/circles.p8 b/examples/cx16/circles.p8 index eb3c49adb..3a69424c2 100644 --- a/examples/cx16/circles.p8 +++ b/examples/cx16/circles.p8 @@ -1,4 +1,5 @@ %import graphics +%import math ; note: this program is tuned for the CX16, but with some minor modifications can run on other systems too. @@ -15,7 +16,7 @@ main { graphics.enable_bitmap_mode() repeat { - background_color = rnd() + background_color = math.rnd() graphics.clear_screen(0, background_color) num_circles = 0 draw_circles() @@ -28,14 +29,14 @@ main { ubyte @zp radius while num_circles s { + if math.rnd() > s { b |= bittab[ii] } } diff --git a/examples/sprites.p8 b/examples/sprites.p8 index b15f92efd..f5e6e55c2 100644 --- a/examples/sprites.p8 +++ b/examples/sprites.p8 @@ -1,5 +1,6 @@ %import textio %import syslib +%import math %zeropage basicsafe @@ -41,7 +42,7 @@ main { for i in 0 to 7 { c64.SPRPTR[i] = $0a00 / 64 c64.SPXY[i*2] = 50+25*i - c64.SPXY[i*2+1] = rnd() + c64.SPXY[i*2+1] = math.rnd() } c64.SPENA = 255 ; enable all sprites @@ -59,7 +60,7 @@ irq { ubyte @zp i for i in 0 to 14 step 2 { c64.SPXY[i+1]-- - ubyte @zp r = rnd() + ubyte @zp r = math.rnd() if r>200 c64.SPXY[i]++ else if r<40 diff --git a/examples/tehtriz.p8 b/examples/tehtriz.p8 index 65c0b2b3a..fc9067e4c 100644 --- a/examples/tehtriz.p8 +++ b/examples/tehtriz.p8 @@ -10,6 +10,7 @@ %import syslib %import textio +%import math %import test_stack @@ -261,7 +262,7 @@ waitkey: xpos = startXpos ypos = startYpos speedlevel = 1 - nextBlock = rnd() % 7 + nextBlock = math.rnd() % 7 holding = 255 holdingAllowed = true } @@ -276,7 +277,7 @@ waitkey: sub spawnNextBlock() { swapBlock(nextBlock) - nextBlock = (rnd() + c64.RASTER) % 7 + nextBlock = (math.rnd() + c64.RASTER) % 7 drawNextBlock() holdingAllowed = true } diff --git a/examples/vm/bouncegfx.p8 b/examples/vm/bouncegfx.p8 index 38c3865f5..ecf946eda 100644 --- a/examples/vm/bouncegfx.p8 +++ b/examples/vm/bouncegfx.p8 @@ -1,5 +1,7 @@ ; NOTE: meant to test to virtual machine output target (use -target virtual) +%import math + main { sub start() { @@ -10,10 +12,10 @@ main { ubyte pi for pi in 0 to 127 { - particleX[pi] = rndw() % 319 as word - particleY[pi] = rndw() % 240 as word - particleDX[pi] = (rnd() & 1)*2 as byte - 1 - particleDY[pi] = (rnd() & 1)*2 as byte - 1 + particleX[pi] = math.rndw() % 319 as word + particleY[pi] = math.rndw() % 240 as word + particleDX[pi] = (math.rnd() & 1)*2 as byte - 1 + particleDY[pi] = (math.rnd() & 1)*2 as byte - 1 } sys.gfx_enable(0) ; enable lo res screen diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index f0a223cb2..1fc704100 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -126,7 +126,6 @@ mod reg1, value - remainder (modulo) of unsigned div sqrt reg1, reg2 - reg1 is the square root of reg2 sgn reg1, reg2 - reg1 is the sign of reg2 (0, 1 or -1) cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction) -rnd reg1 - get a random number (byte, word or float) NOTE: because mul/div are constrained (truncated) to remain in 8 or 16 bits, there is NO NEED for separate signed/unsigned mul and div instructions. The result is identical. @@ -288,7 +287,6 @@ enum class Opcode { SQRT, SGN, CMP, - RND, EXT, EXTS, @@ -588,7 +586,6 @@ val instructionFormats = mutableMapOf( Opcode.DIVSM to InstructionFormat.from("BW,r1,fr1,r1,fr1,r1 | F,>fr1"), Opcode.MODR to InstructionFormat.from("BW,<>r1,r1, - - - + + + - + \ No newline at end of file diff --git a/syntax-files/Vim/prog8_builtins.vim b/syntax-files/Vim/prog8_builtins.vim index 9805743f1..c9b11805e 100644 --- a/syntax-files/Vim/prog8_builtins.vim +++ b/syntax-files/Vim/prog8_builtins.vim @@ -13,8 +13,8 @@ syn keyword prog8BuiltInFunc sgn sqrt16 syn keyword prog8BuiltInFunc any all len reverse sort " Miscellaneous functions -syn keyword prog8BuiltInFunc cmp lsb msb mkword peek peekw poke pokew rnd rndw push pushw pop popw rsave rsavex rrestore rrestorex -syn keyword prog8BuiltInFunc rndf rol rol2 ror ror2 sizeof +syn keyword prog8BuiltInFunc cmp lsb msb mkword peek peekw poke pokew push pushw pop popw rsave rsavex rrestore rrestorex +syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof syn keyword prog8BuiltInFunc swap memory callfar callrom diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 325470ef0..34c5a88ec 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -37,6 +37,8 @@ SYSCALLS: 28 = reverse_floats array 29 = compare strings 30 = gfx_getpixel ; get byte pixel value at coordinates r0.w/r1.w +31 = rndseed +32 = rndfseed */ enum class Syscall { @@ -70,7 +72,12 @@ enum class Syscall { REVERSE_WORDS, REVERSE_FLOATS, COMPARE_STRINGS, - GFX_GETPIXEL + GFX_GETPIXEL, + RNDSEED, + RNDFSEED, + RND, + RNDW, + RNDF } object SysCalls { @@ -279,6 +286,26 @@ object SysCalls { val comparison = first.compareTo(second) vm.registers.setSB(0, comparison.toByte()) } + Syscall.RNDFSEED -> { + val seed1 = vm.registers.getUB(0) + val seed2 = vm.registers.getUB(1) + val seed3 = vm.registers.getUB(2) + vm.randomSeedFloat(seed1, seed2, seed3) + } + Syscall.RNDSEED -> { + val seed1 = vm.registers.getUW(0) + val seed2 = vm.registers.getUW(1) + vm.randomSeed(seed1, seed2) + } + Syscall.RND -> { + vm.registers.setUB(0, vm.randomGenerator.nextInt().toUByte()) + } + Syscall.RNDW -> { + vm.registers.setUW(0, vm.randomGenerator.nextInt().toUShort()) + } + Syscall.RNDF -> { + vm.registers.setFloat(0, vm.randomGeneratorFloats.nextFloat()) + } else -> throw AssemblyError("missing syscall ${call.name}") } } diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 3ebaf3acc..14f92d870 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -43,6 +43,8 @@ class VirtualMachine(irProgram: IRProgram) { var statusCarry = false var statusZero = false var statusNegative = false + internal var randomGenerator = Random(0xa55a7653) + internal var randomGeneratorFloats = Random(0xc0d3dbad) private val cx16virtualregsBaseAddress: Int init { @@ -203,7 +205,6 @@ class VirtualMachine(irProgram: IRProgram) { Opcode.MOD -> InsMOD(ins) Opcode.SGN -> InsSGN(ins) Opcode.CMP -> InsCMP(ins) - Opcode.RND -> InsRND(ins) Opcode.SQRT -> InsSQRT(ins) Opcode.EXT -> InsEXT(ins) Opcode.EXTS -> InsEXTS(ins) @@ -1086,15 +1087,6 @@ class VirtualMachine(irProgram: IRProgram) { nextPc() } - private fun InsRND(i: IRInstruction) { - when(i.type!!) { - IRDataType.BYTE -> registers.setUB(i.reg1!!, Random.nextInt().toUByte()) - IRDataType.WORD -> registers.setUW(i.reg1!!, Random.nextInt().toUShort()) - IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, Random.nextFloat()) - } - nextPc() - } - private fun InsCMP(i: IRInstruction) { val comparison: Int when(i.type!!) { @@ -2132,6 +2124,15 @@ class VirtualMachine(irProgram: IRProgram) { fun waitvsync() { Toolkit.getDefaultToolkit().sync() // not really the same as wait on vsync, but there's noting else } + + fun randomSeed(seed1: UShort, seed2: UShort) { + randomGenerator = Random(((seed1.toUInt() shl 16) or seed2.toUInt()).toInt()) + } + + fun randomSeedFloat(seed1: UByte, seed2: UByte, seed3: UByte) { + val seed = (seed1.toUInt() shl 24) or (seed2.toUInt() shl 16) or (seed3.toUInt()) + randomGeneratorFloats = Random(seed.toInt()) + } } // probably called via reflection