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..47fe1b150 100644 --- a/compiler/res/prog8lib/math.asm +++ b/compiler/res/prog8lib/math.asm @@ -229,23 +229,7 @@ _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 +randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword) randword .proc ; -- 16 bit pseudo random number generator into AY diff --git a/compiler/res/prog8lib/math.p8 b/compiler/res/prog8lib/math.p8 index 7564760d2..d30469d66 100644 --- a/compiler/res/prog8lib/math.p8 +++ b/compiler/res/prog8lib/math.p8 @@ -71,4 +71,16 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0))) }} } + asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) { + ; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653. + %asm {{ + sta math.randword.sr1 + sty math.randword.sr1+1 + lda cx16.r0L + ldy cx16.r0H + sta math.randword.sr2 + sty math.randword.sr2+1 + rts + }} + } } diff --git a/compiler/res/prog8lib/virtual/floats.p8 b/compiler/res/prog8lib/virtual/floats.p8 index 1dcd61d63..4fd876328 100644 --- a/compiler/res/prog8lib/virtual/floats.p8 +++ b/compiler/res/prog8lib/virtual/floats.p8 @@ -130,4 +130,14 @@ sub rndf() -> float { 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..ce51e0f56 100644 --- a/compiler/res/prog8lib/virtual/math.p8 +++ b/compiler/res/prog8lib/virtual/math.p8 @@ -159,4 +159,13 @@ math { return costab[radians] as byte } + + 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 + }} + } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 3e20573fb..f7d0792f2 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,10 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- ir: get rid of IRCodeLabel, make every label start a new code chunk, give those a 'label' property. -- ir: fix joinChunks() in the IR optimizer ? +- replace rnd() builtin functions by regular functions in math and in floats +- update docs for rnd() and rndseed() functions +- ir: replace RND opcodes by syscalls +- ir: asmsub contents remains blank in IR file ... diff --git a/examples/test.p8 b/examples/test.p8 index fb3ad4aba..41d0dbafb 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,23 +1,45 @@ +%import textio +%import math +%import floats +%zeropage basicsafe + main { - asmsub multi() -> ubyte @A, ubyte @Pc { - %asm {{ - lda #42 - sec - rts - }} + + sub printnumbers() { + txt.print_ub(rnd()) + txt.spc() + txt.print_ub(rnd()) + txt.spc() + txt.print_ub(rnd()) + txt.nl() + txt.print_uw(rndw()) + txt.spc() + txt.print_uw(rndw()) + txt.spc() + txt.print_uw(rndw()) + txt.nl() + floats.print_f(floats.rndf()) + txt.spc() + floats.print_f(floats.rndf()) + txt.spc() + floats.print_f(floats.rndf()) + txt.nl() } + sub start() { - ubyte value - - value = multi() - - while 0==multi() { - value++ - } - - if multi() { - value++ - } + txt.print("initial:\n") + math.rndseed($a55a, $7653) + floats.rndseedf(11,22,33) + printnumbers() + txt.print("\nsame seeds:\n") + math.rndseed($a55a, $7653) + floats.rndseedf(11,22,33) + printnumbers() + txt.print("\ndifferent seeds:\n") + math.rndseed($1234, $5678) + floats.rndseedf(44,55,66) + printnumbers() + txt.nl() } } diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 325470ef0..27af750b7 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,9 @@ enum class Syscall { REVERSE_WORDS, REVERSE_FLOATS, COMPARE_STRINGS, - GFX_GETPIXEL + GFX_GETPIXEL, + RNDSEED, + RNDFSEED } object SysCalls { @@ -279,6 +283,17 @@ 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) + } else -> throw AssemblyError("missing syscall ${call.name}") } } diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 1ca62bc12..f9cc7afd8 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -41,6 +41,8 @@ class VirtualMachine(irProgram: IRProgram) { var statusCarry = false var statusZero = false var statusNegative = false + private var randomGenerator = Random(0xa55a7653) + private var randomGeneratorFloats = Random(0xc0d3dbad) private val cx16virtualregsBaseAddress: Int init { @@ -1061,9 +1063,9 @@ class VirtualMachine(irProgram: IRProgram) { 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()) + IRDataType.BYTE -> registers.setUB(i.reg1!!, randomGenerator.nextInt().toUByte()) + IRDataType.WORD -> registers.setUW(i.reg1!!, randomGenerator.nextInt().toUShort()) + IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, randomGeneratorFloats.nextFloat()) } pc++ } @@ -2105,6 +2107,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