From 53b0b562e619498f5db129c3bb54b5ed8428dd2f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 19 Oct 2022 23:23:49 +0200 Subject: [PATCH 1/9] fix check for routine that returns multiple values but in status bit. Fixes #79 --- .../compiler/astprocessing/AstChecker.kt | 2 +- .../astprocessing/VerifyFunctionArgTypes.kt | 9 ++++++- examples/test.p8 | 24 +++++++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) 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/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/examples/test.p8 b/examples/test.p8 index b14e51429..fb3ad4aba 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,9 +1,23 @@ main { - sub start() { - ubyte aa = 42 - ubyte bb = 99 - aa += bb + asmsub multi() -> ubyte @A, ubyte @Pc { + %asm {{ + lda #42 + sec + rts + }} + } - %asmbinary "build.gradle" + sub start() { + ubyte value + + value = multi() + + while 0==multi() { + value++ + } + + if multi() { + value++ + } } } From 733c17ad3aeed08e37c10b35bf56812c893904ac Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 19 Oct 2022 23:53:15 +0200 Subject: [PATCH 2/9] improve docs on if syntax. fixes #81 --- docs/source/programming.rst | 26 ++++++++++++++++++-------- docs/source/syntaxreference.rst | 14 +++++++++----- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 8b4de1021..e3770b8dd 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 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 { From ec5adffdc24f4adbd2c936a2e641944740260e7f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 22 Oct 2022 13:33:35 +0200 Subject: [PATCH 3/9] rnd()/rndf() routines can now be seeded with new rndseed()/rndseedf() routines. fixes #80 --- compiler/res/prog8lib/c128/floats.p8 | 28 +++++++++- compiler/res/prog8lib/c64/floats.p8 | 23 ++++++++ compiler/res/prog8lib/cx16/floats.p8 | 30 +++++++--- compiler/res/prog8lib/floats_functions.p8 | 11 ---- compiler/res/prog8lib/math.asm | 18 +----- compiler/res/prog8lib/math.p8 | 12 ++++ compiler/res/prog8lib/virtual/floats.p8 | 10 ++++ compiler/res/prog8lib/virtual/math.p8 | 9 +++ docs/source/todo.rst | 6 +- examples/test.p8 | 56 +++++++++++++------ virtualmachine/src/prog8/vm/SysCalls.kt | 17 +++++- virtualmachine/src/prog8/vm/VirtualMachine.kt | 17 +++++- 12 files changed, 177 insertions(+), 60 deletions(-) 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 From e94bf4c63c42c7e669dcbdfadeede7f06d30e703 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 22 Oct 2022 16:35:57 +0200 Subject: [PATCH 4/9] replace rnd()/rndw() builtin functions by regular routines in math module --- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 23 ---------------- .../codegen/intermediate/BuiltinFuncGen.kt | 14 ---------- compiler/res/prog8lib/math.p8 | 12 +++++++++ compiler/res/prog8lib/prog8_funcs.asm | 18 ------------- compiler/res/prog8lib/virtual/math.p8 | 14 ++++++++++ compiler/test/TestBuiltinFunctions.kt | 18 ++++++------- compiler/test/TestSubroutines.kt | 4 +-- compiler/test/arithmetic/builtins.p8 | 9 ++++--- .../src/prog8/compiler/BuiltinFunctions.kt | 2 -- docs/source/index.rst | 2 +- docs/source/libraries.rst | 27 ++++++++++++++----- docs/source/programming.rst | 6 ----- docs/source/todo.rst | 3 +-- examples/balloonflight.p8 | 11 ++++---- examples/balls.p8 | 11 ++++---- examples/cx16/circles.p8 | 11 ++++---- examples/cx16/colorbars.p8 | 3 ++- examples/cx16/highresbitmap.p8 | 3 ++- examples/cx16/tehtriz.p8 | 5 ++-- examples/cxlogo.p8 | 5 ++-- examples/maze.p8 | 7 ++--- examples/numbergame.p8 | 3 ++- examples/plasma.p8 | 2 +- examples/sprites.p8 | 5 ++-- examples/tehtriz.p8 | 5 ++-- examples/test.p8 | 12 ++++----- examples/vm/bouncegfx.p8 | 10 ++++--- syntax-files/IDEA/Prog8.xml | 8 +++--- syntax-files/Vim/prog8_builtins.vim | 4 +-- 29 files changed, 124 insertions(+), 133 deletions(-) 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/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 903feb67b..a420ccf5b 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -26,8 +26,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "rsavex", "rrestore", "rrestorex" -> IRCodeChunk(call.position) // 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) @@ -309,18 +307,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe return code } - private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunk { - val code = IRCodeChunk(position) - code += IRInstruction(Opcode.RND, IRDataType.BYTE, reg1=resultRegister) - return code - } - - private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunk { - val code = IRCodeChunk(position) - code += IRInstruction(Opcode.RND, IRDataType.WORD, reg1=resultRegister) - return code - } - private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { val name = (call.args[0] as PtString).value val code = IRCodeChunk(call.position) diff --git a/compiler/res/prog8lib/math.p8 b/compiler/res/prog8lib/math.p8 index d30469d66..028c3a786 100644 --- a/compiler/res/prog8lib/math.p8 +++ b/compiler/res/prog8lib/math.p8 @@ -71,6 +71,18 @@ _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) { ; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653. %asm {{ 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/math.p8 b/compiler/res/prog8lib/virtual/math.p8 index ce51e0f56..76782ccde 100644 --- a/compiler/res/prog8lib/virtual/math.p8 +++ b/compiler/res/prog8lib/virtual/math.p8 @@ -159,6 +159,19 @@ math { return costab[radians] as byte } + sub rnd() -> ubyte { + %ir {{ + rnd.b r0 + return + }} + } + + sub rndw() -> uword { + %ir {{ + rnd.w r0 + return + }} + } sub rndseed(uword seed1, uword seed2) { ; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653. @@ -166,6 +179,7 @@ math { loadm.w r0,math.rndseed.seed1 loadm.w r1,math.rndseed.seed2 syscall 31 + return }} } } 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/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..fc9f2beb0 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -257,7 +257,13 @@ 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. + + .. attention:: + The rndseedf and maybe the rndf routines may change a bit in the future. graphics @@ -276,12 +282,21 @@ 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. 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 e3770b8dd..c7525302e 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -858,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/todo.rst b/docs/source/todo.rst index f7d0792f2..45653dffd 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,9 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- 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 +- optimizer: check that simple trampoline asmsub gets optimized away (such as math.rnd) - ir: asmsub contents remains blank in IR file ... 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/test.p8 b/examples/test.p8 index 41d0dbafb..cef336627 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,17 +6,17 @@ main { sub printnumbers() { - txt.print_ub(rnd()) + txt.print_ub(math.rnd()) txt.spc() - txt.print_ub(rnd()) + txt.print_ub(math.rnd()) txt.spc() - txt.print_ub(rnd()) + txt.print_ub(math.rnd()) txt.nl() - txt.print_uw(rndw()) + txt.print_uw(math.rndw()) txt.spc() - txt.print_uw(rndw()) + txt.print_uw(math.rndw()) txt.spc() - txt.print_uw(rndw()) + txt.print_uw(math.rndw()) txt.nl() floats.print_f(floats.rndf()) txt.spc() 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/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index ecb77cdc9..ad2c0ed76 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -12,12 +12,12 @@