Merge branch 'master' into labeledchunks

# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt
#	docs/source/todo.rst
#	examples/test.p8
#	virtualmachine/src/prog8/vm/VirtualMachine.kt
This commit is contained in:
Irmen de Jong 2022-10-23 12:16:09 +02:00
commit 76428b16f0
42 changed files with 347 additions and 238 deletions

View File

@ -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 -> {

View File

@ -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 -> {

View File

@ -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)

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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
}}
}
}

View File

@ -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

View File

@ -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
}}
}
}

View File

@ -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

View File

@ -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
}}
}
}

View File

@ -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
}}
}
}

View File

@ -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 }

View File

@ -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

View File

@ -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)
}
}
}
}

View File

@ -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") {

View File

@ -258,8 +258,8 @@ class TestSubroutines: FunSpec({
sub start() {
label()
label(1)
void rnd()
void rnd(1)
void cmp(22,44)
void cmp(11)
}
}
"""

View File

@ -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
}
})

View File

@ -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()

View File

@ -119,8 +119,6 @@ private val functionSignatures: List<FSignature> = 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),

View File

@ -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)!

View File

@ -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

View File

@ -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,

View File

@ -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 <expression> <statements> [else <statements> ]
where <statements> can be just a single statement for instance just a ``goto``, or it can be a block such as this::
If <statements> 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 <statements> is a block of multiple statements, you'll have to enclose it in curly braces::
if <expression> {
<statements>
} else if <expression> {
<statements>
} else {
<alternative statements>
<statements>
}
@ -807,7 +811,7 @@ itself defines on what status register bit it should branch on::
if_XX <statements> [else <statements> ]
where <statements> can be just a single statement for instance just a ``goto``, or it can be a block such as this::
where <statements> can be just a single statement or a block again::
if_XX {
<statements>

View File

@ -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)
}

View File

@ -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

View File

@ -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<MAX_NUM_CIRCLES {
x = rndw() % graphics.WIDTH
y = rndw() % graphics.HEIGHT
x = math.rndw() % graphics.WIDTH
y = math.rndw() % graphics.HEIGHT
radius = GROWTH_RATE * 2 ; use a bit of a buffer between circles.
if not_colliding() {
radius -= GROWTH_RATE
ubyte color = rnd()
ubyte color = math.rnd()
while color==background_color
color = rnd()
color = math.rnd()
graphics.colors(color, 0)
while not_edge() and not_colliding() {
graphics.disc(x, y as ubyte, radius)

View File

@ -1,4 +1,5 @@
%import textio
%import math
; Amiga 'copper' bars color cycling effect
@ -126,7 +127,7 @@ colors {
sub random_rgb12() {
do {
uword rr = rndw()
uword rr = math.rndw()
target_red = msb(rr) & 15
target_green = lsb(rr)
target_blue = target_green & 15

View File

@ -1,6 +1,7 @@
%import gfx2
%import floats
%import textio
%import math
%zeropage dontuse
main {
@ -76,7 +77,7 @@ main {
sys.wait(2*60)
repeat 255
gfx2.line(rndw() % 640, rndw() % 480, rndw() % 640, rndw() % 480, 1)
gfx2.line(math.rndw() % 640, math.rndw() % 480, math.rndw() % 640, math.rndw() % 480, 1)
sys.wait(1*60)
}

View File

@ -9,6 +9,7 @@
%import syslib
%import textio
%import math
%import test_stack
%import psg
@ -346,7 +347,7 @@ waitkey:
xpos = startXpos
ypos = startYpos
speedlevel = 1
nextBlock = rnd() % 7
nextBlock = math.rnd() % 7
holding = 255
holdingAllowed = true
ticks_since_previous_action = 0
@ -362,7 +363,7 @@ waitkey:
sub spawnNextBlock() {
swapBlock(nextBlock)
nextBlock = (rnd() + lsb(c64.RDTIM16())) % 7
nextBlock = (math.rnd() + lsb(c64.RDTIM16())) % 7
drawNextBlock()
holdingAllowed = true
}

View File

@ -1,4 +1,5 @@
%import textio
%import math
%import cx16logo
; Note: this program is compatible with C64 and CX16.
@ -6,8 +7,8 @@
main {
sub start() {
repeat {
ubyte col = rnd() % (txt.DEFAULT_WIDTH-13) + 3
ubyte row = rnd() % (txt.DEFAULT_HEIGHT-7)
ubyte col = math.rnd() % (txt.DEFAULT_WIDTH-13) + 3
ubyte row = math.rnd() % (txt.DEFAULT_HEIGHT-7)
cx16logo.logo_at(col, row)
txt.plot(col-3, row+7 )
txt.print("commander x16")

View File

@ -1,4 +1,5 @@
%import textio
%import math
; Even though prog8 only has support for extremely limited recursion,
; you can write recursive algorithms with a bit of extra work by building your own explicit stack structure.
@ -135,8 +136,8 @@ carve_restart_after_repath:
; for too long on bad rng... just accept a few unused cells in that case.
repeat 255 {
do {
cx = rnd() % numCellsHoriz
cy = rnd() % numCellsVert
cx = math.rnd() % numCellsHoriz
cy = math.rnd() % numCellsVert
} until not @(celladdr(cx, cy)) & STONE
if available_uncarved()
return true
@ -164,7 +165,7 @@ carve_restart_after_repath:
ubyte[4] bitflags = [LEFT,RIGHT,UP,DOWN]
repeat {
ubyte choice = candidates & bitflags[rnd() & 3]
ubyte choice = candidates & bitflags[math.rnd() & 3]
if choice
return choice
}

View File

@ -1,5 +1,6 @@
%import textio
%import conv
%import math
%import test_stack
%zeropage basicsafe
@ -12,7 +13,7 @@ main {
sub start() {
str name = "????????????????????????????????????????"
str input = "??????????"
ubyte secretnumber = rnd() % 99 + 1 ; random number 1..100
ubyte secretnumber = math.rnd() % 99 + 1 ; random number 1..100
ubyte attempts_left
txt.lowercase()

View File

@ -106,7 +106,7 @@ main {
ubyte @zp ii
for ii in 0 to 7 {
; use 16 bit rng for a bit more randomness instead of the 8-bit rng
if rnd() > s {
if math.rnd() > s {
b |= bittab[ii]
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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,<v | F,<fr1,<v"),
Opcode.SQRT to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.SGN to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.RND to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.MODR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.MOD to InstructionFormat.from("BW,<>r1,<v"),
Opcode.CMP to InstructionFormat.from("BW,<r1,<r2"),

View File

@ -12,12 +12,12 @@
<option name="HAS_STRING_ESCAPES" value="true" />
</options>
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;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;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%ir;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%zeropage;%zpreserved;iso:;petscii:;sc:" />
<keywords3 keywords="@requirezp;@shared;@zp;byte;const;float;str;ubyte;uword;bool;void;word" />
<keywords4 keywords="abs;all;any;avg;callfar;callrom;cmp;len;lsb;memory;mkword;msb;peek;peekw;poke;pokew;pop;popw;push;pushw;reverse;rnd;rndw;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;sgn;sizeof;sort;sqrt16;swap;|&gt;" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%ir;%launcher;%option;%output;%zeropage;%zpreserved;iso:;petscii:;sc:" />
<keywords3 keywords="@requirezp;@shared;@zp;bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;all;any;avg;callfar;callrom;cmp;len;lsb;memory;mkword;msb;peek;peekw;poke;pokew;pop;popw;push;pushw;reverse;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;sgn;sizeof;sort;sqrt16;swap;|&gt;" />
</highlighting>
<extensionMap>
<mapping ext="p8" />
<mapping ext="prog8" />
</extensionMap>
</filetype>
</filetype>

View File

@ -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

View File

@ -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}")
}
}

View File

@ -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