diff --git a/compiler/examples/comparison_ifs_byte.p8 b/compiler/examples/comparison_ifs_byte.p8 index 7d0410d87..b55e4f0ca 100644 --- a/compiler/examples/comparison_ifs_byte.p8 +++ b/compiler/examples/comparison_ifs_byte.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib %option enable_floats diff --git a/compiler/examples/comparison_ifs_float.p8 b/compiler/examples/comparison_ifs_float.p8 index 7fd223b6f..dab3836a6 100644 --- a/compiler/examples/comparison_ifs_float.p8 +++ b/compiler/examples/comparison_ifs_float.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib %option enable_floats diff --git a/compiler/examples/comparison_ifs_ubyte.p8 b/compiler/examples/comparison_ifs_ubyte.p8 index 262a7db10..7dd3729ec 100644 --- a/compiler/examples/comparison_ifs_ubyte.p8 +++ b/compiler/examples/comparison_ifs_ubyte.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib %option enable_floats diff --git a/compiler/examples/comparison_ifs_uword.p8 b/compiler/examples/comparison_ifs_uword.p8 index 6975f0f06..b827cf998 100644 --- a/compiler/examples/comparison_ifs_uword.p8 +++ b/compiler/examples/comparison_ifs_uword.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib %option enable_floats diff --git a/compiler/examples/comparison_ifs_word.p8 b/compiler/examples/comparison_ifs_word.p8 index bb288b69c..9eff90569 100644 --- a/compiler/examples/comparison_ifs_word.p8 +++ b/compiler/examples/comparison_ifs_word.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib %option enable_floats diff --git a/compiler/examples/comparisons_byte.p8 b/compiler/examples/comparisons_byte.p8 index 063a7a743..29d1df492 100644 --- a/compiler/examples/comparisons_byte.p8 +++ b/compiler/examples/comparisons_byte.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib ~ main { diff --git a/compiler/examples/comparisons_float.p8 b/compiler/examples/comparisons_float.p8 index 954464ffa..b28cf4d77 100644 --- a/compiler/examples/comparisons_float.p8 +++ b/compiler/examples/comparisons_float.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib %option enable_floats diff --git a/compiler/examples/comparisons_ubyte.p8 b/compiler/examples/comparisons_ubyte.p8 index 0b420b9cb..5b10d96f3 100644 --- a/compiler/examples/comparisons_ubyte.p8 +++ b/compiler/examples/comparisons_ubyte.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib ~ main { diff --git a/compiler/examples/comparisons_uword.p8 b/compiler/examples/comparisons_uword.p8 index d5f6cd0b3..a97752957 100644 --- a/compiler/examples/comparisons_uword.p8 +++ b/compiler/examples/comparisons_uword.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib ~ main { diff --git a/compiler/examples/comparisons_word.p8 b/compiler/examples/comparisons_word.p8 index ce289c804..c0330ed13 100644 --- a/compiler/examples/comparisons_word.p8 +++ b/compiler/examples/comparisons_word.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib ~ main { diff --git a/compiler/examples/cube3d-novm.p8 b/compiler/examples/cube3d-c64.p8 similarity index 94% rename from compiler/examples/cube3d-novm.p8 rename to compiler/examples/cube3d-c64.p8 index 703b94748..cd175e3c5 100644 --- a/compiler/examples/cube3d-novm.p8 +++ b/compiler/examples/cube3d-c64.p8 @@ -43,7 +43,7 @@ ;vm_gfx_line(x, 130, i*10.w, 199, 6) } - rotate_vertices(flt(irq.global_time) / 30.0) + rotate_vertices(irq.global_time as float / 30.0) draw_edges() } } @@ -81,11 +81,11 @@ sub draw_edges() { sub toscreenx(float x, float z) -> word { - return fintw(x/(4.2+z) * flt(height)) + width // 2 + return x/(4.2+z) * (height as float) as word + width // 2 } sub toscreeny(float y, float z) -> word { - return fintw(y/(4.2+z) * flt(height)) + height // 2 + return y/(4.2+z) * (height as float) as word + height // 2 } ; draw all edges of the object diff --git a/compiler/examples/cube3d.p8 b/compiler/examples/cube3d.p8 index 4171a16bc..f8e2308d2 100644 --- a/compiler/examples/cube3d.p8 +++ b/compiler/examples/cube3d.p8 @@ -42,7 +42,7 @@ vm_gfx_line(i*2+width//2-width//10, 130, i*10.w, 199, 6) } - rotate_vertices(flt(irq.global_time) / 30.0) + rotate_vertices(irq.global_time as float / 30.0) draw_edges() } } @@ -80,11 +80,11 @@ sub draw_edges() { sub toscreenx(float x, float z) -> word { - return fintw(x/(4.2+z) * flt(height)) + width // 2 + return x/(4.2+z) * (height as float) as word + width // 2 } sub toscreeny(float y, float z) -> word { - return fintw(y/(4.2+z) * flt(height)) + height // 2 + return y/(4.2+z) * (height as float) as word + height // 2 } ; draw all edges of the object diff --git a/compiler/examples/hello.p8 b/compiler/examples/hello.p8 index 18951a283..4e681b8bf 100644 --- a/compiler/examples/hello.p8 +++ b/compiler/examples/hello.p8 @@ -23,7 +23,7 @@ ; use loop to write characters str bye = "Goodbye!\n" - for ubyte c in 0 to len(bye) { ; @TODO fix compiler crash. Parent of assignment to c should be the anonymous scope 'c' is in, instead of the for loop itself. + for ubyte c in 0 to len(bye) { c64.CHROUT(bye[c]) } diff --git a/compiler/examples/mandelbrot-c64.p8 b/compiler/examples/mandelbrot-c64.p8 index 0d8a2670c..1d1896e70 100644 --- a/compiler/examples/mandelbrot-c64.p8 +++ b/compiler/examples/mandelbrot-c64.p8 @@ -14,10 +14,10 @@ c64.TIME_LO=0 for ubyte pixely in 0 to height-1 { - float yy = flt(pixely)/height/0.4-1.0 + float yy = (pixely as float)/0.4/height-1.0 for ubyte pixelx in 0 to width-1 { - float xx = flt(pixelx)/width/0.3-2.0 + float xx = (pixelx as float)/0.3/width-2.0 float xsquared = 0.0 float ysquared = 0.0 @@ -37,7 +37,7 @@ } c64.CHROUT('\n') } - float duration = floor((c64.TIME_LO + c64.TIME_MID*256.0 + c64.TIME_HI*65536.0)/60.0) + float duration = floor(((c64.TIME_LO as float) + 256.0*(c64.TIME_MID as float) + 65536.0*(c64.TIME_HI as float))/60.0) c64scr.print("finished in ") c64flt.print_f(duration) c64scr.print(" seconds!\n") diff --git a/compiler/examples/mandelbrot.p8 b/compiler/examples/mandelbrot.p8 index c66da48c7..329c92753 100644 --- a/compiler/examples/mandelbrot.p8 +++ b/compiler/examples/mandelbrot.p8 @@ -12,10 +12,10 @@ vm_gfx_text(2, 1, 1, "Calculating Mandelbrot Fractal...") for ubyte pixely in yoffset to yoffset+height-1 { - float yy = flt((pixely-yoffset))/height/3.6+0.4 + float yy = (pixely-yoffset as float)/3.6/height+0.4 for uword pixelx in xoffset to xoffset+width-1 { - float xx = flt((pixelx-xoffset))/width/3.0+0.2 + float xx = (pixelx-xoffset as float)/3.0/width+0.2 float xsquared = 0.0 float ysquared = 0.0 diff --git a/compiler/examples/numbergame-novm.p8 b/compiler/examples/numbergame-novm.p8 index 5d4b4aa9c..01081dac7 100644 --- a/compiler/examples/numbergame-novm.p8 +++ b/compiler/examples/numbergame-novm.p8 @@ -1,5 +1,4 @@ %import c64utils -%import mathlib ; The classic number guessing game. ; This version uses mostly high level subroutine calls and loops. diff --git a/compiler/examples/swirl-c64.p8 b/compiler/examples/swirl-c64.p8 index 8e2be9f22..fa60236d5 100644 --- a/compiler/examples/swirl-c64.p8 +++ b/compiler/examples/swirl-c64.p8 @@ -28,9 +28,9 @@ } sub screenx(float x) -> ubyte { - return (x * width/2.2) + width//2 as ubyte + return (x * width/2.2) + width/2.0 as ubyte } sub screeny(float y) -> ubyte { - return (y * height/2.2) + height//2 as ubyte + return (y * height/2.2) + height/2.0 as ubyte } } diff --git a/compiler/examples/swirl.p8 b/compiler/examples/swirl.p8 index 2a26f1b73..35b348a05 100644 --- a/compiler/examples/swirl.p8 +++ b/compiler/examples/swirl.p8 @@ -23,13 +23,13 @@ } sub screenx(float x) -> word { - ; return (x/4.1* (width as float) as word) + width // 2 ; @todo fix calculation + ;return ((x/4.1* (width as float)) + 160.0) as word ;width // 2 ; @todo fix calculation float wf = width - return (x/4.1* wf + wf / 2) as word + return (x/4.1* wf + wf / 2.0) as word } sub screeny(float y) -> word { ;return (y/4.1 * (height as float) as word) + height // 2 ; @todo fix calculation float hf = height - return (y/4.1 * hf + hf/ 2) as word + return (y/4.1 * hf + hf/ 2.0) as word } } diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index bb60957a8..2b0a51c85 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,5 +1,4 @@ %import c64utils -;%import mathlib ;%option enable_floats ~ main { @@ -8,6 +7,11 @@ ;c64scr.PLOT(screenx(x), screeny(y)) ; @todo fix argument calculation of parameters ???!!! + sub screenx(float x) -> word { + ;return ((x/4.1* (width as float)) + 160.0) as word ;width // 2 ; @todo fix calculation + float wf = width + return (x/4.1* wf + wf / 2.0) as word + } sub start() { diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 67b1edcec..8fb39fce9 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -867,6 +867,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I else -> throw FatalAstException("invalid rightDt $rightDt") } DataType.FLOAT -> DataType.FLOAT + null -> DataType.FLOAT else -> throw FatalAstException("invalid leftDt $leftDt") } } else if(leftDt==null || rightDt==null) null else arithmeticOpDt(leftDt, rightDt) @@ -1434,19 +1435,8 @@ class FunctionCall(override var target: IdentifierReference, private fun constValue(namespace: INameScope, heap: HeapValues, withDatatypeCheck: Boolean): LiteralValue? { // if the function is a built-in function and the args are consts, should try to const-evaluate! + // lenghts of arrays and strings are constants that are determined at compile time! if(target.nameInSource.size>1) return null - if(target.nameInSource[0]=="len" && arglist.size==1) { - val arg=arglist[0] - if(arg is IdentifierReference) { - val target=arg.targetStatement(namespace) - if(target!=null) { - if (arg.resultingDatatype(namespace, heap) in StringDatatypes) { - // len on strings should be dynamic, all other cases are a compile-time constant - return null - } - } - } - } try { var resultValue: LiteralValue? = null val func = BuiltinFunctions[target.nameInSource[0]] diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index b666e39e5..94beb062b 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -61,6 +61,16 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { return super.process(block) } + override fun process(functionCall: FunctionCall): IExpression { + if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") { + // lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte" + val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, functionCall.position) + typecast.linkParents(functionCall.parent) + return typecast + } + return functionCall + } + override fun process(decl: VarDecl): IStatement { // first, check if there are datatype errors on the vardecl decl.datatypeErrors.forEach { checkResult.add(it) } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 140af82d1..d2780d5aa 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -462,7 +462,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, // word + word -> word // a combination with a float will be float (but give a warning about this!) - val floatWarning = "byte or word value implicitly converted to float. Suggestion: use explicit flt() conversion, a float number, or revert to integer arithmetic" + val floatWarning = "byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic" return when(leftDt) { DataType.UBYTE -> { @@ -725,8 +725,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, else -> throw CompilerException("wrong datatype for $funcname()") } } - "msb" -> prog.instr(Opcode.MSB) - "lsb" -> TODO("lsb -> uw2ub or w2ub?") + "msb" -> prog.instr(Opcode.MSB) // note: LSB is not a function, it's just an alias for the cast "... as ubyte" "lsl" -> { val arg = args.single() val dt = arg.resultingDatatype(namespace, heap) @@ -1794,7 +1793,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, } val startAssignment = Assignment(listOf(makeAssignmentTarget()), null, range.from, range.position) - startAssignment.linkParents(range.parent) + startAssignment.linkParents(body) translate(startAssignment) val loopLabel = makeLabel("loop") @@ -1826,7 +1825,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, AnonymousScope(mutableListOf(Jump(null, null, breakLabel, range.position)), range.position), AnonymousScope(mutableListOf(), range.position), range.position) - ifstmt.linkParents(range.parent) + ifstmt.linkParents(body) translate(ifstmt) } else { // Step is a variable. We can't optimize anything... @@ -1836,7 +1835,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, translate(body) prog.label(continueLabel) val lvTarget = makeAssignmentTarget() - lvTarget.linkParents(range.parent) + lvTarget.linkParents(body) val targetStatement: VarDecl? = if(lvTarget.identifier!=null) { lvTarget.identifier.targetStatement(namespace) as VarDecl @@ -1850,7 +1849,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, 1 -> { // LV++ val postIncr = PostIncrDecr(lvTarget, "++", range.position) - postIncr.linkParents(range.parent) + postIncr.linkParents(body) translate(postIncr) if(lvTarget.register!=null) prog.instr(Opcode.PUSH_VAR_BYTE, callLabel =lvTarget.register.toString()) @@ -1863,13 +1862,13 @@ private class StatementTranslator(private val prog: IntermediateProgram, AnonymousScope(mutableListOf(Jump(null, null, loopLabel, range.position)), range.position), AnonymousScope(mutableListOf(), range.position), range.position) - branch.linkParents(range.parent) + branch.linkParents(body) translate(branch) } -1 -> { // LV-- val postIncr = PostIncrDecr(makeAssignmentTarget(), "--", range.position) - postIncr.linkParents(range.parent) + postIncr.linkParents(body) translate(postIncr) TODO("signed numbers and/or special condition are needed for decreasing for loop. Try an increasing loop and/or constant loop values instead? At: ${range.position}") // fix with signed numbers } diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 6f4b50c49..6f20a1b97 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -679,8 +679,8 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, Opcode.CAST_B_TO_F -> " jsr prog8_lib.stack_b2float" Opcode.CAST_UW_TO_F -> " jsr prog8_lib.stack_uw2float" Opcode.CAST_W_TO_F -> " jsr prog8_lib.stack_w2float" - Opcode.CAST_F_TO_UB -> " jsr prog8_lib.stack_float2ub" - Opcode.CAST_F_TO_B -> " jsr prog8_lib.stack_float2b" + Opcode.CAST_F_TO_UB -> " jsr prog8_lib.stack_float2uw" + Opcode.CAST_F_TO_B -> " jsr prog8_lib.stack_float2w" Opcode.CAST_F_TO_UW -> " jsr prog8_lib.stack_float2uw" Opcode.CAST_F_TO_W -> " jsr prog8_lib.stack_float2w" Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta ${ESTACK_HI+1},x" // clear the msb diff --git a/docs/source/building.rst b/docs/source/building.rst index 14f331b98..375ff23a2 100644 --- a/docs/source/building.rst +++ b/docs/source/building.rst @@ -48,8 +48,7 @@ It consists of compilation options and other directives, imports of other module and source code for one or more code blocks. Prog8 has a couple of *LIBRARY* modules that are defined in special internal files provided by the compiler: -``c64lib``, ``prog8lib``, ``mathlib``. -You should not overwrite these or reuse their names. +``c64lib``, ``c64utils``, ``prog8lib``. You should not overwrite these or reuse their names. .. _debugging: diff --git a/docs/source/programming.rst b/docs/source/programming.rst index ea0beb83f..b9bcc6ab8 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -543,6 +543,8 @@ sum(x) len(x) Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte). Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. + Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual + length of the string during execution, the value of len(string) may no longer be correct! lsb(x) Get the least significant byte of the word x. Equivalent to the cast "x as ubyte". diff --git a/prog8lib/c64utils.p8 b/prog8lib/c64utils.p8 index e224f5ca7..deb1e90ef 100644 --- a/prog8lib/c64utils.p8 +++ b/prog8lib/c64utils.p8 @@ -281,6 +281,132 @@ asmsub str2byte(str string @ AY) -> clobbers(Y) -> (byte @ A) { ; @todo string to 32 bit unsigned integer http://www.6502.org/source/strings/ascii-to-32bit.html +%asm {{ +; copy memory UP from (SCRATCH_ZPWORD1) to (SCRATCH_ZPWORD2) of length X/Y (16-bit, X=lo, Y=hi) +; clobbers register A,X,Y +memcopy16_up .proc + source = SCRATCH_ZPWORD1 + dest = SCRATCH_ZPWORD2 + length = SCRATCH_ZPB1 ; (and SCRATCH_ZPREG) + + stx length + sty length+1 + + ldx length ; move low byte of length into X + bne + ; jump to start if X > 0 + dec length ; subtract 1 from length ++ ldy #0 ; set Y to 0 +- lda (source),y ; set A to whatever (source) points to offset by Y + sta (dest),y ; move A to location pointed to by (dest) offset by Y + iny ; increment Y + bne + ; if Y<>0 then (rolled over) then still moving bytes + inc source+1 ; increment hi byte of source + inc dest+1 ; increment hi byte of dest ++ dex ; decrement X (lo byte counter) + bne - ; if X<>0 then move another byte + dec length ; weve moved 255 bytes, dec length + bpl - ; if length is still positive go back and move more + rts ; done + .pend + + +; copy memory UP from (SCRATCH_ZPWORD1) to (AY) with length X (1 to 256, 0 meaning 256) +; destination must not overlap, or be before start, then overlap is possible. +; clobbers A, X, Y + +memcopy .proc + sta SCRATCH_ZPWORD2 + sty SCRATCH_ZPWORD2+1 + ldy #0 +- lda (SCRATCH_ZPWORD1), y + sta (SCRATCH_ZPWORD2), y + iny + dex + bne - + rts + .pend + + +; fill memory from (SCRATCH_ZPWORD1), length XY, with value in A. +; clobbers X, Y +memset .proc + stx SCRATCH_ZPB1 + sty SCRATCH_ZPREG + ldy #0 + ldx SCRATCH_ZPREG + beq _lastpage + +_fullpage sta (SCRATCH_ZPWORD1),y + iny + bne _fullpage + inc SCRATCH_ZPWORD1+1 ; next page + dex + bne _fullpage + +_lastpage ldy SCRATCH_ZPB1 + beq + +- dey + sta (SCRATCH_ZPWORD1),y + bne - + ++ rts + .pend + + +; fill memory from (SCRATCH_ZPWORD1) number of words in SCRATCH_ZPWORD2, with word value in AY. +; clobbers A, X, Y +memsetw .proc + sta _mod1+1 ; self-modify + sty _mod1b+1 ; self-modify + sta _mod2+1 ; self-modify + sty _mod2b+1 ; self-modify + ldx SCRATCH_ZPWORD1 + stx SCRATCH_ZPB1 + ldx SCRATCH_ZPWORD1+1 + inx + stx SCRATCH_ZPREG ; second page + + ldy #0 + ldx SCRATCH_ZPWORD2+1 + beq _lastpage + +_fullpage +_mod1 lda #0 ; self-modified + sta (SCRATCH_ZPWORD1),y ; first page + sta (SCRATCH_ZPB1),y ; second page + iny +_mod1b lda #0 ; self-modified + sta (SCRATCH_ZPWORD1),y ; first page + sta (SCRATCH_ZPB1),y ; second page + iny + bne _fullpage + inc SCRATCH_ZPWORD1+1 ; next page pair + inc SCRATCH_ZPWORD1+1 ; next page pair + inc SCRATCH_ZPB1+1 ; next page pair + inc SCRATCH_ZPB1+1 ; next page pair + dex + bne _fullpage + +_lastpage ldx SCRATCH_ZPWORD2 + beq _done + + ldy #0 +- +_mod2 lda #0 ; self-modified + sta (SCRATCH_ZPWORD1), y + inc SCRATCH_ZPWORD1 + bne _mod2b + inc SCRATCH_ZPWORD1+1 +_mod2b lda #0 ; self-modified + sta (SCRATCH_ZPWORD1), y + inc SCRATCH_ZPWORD1 + bne + + inc SCRATCH_ZPWORD1+1 ++ dex + bne - +_done rts + .pend +}} } ; ------ end of block c64utils diff --git a/prog8lib/mathlib.p8 b/prog8lib/mathlib.p8 deleted file mode 100644 index 6c522295c..000000000 --- a/prog8lib/mathlib.p8 +++ /dev/null @@ -1,255 +0,0 @@ -; Prog8 integer math library for 6502 -; (floating point math is done via the C-64's BASIC ROM routines) -; -; some more interesting routines can be found here: -; http://6502org.wikidot.com/software-math -; http://codebase64.org/doku.php?id=base:6502_6510_maths -; -; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -; -; indent format: TABS, size=8 - - -~ math { - ; note: the following ZP scratch registers must be the same as in c64lib - memory ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP - memory ubyte SCRATCH_ZPREG = $03 ; scratch register in ZP - memory uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc) - memory uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe) - - - -asmsub multiply_bytes (ubyte byte1 @ A, ubyte byte2 @ Y) -> clobbers() -> (ubyte @ A) { - ; ---- multiply 2 bytes, result as byte in A (signed or unsigned) - %asm {{ - sta SCRATCH_ZPB1 ; num1 - sty SCRATCH_ZPREG ; num2 - lda #0 - beq _enterloop -_doAdd clc - adc SCRATCH_ZPB1 -_loop asl SCRATCH_ZPB1 -_enterloop lsr SCRATCH_ZPREG - bcs _doAdd - bne _loop - rts - }} -} - - -asmsub multiply_bytes_16 (ubyte byte1 @ A, ubyte byte2 @ Y) -> clobbers(X) -> (uword @ AY) { - ; ---- multiply 2 bytes, result as word in A/Y (unsigned) - %asm {{ - sta SCRATCH_ZPB1 - sty SCRATCH_ZPREG - lda #0 - ldx #8 - lsr SCRATCH_ZPB1 -- bcc + - clc - adc SCRATCH_ZPREG -+ ror a - ror SCRATCH_ZPB1 - dex - bne - - tay - lda SCRATCH_ZPB1 - rts - }} -} - -asmsub multiply_words (uword number @ AY) -> clobbers(A,X) -> () { - ; ---- multiply two 16-bit words into a 32-bit result (signed and unsigned) - ; input: A/Y = first 16-bit number, SCRATCH_ZPWORD1 in ZP = second 16-bit number - ; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) - - %asm {{ - sta SCRATCH_ZPWORD2 - sty SCRATCH_ZPWORD2+1 - -mult16 lda #$00 - sta multiply_words_result+2 ; clear upper bits of product - sta multiply_words_result+3 - ldx #16 ; for all 16 bits... -- lsr SCRATCH_ZPWORD1+1 ; divide multiplier by 2 - ror SCRATCH_ZPWORD1 - bcc + - lda multiply_words_result+2 ; get upper half of product and add multiplicand - clc - adc SCRATCH_ZPWORD2 - sta multiply_words_result+2 - lda multiply_words_result+3 - adc SCRATCH_ZPWORD2+1 -+ ror a ; rotate partial product - sta multiply_words_result+3 - ror multiply_words_result+2 - ror multiply_words_result+1 - ror multiply_words_result - dex - bne - - rts - -multiply_words_result .byte 0,0,0,0 - - }} -} - -asmsub divmod_bytes (ubyte number @ X, ubyte divisor @ Y) -> clobbers() -> (ubyte @ X, ubyte @ A) { - ; ---- divide X by Y, result quotient in X, remainder in A (unsigned) - ; division by zero will result in quotient = 255 and remainder = original number - %asm {{ - stx SCRATCH_ZPB1 - sty SCRATCH_ZPREG - - lda #0 - ldx #8 - asl SCRATCH_ZPB1 -- rol a - cmp SCRATCH_ZPREG - bcc + - sbc SCRATCH_ZPREG -+ rol SCRATCH_ZPB1 - dex - bne - - - ldx SCRATCH_ZPB1 - rts - }} -} - -asmsub divmod_words (uword divisor @ AY) -> clobbers(X) -> (uword @ AY) { - ; ---- divide two words (16 bit each) into 16 bit results - ; input: SCRATCH_ZPWORD1 in ZP: 16 bit number, A/Y: 16 bit divisor - ; output: SCRATCH_ZPWORD1 in ZP: 16 bit result, A/Y: 16 bit remainder - ; division by zero will result in quotient = 65535 and remainder = divident - - %asm {{ -remainder = SCRATCH_ZPB1 - - sta SCRATCH_ZPWORD2 - sty SCRATCH_ZPWORD2+1 - lda #0 ;preset remainder to 0 - sta remainder - sta remainder+1 - ldx #16 ;repeat for each bit: ... - -- asl SCRATCH_ZPWORD1 ;number lb & hb*2, msb -> Carry - rol SCRATCH_ZPWORD1+1 - rol remainder ;remainder lb & hb * 2 + msb from carry - rol remainder+1 - lda remainder - sec - sbc SCRATCH_ZPWORD2 ;substract divisor to see if it fits in - tay ;lb result -> Y, for we may need it later - lda remainder+1 - sbc SCRATCH_ZPWORD2+1 - bcc + ;if carry=0 then divisor didn't fit in yet - - sta remainder+1 ;else save substraction result as new remainder, - sty remainder - inc SCRATCH_ZPWORD1 ;and INCrement result cause divisor fit in 1 times - -+ dex - bne - - - lda remainder ; copy remainder to ZPWORD2 result register - sta SCRATCH_ZPWORD2 - lda remainder+1 - sta SCRATCH_ZPWORD2+1 - - lda SCRATCH_ZPWORD1 ; load division result in A/Y - ldy SCRATCH_ZPWORD1+1 - - rts - - }} -} - -asmsub randseed (uword seed @ AY) -> clobbers(A, Y) -> () { - ; ---- reset the random seeds for the byte and word random generators - ; default starting values are: A=$2c Y=$9e - %asm {{ - sta randword._seed - sty randword._seed+1 - stx SCRATCH_ZPREG - clc - adc #14 - sta randbyte._seed - ora #$80 ; make negative - jsr c64.FREADSA - jsr c64.RND ; reseed the float rng using the (negative) number in A - ldx SCRATCH_ZPREG - rts - }} -} - - -asmsub randbyte () -> clobbers() -> (ubyte @ A) { - ; ---- 8-bit pseudo random number generator into A - - %asm {{ - lda _seed - beq + - asl a - beq ++ ;if the input was $80, skip the EOR - bcc ++ -+ eor _magic ; #$1d ; could be self-modifying code to set new magic -+ sta _seed - rts - -_seed .byte $3a -_magic .byte $1d -_magiceors .byte $1d, $2b, $2d, $4d, $5f, $63, $65, $69 - .byte $71, $87, $8d, $a9, $c3, $cf, $e7, $f5 - - }} -} - -asmsub randword () -> clobbers() -> (uword @ AY) { - ; ---- 16 bit pseudo random number generator into AY - - %asm {{ - lda _seed - beq _lowZero ; $0000 and $8000 are special values to test for - - ; Do a normal shift - asl _seed - lda _seed+1 - rol a - bcc _noEor - -_doEor ; high byte is in A - eor _magic+1 ; #>magic ; could be self-modifying code to set new magic - sta _seed+1 - lda _seed - eor _magic ; # clobbers() -> (ubyte @ A) { + ; ---- multiply 2 bytes, result as byte in A (signed or unsigned) + %asm {{ + sta SCRATCH_ZPB1 ; num1 + sty SCRATCH_ZPREG ; num2 + lda #0 + beq _enterloop +_doAdd clc + adc SCRATCH_ZPB1 +_loop asl SCRATCH_ZPB1 +_enterloop lsr SCRATCH_ZPREG + bcs _doAdd + bne _loop + rts + }} +} + + +asmsub multiply_bytes_16 (ubyte byte1 @ A, ubyte byte2 @ Y) -> clobbers(X) -> (uword @ AY) { + ; ---- multiply 2 bytes, result as word in A/Y (unsigned) + %asm {{ + sta SCRATCH_ZPB1 + sty SCRATCH_ZPREG + lda #0 + ldx #8 + lsr SCRATCH_ZPB1 +- bcc + + clc + adc SCRATCH_ZPREG ++ ror a + ror SCRATCH_ZPB1 + dex + bne - + tay + lda SCRATCH_ZPB1 + rts + }} +} + +asmsub multiply_words (uword number @ AY) -> clobbers(A,X) -> () { + ; ---- multiply two 16-bit words into a 32-bit result (signed and unsigned) + ; input: A/Y = first 16-bit number, SCRATCH_ZPWORD1 in ZP = second 16-bit number + ; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) + + %asm {{ + sta SCRATCH_ZPWORD2 + sty SCRATCH_ZPWORD2+1 + +mult16 lda #$00 + sta multiply_words_result+2 ; clear upper bits of product + sta multiply_words_result+3 + ldx #16 ; for all 16 bits... +- lsr SCRATCH_ZPWORD1+1 ; divide multiplier by 2 + ror SCRATCH_ZPWORD1 + bcc + + lda multiply_words_result+2 ; get upper half of product and add multiplicand + clc + adc SCRATCH_ZPWORD2 + sta multiply_words_result+2 + lda multiply_words_result+3 + adc SCRATCH_ZPWORD2+1 ++ ror a ; rotate partial product + sta multiply_words_result+3 + ror multiply_words_result+2 + ror multiply_words_result+1 + ror multiply_words_result + dex + bne - + rts + +multiply_words_result .byte 0,0,0,0 + + }} +} + +asmsub divmod_bytes (ubyte number @ X, ubyte divisor @ Y) -> clobbers() -> (ubyte @ X, ubyte @ A) { + ; ---- divide X by Y, result quotient in X, remainder in A (unsigned) + ; division by zero will result in quotient = 255 and remainder = original number + %asm {{ + stx SCRATCH_ZPB1 + sty SCRATCH_ZPREG + + lda #0 + ldx #8 + asl SCRATCH_ZPB1 +- rol a + cmp SCRATCH_ZPREG + bcc + + sbc SCRATCH_ZPREG ++ rol SCRATCH_ZPB1 + dex + bne - + + ldx SCRATCH_ZPB1 + rts + }} +} + +asmsub divmod_words (uword divisor @ AY) -> clobbers(X) -> (uword @ AY) { + ; ---- divide two words (16 bit each) into 16 bit results + ; input: SCRATCH_ZPWORD1 in ZP: 16 bit number, A/Y: 16 bit divisor + ; output: SCRATCH_ZPWORD1 in ZP: 16 bit result, A/Y: 16 bit remainder + ; division by zero will result in quotient = 65535 and remainder = divident + + %asm {{ +remainder = SCRATCH_ZPB1 + + sta SCRATCH_ZPWORD2 + sty SCRATCH_ZPWORD2+1 + lda #0 ;preset remainder to 0 + sta remainder + sta remainder+1 + ldx #16 ;repeat for each bit: ... + +- asl SCRATCH_ZPWORD1 ;number lb & hb*2, msb -> Carry + rol SCRATCH_ZPWORD1+1 + rol remainder ;remainder lb & hb * 2 + msb from carry + rol remainder+1 + lda remainder + sec + sbc SCRATCH_ZPWORD2 ;substract divisor to see if it fits in + tay ;lb result -> Y, for we may need it later + lda remainder+1 + sbc SCRATCH_ZPWORD2+1 + bcc + ;if carry=0 then divisor didn't fit in yet + + sta remainder+1 ;else save substraction result as new remainder, + sty remainder + inc SCRATCH_ZPWORD1 ;and INCrement result cause divisor fit in 1 times + ++ dex + bne - + + lda remainder ; copy remainder to ZPWORD2 result register + sta SCRATCH_ZPWORD2 + lda remainder+1 + sta SCRATCH_ZPWORD2+1 + + lda SCRATCH_ZPWORD1 ; load division result in A/Y + ldy SCRATCH_ZPWORD1+1 + + rts + + }} +} + +asmsub randseed (uword seed @ AY) -> clobbers(A, Y) -> () { + ; ---- reset the random seeds for the byte and word random generators + ; default starting values are: A=$2c Y=$9e + %asm {{ + sta randword._seed + sty randword._seed+1 + stx SCRATCH_ZPREG + clc + adc #14 + sta randbyte._seed + ora #$80 ; make negative + jsr c64.FREADSA + jsr c64.RND ; reseed the float rng using the (negative) number in A + ldx SCRATCH_ZPREG + rts + }} +} + + +asmsub randbyte () -> clobbers() -> (ubyte @ A) { + ; ---- 8-bit pseudo random number generator into A + + %asm {{ + lda _seed + beq + + asl a + beq ++ ;if the input was $80, skip the EOR + bcc ++ ++ eor _magic ; #$1d ; could be self-modifying code to set new magic ++ sta _seed + rts + +_seed .byte $3a +_magic .byte $1d +_magiceors .byte $1d, $2b, $2d, $4d, $5f, $63, $65, $69 + .byte $71, $87, $8d, $a9, $c3, $cf, $e7, $f5 + + }} +} + +asmsub randword () -> clobbers() -> (uword @ AY) { + ; ---- 16 bit pseudo random number generator into AY + + %asm {{ + lda _seed + beq _lowZero ; $0000 and $8000 are special values to test for + + ; Do a normal shift + asl _seed + lda _seed+1 + rol a + bcc _noEor + +_doEor ; high byte is in A + eor _magic+1 ; #>magic ; could be self-modifying code to set new magic + sta _seed+1 + lda _seed + eor _magic ; # 0 - dec length ; subtract 1 from length -+ ldy #0 ; set Y to 0 -- lda (source),y ; set A to whatever (source) points to offset by Y - sta (dest),y ; move A to location pointed to by (dest) offset by Y - iny ; increment Y - bne + ; if Y<>0 then (rolled over) then still moving bytes - inc source+1 ; increment hi byte of source - inc dest+1 ; increment hi byte of dest -+ dex ; decrement X (lo byte counter) - bne - ; if X<>0 then move another byte - dec length ; weve moved 255 bytes, dec length - bpl - ; if length is still positive go back and move more - rts ; done - - -; copy memory UP from (SCRATCH_ZPWORD1) to (AY) with length X (1 to 256, 0 meaning 256) -; destination must not overlap, or be before start, then overlap is possible. -; clobbers A, X, Y - -memcopy - sta SCRATCH_ZPWORD2 - sty SCRATCH_ZPWORD2+1 - ldy #0 -- lda (SCRATCH_ZPWORD1), y - sta (SCRATCH_ZPWORD2), y - iny - dex - bne - - rts - - -; fill memory from (SCRATCH_ZPWORD1), length XY, with value in A. -; clobbers X, Y -memset stx SCRATCH_ZPB1 - sty SCRATCH_ZPREG - ldy #0 - ldx SCRATCH_ZPREG - beq _lastpage - -_fullpage sta (SCRATCH_ZPWORD1),y - iny - bne _fullpage - inc SCRATCH_ZPWORD1+1 ; next page - dex - bne _fullpage - -_lastpage ldy SCRATCH_ZPB1 - beq + -- dey - sta (SCRATCH_ZPWORD1),y - bne - - -+ rts - - - -; fill memory from (SCRATCH_ZPWORD1) number of words in SCRATCH_ZPWORD2, with word value in AY. -; clobbers A, X, Y -memsetw - sta _mod1+1 ; self-modify - sty _mod1b+1 ; self-modify - sta _mod2+1 ; self-modify - sty _mod2b+1 ; self-modify - ldx SCRATCH_ZPWORD1 - stx SCRATCH_ZPB1 - ldx SCRATCH_ZPWORD1+1 - inx - stx SCRATCH_ZPREG ; second page - - ldy #0 - ldx SCRATCH_ZPWORD2+1 - beq _lastpage - -_fullpage -_mod1 lda #0 ; self-modified - sta (SCRATCH_ZPWORD1),y ; first page - sta (SCRATCH_ZPB1),y ; second page - iny -_mod1b lda #0 ; self-modified - sta (SCRATCH_ZPWORD1),y ; first page - sta (SCRATCH_ZPB1),y ; second page - iny - bne _fullpage - inc SCRATCH_ZPWORD1+1 ; next page pair - inc SCRATCH_ZPWORD1+1 ; next page pair - inc SCRATCH_ZPB1+1 ; next page pair - inc SCRATCH_ZPB1+1 ; next page pair - dex - bne _fullpage - -_lastpage ldx SCRATCH_ZPWORD2 - beq _done - - ldy #0 -- -_mod2 lda #0 ; self-modified - sta (SCRATCH_ZPWORD1), y - inc SCRATCH_ZPWORD1 - bne _mod2b - inc SCRATCH_ZPWORD1+1 -_mod2b lda #0 ; self-modified - sta (SCRATCH_ZPWORD1), y - inc SCRATCH_ZPWORD1 - bne + - inc SCRATCH_ZPWORD1+1 -+ dex - bne - -_done rts - - -; increments/decrements a byte referenced by indirect register pair by 1 -; clobbers A/Y, Carry flag determines incr or decr -incrdecr_deref_byte_reg_AX - sta SCRATCH_ZPWORD1 - stx SCRATCH_ZPWORD1+1 - bcc incr_deref_byte - bcs decr_deref_byte -incrdecr_deref_byte_reg_AY - sta SCRATCH_ZPWORD1 - sty SCRATCH_ZPWORD1+1 - bcc incr_deref_byte - bcs decr_deref_byte -incrdecr_deref_byte_reg_XY - stx SCRATCH_ZPWORD1 - sty SCRATCH_ZPWORD1+1 - bcs decr_deref_byte - -incr_deref_byte - ldy #0 - lda (SCRATCH_ZPWORD1), y - adc #1 ; carry's cleared already - sta (SCRATCH_ZPWORD1), y - rts -decr_deref_byte - ldy #0 - lda (SCRATCH_ZPWORD1), y - sbc #1 ; carry's set already - sta (SCRATCH_ZPWORD1), y - rts - -; increments/decrements a word referenced by indirect register pair by 1 -; clobbers A/Y, Carry flag determines incr or decr -incrdecr_deref_word_reg_AX - sta SCRATCH_ZPWORD1 - stx SCRATCH_ZPWORD1+1 - bcc incr_deref_word - bcs decr_deref_word -incrdecr_deref_word_reg_AY - sta SCRATCH_ZPWORD1 - sty SCRATCH_ZPWORD1+1 - bcc incr_deref_word - bcs decr_deref_word -incrdecr_deref_word_reg_XY - stx SCRATCH_ZPWORD1 - sty SCRATCH_ZPWORD1+1 - bcs decr_deref_word - -incr_deref_word - ldy #0 - lda (SCRATCH_ZPWORD1), y - adc #1 ; carry's cleared already - sta (SCRATCH_ZPWORD1), y - bcc + - iny - lda (SCRATCH_ZPWORD1), y - adc #0 ; carry is set - sta (SCRATCH_ZPWORD1), y -+ rts - -decr_deref_word - ldy #0 - lda (SCRATCH_ZPWORD1), y - bne + - pha - iny - lda (SCRATCH_ZPWORD1), y - sbc #1 ; carry's set already - sta (SCRATCH_ZPWORD1), y - dey - pla -+ sec - sbc #1 - sta (SCRATCH_ZPWORD1), y - rts - - -; shift bits in A right by X positions -lsr_A_by_X - cpx #8 - bcc _shift - lda #0 ; x >=8, result always 0 - rts -_shift cpx #0 - beq + -- lsr a - dex - bne - -+ rts - - -; shift bits in A left by X positions -asl_A_by_X - cpx #8 - bcc _shift - lda #0 ; x >=8, result always 0 - rts -_shift cpx #0 - beq + -- asl a - dex - bne - -+ rts - - - }} -} diff --git a/python-prototype/README.txt b/python-prototype/README.txt deleted file mode 100644 index 1dd39a954..000000000 --- a/python-prototype/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -This is the old Python version of an early prototype of the language and compiler. -It was able to produce actual running c64 binary programs, but only -from a very limited language subset. - diff --git a/python-prototype/il65/__init__.py b/python-prototype/il65/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/python-prototype/il65/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/python-prototype/il65/__main__.py b/python-prototype/il65/__main__.py deleted file mode 100644 index 4ea58a929..000000000 --- a/python-prototype/il65/__main__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -from . import main -main.main() diff --git a/python-prototype/il65/codegen/__init__.py b/python-prototype/il65/codegen/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/python-prototype/il65/codegen/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/python-prototype/il65/codegen/mos6502/__init__.py b/python-prototype/il65/codegen/mos6502/__init__.py deleted file mode 100644 index 2dc6af5e3..000000000 --- a/python-prototype/il65/codegen/mos6502/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the 6502 assembly code generator (directly from the parse tree) - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import contextlib -import attr -from typing import Set, Callable -from ...plyparse import Scope, AstNode -from ...compile import Zeropage - - -@attr.s(repr=False, cmp=False) -class Context: - out = attr.ib(type=Callable) - stmt = attr.ib(type=AstNode) - scope = attr.ib(type=Scope) - floats_enabled = attr.ib(type=bool) - - -@contextlib.contextmanager -def preserving_registers(registers: Set[str], scope: Scope, out: Callable, loads_a_within: bool=False, force_preserve: bool=False): - # this sometimes clobbers a ZP scratch register and is therefore NOT safe to use in interrupts - # see http://6502.org/tutorials/register_preservation.html - if not scope.save_registers and not force_preserve: - yield - return - if registers == {'A'}: - out("\t\tpha") - yield - out("\t\tpla") - elif registers: - if not loads_a_within: - out("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2)) - if 'A' in registers: - out("\t\tpha") - if 'X' in registers: - out("\t\ttxa\n\t\tpha") - if 'Y' in registers: - out("\t\ttya\n\t\tpha") - if not loads_a_within: - out("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2)) - yield - if 'X' in registers and 'Y' in registers: - if 'A' not in registers: - out("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2)) - out("\t\tpla\n\t\ttay") - out("\t\tpla\n\t\ttax") - out("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2)) - else: - out("\t\tpla\n\t\ttay") - out("\t\tpla\n\t\ttax") - else: - if 'Y' in registers: - if 'A' not in registers: - out("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2)) - out("\t\tpla\n\t\ttay") - out("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2)) - else: - out("\t\tpla\n\t\ttay") - if 'X' in registers: - if 'A' not in registers: - out("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2)) - out("\t\tpla\n\t\ttax") - out("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2)) - else: - out("\t\tpla\n\t\ttax") - if 'A' in registers: - out("\t\tpla") - else: - yield - - diff --git a/python-prototype/il65/codegen/mos6502/assignment.py b/python-prototype/il65/codegen/mos6502/assignment.py deleted file mode 100644 index 4ba93160e..000000000 --- a/python-prototype/il65/codegen/mos6502/assignment.py +++ /dev/null @@ -1,585 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the code generator for assignment statements. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -from typing import Callable -from . import preserving_registers, Context -from ..shared import CodeGenerationError, to_hex -from ...plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef, Dereference -from ...datatypes import REGISTER_BYTES, VarType, DataType -from ...compile import Zeropage - - -def generate_assignment(ctx: Context) -> None: - assert isinstance(ctx.stmt, Assignment) - assert not isinstance(ctx.stmt.right, Assignment), "assignment should have been flattened" - ctx.out("\v\t\t\t; " + ctx.stmt.lineref) - ctx.out("\v; @todo assignment: {} = {}".format(ctx.stmt.left, ctx.stmt.right)) - # @todo assignment - - -def generate_aug_assignment(ctx: Context) -> None: - # for instance: value += 33 - # (note that with += and -=, values 0..255 usually occur as the more efficient incrdecr statements instead) - # left: Register, SymbolName, or Dereference. right: Expression/LiteralValue - out = ctx.out - stmt = ctx.stmt - assert isinstance(stmt, AugAssignment) - out("\v\t\t\t; " + stmt.lineref) - lvalue = stmt.left - rvalue = stmt.right - if isinstance(lvalue, Register): - if isinstance(rvalue, LiteralValue): - if type(rvalue.value) is int: - assert rvalue.value >= 0, "augassign value can't be < 0" - _generate_aug_reg_int(out, lvalue, stmt.operator, rvalue.value, "", ctx.scope) - else: - raise CodeGenerationError("constant integer literal or variable required for now", rvalue) # XXX - elif isinstance(rvalue, SymbolName): - symdef = ctx.scope.lookup(rvalue.name) - if isinstance(symdef, VarDef): - if symdef.vartype == VarType.CONST: - if symdef.datatype.isinteger(): - assert symdef.value.const_value() >= 0, "augassign value can't be <0" # type: ignore - _generate_aug_reg_int(out, lvalue, stmt.operator, symdef.value.const_value(), "", ctx.scope) # type: ignore - else: - raise CodeGenerationError("aug. assignment value must be integer", rvalue) - elif symdef.datatype == DataType.BYTE: - _generate_aug_reg_int(out, lvalue, stmt.operator, 0, symdef.name, ctx.scope) - else: - raise CodeGenerationError("variable must be of type byte for now", rvalue) # XXX - else: - raise CodeGenerationError("can only use variable name as symbol for aug assign rvalue", rvalue) - elif isinstance(rvalue, Register): - if lvalue.datatype == DataType.BYTE and rvalue.datatype == DataType.WORD: - raise CodeGenerationError("cannot assign a combined 16-bit register to a single 8-bit register", rvalue) - _generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, ctx.scope) - elif isinstance(rvalue, Dereference): - print("warning: {}: using indirect/dereferece is very costly".format(rvalue.sourceref)) - if rvalue.datatype != DataType.BYTE: - raise CodeGenerationError("aug. assignment value must be a byte for now", rvalue) - if isinstance(rvalue.operand, (LiteralValue, SymbolName)): - if isinstance(rvalue.operand, LiteralValue): - what = to_hex(rvalue.operand.value) - else: - symdef = rvalue.my_scope().lookup(rvalue.operand.name) - if isinstance(symdef, VarDef) and symdef.vartype == VarType.MEMORY: - what = to_hex(symdef.value.value) # type: ignore - else: - what = rvalue.operand.name - out("\vpha\n\vtya\n\vpha") # save A, Y on stack - out("\vlda " + what) - out("\vsta il65_lib.SCRATCH_ZPWORD1") - out("\vlda {:s}+1".format(what)) - out("\vsta il65_lib.SCRATCH_ZPWORD1+1") - out("\vldy #0") - out("\vlda (il65_lib.SCRATCH_ZPWORD1), y") - a_reg = Register(name="A", sourceref=stmt.sourceref) # type: ignore - if 'A' in lvalue.name: - raise CodeGenerationError("can't yet use register A in this aug assign lhs", lvalue.sourceref) # @todo - _generate_aug_reg_reg(out, lvalue, stmt.operator, a_reg, ctx.scope) - if lvalue.name in REGISTER_BYTES: - out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower())) - else: - out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name[0].lower())) - out("\vst{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower())) - out("\vpla\n\vtay\n\vpla") # restore A, Y from stack - if lvalue.name in REGISTER_BYTES: - out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower())) - else: - out("\vld{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[0].lower())) - out("\vld{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower())) - elif isinstance(rvalue.operand, Register): - assert rvalue.operand.datatype == DataType.WORD - reg = rvalue.operand.name - out("\vst{:s} il65_lib.SCRATCH_ZPWORD1".format(reg[0].lower())) - out("\vst{:s} il65_lib.SCRATCH_ZPWORD1+1".format(reg[1].lower())) - out("\vpha\n\vtya\n\vpha") # save A, Y on stack - out("\vldy #0") - out("\vlda (il65_lib.SCRATCH_ZPWORD1), y") - a_reg = Register(name="A", sourceref=stmt.sourceref) # type: ignore - if 'A' in lvalue.name: - raise CodeGenerationError("can't yet use register A in this aug assign lhs", lvalue.sourceref) # @todo - _generate_aug_reg_reg(out, lvalue, stmt.operator, a_reg, ctx.scope) - if lvalue.name != 'X': - if lvalue.name in REGISTER_BYTES: - out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower())) - else: - out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name[0].lower())) - out("\vst{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower())) - out("\vpla\n\vtay\n\vpla") # restore A, Y from stack - if lvalue.name != 'X': - if lvalue.name in REGISTER_BYTES: - out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower())) - else: - out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name[0].lower())) - out("\vld{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower())) - else: - raise CodeGenerationError("invalid dereference operand type", rvalue) - else: - raise CodeGenerationError("invalid rvalue type", rvalue) - elif isinstance(lvalue, SymbolName): - raise NotImplementedError("symbolname augassign", lvalue) # XXX - else: - raise CodeGenerationError("aug. assignment only implemented for registers and symbols for now", lvalue, stmt.sourceref) # XXX - - -def _generate_aug_reg_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None: - if rname: - right_str = rname - else: - # an immediate value is provided in rvalue - right_str = "#" + str(rvalue) - if operator == "+=": - assert 0 <= rvalue <= 255, "+= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo - if rvalue > 0: - _gen_aug_plus_reg_int(lvalue, out, right_str, scope) - elif operator == "-=": - assert 0 <= rvalue <= 255, "-= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo - if rvalue > 0: - _gen_aug_minus_reg_int(lvalue, out, right_str, scope) - elif operator == "&=": - assert 0 <= rvalue <= 255, "&= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo - if rvalue == 0: - # output '=0' - assignment = Assignment(sourceref=lvalue.sourceref) # type: ignore - assignment.nodes.append(lvalue) - assignment.nodes.append(LiteralValue(value=0, sourceref=lvalue.sourceref)) # type: ignore - ctx = Context(out=out, stmt=assignment, scope=scope, floats_enabled=False) # type: ignore - generate_assignment(ctx) - else: - _gen_aug_and_reg_int(lvalue, out, right_str, scope) - elif operator == "|=": - assert 0 <= rvalue <= 255, "|= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo - if rvalue > 0: - _gen_aug_or_reg_int(lvalue, out, right_str, scope) - elif operator == "^=": - assert 0 <= rvalue <= 255, "^= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo - if rvalue > 0: - _gen_aug_xor_reg_int(lvalue, out, right_str, scope) - elif operator == ">>=": - _gen_aug_shiftright_reg_int(lvalue, out, rname, rvalue, scope) - elif operator == "<<=": - _gen_aug_shiftleft_reg_int(lvalue, out, rname, rvalue, scope) - else: - raise ValueError("invalid operator: " + operator, str(lvalue.sourceref)) # @todo implement more operators such as *=, /= - - -def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue: Register, scope: Scope) -> None: - if operator == "+=": - _gen_aug_plus_reg_reg(lvalue, out, rvalue, scope) - elif operator == "-=": - _gen_aug_minus_reg_reg(lvalue, out, rvalue, scope) - elif operator == "&=": - _gen_aug_and_reg_reg(lvalue, out, rvalue, scope) - elif operator == "|=": - _gen_aug_or_reg_reg(lvalue, out, rvalue, scope) - elif operator == "^=": - _gen_aug_xor_reg_reg(lvalue, out, rvalue, scope) - elif operator == ">>=": - _gen_aug_shiftright_reg_reg(lvalue, out, rvalue, scope) - elif operator == "<<=": - _gen_aug_shiftleft_reg_reg(lvalue, out, rvalue, scope) - else: - raise ValueError("invalid operator: " + operator, str(lvalue.sourceref)) # @todo implement more operators such as *=, /= - - -def _gen_aug_shiftleft_reg_int(lvalue: Register, out: Callable, rname: str, rvalue: int, scope: Scope) -> None: - if rname: - assert lvalue.name in REGISTER_BYTES, "only single registers for now" # @todo <<=.word - if lvalue.name == "A": - preserve_regs = {'X'} - elif lvalue.name == "X": - preserve_regs = {'A'} - out("\vtxa") - elif lvalue.name == "Y": - preserve_regs = {'A', 'X'} - out("\vtya") - with preserving_registers(preserve_regs, scope, out): - out("\vldx " + rname) - out("\vjsr il65_lib.asl_A_by_X") - # put A back into target register - if lvalue.name == "X": - out("\vtax") - elif lvalue.name == "Y": - out("\vtay") - else: - def shifts_A(times: int) -> None: - if times >= 8: - out("\vlda #0") - else: - for _ in range(min(8, times)): - out("\vasl a") - - if lvalue.name == "A": - shifts_A(rvalue) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - shifts_A(rvalue) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - shifts_A(rvalue) - out("\vtay") - else: - raise CodeGenerationError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word - - -def _gen_aug_shiftright_reg_int(lvalue: Register, out: Callable, rname: str, rvalue: int, scope: Scope) -> None: - if rname: - assert lvalue.name in REGISTER_BYTES, "only single registers for now" # @todo >>=.word - if lvalue.name == "A": - preserve_regs = {'X'} - elif lvalue.name == "X": - preserve_regs = {'A'} - out("\vtxa") - elif lvalue.name == "Y": - preserve_regs = {'A', 'X'} - out("\vtya") - with preserving_registers(preserve_regs, scope, out): - out("\vldx " + rname) - out("\vjsr il65_lib.lsr_A_by_X") - # put A back into target register - if lvalue.name == "X": - out("\vtax") - if lvalue.name == "Y": - out("\vtay") - else: - def shifts_A(times: int) -> None: - if times >= 8: - out("\vlda #0") - else: - for _ in range(min(8, times)): - out("\vlsr a") - - if lvalue.name == "A": - shifts_A(rvalue) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - shifts_A(rvalue) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - shifts_A(rvalue) - out("\vtay") - else: - raise CodeGenerationError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word - - -def _gen_aug_xor_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None: - if lvalue.name == "A": - out("\veor " + right_str) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\veor " + right_str) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\veor " + right_str) - out("\vtay") - else: - raise CodeGenerationError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word - - -def _gen_aug_or_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None: - if lvalue.name == "A": - out("\vora " + right_str) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vora " + right_str) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vora " + right_str) - out("\vtay") - else: - raise CodeGenerationError("unsupported register for aug assign", str(lvalue)) # @todo |=.word - - -def _gen_aug_and_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None: - if lvalue.name == "A": - out("\vand " + right_str) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vand " + right_str) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vand " + right_str) - out("\vtay") - else: - raise CodeGenerationError("unsupported register for aug assign", str(lvalue)) # @todo &=.word - - -def _gen_aug_minus_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None: - if lvalue.name == "A": - out("\vsec") - out("\vsbc " + right_str) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vsec") - out("\vsbc " + right_str) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vsec") - out("\vsbc " + right_str) - out("\vtay") - elif lvalue.name == "AX": - out("\vsec") - out("\vsbc " + right_str) - out("\vbcs +") - out("\vdex") - out("+") - elif lvalue.name == "AY": - out("\vsec") - out("\vsbc " + right_str) - out("\vbcs +") - out("\vdey") - out("+") - elif lvalue.name == "XY": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vsec") - out("\vsbc " + right_str) - out("\vtax") - out("\vbcs +") - out("\vdey") - out("+") - else: - raise ValueError("invalid register", str(lvalue)) - - -def _gen_aug_plus_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None: - if lvalue.name == "A": - out("\vclc") - out("\vadc " + right_str) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vclc") - out("\vadc " + right_str) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vclc") - out("\vadc " + right_str) - out("\vtay") - elif lvalue.name == "AX": - out("\vclc") - out("\vadc " + right_str) - out("\vbcc +") - out("\vinx") - out("+") - elif lvalue.name == "AY": - out("\vclc") - out("\vadc " + right_str) - out("\vbcc +") - out("\viny") - out("+") - elif lvalue.name == "XY": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vclc") - out("\vadc " + right_str) - out("\vtax") - out("\vbcc +") - out("\viny") - out("+") - else: - raise ValueError("invalid register", str(lvalue)) - - -def _gen_aug_shiftleft_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None: - if rvalue.name not in REGISTER_BYTES: - raise CodeGenerationError("unsupported rvalue register for aug assign", str(rvalue)) # @todo <<=.word - if lvalue.name == "A": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - out("\vasl " + to_hex(Zeropage.SCRATCH_B1)) - elif lvalue.name == "X": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vasl " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - elif lvalue.name == "Y": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vasl " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtay") - else: - raise CodeGenerationError("unsupported lvalue register for aug assign", str(lvalue)) # @todo <<=.word - - -def _gen_aug_shiftright_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None: - if rvalue.name not in REGISTER_BYTES: - raise CodeGenerationError("unsupported rvalue register for aug assign", str(rvalue)) # @todo >>=.word - if lvalue.name == "A": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - out("\vlsr " + to_hex(Zeropage.SCRATCH_B1)) - elif lvalue.name == "X": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vlsr " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - elif lvalue.name == "Y": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vlsr " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtay") - else: - raise CodeGenerationError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word - - -def _gen_aug_xor_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None: - if rvalue.name not in REGISTER_BYTES: - raise CodeGenerationError("unsupported rvalue register for aug assign", str(rvalue)) # @todo ^=.word - if lvalue.name == "A": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - out("\veor " + to_hex(Zeropage.SCRATCH_B1)) - elif lvalue.name == "X": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\veor " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - elif lvalue.name == "Y": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\veor " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtay") - else: - raise CodeGenerationError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word - - -def _gen_aug_or_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None: - if rvalue.name not in REGISTER_BYTES: - raise CodeGenerationError("unsupported rvalue register for aug assign", str(rvalue)) # @todo |=.word - if lvalue.name == "A": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - out("\vora " + to_hex(Zeropage.SCRATCH_B1)) - elif lvalue.name == "X": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vora " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - elif lvalue.name == "Y": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vora " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtay") - else: - raise CodeGenerationError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word - - -def _gen_aug_and_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None: - if rvalue.name not in REGISTER_BYTES: - raise CodeGenerationError("unsupported rvalue register for aug assign", str(rvalue)) # @todo &=.word - if lvalue.name == "A": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - out("\vand " + to_hex(Zeropage.SCRATCH_B1)) - elif lvalue.name == "X": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vand " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - elif lvalue.name == "Y": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vand " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtay") - else: - raise CodeGenerationError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word - - -def _gen_aug_minus_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None: - if rvalue.name not in REGISTER_BYTES: - raise CodeGenerationError("unsupported rvalue register for aug assign", str(rvalue)) # @todo -=.word - if lvalue.name == "A": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - out("\vsec") - out("\vsbc " + to_hex(Zeropage.SCRATCH_B1)) - elif lvalue.name == "X": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vsec") - out("\vsbc " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - elif lvalue.name == "Y": - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vsec") - out("\vsbc " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtay") - else: - raise CodeGenerationError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word - - -def _gen_aug_plus_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None: - if rvalue.name not in REGISTER_BYTES: - raise CodeGenerationError("unsupported rvalue register for aug assign", str(rvalue)) # @todo +=.word - out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1))) - if lvalue.name == "A": - out("\vclc") - out("\vadc " + to_hex(Zeropage.SCRATCH_B1)) - elif lvalue.name == "X": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vclc") - out("\vadc " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - elif lvalue.name == "Y": - with preserving_registers({'A'}, scope, out): - out("\vtya") - out("\vclc") - out("\vadc " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtay") - elif lvalue.name == "AX": - out("\vclc") - out("\vadc " + to_hex(Zeropage.SCRATCH_B1)) - out("\vbcc +") - out("\vinx") - out("+") - elif lvalue.name == "AY": - out("\vclc") - out("\vadc " + to_hex(Zeropage.SCRATCH_B1)) - out("\vbcc +") - out("\viny") - out("+") - elif lvalue.name == "XY": - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vclc") - out("\vadc " + to_hex(Zeropage.SCRATCH_B1)) - out("\vtax") - out("\vbcc +") - out("\viny") - out("+") - else: - raise ValueError("invalid register", str(lvalue)) diff --git a/python-prototype/il65/codegen/mos6502/calls.py b/python-prototype/il65/codegen/mos6502/calls.py deleted file mode 100644 index 220f90e28..000000000 --- a/python-prototype/il65/codegen/mos6502/calls.py +++ /dev/null @@ -1,150 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the code generator for gotos and subroutine calls. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -from . import Context -from ..shared import CodeGenerationError, to_hex -from ...plyparse import Goto, SubCall, LiteralValue, SymbolName, Dereference - - -def generate_goto(ctx: Context) -> None: - stmt = ctx.stmt - assert isinstance(stmt, Goto) - ctx.out("\v\t\t\t; " + ctx.stmt.lineref) - if stmt.condition: - if stmt.if_stmt: - _gen_goto_cond(ctx, stmt, "true") - else: - _gen_goto_cond(ctx, stmt, stmt.if_cond) - else: - if stmt.if_stmt: - _gen_goto_special_if(ctx, stmt) - else: - _gen_goto_unconditional(ctx, stmt) - - -def _gen_goto_special_if(ctx: Context, stmt: Goto) -> None: - # a special if, but no conditional expression - if isinstance(stmt.target, Dereference): - # dereference is symbol, literal, or register (pair) - if isinstance(stmt.target.operand, LiteralValue): - targetstr = to_hex(stmt.target.operand.value) - elif isinstance(stmt.target.operand, SymbolName): - targetstr = stmt.target.operand.name - else: - # register pair - ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1".format(stmt.target.operand.name[0])) - ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1+1".format(stmt.target.operand.name[1])) - targetstr = "il65_lib.SCRATCH_ZPWORD1" - if stmt.if_cond == "true": - ctx.out("\vbeq +") - ctx.out("\vjmp ({:s})".format(targetstr)) - ctx.out("+") - elif stmt.if_cond in ("not", "zero"): - ctx.out("\vbne +") - ctx.out("\vjmp ({:s})".format(targetstr)) - ctx.out("+") - elif stmt.if_cond in ("cc", "cs", "vc", "vs", "eq", "ne"): - if stmt.if_cond == "cc": - ctx.out("\vbcs +") - elif stmt.if_cond == "cs": - ctx.out("\vbcc +") - elif stmt.if_cond == "vc": - ctx.out("\vbvs +") - elif stmt.if_cond == "vs": - ctx.out("\vbvc +") - elif stmt.if_cond == "eq": - ctx.out("\vbne +") - elif stmt.if_cond == "ne": - ctx.out("\vbeq +") - ctx.out("\vjmp ({:s})".format(targetstr)) - ctx.out("+") - elif stmt.if_cond == "lt": - ctx.out("\vbcs +") - ctx.out("\vjmp ({:s})".format(targetstr)) - ctx.out("+") - elif stmt.if_cond == "gt": - ctx.out("\vbcc +") - ctx.out("\vbeq +") - ctx.out("\vjmp ({:s})".format(targetstr)) - ctx.out("+") - elif stmt.if_cond == "ge": - ctx.out("\vbcc +") - ctx.out("\vjmp ({:s})".format(targetstr)) - ctx.out("+") - elif stmt.if_cond == "le": - ctx.out("\vbeq +") - ctx.out("\vbcs ++") - ctx.out("+\t\tjmp ({:s})".format(targetstr)) - ctx.out("+") - else: - raise CodeGenerationError("invalid if status " + stmt.if_cond) - else: - if isinstance(stmt.target, LiteralValue) and type(stmt.target.value) is int: - targetstr = to_hex(stmt.target.value) - elif isinstance(stmt.target, SymbolName): - targetstr = stmt.target.name - else: - raise CodeGenerationError("invalid goto target type", stmt) - if stmt.if_cond == "true": - ctx.out("\vbne " + targetstr) - elif stmt.if_cond in ("not", "zero"): - ctx.out("\vbeq " + targetstr) - elif stmt.if_cond in ("cc", "cs", "vc", "vs", "eq", "ne"): - ctx.out("\vb{:s} {:s}".format(stmt.if_cond, targetstr)) - elif stmt.if_cond == "pos": - ctx.out("\vbpl " + targetstr) - elif stmt.if_cond == "neg": - ctx.out("\vbmi " + targetstr) - elif stmt.if_cond == "lt": - ctx.out("\vbcc " + targetstr) - elif stmt.if_cond == "gt": - ctx.out("\vbeq +") - ctx.out("\vbcs " + targetstr) - ctx.out("+") - elif stmt.if_cond == "ge": - ctx.out("\vbcs " + targetstr) - elif stmt.if_cond == "le": - ctx.out("\vbcc " + targetstr) - ctx.out("\vbeq " + targetstr) - else: - raise CodeGenerationError("invalid if status " + stmt.if_cond) - - -def _gen_goto_unconditional(ctx: Context, stmt: Goto) -> None: - # unconditional jump to - if isinstance(stmt.target, LiteralValue) and type(stmt.target.value) is int: - ctx.out("\vjmp " + to_hex(stmt.target.value)) - elif isinstance(stmt.target, SymbolName): - ctx.out("\vjmp " + stmt.target.name) - elif isinstance(stmt.target, Dereference): - # dereference is symbol, literal, or register (pair) - if isinstance(stmt.target.operand, LiteralValue): - ctx.out("\vjmp ({:s})".format(to_hex(stmt.target.operand.value))) - elif isinstance(stmt.target.operand, SymbolName): - ctx.out("\vjmp ({:s})".format(stmt.target.operand.name)) - else: - # register pair - ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1".format(stmt.target.operand.name[0])) - ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1+1".format(stmt.target.operand.name[1])) - ctx.out("\vjmp (il65_lib.SCRATCH_ZPWORD1)") - else: - raise CodeGenerationError("invalid goto target type", stmt) - - -def _gen_goto_cond(ctx: Context, stmt: Goto, if_cond: str) -> None: - if isinstance(stmt.condition, LiteralValue): - pass # @todo if WITH conditional expression - else: - raise CodeGenerationError("no support for evaluating conditional expression yet", stmt) # @todo - - -def generate_subcall(ctx: Context) -> None: - stmt = ctx.stmt - assert isinstance(stmt, SubCall) - ctx.out("\v\t\t\t; " + ctx.stmt.lineref) - ctx.out("\v; @todo sub call: {}".format(ctx.stmt.target)) - # @todo subcall diff --git a/python-prototype/il65/codegen/mos6502/generate.py b/python-prototype/il65/codegen/mos6502/generate.py deleted file mode 100644 index fd00b8ae0..000000000 --- a/python-prototype/il65/codegen/mos6502/generate.py +++ /dev/null @@ -1,221 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the assembly code generator (from the parse tree) - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import os -import datetime -from typing import TextIO, Callable, no_type_check -from . import Context -from .variables import generate_block_init, generate_block_vars -from .assignment import generate_assignment, generate_aug_assignment -from .calls import generate_goto, generate_subcall -from .incrdecr import generate_incrdecr -from ..shared import CodeGenerationError, to_hex, to_mflpt5, sanitycheck -from ...plyparse import (Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, ZpOptions, - InlineAssembly, Return, Register, Goto, SubCall, Assignment, AugAssignment, IncrDecr) - - -class Output: - def __init__(self, stream: TextIO) -> None: - self.stream = stream - - def __call__(self, text, *args, **vargs): - # replace '\v' (vertical tab) char by the actual line indent (2 tabs) and write to the stringIo - print(text.replace("\v", "\t\t"), *args, file=self.stream, **vargs) - - -class AssemblyGenerator: - def __init__(self, module: Module, enable_floats: bool) -> None: - self.module = module - self.floats_enabled = enable_floats - self.cur_block = None - self.output = None # type: Output - - def generate(self, filename: str) -> None: - with open(filename, "wt") as stream: - output = Output(stream) - try: - self._generate(output) - except Exception as x: - output(".error \"****** ABORTED DUE TO ERROR:", x, "\"\n") - raise - - def _generate(self, out: Callable) -> None: - sanitycheck(self.module) - self.header(out) - self.blocks(out) - out("\t.end") - - def header(self, out: Callable) -> None: - out("; 6502 code generated by il65.py - codename 'Sick'") - out("; source file:", self.module.sourceref.file) - out("; compiled on:", datetime.datetime.now()) - out("; output options:", self.module.format, self.module.zp_options) - out("; assembler syntax is for the 64tasm cross-assembler") - out("\n.cpu '6502'\n.enc 'none'\n") - assert self.module.address is not None - if self.module.format in (ProgramFormat.PRG, ProgramFormat.BASIC): - if self.module.format == ProgramFormat.BASIC: - if self.module.address != 0x0801: - raise CodeGenerationError("BASIC output mode must have load address $0801") - out("; ---- basic program with sys call ----") - out("* = " + to_hex(self.module.address)) - year = datetime.datetime.now().year - out("\v.word (+), {:d}".format(year)) - out("\v.null $9e, format(' %d ', _il65_entrypoint), $3a, $8f, ' il65 by idj'") - out("+\v.word 0") - out("_il65_entrypoint\v; assembly code starts here\n") - else: - out("; ---- program without sys call ----") - out("* = " + to_hex(self.module.address) + "\n") - elif self.module.format == ProgramFormat.RAW: - out("; ---- raw assembler program ----") - out("* = " + to_hex(self.module.address) + "\n") - # call the block init methods and jump to the user's main.start entrypoint - if self.module.zp_options == ZpOptions.CLOBBER_RESTORE: - out("\vjsr _il65_save_zeropage") - out("\v; initialize all blocks (reset vars)") - if self.module.zeropage(): - out("\vjsr ZP._il65_init_block") - for block in self.module.nodes: - if isinstance(block, Block) and block.name != "ZP": - out("\vjsr {}._il65_init_block".format(block.name)) - out("\v; call user code") - if self.module.zp_options == ZpOptions.CLOBBER_RESTORE: - out("\vjsr {:s}.start".format(self.module.main().label)) - out("\vcld") - out("\vjmp _il65_restore_zeropage\n") - # include the assembly code for the save/restore zeropage routines - zprestorefile = os.path.join(os.path.split(__file__)[0], "../lib", "restorezp.asm") - with open(zprestorefile, "rU") as f: - for line in f.readlines(): - out(line.rstrip("\n")) - else: - out("\vjmp {:s}.start".format(self.module.main().label)) - out("") - - @no_type_check - def blocks(self, out: Callable) -> None: - zpblock = self.module.zeropage() - if zpblock: - # if there's a Zeropage block, it always goes first - self.cur_block = zpblock # type: ignore - out("\n; ---- ZeroPage block: '{:s}' ----".format(zpblock.name)) - out("; file: '{:s}' src l. {:d}\n".format(zpblock.sourceref.file, zpblock.sourceref.line)) - out("{:s}\t.proc\n".format(zpblock.label)) - generate_block_init(out, zpblock) - generate_block_vars(out, zpblock, True) - # there's no code in the ZeroPage block. - out("\v.pend\n") - for block in sorted(self.module.all_nodes(Block), key=lambda b: b.address or 0): - ctx = Context(out=out, stmt=None, scope=block.scope, floats_enabled=self.floats_enabled) - if block.name == "ZP": - continue # already processed - self.cur_block = block - out("\n; ---- block: '{:s}' ----".format(block.name)) - out("; file: '{:s}' src l. {:d}\n".format(block.sourceref.file, block.sourceref.line)) - if block.address: - out(".cerror * > ${0:04x}, 'block address overlaps by ', *-${0:04x},' bytes'".format(block.address)) - out("* = ${:04x}".format(block.address)) - out("{:s}\t.proc\n".format(block.label)) - generate_block_init(out, block) - generate_block_vars(out, block) - subroutines = list(sub for sub in block.all_nodes(Subroutine) if sub.address is not None) - if subroutines: - # these are (external) subroutines that are defined by address instead of a scope/code - out("; external subroutines") - for subdef in subroutines: - assert subdef.scope is None - out("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address))) - out("; end external subroutines\n") - for stmt in block.scope.nodes: - if isinstance(stmt, (VarDef, Subroutine)): - continue # should have been handled already or will be later - ctx.stmt = stmt - self.generate_statement(ctx) - if block.name == "main" and isinstance(stmt, Label) and stmt.name == "start": - # make sure the main.start routine clears the decimal and carry flags as first steps - out("\vcld\n\vclc\n\vclv") - subroutines = list(sub for sub in block.all_nodes(Subroutine) if sub.address is None) - if subroutines: - # these are subroutines that are defined by a scope/code - out("; -- block subroutines") - for subdef in subroutines: - assert subdef.scope is not None - out("{:s}\v; src l. {:d}".format(subdef.name, subdef.sourceref.line)) - params = ", ".join("{:s} -> {:s}".format(name or "", registers) for name, registers in subdef.param_spec) - returns = ",".join(sorted(register for register in subdef.result_spec if register[-1] != '?')) - clobbers = ",".join(sorted(register for register in subdef.result_spec if register[-1] == '?')) - out("\v; params: {}\n\v; returns: {} clobbers: {}".format(params or "-", returns or "-", clobbers or "-")) - cur_block = self.cur_block - self.cur_block = subdef.scope - for ctx.stmt in subdef.scope.nodes: - self.generate_statement(ctx) - self.cur_block = cur_block - out("") - out("; -- end block subroutines") - if block.scope.float_const_values: - if not self.floats_enabled: - raise CodeGenerationError("floating point numbers not enabled via option") - # generate additional float constants that are used in floating point expressions - out("\n; -- float constants") - for name, value in block.scope.float_const_values.items(): - out("{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(name, *to_mflpt5(value), value)) - out("\n\v.pend\n") - - @no_type_check - def generate_statement(self, ctx: Context) -> None: - stmt = ctx.stmt - if isinstance(stmt, Label): - ctx.out("\n{:s}\v\t\t; {:s}".format(stmt.name, stmt.lineref)) - elif isinstance(stmt, Return): - if stmt.value_A: - reg = Register(name="A", sourceref=stmt.sourceref) - assignment = Assignment(sourceref=stmt.sourceref) - assignment.nodes.append(reg) - assignment.nodes.append(stmt.value_A) - assignment.mark_lhs() - ctx.stmt = assignment - generate_assignment(ctx) - if stmt.value_X: - reg = Register(name="X", sourceref=stmt.sourceref) - assignment = Assignment(sourceref=stmt.sourceref) - assignment.nodes.append(reg) - assignment.nodes.append(stmt.value_X) - assignment.mark_lhs() - ctx.stmt = assignment - generate_assignment(ctx) - if stmt.value_Y: - reg = Register(name="Y", sourceref=stmt.sourceref) - assignment = Assignment(sourceref=stmt.sourceref) - assignment.nodes.append(reg) - assignment.nodes.append(stmt.value_Y) - assignment.mark_lhs() - ctx.stmt = assignment - generate_assignment(ctx) - ctx.out("\vrts") - elif isinstance(stmt, InlineAssembly): - ctx.out("\n\v; inline asm, " + stmt.lineref) - ctx.out(stmt.assembly) - ctx.out("\v; end inline asm, " + stmt.lineref + "\n") - elif isinstance(stmt, IncrDecr): - generate_incrdecr(ctx) - elif isinstance(stmt, Goto): - generate_goto(ctx) - elif isinstance(stmt, SubCall): - generate_subcall(ctx) - elif isinstance(stmt, Assignment): - generate_assignment(ctx) - elif isinstance(stmt, AugAssignment): - generate_aug_assignment(ctx) - elif isinstance(stmt, Directive): - if stmt.name == "breakpoint": - # put a marker in the source so that we can generate a list of breakpoints later - # this label is later extracted from the label dump file to turn it into a breakpoint instruction - ctx.out("_il65_breakpoint_{:d}".format(id(stmt))) - # other directives are ignored here - else: - raise NotImplementedError("statement", stmt) diff --git a/python-prototype/il65/codegen/mos6502/incrdecr.py b/python-prototype/il65/codegen/mos6502/incrdecr.py deleted file mode 100644 index 6b721e506..000000000 --- a/python-prototype/il65/codegen/mos6502/incrdecr.py +++ /dev/null @@ -1,267 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the code generator for the in-place incr and decr instructions. -Incrementing or decrementing variables by a small byte value 0..255 -is quite frequent and this generates assembly code tweaked for this case. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -from . import Context, preserving_registers -from ..shared import CodeGenerationError, to_hex -from ...plyparse import VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue, scoped_name -from ...datatypes import VarType, DataType, REGISTER_BYTES - - -def generate_incrdecr(ctx: Context) -> None: - out = ctx.out - stmt = ctx.stmt - scope = ctx.scope - assert isinstance(stmt, IncrDecr) - if stmt.howmuch == 0: - return - if not 0 <= stmt.howmuch <= 255: - raise CodeGenerationError("incr/decr value must be 0..255 - other values should have been converted into an AugAssignment") - target = stmt.target # one of Register/SymbolName/Dereference, or a VarDef - if isinstance(target, SymbolName): - symdef = scope.lookup(target.name) - if isinstance(symdef, VarDef): - target = symdef # type: ignore - else: - raise CodeGenerationError("cannot incr/decr this", symdef) - howmuch_str = str(stmt.howmuch) - - if isinstance(target, Register): - reg = target.name - # note: these operations below are all checked to be ok - if stmt.operator == "++": - if reg == 'A': - # a += 1..255 - out("\vclc") - out("\vadc #" + howmuch_str) - elif reg in REGISTER_BYTES: - if stmt.howmuch == 1: - # x/y += 1 - out("\vin{:s}".format(reg.lower())) - else: - # x/y += 2..255 - with preserving_registers({'A'}, scope, out): - out("\vt{:s}a".format(reg.lower())) - out("\vclc") - out("\vadc #" + howmuch_str) - out("\vta{:s}".format(reg.lower())) - elif reg == "AX": - # AX += 1..255 - out("\vclc") - out("\vadc #" + howmuch_str) - out("\vbcc +") - out("\vinx") - out("+") - elif reg == "AY": - # AY += 1..255 - out("\vclc") - out("\vadc # " + howmuch_str) - out("\vbcc +") - out("\viny") - out("+") - elif reg == "XY": - if stmt.howmuch == 1: - # XY += 1 - out("\vinx") - out("\vbne +") - out("\viny") - out("+") - else: - # XY += 2..255 - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vclc") - out("\vadc #" + howmuch_str) - out("\vtax") - out("\vbcc +") - out("\viny") - out("+") - else: - raise CodeGenerationError("invalid incr register: " + reg) - else: - if reg == 'A': - # a -= 1..255 - out("\vsec") - out("\vsbc #" + howmuch_str) - elif reg in REGISTER_BYTES: - if stmt.howmuch == 1: - # x/y -= 1 - out("\vde{:s}".format(reg.lower())) - else: - # x/y -= 2..255 - with preserving_registers({'A'}, scope, out): - out("\vt{:s}a".format(reg.lower())) - out("\vsec") - out("\vsbc #" + howmuch_str) - out("\vta{:s}".format(reg.lower())) - elif reg == "AX": - # AX -= 1..255 - out("\vsec") - out("\vsbc #" + howmuch_str) - out("\vbcs +") - out("\vdex") - out("+") - elif reg == "AY": - # AY -= 1..255 - out("\vsec") - out("\vsbc #" + howmuch_str) - out("\vbcs +") - out("\vdey") - out("+") - elif reg == "XY": - if stmt.howmuch == 1: - # XY -= 1 - out("\vcpx #0") - out("\vbne +") - out("\vdey") - out("+\t\tdex") - else: - # XY -= 2..255 - with preserving_registers({'A'}, scope, out): - out("\vtxa") - out("\vsec") - out("\vsbc #" + howmuch_str) - out("\vtax") - out("\vbcs +") - out("\vdey") - out("+") - else: - raise CodeGenerationError("invalid decr register: " + reg) - - elif isinstance(target, VarDef): - if target.vartype == VarType.CONST: - raise CodeGenerationError("cannot modify a constant", target) - what_str = scoped_name(target, scope) - if target.datatype == DataType.BYTE: - if stmt.howmuch == 1: - out("\v{:s} {:s}".format("inc" if stmt.operator == "++" else "dec", what_str)) - else: - with preserving_registers({'A'}, scope, out): - out("\vlda " + what_str) - if stmt.operator == "++": - out("\vclc") - out("\vadc #" + howmuch_str) - else: - out("\vsec") - out("\vsbc #" + howmuch_str) - out("\vsta " + what_str) - elif target.datatype == DataType.WORD: - if stmt.howmuch == 1: - # mem.word +=/-= 1 - if stmt.operator == "++": - out("\vinc " + what_str) - out("\vbne +") - out("\vinc {:s}+1".format(what_str)) - out("+") - else: - with preserving_registers({'A'}, scope, out): - out("\vlda " + what_str) - out("\vbne +") - out("\vdec {:s}+1".format(what_str)) - out("+\t\tdec " + what_str) - else: - # mem.word +=/-= 2..255 - if stmt.operator == "++": - with preserving_registers({'A'}, scope, out): - out("\vclc") - out("\vlda " + what_str) - out("\vadc #" + howmuch_str) - out("\vsta " + what_str) - out("\vbcc +") - out("\vinc {:s}+1".format(what_str)) - out("+") - else: - with preserving_registers({'A'}, scope, out): - out("\vsec") - out("\vlda " + what_str) - out("\vsbc #" + howmuch_str) - out("\vsta " + what_str) - out("\vbcs +") - out("\vdec {:s}+1".format(what_str)) - out("+") - elif target.datatype == DataType.FLOAT: - if not ctx.floats_enabled: - raise CodeGenerationError("floating point numbers not enabled via option") - if stmt.howmuch == 1.0: - # special case for +/-1 - with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True): - out("\vldx #<" + what_str) - out("\vldy #>" + what_str) - if stmt.operator == "++": - out("\vjsr c64flt.float_add_one") - else: - out("\vjsr c64flt.float_sub_one") - elif stmt.howmuch != 0: - float_name = scope.define_float_constant(stmt.howmuch) - with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True): - out("\vlda #<" + float_name) - out("\vsta c64.SCRATCH_ZPWORD1") - out("\vlda #>" + float_name) - out("\vsta c64.SCRATCH_ZPWORD1+1") - out("\vldx #<" + what_str) - out("\vldy #>" + what_str) - if stmt.operator == "++": - out("\vjsr c64flt.float_add_SW1_to_XY") - else: - out("\vjsr c64flt.float_sub_SW1_from_XY") - else: - raise CodeGenerationError("cannot in/decrement memory of type " + str(target.datatype), stmt.howmuch) - - elif isinstance(target, Dereference): - if isinstance(target.operand, (LiteralValue, SymbolName)): - if isinstance(target.operand, LiteralValue): - what = to_hex(target.operand.value) - else: - symdef = target.my_scope().lookup(target.operand.name) - if isinstance(symdef, VarDef) and symdef.vartype == VarType.MEMORY: - what = to_hex(symdef.value.value) # type: ignore - else: - what = target.operand.name - if stmt.howmuch == 1: - if target.datatype == DataType.FLOAT: - if not ctx.floats_enabled: - raise CodeGenerationError("floating point numbers not enabled via option") - with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True): - out("\vldx " + what) - out("\vldy {:s}+1".format(what)) - if stmt.operator == "++": - out("\vjsr c64flt.float_add_one") - else: - out("\vjsr c64flt.float_sub_one") - else: - with preserving_registers({'A', 'Y'}, scope, out, loads_a_within=True): - out("\vlda " + what) - out("\vldy {:s}+1".format(what)) - if target.datatype == DataType.BYTE: - out("\vclc" if stmt.operator == "++" else "\vsec") - out("\vjsr il65_lib.incrdecr_deref_byte_reg_AY") - elif target.datatype == DataType.WORD: - out("\vclc" if stmt.operator == "++" else "\vsec") - out("\vjsr il65_lib.incrdecr_deref_word_reg_AY") - else: - raise CodeGenerationError("cannot inc/decrement dereferenced literal of type " + str(target.datatype), stmt) - else: - raise CodeGenerationError("can't inc/dec this by something else as 1 right now", stmt) # XXX - elif isinstance(target.operand, Register): - assert target.operand.datatype == DataType.WORD - reg = target.operand.name - if stmt.howmuch == 1: - out("\vclc" if stmt.operator == "++" else "\vsec") - if target.datatype == DataType.BYTE: - with preserving_registers({'A', 'Y'}, scope, out, loads_a_within=True): - out("\vjsr il65_lib.incrdecr_deref_byte_reg_" + reg) - else: - with preserving_registers({'A', 'Y'}, scope, out, loads_a_within=True): - out("\vjsr il65_lib.incrdecr_deref_word_reg_" + reg) - else: - raise CodeGenerationError("can't inc/dec this by something else as 1 right now", stmt) # XXX - else: - raise TypeError("invalid dereference operand type", target) - - else: - raise CodeGenerationError("cannot inc/decrement", target) # @todo support more diff --git a/python-prototype/il65/codegen/mos6502/variables.py b/python-prototype/il65/codegen/mos6502/variables.py deleted file mode 100644 index eee24443f..000000000 --- a/python-prototype/il65/codegen/mos6502/variables.py +++ /dev/null @@ -1,269 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the code generator for variable declarations and initialization. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -from collections import defaultdict -from typing import Dict, List, Callable, Any, no_type_check -from ..shared import to_hex, to_mflpt5, CodeGenerationError -from ...plyparse import Block, VarDef, LiteralValue, AddressOf -from ...datatypes import DataType, VarType, STRING_DATATYPES - - -def generate_block_init(out: Callable, block: Block) -> None: - # generate the block initializer - # @todo add a block initializer subroutine that can contain custom reset/init code? (static initializer) - # @todo will be called at program start automatically, so there's no risk of forgetting to call it manually - - def _memset(varname: str, value: int, size: int) -> None: - if size > 6: - out("\vlda #<" + varname) - out("\vsta il65_lib.SCRATCH_ZPWORD1") - out("\vlda #>" + varname) - out("\vsta il65_lib.SCRATCH_ZPWORD1+1") - out("\vlda #" + to_hex(value)) - out("\vldx #<" + to_hex(size)) - out("\vldy #>" + to_hex(size)) - out("\vjsr il65_lib.memset") - else: - out("\vlda #" + to_hex(value)) - for i in range(size): - out("\vsta {:s}+{:d}".format(varname, i)) - - def _memsetw(varname: str, value: int, size: int) -> None: - if size > 4: - out("\vlda #<" + varname) - out("\vsta il65_lib.SCRATCH_ZPWORD1") - out("\vlda #>" + varname) - out("\vsta il65_lib.SCRATCH_ZPWORD1+1") - out("\vlda #<" + to_hex(size)) - out("\vsta il65_lib.SCRATCH_ZPWORD2") - out("\vlda #>" + to_hex(size)) - out("\vsta il65_lib.SCRATCH_ZPWORD2+1") - out("\vlda #<" + to_hex(value)) - out("\vldx #>" + to_hex(value)) - out("\vjsr il65_lib.memsetw") - else: - out("\vlda #<" + to_hex(value)) - out("\vldy #>" + to_hex(value)) - for i in range(size): - out("\vsta {:s}+{:d}".format(varname, i * 2)) - out("\vsty {:s}+{:d}".format(varname, i * 2 + 1)) - - out("_il65_init_block\v; (re)set vars to initial values") - float_inits = {} - prev_value_a, prev_value_x = None, None - vars_by_datatype = defaultdict(list) # type: Dict[DataType, List[VarDef]] - for vardef in block.all_nodes(VarDef): - if vardef.vartype == VarType.VAR: # type: ignore - vars_by_datatype[vardef.datatype].append(vardef) # type: ignore - for bytevar in sorted(vars_by_datatype[DataType.BYTE], key=lambda vd: vd.value): - assert isinstance(bytevar.value, LiteralValue) and type(bytevar.value.value) is int - if bytevar.value.value != prev_value_a: - out("\vlda #${:02x}".format(bytevar.value.value)) - prev_value_a = bytevar.value.value - out("\vsta {:s}".format(bytevar.name)) - for wordvar in sorted(vars_by_datatype[DataType.WORD], key=lambda vd: vd.value): - if isinstance(wordvar.value, AddressOf): - raise CodeGenerationError("addressof is not a compile-time constant value", wordvar.sourceref) - assert isinstance(wordvar.value, LiteralValue) and type(wordvar.value.value) is int - v_hi, v_lo = divmod(wordvar.value.value, 256) - if v_hi != prev_value_a: - out("\vlda #${:02x}".format(v_hi)) - prev_value_a = v_hi - if v_lo != prev_value_x: - out("\vldx #${:02x}".format(v_lo)) - prev_value_x = v_lo - out("\vsta {:s}".format(wordvar.name)) - out("\vstx {:s}+1".format(wordvar.name)) - for floatvar in vars_by_datatype[DataType.FLOAT]: - assert isinstance(floatvar.value, LiteralValue) and type(floatvar.value.value) in (int, float) - fpbytes = to_mflpt5(floatvar.value.value) # type: ignore - float_inits[floatvar.name] = (floatvar.name, fpbytes, floatvar.value) - for arrayvar in vars_by_datatype[DataType.BYTEARRAY]: - assert isinstance(arrayvar.value, LiteralValue) and type(arrayvar.value.value) is int - _memset(arrayvar.name, arrayvar.value.value, arrayvar.size[0]) - for arrayvar in vars_by_datatype[DataType.WORDARRAY]: - assert isinstance(arrayvar.value, LiteralValue) and type(arrayvar.value.value) is int - _memsetw(arrayvar.name, arrayvar.value.value, arrayvar.size[0]) - for arrayvar in vars_by_datatype[DataType.MATRIX]: - assert isinstance(arrayvar.value, LiteralValue) and type(arrayvar.value.value) is int - _memset(arrayvar.name, arrayvar.value.value, arrayvar.size[0] * arrayvar.size[1]) - if float_inits: - out("\vldx #4") - out("-") - for varname, (vname, b, fv) in sorted(float_inits.items()): - out("\vlda _init_float_{:s},x".format(varname)) - out("\vsta {:s},x".format(vname)) - out("\vdex") - out("\vbpl -") - out("\vrts\n") - for varname, (vname, fpbytes, fpvalue) in sorted(float_inits.items()): - assert isinstance(fpvalue, LiteralValue) - out("_init_float_{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(varname, *fpbytes, fpvalue.value)) - all_string_vars = [] - for svtype in STRING_DATATYPES: - all_string_vars.extend(vars_by_datatype[svtype]) - for strvar in all_string_vars: - # string vars are considered to be a constant, and are statically initialized. - _generate_string_var(out, strvar) - out("") - - -def generate_block_vars(out: Callable, block: Block, zeropage: bool=False) -> None: - # Generate the block variable storage. - # The memory bytes of the allocated variables is set to zero (so it compresses very well), - # their actual starting values are set by the block init code. - vars_by_vartype = defaultdict(list) # type: Dict[VarType, List[VarDef]] - for vardef in block.all_nodes(VarDef): - vars_by_vartype[vardef.vartype].append(vardef) # type: ignore - out("; constants") - for vardef in vars_by_vartype.get(VarType.CONST, []): - if vardef.datatype == DataType.FLOAT: - out("\v{:s} = {}".format(vardef.name, _numeric_value_str(vardef.value))) - elif vardef.datatype in (DataType.BYTE, DataType.WORD): - assert isinstance(vardef.value.value, int) # type: ignore - out("\v{:s} = {:s}".format(vardef.name, _numeric_value_str(vardef.value, True))) - elif vardef.datatype.isstring(): - # a const string is just a string variable in the generated assembly - _generate_string_var(out, vardef) - else: - raise CodeGenerationError("invalid const type", vardef) - out("; memory mapped variables") - for vardef in vars_by_vartype.get(VarType.MEMORY, []): - # create a definition for variables at a specific place in memory (memory-mapped) - assert isinstance(vardef.value.value, int) # type: ignore - if vardef.datatype.isnumeric(): - assert vardef.size == [1] - out("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value.value), vardef.datatype.name.lower())) # type: ignore - elif vardef.datatype == DataType.BYTEARRAY: - assert len(vardef.size) == 1 - out("\v{:s} = {:s}\t; array of {:d} bytes".format(vardef.name, to_hex(vardef.value.value), vardef.size[0])) # type: ignore - elif vardef.datatype == DataType.WORDARRAY: - assert len(vardef.size) == 1 - out("\v{:s} = {:s}\t; array of {:d} words".format(vardef.name, to_hex(vardef.value.value), vardef.size[0])) # type: ignore - elif vardef.datatype == DataType.MATRIX: - assert len(vardef.size) in (2, 3) - if len(vardef.size) == 2: - comment = "matrix of {:d} by {:d} = {:d} bytes".format(vardef.size[0], vardef.size[1], vardef.size[0]*vardef.size[1]) - elif len(vardef.size) == 3: - comment = "matrix of {:d} by {:d}, interleave {:d}".format(vardef.size[0], vardef.size[1], vardef.size[2]) - else: - raise CodeGenerationError("matrix size must be 2 or 3 numbers") - out("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value.value), comment)) # type: ignore - else: - raise CodeGenerationError("invalid var type") - out("; normal variables - initial values will be set by init code") - if zeropage: - # zeropage uses the zp_address we've allocated, instead of allocating memory here - for vardef in vars_by_vartype.get(VarType.VAR, []): - assert vardef.zp_address is not None - if vardef.datatype.isstring(): - raise CodeGenerationError("cannot put strings in the zeropage", vardef.sourceref) - if vardef.datatype.isarray(): - size_str = "size " + str(vardef.size) - else: - size_str = "" - out("\v{:s} = {:s}\t; {:s} {:s}".format(vardef.name, to_hex(vardef.zp_address), vardef.datatype.name.lower(), size_str)) - else: - # create definitions for the variables that takes up empty space and will be initialized at startup - string_vars = [] - for vardef in vars_by_vartype.get(VarType.VAR, []): - if vardef.datatype.isnumeric(): - assert vardef.size == [1] - assert isinstance(vardef.value, LiteralValue) - if vardef.datatype == DataType.BYTE: - out("{:s}\v.byte ?\t; {:s}".format(vardef.name, to_hex(vardef.value.value))) - elif vardef.datatype == DataType.WORD: - out("{:s}\v.word ?\t; {:s}".format(vardef.name, to_hex(vardef.value.value))) - elif vardef.datatype == DataType.FLOAT: - out("{:s}\v.fill 5\t\t; float {}".format(vardef.name, vardef.value.value)) - else: - raise CodeGenerationError("weird datatype") - elif vardef.datatype in (DataType.BYTEARRAY, DataType.WORDARRAY): - assert len(vardef.size) == 1 - if vardef.datatype == DataType.BYTEARRAY: - out("{:s}\v.fill {:d}\t\t; bytearray".format(vardef.name, vardef.size[0])) - elif vardef.datatype == DataType.WORDARRAY: - out("{:s}\v.fill {:d}*2\t\t; wordarray".format(vardef.name, vardef.size[0])) - else: - raise CodeGenerationError("invalid datatype", vardef.datatype) - elif vardef.datatype == DataType.MATRIX: - assert len(vardef.size) == 2 - out("{:s}\v.fill {:d}\t\t; matrix {:d}*{:d} bytes" - .format(vardef.name, vardef.size[0] * vardef.size[1], vardef.size[0], vardef.size[1])) - elif vardef.datatype.isstring(): - string_vars.append(vardef) - else: - raise CodeGenerationError("unknown variable type " + str(vardef.datatype)) - # string vars are considered to be a constant, and are not re-initialized. - out("") - - -@no_type_check -def _generate_string_var(out: Callable, vardef: VarDef) -> None: - if vardef.datatype == DataType.STRING: - # 0-terminated string - out("{:s}\n\v.null {:s}".format(vardef.name, _format_string(str(vardef.value.value)))) - elif vardef.datatype == DataType.STRING_P: - # pascal string - out("{:s}\n\v.strp {:s}".format(vardef.name, _format_string(str(vardef.value.value)))) - elif vardef.datatype == DataType.STRING_S: - # 0-terminated string in screencode encoding - out(".enc 'screen'") - out("{:s}\n\v.null {:s}".format(vardef.name, _format_string(str(vardef.value.value), True))) - out(".enc 'none'") - elif vardef.datatype == DataType.STRING_PS: - # 0-terminated pascal string in screencode encoding - out(".enc 'screen'") - out("{:s}n\v.strp {:s}".format(vardef.name, _format_string(str(vardef.value.value), True))) - out(".enc 'none'") - - -def _format_string(value: str, screencodes: bool = False) -> str: - if len(value) == 1 and screencodes: - if value[0].isprintable() and ord(value[0]) < 128: - return "'{:s}'".format(value[0]) - else: - return str(ord(value[0])) - result = '"' - for char in value: - if char in "{}": - result += '", {:d}, "'.format(ord(char)) - elif char.isprintable() and ord(char) < 128: - result += char - else: - if screencodes: - result += '", {:d}, "'.format(ord(char)) - else: - if char == '\f': - result += "{clear}" - elif char == '\b': - result += "{delete}" - elif char == '\n': - result += "{cr}" - elif char == '\r': - result += "{down}" - elif char == '\t': - result += "{tab}" - else: - result += '", {:d}, "'.format(ord(char)) - return result + '"' - - -def _numeric_value_str(value: Any, as_hex: bool=False) -> str: - if isinstance(value, LiteralValue): - value = value.value - if isinstance(value, bool): - return "1" if value else "0" - if type(value) is int: - if as_hex: - return to_hex(value) - return str(value) - if isinstance(value, (int, float)): - if as_hex: - raise TypeError("cannot output float as hex") - return str(value) - raise TypeError("no numeric representation possible", value) diff --git a/python-prototype/il65/codegen/shared.py b/python-prototype/il65/codegen/shared.py deleted file mode 100644 index 28989c267..000000000 --- a/python-prototype/il65/codegen/shared.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -Shared logic for the code generators. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - - -import math -from ..datatypes import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE -from ..plyparse import Module, Label, Block, Directive, VarDef -from ..plylex import print_bold - - -class CodeGenerationError(Exception): - pass - - -def sanitycheck(module: Module) -> None: - for label in module.all_nodes(Label): - if label.name == "start" and label.my_scope().name == "main": # type: ignore - break - else: - print_bold("ERROR: program entry point is missing ('start' label in 'main' block)\n") - raise SystemExit(1) - all_blocknames = [b.name for b in module.all_nodes(Block)] # type: ignore - unique_blocknames = set(all_blocknames) - if len(all_blocknames) != len(unique_blocknames): - for name in unique_blocknames: - all_blocknames.remove(name) - raise CodeGenerationError("there are duplicate block names", all_blocknames) - zpblock = module.zeropage() - if zpblock: - # ZP block contains no code? - for stmt in zpblock.scope.nodes: - if not isinstance(stmt, (Directive, VarDef)): - raise CodeGenerationError("ZP block can only contain directive and var") - - -def to_hex(number: int) -> str: - # 0..15 -> "0".."15" - # 16..255 -> "$10".."$ff" - # 256..65536 -> "$0100".."$ffff" - assert type(number) is int - if number is None: - raise ValueError("number") - if 0 <= number < 16: - return str(number) - if 0 <= number < 0x100: - return "${:02x}".format(number) - if 0 <= number < 0x10000: - return "${:04x}".format(number) - raise OverflowError(number) - - -def to_mflpt5(number: float) -> bytearray: - # algorithm here https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a - number = float(number) - if number < FLOAT_MAX_NEGATIVE or number > FLOAT_MAX_POSITIVE: - raise OverflowError("floating point number out of 5-byte mflpt range", number) - if number == 0.0: - return bytearray([0, 0, 0, 0, 0]) - if number < 0.0: - sign = 0x80000000 - number = -number - else: - sign = 0x00000000 - mant, exp = math.frexp(number) - exp += 128 - if exp < 1: - # underflow, use zero instead - return bytearray([0, 0, 0, 0, 0]) - if exp > 255: - raise OverflowError("floating point number out of 5-byte mflpt range", number) - mant = sign | int(mant * 0x100000000) & 0x7fffffff - return bytearray([exp]) + int.to_bytes(mant, 4, "big") - - -def mflpt5_to_float(mflpt: bytearray) -> float: - # algorithm here https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a - if mflpt == bytearray([0, 0, 0, 0, 0]): - return 0.0 - exp = mflpt[0] - 128 - sign = mflpt[1] & 0x80 - number = 0x80000000 | int.from_bytes(mflpt[1:], "big") - number = float(number) * 2**exp / 0x100000000 - return -number if sign else number diff --git a/python-prototype/il65/codegen/tinyvm/__init__.py b/python-prototype/il65/codegen/tinyvm/__init__.py deleted file mode 100644 index 5f58cd296..000000000 --- a/python-prototype/il65/codegen/tinyvm/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the tinyvm stack based program generator - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - - diff --git a/python-prototype/il65/codegen/tinyvm/generate.py b/python-prototype/il65/codegen/tinyvm/generate.py deleted file mode 100644 index 824531c73..000000000 --- a/python-prototype/il65/codegen/tinyvm/generate.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the tinyvm stack based program generator. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -# @todo - -import os -import datetime -import pickle -from typing import BinaryIO, List, Tuple, Dict, Optional -from ..shared import CodeGenerationError, sanitycheck -from ...plyparse import Module, Block, Scope, VarDef, Expression, LiteralValue -from ...datatypes import VarType, DataType -import tinyvm.core -import tinyvm.program - - -class AssemblyGenerator: - def __init__(self, module: Module, enable_floats: bool) -> None: - self.module = module - self.floats_enabled = enable_floats - - def generate(self, filename: str) -> None: - with open(filename+".pickle", "wb") as stream: - self._generate(stream) - - def _generate(self, out: BinaryIO) -> None: - sanitycheck(self.module) - program = self.header() - program.blocks = self.blocks(self.module.nodes[0], None) # type: ignore - pickle.dump(program, out, pickle.HIGHEST_PROTOCOL) - - def header(self) -> tinyvm.program.Program: - return tinyvm.program.Program([]) - - def blocks(self, scope: Scope, parentblock_vm: Optional[tinyvm.program.Block]) -> List[tinyvm.program.Block]: - blocks = [] - for node in scope.nodes: - if isinstance(node, Block): - variables = self.make_vars(node) - labels, instructions = self.make_instructions(node) - vmblock = tinyvm.program.Block(node.name, parentblock_vm, variables, instructions, labels) - print(vmblock) - blocks.append(vmblock) - vmblock.blocks = self.blocks(node.nodes[0], vmblock) # type: ignore - return blocks - - def make_vars(self, block: Block) -> List[tinyvm.program.Variable]: - variables = [] - for vardef in block.all_nodes(VarDef): - assert isinstance(vardef, VarDef) - dtype = self.translate_datatype(vardef.datatype) - value = self.translate_value(vardef.value, dtype) - if vardef.vartype == VarType.CONST: - const = True - elif vardef.vartype == VarType.VAR: - const = False - else: - raise CodeGenerationError("unsupported vartype", vardef.vartype) - variables.append(tinyvm.program.Variable(vardef.name, dtype, value, const)) - return variables - - def make_instructions(self, block: Block) -> Tuple[Dict[str, tinyvm.program.Instruction], List[tinyvm.program.Instruction]]: - # returns a dict with the labels (named instruction pointers), - # and a list of the program instructions. - return {}, [] - - def translate_datatype(self, datatype: DataType) -> tinyvm.core.DataType: - table = { - DataType.BYTE: tinyvm.core.DataType.BYTE, - DataType.WORD: tinyvm.core.DataType.WORD, - DataType.FLOAT: tinyvm.core.DataType.FLOAT, - DataType.BYTEARRAY: tinyvm.core.DataType.ARRAY_BYTE, - DataType.WORDARRAY: tinyvm.core.DataType.ARRAY_WORD, - DataType.MATRIX: tinyvm.core.DataType.MATRIX_BYTE - } - dt = table.get(datatype, None) - if dt: - return dt - raise CodeGenerationError("unsupported datatype", datatype) - - def translate_value(self, expr: Expression, dtypehint: Optional[tinyvm.core.DataType]) -> tinyvm.program.Value: - if isinstance(expr, LiteralValue): - dtype = dtypehint or tinyvm.core.DataType.guess_datatype_for(expr.value) - return tinyvm.program.Value(dtype, expr.value) - else: - raise CodeGenerationError("cannot yet generate value for expression node", expr) - diff --git a/python-prototype/il65/compile.py b/python-prototype/il65/compile.py deleted file mode 100644 index c1fd7bd4c..000000000 --- a/python-prototype/il65/compile.py +++ /dev/null @@ -1,592 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the compiler of the IL65 code, that prepares the parse tree for code generation. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import re -import os -import sys -import linecache -from typing import no_type_check, Set, List, Dict, Tuple, Optional, Any -import attr -from .datatypes import DataType, VarType -from .plylex import SourceRef, print_bold -from .constantfold import ConstantFold -from .plyparse import * - - -class CompileError(Exception): - pass - - -class PlyParser: - def __init__(self, *, enable_floats: bool=False, imported_module: bool=False) -> None: - self.parse_errors = 0 - self.imported_module = imported_module - self.floats_enabled = enable_floats - - def parse_file(self, filename: str) -> Module: - print("parsing:", filename) - module = None - try: - module = parse_file(filename, self.lexer_error) - self.check_directives_and_const_defs(module) - self.apply_directive_options(module) - module.scope.define_builtin_functions() - self.process_imports(module) - self.check_and_merge_zeropages(module) - if not self.imported_module: - # the following shall only be done on the main module after all imports have been done: - self.check_all_symbolnames(module) - self.determine_subroutine_usage(module) - self.all_parents_connected(module) - cf = ConstantFold(module) - cf.fold_constants() - self.semantic_check(module) - self.coerce_values(module) - self.check_floats_enabled(module) - self.allocate_zeropage_vars(module) - except ParseError as x: - self.handle_parse_error(x) - if self.parse_errors: - print_bold("\nNo output; there were {:d} errors.\n".format(self.parse_errors)) - raise SystemExit(1) - return module - - def lexer_error(self, sourceref: SourceRef, fmtstring: str, *args: str) -> None: - self.parse_errors += 1 - self.print_error_sourceline(sourceref) - print_bold("ERROR: {}: {}".format(sourceref, fmtstring.format(*args))) - - def _check_last_statement_is_return(self, last_stmt: AstNode) -> None: - if isinstance(last_stmt, (Subroutine, Return, Goto)): - return - if isinstance(last_stmt, Directive) and last_stmt.name == "noreturn": - return - if isinstance(last_stmt, InlineAssembly): - for line in reversed(last_stmt.assembly.splitlines()): - line = line.strip() - if line.startswith(";"): - continue - if "jmp " in line or "jmp\t" in line or "rts" in line or "rti" in line: - return - raise ParseError("last statement in a block/subroutine must be a return or goto, " - "(or %noreturn directive to silence this error)", last_stmt.sourceref) - - def check_floats_enabled(self, module: Module) -> None: - if self.floats_enabled: - return - for node in module.all_nodes(): - if isinstance(node, LiteralValue): - if type(node.value) is float: - raise ParseError("floating point numbers not enabled via option", node.sourceref) - elif isinstance(node, VarDef): - if node.datatype == DataType.FLOAT: - raise ParseError("floating point numbers not enabled via option", node.sourceref) - - def coerce_values(self, module: Module) -> None: - for node in module.all_nodes(): - try: - # note: not processing regular assignments, because they can contain multiple targets of different datatype. - # this has to be dealt with anyway later, so we don't bother dealing with it here for just a special case. - if isinstance(node, AugAssignment): - if node.right.is_compiletime_const(): - _, node.right = coerce_constant_value(datatype_of(node.left, node.my_scope()), node.right, node.right.sourceref) - elif isinstance(node, Goto): - if node.condition is not None and node.condition.is_compiletime_const(): - _, node.nodes[1] = coerce_constant_value(DataType.WORD, node.nodes[1], node.nodes[1].sourceref) # type: ignore - elif isinstance(node, Return): - if node.value_A is not None and node.value_A.is_compiletime_const(): - _, node.nodes[0] = coerce_constant_value(DataType.BYTE, node.nodes[0], node.nodes[0].sourceref) # type: ignore - if node.value_X is not None and node.value_X.is_compiletime_const(): - _, node.nodes[1] = coerce_constant_value(DataType.BYTE, node.nodes[1], node.nodes[1].sourceref) # type: ignore - if node.value_Y is not None and node.value_Y.is_compiletime_const(): - _, node.nodes[2] = coerce_constant_value(DataType.BYTE, node.nodes[2], node.nodes[2].sourceref) # type: ignore - elif isinstance(node, VarDef): - if node.value is not None: - if node.value.is_compiletime_const(): - _, node.value = coerce_constant_value(datatype_of(node, node.my_scope()), node.value, node.value.sourceref) - except OverflowError as x: - raise ParseError(str(x), node.sourceref) - - def all_parents_connected(self, module: Module) -> None: - # check that all parents are connected in all nodes - def check(node: AstNode, expected_parent: AstNode) -> None: - if node.parent is not expected_parent: - print("\nINTERNAL ERROR: parent node invalid of node", node, node.sourceref) - print(" current parent:", node.parent) - print(" expected parent:", expected_parent, expected_parent.sourceref) - raise CompileError("parent node invalid, see message above") - for child_node in node.nodes: - if isinstance(child_node, AstNode): - check(child_node, node) - else: - raise TypeError("invalid child node type", child_node, " in ", node, " sref", node.sourceref) - check(module, None) - - def semantic_check(self, module: Module) -> None: - # perform semantic analysis / checks on the syntactic parse tree we have so far - # (note: symbol names have already been checked to exist when we start this) - previous_stmt = None - encountered_block_names = set() # type: Set[str] - encountered_blocks = set() # type: Set[Block] - for node in module.all_nodes(): - if isinstance(node, Block): - if node in encountered_blocks: - raise CompileError("parse tree malformed; block duplicated", node, node.name, node.sourceref) - if node.name is None: - # blocks without names are possible, in this case their address is specified - if node.address is not None: - continue - else: - raise ParseError("block without name must have address", node.sourceref) - parentname = (node.parent.name + ".") if node.parent else "" # type: ignore - blockname = parentname + node.name - if blockname in encountered_block_names: - raise CompileError("block names not unique:", blockname) - encountered_block_names.add(blockname) - encountered_blocks.add(node) - if isinstance(node, Scope): - if node.nodes and isinstance(node.parent, (Block, Subroutine)): - if isinstance(node.parent, Block) and node.parent.name != "ZP": - self._check_last_statement_is_return(node.nodes[-1]) - elif isinstance(node, SubCall): - if isinstance(node.target, SymbolName): - subdef = node.my_scope().lookup(node.target.name) - if isinstance(subdef, Subroutine): - self.check_subroutine_arguments(node, subdef) - elif isinstance(node, Subroutine): - # the previous statement (if any) must be a Goto or Return - if not isinstance(previous_stmt, (Scope, Goto, Return, VarDef, Subroutine)): - raise ParseError("statement preceding subroutine must be a goto or return or another subroutine", node.sourceref) - elif isinstance(node, IncrDecr): - if isinstance(node.target, SymbolName): - symdef = node.my_scope().lookup(node.target.name) - if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST: - raise ParseError("cannot modify a constant", node.sourceref) - elif isinstance(node, Assignment): - scope = node.my_scope() - for target in node.left.nodes: - if isinstance(target, SymbolName): - symdef = scope.lookup(target.name) - if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST: - raise ParseError("cannot modify a constant", target.sourceref) - elif isinstance(node, AugAssignment): - # the assignment target must not be a constant - if isinstance(node.left, SymbolName): - symdef = node.my_scope().lookup(node.left.name) - if isinstance(symdef, VarDef): - if symdef.vartype == VarType.CONST: - raise ParseError("cannot modify a constant", node.sourceref) - elif symdef.datatype not in {DataType.BYTE, DataType.WORD, DataType.FLOAT}: - raise ParseError("cannot modify that datatype ({:s}) in this way" - .format(symdef.datatype.name.lower()), node.sourceref) - # check for divide by (constant) zero - if node.operator in ("/=", "//="): - if isinstance(node.right, LiteralValue) and node.right.value == 0: - raise ParseError("division by zero", node.right.sourceref) - elif isinstance(node, VarDef): - if node.value is not None and not node.value.is_compiletime_const(): - raise ParseError("variable initialization value should be a compile-time constant", node.value.sourceref) - elif isinstance(node, Dereference): - if isinstance(node.operand, Register) and node.operand.datatype == DataType.BYTE: - raise ParseError("can't dereference a single register; use a register pair", node.operand.sourceref) - previous_stmt = node - - def check_subroutine_arguments(self, call: SubCall, subdef: Subroutine) -> None: - if len(call.arguments.nodes) != len(subdef.param_spec): - raise ParseError("invalid number of arguments ({:d}, required: {:d})" - .format(len(call.arguments.nodes), len(subdef.param_spec)), call.sourceref) - for arg, param in zip(call.arguments.nodes, subdef.param_spec): - if arg.name: - if not param[0]: - raise ParseError("parameter is unnamed but name was used", arg.sourceref) - if arg.name != param[0]: - raise ParseError("parameter name mismatch", arg.sourceref) - - @no_type_check - def check_and_merge_zeropages(self, module: Module) -> None: - # merge all ZP blocks into one - for zeropage in module.all_nodes(Block): - if zeropage.name == "ZP": - break - else: - return - for block in list(module.all_nodes(Block)): - if block is not zeropage and block.name == "ZP": - # merge other ZP block into first ZP block - for node in block.scope.nodes: - if isinstance(node, Directive): - zeropage.scope.add_node(node, 0) - elif isinstance(node, VarDef): - zeropage.scope.add_node(node) - else: - raise ParseError("only variables and directives allowed in zeropage block", node.sourceref) - block.parent.remove_node(block) - block.parent._populate_symboltable(zeropage) # re-add the 'ZP' symbol - - def allocate_zeropage_vars(self, module: Module) -> None: - # allocate zeropage variables to the available free zp addresses - if not module.scope.nodes: - return - zpnode = module.zeropage() - if zpnode is None: - return - zeropage = Zeropage(module.zp_options, self.floats_enabled) - for vardef in zpnode.all_nodes(VarDef): - if vardef.datatype.isstring(): - raise ParseError("cannot put strings in the zeropage", vardef.sourceref) - try: - if vardef.vartype == VarType.VAR: - vardef.zp_address = zeropage.allocate(vardef) - except CompileError as x: - raise ParseError(str(x), vardef.sourceref) - - def check_all_symbolnames(self, module: Module) -> None: - for node in module.all_nodes(SymbolName): - check_symbol_definition(node.name, node.my_scope(), node.sourceref) # type: ignore - - @no_type_check - def apply_directive_options(self, module: Module) -> None: - def set_save_registers(scope: Scope, save_dir: Directive) -> None: - if not scope: - return - if len(save_dir.args) > 1: - raise ParseError("expected zero or one directive argument", save_dir.sourceref) - if save_dir.args: - if save_dir.args[0] in ("yes", "true", True): - scope.save_registers = True - elif save_dir.args[0] in ("no", "false", False): - scope.save_registers = False - else: - raise ParseError("invalid directive args", save_dir.sourceref) - else: - scope.save_registers = True - - for directive in module.all_nodes(Directive): - node = directive.my_scope().parent - if isinstance(node, Module): - # process the module's directives - if directive.name == "output": - if len(directive.args) != 1 or not isinstance(directive.args[0], str): - raise ParseError("expected one str directive argument", directive.sourceref) - if directive.args[0] == "raw": - node.format = ProgramFormat.RAW - node.address = 0xc000 - elif directive.args[0] == "prg": - node.format = ProgramFormat.PRG - node.address = 0xc000 - elif directive.args[0] == "basic": - node.format = ProgramFormat.BASIC - node.address = 0x0801 - elif directive.args[0] == "enable_floats": - self.floats_enabled = module.floats_enabled = True - else: - raise ParseError("invalid directive args", directive.sourceref) - elif directive.name == "address": - if len(directive.args) != 1 or type(directive.args[0]) is not int: - raise ParseError("expected one integer directive argument", directive.sourceref) - if node.format == ProgramFormat.BASIC: - raise ParseError("basic cannot have a custom load address", directive.sourceref) - node.address = directive.args[0] - attr.validate(node) - elif directive.name in "import": - pass # is processed earlier - elif directive.name == "zp": - if len(directive.args) not in (1, 2) or set(directive.args) - {"clobber", "restore"}: - raise ParseError("invalid directive args", directive.sourceref) - if "clobber" in directive.args and "restore" in directive.args: - module.zp_options = ZpOptions.CLOBBER_RESTORE - elif "clobber" in directive.args: - module.zp_options = ZpOptions.CLOBBER - elif "restore" in directive.args: - raise ParseError("invalid directive args", directive.sourceref) - elif directive.name == "saveregisters": - set_save_registers(directive.my_scope(), directive) - else: - raise NotImplementedError(directive.name) - elif isinstance(node, Block): - # process the block's directives - if directive.name == "saveregisters": - set_save_registers(directive.my_scope(), directive) - elif directive.name in ("breakpoint", "asmbinary", "asminclude", "noreturn"): - continue - else: - raise NotImplementedError(directive.name) - elif isinstance(node, Subroutine): - # process the sub's directives - if directive.name == "saveregisters": - set_save_registers(directive.my_scope(), directive) - elif directive.name in ("breakpoint", "asmbinary", "asminclude", "noreturn"): - continue - else: - raise NotImplementedError(directive.name) - - @no_type_check - def determine_subroutine_usage(self, module: Module) -> None: - module.subroutine_usage.clear() - for node in module.all_nodes(): - if isinstance(node, InlineAssembly): - self._get_subroutine_usages_from_asm(module.subroutine_usage, node, node.my_scope()) - elif isinstance(node, SubCall): - self._get_subroutine_usages_from_subcall(module.subroutine_usage, node, node.my_scope()) - elif isinstance(node, Goto): - self._get_subroutine_usages_from_goto(module.subroutine_usage, node, node.my_scope()) - elif isinstance(node, Return): - self._get_subroutine_usages_from_return(module.subroutine_usage, node, node.my_scope()) - elif isinstance(node, Assignment): - self._get_subroutine_usages_from_assignment(module.subroutine_usage, node, node.my_scope()) - # print("----------SUBROUTINES IN USE-------------") - # import pprint - # pprint.pprint(module.subroutine_usage) - # print("----------/SUBROUTINES IN USE-------------") - - def _get_subroutine_usages_from_subcall(self, usages: Dict[Tuple[str, str], Set[str]], - subcall: SubCall, parent_scope: Scope) -> None: - if isinstance(subcall.target, SymbolName): - usages[(parent_scope.name, subcall.target.name)].add(str(subcall.sourceref)) - for arg in subcall.arguments.nodes: - self._get_subroutine_usages_from_expression(usages, arg.value, parent_scope) - - def _get_subroutine_usages_from_expression(self, usages: Dict[Tuple[str, str], Set[str]], - expr: Any, parent_scope: Scope) -> None: - if expr is None or isinstance(expr, (int, str, float, bool, Register)): - return - elif isinstance(expr, SubCall): - self._get_subroutine_usages_from_subcall(usages, expr, parent_scope) - elif isinstance(expr, ExpressionWithOperator): - self._get_subroutine_usages_from_expression(usages, expr.left, parent_scope) - self._get_subroutine_usages_from_expression(usages, expr.right, parent_scope) - elif isinstance(expr, (LiteralValue, AddressOf)): - return - elif isinstance(expr, Dereference): - return self._get_subroutine_usages_from_expression(usages, expr.operand, parent_scope) - elif isinstance(expr, SymbolName): - try: - symbol = parent_scope.lookup(expr.name) - if isinstance(symbol, Subroutine): - usages[(parent_scope.name, expr.name)].add(str(expr.sourceref)) - except UndefinedSymbolError: - pass - else: - raise TypeError("unknown expr type to scan for sub usages", expr, expr.sourceref) - - @no_type_check - def _get_subroutine_usages_from_goto(self, usages: Dict[Tuple[str, str], Set[str]], - goto: Goto, parent_scope: Scope) -> None: - if isinstance(goto.target, SymbolName): - usages[(parent_scope.name, goto.target.name)].add(str(goto.sourceref)) - self._get_subroutine_usages_from_expression(usages, goto.condition, parent_scope) - - def _get_subroutine_usages_from_return(self, usages: Dict[Tuple[str, str], Set[str]], - returnnode: Return, parent_scope: Scope) -> None: - # node.value_A (expression), value_X (expression), value_Y (expression) - self._get_subroutine_usages_from_expression(usages, returnnode.value_A, parent_scope) - self._get_subroutine_usages_from_expression(usages, returnnode.value_X, parent_scope) - self._get_subroutine_usages_from_expression(usages, returnnode.value_Y, parent_scope) - - def _get_subroutine_usages_from_assignment(self, usages: Dict[Tuple[str, str], Set[str]], - assignment: Assignment, parent_scope: Scope) -> None: - # node.right (expression, or another Assignment) - if isinstance(assignment.right, Assignment): - self._get_subroutine_usages_from_assignment(usages, assignment.right, parent_scope) - else: - self._get_subroutine_usages_from_expression(usages, assignment.right, parent_scope) - - def _get_subroutine_usages_from_asm(self, usages: Dict[Tuple[str, str], Set[str]], - asmnode: InlineAssembly, parent_scope: Scope) -> None: - # asm can refer to other symbols as well, track subroutine usage - for line in asmnode.assembly.splitlines(): - splits = line.split(maxsplit=1) - if len(splits) == 2: - for match in re.finditer(r"(?P[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]): - name = match.group("symbol") - if name[0] == '$': - continue - try: - symbol = parent_scope.lookup(name) - except UndefinedSymbolError: - pass - else: - if isinstance(symbol, Subroutine): - if symbol.scope: - namespace = symbol.scope.parent_scope.name - else: - namespace, name = name.rsplit(".", maxsplit=2) - usages[(namespace, symbol.name)].add(str(asmnode.sourceref)) - - def check_directives_and_const_defs(self, module: Module) -> None: - imports = set() # type: Set[str] - for node in module.all_nodes(): - if isinstance(node, VarDef): - if node.value is None and node.vartype == VarType.CONST: - raise ParseError("const should be initialized with a compile-time constant value", node.sourceref) - elif isinstance(node, Directive): - assert isinstance(node.parent, Scope) - if node.parent.level == "module": - if node.name not in {"output", "zp", "address", "import", "saveregisters", "noreturn"}: - raise ParseError("invalid directive in module", node.sourceref) - if node.name == "import": - if imports & set(node.args): - raise ParseError("duplicate import", node.sourceref) - imports |= set(node.args) - else: - if node.name not in {"asmbinary", "asminclude", "breakpoint", "saveregisters", "noreturn"}: - raise ParseError("invalid directive in " + node.parent.__class__.__name__.lower(), node.sourceref) - if node.name == "saveregisters": - # it should be the first node in the scope - if node.parent.nodes[0] is not node: - raise ParseError("saveregisters directive must be first in this scope", node.sourceref) - - def process_imports(self, module: Module) -> None: - # (recursively) imports the modules - imported = [] - for directive in module.all_nodes(Directive): - if directive.name == "import": # type: ignore - if len(directive.args) < 1: # type: ignore - raise ParseError("missing argument(s) for import directive", directive.sourceref) - for arg in directive.args: # type: ignore - filename = self.find_import_file(arg, directive.sourceref.file) - if not filename: - raise ParseError("imported file not found", directive.sourceref) - imported_module, import_parse_errors = self.import_file(filename) - imported.append(imported_module) - self.parse_errors += import_parse_errors - if not self.imported_module: - # compiler support library is always imported (in main parser) - filename = self.find_import_file("il65lib", module.sourceref.file) - if filename: - imported_module, import_parse_errors = self.import_file(filename) - imported.append(imported_module) - self.parse_errors += import_parse_errors - else: - raise FileNotFoundError("missing il65lib") - if sum(m.floats_enabled for m in imported): - self.floats_enabled = module.floats_enabled = True - # append the imported module's contents (blocks) at the end of the current module - for block in (node for imported_module in imported - for node in imported_module.scope.nodes - if isinstance(node, Block)): - module.scope.add_node(block) - - def import_file(self, filename: str) -> Tuple[Module, int]: - sub_parser = PlyParser(imported_module=True) - return sub_parser.parse_file(filename), sub_parser.parse_errors - - def find_import_file(self, modulename: str, sourcefile: str) -> Optional[str]: - candidates = [modulename+".ill", modulename] - filename_at_source_location = os.path.join(os.path.split(sourcefile)[0], modulename) - if filename_at_source_location not in candidates: - candidates.append(filename_at_source_location+".ill") - candidates.append(filename_at_source_location) - filename_at_libs_location = os.path.join(os.path.split(__file__)[0], "lib", modulename) - if filename_at_libs_location not in candidates: - candidates.append(filename_at_libs_location+".ill") - candidates.append(filename_at_libs_location) - for filename in candidates: - if os.path.isfile(filename): - return filename - return None - - def handle_parse_error(self, exc: ParseError) -> None: - self.parse_errors += 1 - out = sys.stdout - if out.isatty(): - print("\x1b[1m", file=out) - if self.imported_module: - print("Error (in imported file):", str(exc), file=out) - else: - print("Error:", str(exc), file=out) - self.print_error_sourceline(exc.sourceref) - if out.isatty(): - print("\x1b[0m", file=out, end="", flush=True) - raise exc # XXX temporary to see where the error occurred - - def print_error_sourceline(self, sref: SourceRef) -> None: - if not sref: - return - sourcetext = linecache.getline(sref.file, sref.line).rstrip() - if sourcetext: - print(" " + sourcetext.expandtabs(8)) - if sref.column: - print(' ' * (1+sref.column) + '^') - - -class Zeropage: - SCRATCH_B1 = 0x02 - SCRATCH_B2 = 0x03 - SCRATCH_W1 = 0xfb # $fb/$fc - SCRATCH_W2 = 0xfd # $fd/$fe - - def __init__(self, options: ZpOptions, enable_floats: bool) -> None: - self.floats_enabled = enable_floats - self.free = [] # type: List[int] - self.allocations = {} # type: Dict[int, Tuple[str, DataType]] - if options in (ZpOptions.CLOBBER_RESTORE, ZpOptions.CLOBBER): - # clobber the zp, more free storage, yay! - self.free = list(range(0x04, 0xfb)) + [0xff] - for updated_by_irq in [0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6]: - self.free.remove(updated_by_irq) - else: - # these are valid for the C-64 (when no RS232 I/O is performed): - # ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines) - self.free = [0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa] - assert self.SCRATCH_B1 not in self.free - assert self.SCRATCH_B2 not in self.free - assert self.SCRATCH_W1 not in self.free - assert self.SCRATCH_W2 not in self.free - - def allocate(self, vardef: VarDef) -> int: - assert not vardef.name or vardef.name not in {a[0] for a in self.allocations.values()}, "var name is not unique" - assert vardef.vartype == VarType.VAR, "can only allocate var" - - def sequential_free(location: int) -> bool: - return all(location + i in self.free for i in range(size)) - - def lone_byte(location: int) -> bool: - return (location-1) not in self.free and (location+1) not in self.free and location in self.free - - def make_allocation(location: int) -> int: - for loc in range(location, location + size): - self.free.remove(loc) - self.allocations[location] = (vardef.name or "", vardef.datatype) - return location - - if vardef.datatype == DataType.BYTE: - size = 1 - elif vardef.datatype == DataType.WORD: - size = 2 - elif vardef.datatype == DataType.FLOAT: - if not self.floats_enabled: - raise TypeError("floating point numbers not enabled via option") - print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) - size = 5 - elif vardef.datatype == DataType.BYTEARRAY: - print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) - size = vardef.size[0] - elif vardef.datatype == DataType.WORDARRAY: - print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) - size = vardef.size[0] * 2 - elif vardef.datatype == DataType.MATRIX: - print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) - size = vardef.size[0] * vardef.size[1] - elif vardef.datatype.isstring(): - print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) - size = vardef.size[0] - else: - raise CompileError("cannot put datatype {:s} in ZP".format(vardef.datatype.name)) - if len(self.free) > 0: - if size == 1: - for candidate in range(min(self.free), max(self.free)+1): - if lone_byte(candidate): - return make_allocation(candidate) - return make_allocation(self.free[0]) - for candidate in range(min(self.free), max(self.free)+1): - if sequential_free(candidate): - return make_allocation(candidate) - raise CompileError("ERROR: no free space in ZP to allocate {:d} sequential bytes".format(size)) - - def available(self) -> int: - return len(self.free) diff --git a/python-prototype/il65/constantfold.py b/python-prototype/il65/constantfold.py deleted file mode 100644 index 904fd9e7a..000000000 --- a/python-prototype/il65/constantfold.py +++ /dev/null @@ -1,222 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the part of the compiler/optimizer that simplifies expressions by doing -'constant folding' - replacing expressions with constant, compile-time precomputed values. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import sys -from .plylex import SourceRef -from .datatypes import VarType -from .plyparse import * - - -def handle_internal_error(exc: Exception, msg: str = "") -> None: - out = sys.stdout - if out.isatty(): - print("\x1b[1m", file=out) - print("\nERROR: internal parser/optimizer error: ", exc, file=out) - if msg: - print(" Message:", msg, end="\n\n") - if out.isatty(): - print("\x1b[0m", file=out, end="", flush=True) - raise exc - - -class ConstantFold: - def __init__(self, mod: Module) -> None: - self.num_warnings = 0 - self.module = mod - self.optimizations_performed = False - - def fold_constants(self, once: bool=False) -> None: - self.num_warnings = 0 - if once: - self._constant_folding() - else: - self.optimizations_performed = True - # keep optimizing as long as there were changes made - while self.optimizations_performed: - self.optimizations_performed = False - self._constant_folding() - - def _constant_folding(self) -> None: - for expression in self.module.all_nodes(Expression): - if expression.parent is None or expression.parent.parent is None: - # stale expression node (was part of an expression that was constant-folded away) - continue - if isinstance(expression, LiteralValue): - continue - try: - evaluated = self._process_expression(expression) # type: ignore - if evaluated is not expression: - # replace the node with the newly evaluated result - parent = expression.parent - parent.replace_node(expression, evaluated) - self.optimizations_performed = True - except ParseError: - raise - except Exception as x: - handle_internal_error(x, "process_expressions of node {}".format(expression)) - - def _process_expression(self, expr: Expression) -> Expression: - # process/simplify all expressions (constant folding etc) - if expr.is_lhs: - if isinstance(expr, (Register, SymbolName, Dereference)): - return expr - raise ParseError("invalid lhs expression type", expr.sourceref) - result = None # type: Expression - if expr.is_compiletime_const(): - result = self._process_constant_expression(expr, expr.sourceref) - else: - result = self._process_dynamic_expression(expr, expr.sourceref) - result.parent = expr.parent - return result - - def _process_constant_expression(self, expr: Expression, sourceref: SourceRef) -> LiteralValue: - # the expression must result in a single (constant) value (int, float, whatever) wrapped as LiteralValue. - if isinstance(expr, LiteralValue): - return expr - try: - return LiteralValue(value=expr.const_value(), sourceref=sourceref) # type: ignore - except NotCompiletimeConstantError: - pass - if isinstance(expr, SymbolName): - value = check_symbol_definition(expr.name, expr.my_scope(), expr.sourceref) - if isinstance(value, VarDef): - if value.vartype == VarType.MEMORY: - raise ExpressionEvaluationError("can't take a memory value, must be a constant", expr.sourceref) - value = value.value - if isinstance(value, ExpressionWithOperator): - raise ExpressionEvaluationError("circular reference?", expr.sourceref) - elif isinstance(value, LiteralValue): - return value - elif isinstance(value, (int, float, str, bool)): - raise TypeError("symbol value node should not be a python primitive value", expr) - else: - raise ExpressionEvaluationError("constant symbol required, not {}".format(value.__class__.__name__), expr.sourceref) - elif isinstance(expr, AddressOf): - assert isinstance(expr.name, str) - value = check_symbol_definition(expr.name, expr.my_scope(), expr.sourceref) - if isinstance(value, VarDef): - if value.vartype == VarType.MEMORY: - if isinstance(value.value, LiteralValue): - return value.value - else: - raise ExpressionEvaluationError("constant literal value required", value.sourceref) - if value.vartype == VarType.CONST: - raise ExpressionEvaluationError("can't take the address of a constant", expr.sourceref) - raise ExpressionEvaluationError("address-of this {} isn't a compile-time constant" - .format(value.__class__.__name__), expr.sourceref) - else: - raise ExpressionEvaluationError("constant address required, not {}" - .format(value.__class__.__name__), expr.sourceref) - elif isinstance(expr, SubCall): - if isinstance(expr.target, SymbolName): # 'function(1,2,3)' - funcname = expr.target.name - if funcname in math_functions or funcname in builtin_functions: - func_args = [] - for a in (self._process_constant_expression(callarg.value, sourceref) for callarg in list(expr.arguments.nodes)): - if isinstance(a, LiteralValue): - func_args.append(a.value) - else: - func_args.append(a) - func = math_functions.get(funcname, builtin_functions.get(funcname)) - try: - return LiteralValue(value=func(*func_args), sourceref=expr.arguments.sourceref) # type: ignore - except Exception as x: - raise ExpressionEvaluationError(str(x), expr.sourceref) - else: - raise ExpressionEvaluationError("can only use math- or builtin function", expr.sourceref) - elif isinstance(expr.target, Dereference): # '[...](1,2,3)' - raise ExpressionEvaluationError("dereferenced value call is not a constant value", expr.sourceref) - elif isinstance(expr.target, LiteralValue) and type(expr.target.value) is int: # '64738()' - raise ExpressionEvaluationError("immediate address call is not a constant value", expr.sourceref) - else: - raise NotImplementedError("weird call target", expr.target) - elif isinstance(expr, ExpressionWithOperator): - if expr.unary: - left_sourceref = expr.left.sourceref if isinstance(expr.left, AstNode) else sourceref - expr.left = self._process_constant_expression(expr.left, left_sourceref) - expr.left.parent = expr - if isinstance(expr.left, LiteralValue) and type(expr.left.value) in (int, float): - try: - if expr.operator == '-': - return LiteralValue(value=-expr.left.value, sourceref=expr.left.sourceref) # type: ignore - elif expr.operator == '~': - return LiteralValue(value=~expr.left.value, sourceref=expr.left.sourceref) # type: ignore - elif expr.operator in ("++", "--"): - raise ValueError("incr/decr should not be an expression") - raise ValueError("invalid unary operator", expr.operator) - except TypeError as x: - raise ParseError(str(x), expr.sourceref) from None - raise ValueError("invalid operand type for unary operator", expr.left, expr.operator) - else: - left_sourceref = expr.left.sourceref if isinstance(expr.left, AstNode) else sourceref - expr.left = self._process_constant_expression(expr.left, left_sourceref) - expr.left.parent = expr - right_sourceref = expr.right.sourceref if isinstance(expr.right, AstNode) else sourceref - expr.right = self._process_constant_expression(expr.right, right_sourceref) - expr.right.parent = expr - if isinstance(expr.left, LiteralValue): - if isinstance(expr.right, LiteralValue): - return expr.evaluate_primitive_constants(expr.right.sourceref) - else: - raise ExpressionEvaluationError("constant literal value required on right, not {}" - .format(expr.right.__class__.__name__), right_sourceref) - else: - raise ExpressionEvaluationError("constant literal value required on left, not {}" - .format(expr.left.__class__.__name__), left_sourceref) - else: - raise ExpressionEvaluationError("constant value required, not {}".format(expr.__class__.__name__), expr.sourceref) - - def _process_dynamic_expression(self, expr: Expression, sourceref: SourceRef) -> Expression: - # constant-fold a dynamic expression - if isinstance(expr, LiteralValue): - return expr - try: - return LiteralValue(value=expr.const_value(), sourceref=sourceref) # type: ignore - except NotCompiletimeConstantError: - pass - if isinstance(expr, SymbolName): - try: - return self._process_constant_expression(expr, sourceref) - except (ExpressionEvaluationError, NotCompiletimeConstantError): - return expr - elif isinstance(expr, AddressOf): - try: - return self._process_constant_expression(expr, sourceref) - except (ExpressionEvaluationError, NotCompiletimeConstantError): - return expr - elif isinstance(expr, SubCall): - try: - return self._process_constant_expression(expr, sourceref) - except (ExpressionEvaluationError, NotCompiletimeConstantError): - if isinstance(expr.target, SymbolName): - check_symbol_definition(expr.target.name, expr.my_scope(), expr.target.sourceref) - return expr - elif isinstance(expr, (Register, Dereference)): - return expr - elif isinstance(expr, ExpressionWithOperator): - if expr.unary: - left_sourceref = expr.left.sourceref if isinstance(expr.left, AstNode) else sourceref - expr.left = self._process_dynamic_expression(expr.left, left_sourceref) - expr.left.parent = expr - try: - return self._process_constant_expression(expr, sourceref) - except (ExpressionEvaluationError, NotCompiletimeConstantError): - return expr - else: - left_sourceref = expr.left.sourceref if isinstance(expr.left, AstNode) else sourceref - expr.left = self._process_dynamic_expression(expr.left, left_sourceref) - expr.left.parent = expr - right_sourceref = expr.right.sourceref if isinstance(expr.right, AstNode) else sourceref - expr.right = self._process_dynamic_expression(expr.right, right_sourceref) - expr.right.parent = expr - try: - return self._process_constant_expression(expr, sourceref) - except (ExpressionEvaluationError, NotCompiletimeConstantError): - return expr - else: - raise ParseError("expression required, not {}".format(expr.__class__.__name__), expr.sourceref) diff --git a/python-prototype/il65/datatypes.py b/python-prototype/il65/datatypes.py deleted file mode 100644 index a05d3dd44..000000000 --- a/python-prototype/il65/datatypes.py +++ /dev/null @@ -1,183 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -Here are the data type definitions and -conversions. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import enum -from functools import total_ordering - - -@total_ordering -class VarType(enum.Enum): - CONST = 1 - MEMORY = 2 - VAR = 3 - - def __lt__(self, other): - if self.__class__ == other.__class__: - return self.value < other.value - return NotImplemented - - -@total_ordering -class DataType(enum.Enum): - """The possible data types of values""" - BYTE = 1 - WORD = 2 - FLOAT = 3 - BYTEARRAY = 4 - WORDARRAY = 5 - MATRIX = 6 - STRING = 7 - STRING_P = 8 - STRING_S = 9 - STRING_PS = 10 - - def __lt__(self, other): - if self.__class__ == other.__class__: - return self.value < other.value - return NotImplemented - - def isnumeric(self) -> bool: - return self.value in (1, 2, 3) - - def isinteger(self) -> bool: - return self.value in (1, 2) - - def isarray(self) -> bool: - return self.value in (4, 5, 6) - - def isstring(self) -> bool: - return self.value in (7, 8, 9, 10) - - -STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS} - - -REGISTER_SYMBOLS = {"A", "X", "Y", "AX", "AY", "XY", "SC", "SI"} -REGISTER_SYMBOLS_RETURNVALUES = REGISTER_SYMBOLS | {"SZ"} -REGISTER_BYTES = {"A", "X", "Y"} -REGISTER_SBITS = {"SC", "SI", "SZ"} -REGISTER_WORDS = {"AX", "AY", "XY"} - -# 5-byte cbm MFLPT format limitations: -FLOAT_MAX_POSITIVE = 1.7014118345e+38 -FLOAT_MAX_NEGATIVE = -1.7014118345e+38 - - -def char_to_bytevalue(character: str, petscii: bool=True) -> int: - assert len(character) == 1 - if petscii: - return ord(character.translate(ascii_to_petscii_trans)) - else: - raise NotImplementedError("screencode conversion not yet implemented for chars") - - -# ASCII/UNICODE-to-PETSCII translation table -# Unicode symbols supported that map to a PETSCII character: £ ↑ ← ♠ ♥ ♦ ♣ π ● ○ and various others - -# @todo cbmcodecs pypi package? -ascii_to_petscii_trans = str.maketrans({ - '\f': 147, # form feed becomes ClearScreen "{clear}" - '\n': 13, # line feed becomes a RETURN "{cr}" (not a line feed) - '\r': 17, # CR becomes CursorDown "{down}" - 'a': 65, - 'b': 66, - 'c': 67, - 'd': 68, - 'e': 69, - 'f': 70, - 'g': 71, - 'h': 72, - 'i': 73, - 'j': 74, - 'k': 75, - 'l': 76, - 'm': 77, - 'n': 78, - 'o': 79, - 'p': 80, - 'q': 81, - 'r': 82, - 's': 83, - 't': 84, - 'u': 85, - 'v': 86, - 'w': 87, - 'x': 88, - 'y': 89, - 'z': 90, - 'A': 97, - 'B': 98, - 'C': 99, - 'D': 100, - 'E': 101, - 'F': 102, - 'G': 103, - 'H': 104, - 'I': 105, - 'J': 106, - 'K': 107, - 'L': 108, - 'M': 109, - 'N': 110, - 'O': 111, - 'P': 112, - 'Q': 113, - 'R': 114, - 'S': 115, - 'T': 116, - 'U': 117, - 'V': 118, - 'W': 119, - 'X': 120, - 'Y': 121, - 'Z': 122, - '{': 179, # left squiggle - '}': 235, # right squiggle - '£': 92, # pound currency sign - '^': 94, # up arrow - '~': 126, # pi math symbol - 'π': 126, # pi symbol - '`': 39, # single quote - '✓': 250, # check mark - - '|': 221, # vertical bar - '│': 221, # vertical bar - '─': 96, # horizontal bar - '┼': 123, # vertical and horizontal bar - - '↑': 94, # up arrow - '←': 95, # left arrow - - '▔': 163, # upper bar - '_': 164, # lower bar (underscore) - '▁': 164, # lower bar - '▎': 165, # left bar - - '♠': 97, # spades - '●': 113, # circle - '♥': 115, # hearts - '○': 119, # open circle - '♣': 120, # clubs - '♦': 122, # diamonds - - '├': 171, # vertical and right - '┤': 179, # vertical and left - '┴': 177, # horiz and up - '┬': 178, # horiz and down - '└': 173, # up right - '┐': 174, # down left - '┌': 175, # down right - '┘': 189, # up left - '▗': 172, # block lr - '▖': 187, # block ll - '▝': 188, # block ur - '▘': 190, # block ul - '▚': 191, # block ul and lr - '▌': 161, # left half - '▄': 162, # lower half - '▒': 230, # raster -}) diff --git a/python-prototype/il65/main.py b/python-prototype/il65/main.py deleted file mode 100644 index dc5ee8157..000000000 --- a/python-prototype/il65/main.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the main program that drives the rest. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import time -import os -import re -import argparse -import subprocess -from .compile import PlyParser -from .optimize import optimize -from .plylex import print_bold -from .plyparse import ProgramFormat, Module - - -class Assembler64Tass: - def __init__(self, format: ProgramFormat) -> None: - self.format = format - - def assemble(self, inputfilename: str, outputfilename: str) -> None: - args = ["64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool", - "--dump-labels", "--vice-labels", "-l", outputfilename+".vice-mon-list", - "--no-monitor", "--output", outputfilename, inputfilename] - if self.format in (ProgramFormat.PRG, ProgramFormat.BASIC): - args.append("--cbm-prg") - elif self.format == ProgramFormat.RAW: - args.append("--nostart") - else: - raise ValueError("don't know how to create code format "+str(self.format)) - try: - if self.format == ProgramFormat.PRG: - print("\nCreating C-64 prg.") - elif self.format == ProgramFormat.RAW: - print("\nCreating raw binary.") - try: - subprocess.check_call(args) - except FileNotFoundError as x: - raise SystemExit("ERROR: cannot run assembler program: "+str(x)) - except subprocess.CalledProcessError as x: - raise SystemExit("assembler failed with returncode " + str(x.returncode)) - - def generate_breakpoint_list(self, program_filename: str) -> str: - breakpoints = [] - vice_mon_file = program_filename + ".vice-mon-list" - with open(vice_mon_file, "rU") as f: - for line in f: - match = re.fullmatch(r"al (?P
\w+) \S+_il65_breakpoint_\d+.?", line, re.DOTALL) - if match: - breakpoints.append("$" + match.group("address")) - with open(vice_mon_file, "at") as f: - print("; vice monitor breakpoint list now follows", file=f) - print("; {:d} breakpoints have been defined here".format(len(breakpoints)), file=f) - print("del", file=f) - for b in breakpoints: - print("break", b, file=f) - return vice_mon_file - - -def main() -> None: - description = "Compiler for IL65 language, code name 'Sick'" - ap = argparse.ArgumentParser(description=description) - ap.add_argument("-o", "--output", help="output directory") - ap.add_argument("-c", "--codegenerator", choices=["6502", "tinyvm"], default="tinyvm", help="what code generator to use") - ap.add_argument("-f", "--enablefloat", action="store_true", help="enable C64 (mflpt5) floating point operations") - ap.add_argument("-no", "--nooptimize", action="store_true", help="do not optimize the parse tree") - ap.add_argument("-sv", "--startvice", action="store_true", help="autostart vice x64 emulator after compilation") - ap.add_argument("sourcefile", help="the source .ill/.il65 file to compile") - args = ap.parse_args() - assembly_filename = os.path.splitext(args.sourcefile)[0] + ".asm" - program_filename = os.path.splitext(args.sourcefile)[0] + ".prg" - if args.output: - os.makedirs(args.output, mode=0o700, exist_ok=True) - assembly_filename = os.path.join(args.output, os.path.split(assembly_filename)[1]) - program_filename = os.path.join(args.output, os.path.split(program_filename)[1]) - - print("\n" + description) - - start = time.perf_counter() - print("\nParsing program source code.") - parser = PlyParser(enable_floats=args.enablefloat) - parsed_module = parser.parse_file(args.sourcefile) - if parsed_module: - if args.nooptimize: - print_bold("Optimizations disabled!") - else: - print("\nOptimizing code.") - optimize(parsed_module) - if args.codegenerator == "tinyvm": - generate_tinyvm_code(parsed_module, args.enablefloat, assembly_filename, start) - else: - generate_6502_code(parsed_module, args.enablefloat, args.startvice, program_filename, assembly_filename, start) - - -def generate_tinyvm_code(module: Module, float_enabled: bool, assembly_filename: str, compilationstart: float) -> None: - print("\nGenerating tinyvm code.") - from il65.codegen.tinyvm.generate import AssemblyGenerator - cg = AssemblyGenerator(module, float_enabled) - cg.generate(assembly_filename) - duration_total = time.perf_counter() - compilationstart - print("Compile duration: {:.2f} seconds".format(duration_total)) - print() - - -def generate_6502_code(module: Module, float_enabled: bool, startvice: bool, - program_filename: str, assembly_filename: str, compilationstart: float) -> None: - print("\nGenerating 6502 assembly code.") - from il65.codegen.mos6502.generate import AssemblyGenerator - cg = AssemblyGenerator(module, float_enabled) - cg.generate(assembly_filename) - assembler = Assembler64Tass(module.format) - assembler.assemble(assembly_filename, program_filename) - mon_command_file = assembler.generate_breakpoint_list(program_filename) - duration_total = time.perf_counter() - compilationstart - print("Compile duration: {:.2f} seconds".format(duration_total)) - size = os.path.getsize(program_filename) - print("Output size: {:d} bytes".format(size)) - print_bold("Output file: " + program_filename) - print() - if startvice: - print("Autostart vice emulator...") - # "-remotemonitor" - cmdline = ["x64", "-moncommands", mon_command_file, - "-autostartprgmode", "1", "-autostart-warp", "-autostart", program_filename] - with open(os.devnull, "wb") as shutup: - subprocess.call(cmdline, stdout=shutup) diff --git a/python-prototype/il65/oldstuff/codegen.py b/python-prototype/il65/oldstuff/codegen.py deleted file mode 100644 index 0518255ff..000000000 --- a/python-prototype/il65/oldstuff/codegen.py +++ /dev/null @@ -1,724 +0,0 @@ -# old deprecated code, in the process of moving this to the new emit/... modules - - -class CodeGenerator: - def _generate_goto_conditional_truthvalue(self, stmt: CallStmt) -> None: - # the condition is just the 'truth value' of the single value, - # this is translated into assembly by comparing the argument to zero. - def branch_emitter_mmap(targetstr: str, is_goto: bool, target_indirect: bool) -> None: - assert is_goto and not stmt.condition.comparison_op - assert stmt.condition.lvalue and not stmt.condition.rvalue - assert not target_indirect - assert stmt.condition.ifstatus in ("true", "not", "zero") - branch, inverse_branch = ("bne", "beq") if stmt.condition.ifstatus == "true" else ("beq", "bne") - cv = stmt.condition.lvalue - assert isinstance(cv, MemMappedValue) - cv_str = cv.name or Parser.to_hex(cv.address) - if cv.datatype == DataType.BYTE: - self.p("\t\tsta " + Parser.to_hex(Zeropage.SCRATCH_B1)) # need to save A, because the goto may not be taken - self.p("\t\tlda " + cv_str) - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A - elif cv.datatype == DataType.WORD: - self.p("\t\tsta " + Parser.to_hex(Zeropage.SCRATCH_B1)) # need to save A, because the goto may not be taken - self.p("\t\tlda " + cv_str) - if stmt.condition.ifstatus == "true": - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("\t\tlda {:s}+1".format(cv_str)) - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A - else: - self.p("\t\t{:s} +".format(inverse_branch, targetstr)) - self.p("\t\tlda {:s}+1".format(cv_str)) - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("+\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A - else: - raise CodeError("conditions cannot yet use other types than byte or word", # @todo comparisons of other types - cv.datatype, str(cv), stmt.sourceref) - - def branch_emitter_reg(targetstr: str, is_goto: bool, target_indirect: bool) -> None: - assert is_goto and not stmt.condition.comparison_op - assert stmt.condition.lvalue and not stmt.condition.rvalue - assert not target_indirect - assert stmt.condition.ifstatus in ("true", "not", "zero") - branch, inverse_branch = ("bne", "beq") if stmt.condition.ifstatus == "true" else ("beq", "bne") - line_after_branch = "" - cv = stmt.condition.lvalue - assert isinstance(cv, RegisterValue) - if cv.register == 'A': - self.p("\t\tcmp #0") - elif cv.register == 'X': - self.p("\t\tcpx #0") - elif cv.register == 'Y': - self.p("\t\tcpy #0") - else: - if cv.register == 'AX': - line_after_branch = "+" - self.p("\t\tcmp #0") - self.p("\t\t{:s} {:s}".format(inverse_branch, line_after_branch)) - self.p("\t\tcpx #0") - elif cv.register == 'AY': - line_after_branch = "+" - self.p("\t\tcmp #0") - self.p("\t\t{:s} {:s}".format(inverse_branch, line_after_branch)) - self.p("\t\tcpy #0") - elif cv.register == 'XY': - line_after_branch = "+" - self.p("\t\tcpx #0") - self.p("\t\t{:s} {:s}".format(inverse_branch, line_after_branch)) - self.p("\t\tcpy #0") - else: - raise CodeError("invalid register", cv.register) - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - if line_after_branch: - self.p(line_after_branch) - - def branch_emitter_indirect_cond(targetstr: str, is_goto: bool, target_indirect: bool) -> None: - assert is_goto and not stmt.condition.comparison_op - assert stmt.condition.lvalue and not stmt.condition.rvalue - assert stmt.condition.ifstatus in ("true", "not", "zero") - assert not target_indirect - cv = stmt.condition.lvalue.value # type: ignore - if isinstance(cv, RegisterValue): - branch = "bne" if stmt.condition.ifstatus == "true" else "beq" - self.p("\t\tsta " + Parser.to_hex(Zeropage.SCRATCH_B1)) # need to save A, because the goto may not be taken - if cv.register == 'Y': - self.p("\t\tlda ($00),y") - elif cv.register == 'X': - self.p("\t\tstx *+2\t; self-modify") - self.p("\t\tlda $ff") - elif cv.register == 'A': - self.p("\t\tsta *+2\t; self-modify") - self.p("\t\tlda $ff") - else: - self.p("\t\tst{:s} (+)+1\t; self-modify".format(cv.register[0].lower())) - self.p("\t\tst{:s} (+)+2\t; self-modify".format(cv.register[1].lower())) - self.p("+\t\tlda $ffff") - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A - elif isinstance(cv, MemMappedValue): - raise CodeError("memmapped indirect should not occur, use the variable without indirection") - elif isinstance(cv, IntegerValue) and cv.constant: - branch, inverse_branch = ("bne", "beq") if stmt.condition.ifstatus == "true" else ("beq", "bne") - cv_str = cv.name or Parser.to_hex(cv.value) - if cv.datatype == DataType.BYTE: - self.p("\t\tsta " + Parser.to_hex(Zeropage.SCRATCH_B1)) # need to save A, because the goto may not be taken - self.p("\t\tlda " + cv_str) - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A - elif cv.datatype == DataType.WORD: - self.p("\t\tsta " + Parser.to_hex(Zeropage.SCRATCH_B1)) # need to save A, because the goto may not be taken - self.p("\t\tlda " + cv_str) - if stmt.condition.ifstatus == "true": - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("\t\tlda {:s}+1".format(cv_str)) - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A - else: - self.p("\t\t{:s} +".format(inverse_branch, targetstr)) - self.p("\t\tlda {:s}+1".format(cv_str)) - self.p("\t\t{:s} {:s}".format(branch, targetstr)) - self.p("+\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A - else: - raise CodeError("conditions cannot yet use other types than byte or word", # @todo comparisons of other types - cv.datatype, str(cv), stmt.sourceref) - else: - raise CodeError("weird indirect type", str(cv)) - - cv = stmt.condition.lvalue - if isinstance(cv, RegisterValue): - self._generate_call_or_goto(stmt, branch_emitter_reg) - elif isinstance(cv, MemMappedValue): - self._generate_call_or_goto(stmt, branch_emitter_mmap) - elif isinstance(cv, IndirectValue): - if isinstance(cv.value, RegisterValue): - self._generate_call_or_goto(stmt, branch_emitter_indirect_cond) - elif isinstance(cv.value, MemMappedValue): - self._generate_call_or_goto(stmt, branch_emitter_indirect_cond) - elif isinstance(cv.value, IntegerValue) and cv.value.constant: - self._generate_call_or_goto(stmt, branch_emitter_indirect_cond) - else: - raise CodeError("weird indirect type", str(cv)) - else: - raise CodeError("need register, memmapped or indirect value", str(cv)) - - def _generate_goto_conditional_comparison(self, stmt: CallStmt) -> None: - # the condition is lvalue operator rvalue - raise NotImplementedError("no comparisons yet") # XXX comparisons - assert stmt.condition.ifstatus in ("true", "not", "zero") - assert stmt.condition.lvalue != stmt.condition.rvalue # so we know we actually have to compare different things - lv, compare_operator, rv = stmt.condition.lvalue, stmt.condition.comparison_op, stmt.condition.rvalue - if lv.constant and not rv.constant: - # if lv is a constant, swap the whole thing around so the constant is on the right - lv, compare_operator, rv = stmt.condition.swap() - if isinstance(rv, RegisterValue): - # if rv is a register, make sure it comes first instead - lv, compare_operator, rv = stmt.condition.swap() - if lv.datatype != DataType.BYTE or rv.datatype != DataType.BYTE: - raise CodeError("can only generate comparison code for byte values for now") # @todo compare non-bytes - if isinstance(lv, RegisterValue): - if isinstance(rv, RegisterValue): - self.p("\t\tst{:s} {:s}".format(rv.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1))) - if lv.register == "A": - self.p("\t\tcmp " + Parser.to_hex(Zeropage.SCRATCH_B1)) - elif lv.register == "X": - self.p("\t\tcpx " + Parser.to_hex(Zeropage.SCRATCH_B1)) - elif lv.register == "Y": - self.p("\t\tcpy " + Parser.to_hex(Zeropage.SCRATCH_B1)) - else: - raise CodeError("wrong lvalue register") - elif isinstance(rv, IntegerValue): - rvstr = rv.name or Parser.to_hex(rv.value) - if lv.register == "A": - self.p("\t\tcmp #" + rvstr) - elif lv.register == "X": - self.p("\t\tcpx #" + rvstr) - elif lv.register == "Y": - self.p("\t\tcpy #" + rvstr) - else: - raise CodeError("wrong lvalue register") - elif isinstance(rv, MemMappedValue): - rvstr = rv.name or Parser.to_hex(rv.address) - if lv.register == "A": - self.p("\t\tcmp " + rvstr) - elif lv.register == "X": - self.p("\t\tcpx #" + rvstr) - elif lv.register == "Y": - self.p("\t\tcpy #" + rvstr) - else: - raise CodeError("wrong lvalue register") - else: - raise CodeError("invalid rvalue type in comparison", rv) - elif isinstance(lv, MemMappedValue): - assert not isinstance(rv, RegisterValue), "registers as rvalue should have been swapped with lvalue" - if isinstance(rv, IntegerValue): - self.p("\t\tsta " + Parser.to_hex(Zeropage.SCRATCH_B1)) # need to save A, because the goto may not be taken - self.p("\t\tlda " + (lv.name or Parser.to_hex(lv.address))) - self.p("\t\tcmp #" + (rv.name or Parser.to_hex(rv.value))) - line_after_goto = "\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1) # restore A - elif isinstance(rv, MemMappedValue): - rvstr = rv.name or Parser.to_hex(rv.address) - self.p("\t\tsta " + Parser.to_hex(Zeropage.SCRATCH_B1)) # need to save A, because the goto may not be taken - self.p("\t\tlda " + (lv.name or Parser.to_hex(lv.address))) - self.p("\t\tcmp " + rvstr) - line_after_goto = "\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1) # restore A - else: - raise CodeError("invalid rvalue type in comparison", rv) - else: - raise CodeError("invalid lvalue type in comparison", lv) - - def _generate_call_or_goto(self, stmt: CallStmt, branch_emitter: Callable[[str, bool, bool], None]) -> None: - def generate_param_assignments() -> None: - for assign_stmt in stmt.desugared_call_arguments: - self.generate_assignment(assign_stmt) - - def generate_result_assignments() -> None: - for assign_stmt in stmt.desugared_output_assignments: - self.generate_assignment(assign_stmt) - - def params_load_a() -> bool: - for assign_stmt in stmt.desugared_call_arguments: - for lv in assign_stmt.leftvalues: - if isinstance(lv, RegisterValue): - if lv.register == 'A': - return True - return False - - def unclobber_result_registers(registers: Set[str], output_assignments: List[AssignmentStmt]) -> Set[str]: - result = registers.copy() - for a in output_assignments: - for lv in a.leftvalues: - if isinstance(lv, RegisterValue): - if len(lv.register) == 1: - result.discard(lv.register) - else: - for r in lv.register: - result.discard(r) - return result - - if stmt.target.name: - symblock, targetdef = self.cur_block.lookup(stmt.target.name) - else: - symblock = None - targetdef = None - if isinstance(targetdef, SubroutineDef): - if isinstance(stmt.target, MemMappedValue): - targetstr = stmt.target.name or Parser.to_hex(stmt.address) - else: - raise CodeError("call sub target must be mmapped") - if stmt.is_goto: - generate_param_assignments() - branch_emitter(targetstr, True, False) - # no result assignments because it's a goto - return - clobbered = set() # type: Set[str] - if targetdef.clobbered_registers: - if stmt.preserve_regs is not None: - clobbered = targetdef.clobbered_registers & stmt.preserve_regs - clobbered = unclobber_result_registers(clobbered, stmt.desugared_output_assignments) - with self.preserving_registers(clobbered, loads_a_within=params_load_a(), always_preserve=stmt.preserve_regs is not None): - generate_param_assignments() - branch_emitter(targetstr, False, False) - generate_result_assignments() - return - if isinstance(stmt.target, IndirectValue): - if stmt.target.name: - targetstr = stmt.target.name - elif stmt.address is not None: - targetstr = Parser.to_hex(stmt.address) - elif stmt.target.value.name: - targetstr = stmt.target.value.name - elif isinstance(stmt.target.value, RegisterValue): - targetstr = stmt.target.value.register - elif isinstance(stmt.target.value, IntegerValue): - targetstr = stmt.target.value.name or Parser.to_hex(stmt.target.value.value) - else: - raise CodeError("missing name", stmt.target.value) - if stmt.is_goto: - # no need to preserve registers for a goto - generate_param_assignments() - if targetstr in REGISTER_WORDS: - self.p("\t\tst{:s} {:s}".format(targetstr[0].lower(), Parser.to_hex(Zeropage.SCRATCH_B1))) - self.p("\t\tst{:s} {:s}".format(targetstr[1].lower(), Parser.to_hex(Zeropage.SCRATCH_B2))) - branch_emitter(Parser.to_hex(Zeropage.SCRATCH_B1), True, True) - else: - branch_emitter(targetstr, True, True) - # no result assignments because it's a goto - else: - # indirect call to subroutine - preserve_regs = unclobber_result_registers(stmt.preserve_regs or set(), stmt.desugared_output_assignments) - with self.preserving_registers(preserve_regs, loads_a_within=params_load_a(), - always_preserve=stmt.preserve_regs is not None): - generate_param_assignments() - if targetstr in REGISTER_WORDS: - print("warning: {}: indirect register pair call is quite inefficient, use a jump table in memory instead?" - .format(stmt.sourceref)) - if stmt.preserve_regs is not None: - # cannot use zp scratch because it may be used by the register backup. This is very inefficient code! - self.p("\t\tjsr il65_lib.jsr_indirect_nozpuse_"+targetstr) - - else: - self.p("\t\tjsr il65_lib.jsr_indirect_"+targetstr) - else: - self.p("\t\tjsr +") - self.p("\t\tjmp ++") - self.p("+\t\tjmp ({:s})".format(targetstr)) - self.p("+") - generate_result_assignments() - else: - # call to a label or immediate address - if stmt.target.name: - targetstr = stmt.target.name - elif stmt.address is not None: - targetstr = Parser.to_hex(stmt.address) - elif isinstance(stmt.target, IntegerValue): - targetstr = stmt.target.name or Parser.to_hex(stmt.target.value) - else: - raise CodeError("missing name", stmt.target) - if stmt.is_goto: - # no need to preserve registers for a goto - generate_param_assignments() - branch_emitter(targetstr, True, False) - # no result assignments because it's a goto - else: - preserve_regs = unclobber_result_registers(stmt.preserve_regs or set(), stmt.desugared_output_assignments) - with self.preserving_registers(preserve_regs, loads_a_within=params_load_a(), - always_preserve=stmt.preserve_regs is not None): - generate_param_assignments() - branch_emitter(targetstr, False, False) - generate_result_assignments() - - def generate_assignment(self, stmt: AssignmentStmt) -> None: - def unwrap_indirect(iv: IndirectValue) -> MemMappedValue: - if isinstance(iv.value, MemMappedValue): - return iv.value - elif iv.value.constant and isinstance(iv.value, IntegerValue): - return MemMappedValue(iv.value.value, iv.datatype, 1, stmt.sourceref, iv.name) - else: - raise CodeError("cannot yet generate code for assignment: non-constant and non-memmapped indirect") # XXX - - rvalue = stmt.right - if isinstance(rvalue, IndirectValue): - rvalue = unwrap_indirect(rvalue) - self.p("\t\t\t\t\t; " + stmt.lineref) - if isinstance(rvalue, IntegerValue): - for lv in stmt.leftvalues: - if isinstance(lv, RegisterValue): - self.generate_assign_integer_to_reg(lv.register, rvalue) - elif isinstance(lv, MemMappedValue): - self.generate_assign_integer_to_mem(lv, rvalue) - elif isinstance(lv, IndirectValue): - lv = unwrap_indirect(lv) - self.generate_assign_integer_to_mem(lv, rvalue) - else: - raise CodeError("invalid assignment target (1)", str(stmt)) - elif isinstance(rvalue, RegisterValue): - for lv in stmt.leftvalues: - if isinstance(lv, RegisterValue): - self.generate_assign_reg_to_reg(lv, rvalue.register) - elif isinstance(lv, MemMappedValue): - self.generate_assign_reg_to_memory(lv, rvalue.register) - elif isinstance(lv, IndirectValue): - lv = unwrap_indirect(lv) - self.generate_assign_reg_to_memory(lv, rvalue.register) - else: - raise CodeError("invalid assignment target (2)", str(stmt)) - elif isinstance(rvalue, StringValue): - r_str = self.output_string(rvalue.value, True) - for lv in stmt.leftvalues: - if isinstance(lv, RegisterValue): - if len(rvalue.value) == 1: - self.generate_assign_char_to_reg(lv, r_str) - else: - self.generate_assign_string_to_reg(lv, rvalue) - elif isinstance(lv, MemMappedValue): - if len(rvalue.value) == 1: - self.generate_assign_char_to_memory(lv, r_str) - else: - self.generate_assign_string_to_memory(lv, rvalue) - elif isinstance(lv, IndirectValue): - lv = unwrap_indirect(lv) - if len(rvalue.value) == 1: - self.generate_assign_char_to_memory(lv, r_str) - else: - self.generate_assign_string_to_memory(lv, rvalue) - else: - raise CodeError("invalid assignment target (2)", str(stmt)) - elif isinstance(rvalue, MemMappedValue): - for lv in stmt.leftvalues: - if isinstance(lv, RegisterValue): - self.generate_assign_mem_to_reg(lv.register, rvalue) - elif isinstance(lv, MemMappedValue): - self.generate_assign_mem_to_mem(lv, rvalue) - elif isinstance(lv, IndirectValue): - lv = unwrap_indirect(lv) - self.generate_assign_mem_to_mem(lv, rvalue) - else: - raise CodeError("invalid assignment target (4)", str(stmt)) - elif isinstance(rvalue, FloatValue): - for lv in stmt.leftvalues: - if isinstance(lv, MemMappedValue) and lv.datatype == DataType.FLOAT: - self.generate_assign_float_to_mem(lv, rvalue) - elif isinstance(lv, IndirectValue): - lv = unwrap_indirect(lv) - assert lv.datatype == DataType.FLOAT - self.generate_assign_float_to_mem(lv, rvalue) - else: - raise CodeError("cannot assign float to ", str(lv)) - else: - raise CodeError("invalid assignment value type", str(stmt)) - - def generate_assign_float_to_mem(self, mmv: MemMappedValue, - rvalue: Union[FloatValue, IntegerValue]) -> None: - floatvalue = float(rvalue.value) - mflpt = self.to_mflpt5(floatvalue) - target = mmv.name or Parser.to_hex(mmv.address) - with self.preserving_registers({'A'}): - self.p("\t\t\t\t\t; {:s} = {}".format(target, rvalue.name or floatvalue)) - a_reg_value = None - for i, byte in enumerate(mflpt): - if byte != a_reg_value: - self.p("\t\tlda #${:02x}".format(byte)) - a_reg_value = byte - self.p("\t\tsta {:s}+{:d}".format(target, i)) - - def generate_assign_reg_to_memory(self, lv: MemMappedValue, r_register: str) -> None: - # Memory = Register - lv_string = lv.name or Parser.to_hex(lv.address) - if lv.datatype == DataType.BYTE: - if len(r_register) > 1: - raise CodeError("cannot assign register pair to single byte memory") - self.p("\t\tst{:s} {}".format(r_register.lower(), lv_string)) - elif lv.datatype == DataType.WORD: - if len(r_register) == 1: - self.p("\t\tst{:s} {}".format(r_register.lower(), lv_string)) # lsb - with self.preserving_registers({'A'}, loads_a_within=True): - self.p("\t\tlda #0") - self.p("\t\tsta {:s}+1".format(lv_string)) # msb - else: - self.p("\t\tst{:s} {}".format(r_register[0].lower(), lv_string)) - self.p("\t\tst{:s} {}+1".format(r_register[1].lower(), lv_string)) - elif lv.datatype == DataType.FLOAT: - # assigning a register to a float requires c64 ROM routines - if r_register in REGISTER_WORDS: - def do_rom_calls(): - self.p("\t\tjsr c64flt.GIVUAYF") # uword AY -> fac1 - self.p("\t\tldx #<" + lv_string) - self.p("\t\tldy #>" + lv_string) - self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY - if r_register == "AY": - with self.preserving_registers({'A', 'X', 'Y'}): - do_rom_calls() - elif r_register == "AX": - with self.preserving_registers({'A', 'X', 'Y'}): - self.p("\t\tpha\n\t\ttxa\n\t\ttay\n\t\tpla") # X->Y (so we have AY now) - do_rom_calls() - else: # XY - with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True): - self.p("\t\ttxa") # X->A (so we have AY now) - do_rom_calls() - elif r_register in "AXY": - - def do_rom_calls(): - self.p("\t\tjsr c64.FREADUY") # ubyte Y -> fac1 - self.p("\t\tldx #<" + lv_string) - self.p("\t\tldy #>" + lv_string) - self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY - - if r_register == "A": - with self.preserving_registers({'A', 'X', 'Y'}): - self.p("\t\ttay") - do_rom_calls() - elif r_register == "X": - with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True): - self.p("\t\ttxa") - self.p("\t\ttay") - do_rom_calls() - elif r_register == "Y": - with self.preserving_registers({'A', 'X', 'Y'}): - do_rom_calls() - else: - raise CodeError("invalid register to assign to float", r_register) - else: - raise CodeError("invalid lvalue type", lv.datatype) - - def generate_assign_reg_to_reg(self, lv: RegisterValue, r_register: str) -> None: - if lv.register != r_register: - if lv.register == 'A': # x/y -> a - self.p("\t\tt{:s}a".format(r_register.lower())) - elif lv.register == 'Y': - if r_register == 'A': - # a -> y - self.p("\t\ttay") - else: - # x -> y, 6502 doesn't have txy - self.p("\t\tstx ${0:02x}\n\t\tldy ${0:02x}".format(Zeropage.SCRATCH_B1)) - elif lv.register == 'X': - if r_register == 'A': - # a -> x - self.p("\t\ttax") - else: - # y -> x, 6502 doesn't have tyx - self.p("\t\tsty ${0:02x}\n\t\tldx ${0:02x}".format(Zeropage.SCRATCH_B1)) - elif lv.register in REGISTER_WORDS: - if len(r_register) == 1: - # assign one register to a pair, so the hi byte is zero. - if lv.register == "AX" and r_register == "A": - self.p("\t\tldx #0") - elif lv.register == "AX" and r_register == "X": - self.p("\t\ttxa\n\t\tldx #0") - elif lv.register == "AX" and r_register == "Y": - self.p("\t\ttya\n\t\tldx #0") - elif lv.register == "AY" and r_register == "A": - self.p("\t\tldy #0") - elif lv.register == "AY" and r_register == "X": - self.p("\t\ttxa\n\t\tldy #0") - elif lv.register == "AY" and r_register == "Y": - self.p("\t\ttya\n\t\tldy #0") - elif lv.register == "XY" and r_register == "A": - self.p("\t\ttax\n\t\tldy #0") - elif lv.register == "XY" and r_register == "X": - self.p("\t\tldy #0") - elif lv.register == "XY" and r_register == "Y": - self.p("\t\ttyx\n\t\tldy #0") - else: - raise CodeError("invalid register combination", lv.register, r_register) - elif lv.register == "AX" and r_register == "AY": - # y -> x, 6502 doesn't have tyx - self.p("\t\tsty ${0:02x}\n\t\tldx ${0:02x}".format(Zeropage.SCRATCH_B1)) - elif lv.register == "AX" and r_register == "XY": - # x -> a, y -> x, 6502 doesn't have tyx - self.p("\t\ttxa") - self.p("\t\tsty ${0:02x}\n\t\tldx ${0:02x}".format(Zeropage.SCRATCH_B1)) - elif lv.register == "AY" and r_register == "AX": - # x -> y, 6502 doesn't have txy - self.p("\t\tstx ${0:02x}\n\t\tldy ${0:02x}".format(Zeropage.SCRATCH_B1)) - elif lv.register == "AY" and r_register == "XY": - # x -> a - self.p("\t\ttxa") - elif lv.register == "XY" and r_register == "AX": - # x -> y, a -> x, 6502 doesn't have txy - self.p("\t\tstx ${0:02x}\n\t\tldy ${0:02x}".format(Zeropage.SCRATCH_B1)) - self.p("\t\ttax") - elif lv.register == "XY" and r_register == "AY": - # a -> x - self.p("\t\ttax") - else: - raise CodeError("invalid register combination", lv.register, r_register) - else: - raise CodeError("invalid register " + lv.register) - - def generate_assign_integer_to_mem(self, lv: MemMappedValue, rvalue: IntegerValue) -> None: - if lv.name: - symblock, sym = self.cur_block.lookup(lv.name) - if not isinstance(sym, VariableDef): - raise CodeError("invalid lvalue type " + str(sym)) - assign_target = symblock.label + '.' + sym.name if symblock is not self.cur_block else lv.name - lvdatatype = sym.type - else: - assign_target = Parser.to_hex(lv.address) - lvdatatype = lv.datatype - r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.value) - if lvdatatype == DataType.BYTE: - if rvalue.value is not None and not lv.assignable_from(rvalue) or rvalue.datatype != DataType.BYTE: - raise OverflowError("value doesn't fit in a byte") - with self.preserving_registers({'A'}, loads_a_within=True): - self.p("\t\tlda #" + r_str) - self.p("\t\tsta " + assign_target) - elif lvdatatype == DataType.WORD: - if rvalue.value is not None and not lv.assignable_from(rvalue): - raise OverflowError("value doesn't fit in a word") - with self.preserving_registers({'A'}, loads_a_within=True): - self.p("\t\tlda #<" + r_str) - self.p("\t\tsta " + assign_target) - self.p("\t\tlda #>" + r_str) - self.p("\t\tsta {}+1".format(assign_target)) - elif lvdatatype == DataType.FLOAT: - if rvalue.value is not None and not DataType.FLOAT.assignable_from_value(rvalue.value): - raise CodeError("value cannot be assigned to a float") - self.generate_assign_float_to_mem(lv, rvalue) - else: - raise CodeError("invalid lvalue type " + str(lvdatatype)) - - def generate_assign_mem_to_reg(self, l_register: str, rvalue: MemMappedValue) -> None: - r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.address) - if len(l_register) == 1: - if rvalue.datatype != DataType.BYTE: - raise CodeError("can only assign a byte to a register") - self.p("\t\tld{:s} {:s}".format(l_register.lower(), r_str)) - else: - if rvalue.datatype == DataType.BYTE: - self.p("\t\tld{:s} {:s}".format(l_register[0].lower(), r_str)) - self.p("\t\tld{:s} #0".format(l_register[1].lower())) - elif rvalue.datatype == DataType.WORD: - self.p("\t\tld{:s} {:s}".format(l_register[0].lower(), r_str)) - self.p("\t\tld{:s} {:s}+1".format(l_register[1].lower(), r_str)) - else: - raise CodeError("can only assign a byte or word to a register pair") - - def generate_assign_mem_to_mem(self, lv: MemMappedValue, rvalue: MemMappedValue) -> None: - r_str = rvalue.name or Parser.to_hex(rvalue.address) - l_str = lv.name or Parser.to_hex(lv.address) - if lv.datatype == DataType.BYTE: - if rvalue.datatype != DataType.BYTE: - raise CodeError("can only assign a byte to a byte", str(rvalue)) - with self.preserving_registers({'A'}, loads_a_within=True): - self.p("\t\tlda " + r_str) - self.p("\t\tsta " + l_str) - elif lv.datatype == DataType.WORD: - if rvalue.datatype == DataType.BYTE: - with self.preserving_registers({'A'}, loads_a_within=True): - self.p("\t\tlda " + r_str) - self.p("\t\tsta " + l_str) - self.p("\t\tlda #0") - self.p("\t\tsta {:s}+1".format(l_str)) - elif rvalue.datatype == DataType.WORD: - with self.preserving_registers({'A'}, loads_a_within=True): - self.p("\t\tlda {:s}".format(r_str)) - self.p("\t\tsta {:s}".format(l_str)) - self.p("\t\tlda {:s}+1".format(r_str)) - self.p("\t\tsta {:s}+1".format(l_str)) - else: - raise CodeError("can only assign a byte or word to a word", str(rvalue)) - elif lv.datatype == DataType.FLOAT: - if rvalue.datatype == DataType.FLOAT: - with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True): - self.p("\t\tlda #<" + r_str) - self.p("\t\tsta c64.SCRATCH_ZPWORD1") - self.p("\t\tlda #>" + r_str) - self.p("\t\tsta c64.SCRATCH_ZPWORD1+1") - self.p("\t\tldx #<" + l_str) - self.p("\t\tldy #>" + l_str) - self.p("\t\tjsr c64flt.copy_mflt") - elif rvalue.datatype == DataType.BYTE: - with self.preserving_registers({'A', 'X', 'Y'}): - self.p("\t\tldy " + r_str) - self.p("\t\tjsr c64.FREADUY") # ubyte Y -> fac1 - self.p("\t\tldx #<" + l_str) - self.p("\t\tldy #>" + l_str) - self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY - elif rvalue.datatype == DataType.WORD: - with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True): - self.p("\t\tlda " + r_str) - self.p("\t\tldy {:s}+1".format(r_str)) - self.p("\t\tjsr c64flt.GIVUAYF") # uword AY -> fac1 - self.p("\t\tldx #<" + l_str) - self.p("\t\tldy #>" + l_str) - self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY - else: - raise CodeError("unsupported rvalue to memfloat", str(rvalue)) - else: - raise CodeError("invalid lvalue memmapped datatype", str(lv)) - - def generate_assign_char_to_memory(self, lv: MemMappedValue, char_str: str) -> None: - # Memory = Character - with self.preserving_registers({'A'}, loads_a_within=True): - self.p("\t\tlda #" + char_str) - if not lv.name: - self.p("\t\tsta " + Parser.to_hex(lv.address)) - return - # assign char value to a memory location by symbol name - symblock, sym = self.cur_block.lookup(lv.name) - if isinstance(sym, VariableDef): - assign_target = lv.name - if symblock is not self.cur_block: - assign_target = symblock.label + '.' + sym.name - if sym.type == DataType.BYTE: - self.p("\t\tsta " + assign_target) - elif sym.type == DataType.WORD: - self.p("\t\tsta " + assign_target) - self.p("\t\tlda #0") - self.p("\t\tsta {}+1".format(assign_target)) - else: - raise CodeError("invalid lvalue type " + str(sym)) - else: - raise CodeError("invalid lvalue type " + str(sym)) - - def generate_assign_integer_to_reg(self, l_register: str, rvalue: IntegerValue) -> None: - r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.value) - if l_register in ('A', 'X', 'Y'): - self.p("\t\tld{:s} #{:s}".format(l_register.lower(), r_str)) - elif l_register in REGISTER_WORDS: - self.p("\t\tld{:s} #<{:s}".format(l_register[0].lower(), r_str)) - self.p("\t\tld{:s} #>{:s}".format(l_register[1].lower(), r_str)) - elif l_register == "SC": - # set/clear S carry bit - if rvalue.value: - self.p("\t\tsec") - else: - self.p("\t\tclc") - elif l_register == "SI": - # interrupt disable bit - if rvalue.value: - self.p("\t\tsei") - else: - self.p("\t\tcli") - else: - raise CodeError("invalid register in immediate integer assignment", l_register, rvalue.value) - - def generate_assign_char_to_reg(self, lv: RegisterValue, char_str: str) -> None: - # Register = Char (string of length 1) - if lv.register not in ('A', 'X', 'Y'): - raise CodeError("invalid register for char assignment", lv.register) - self.p("\t\tld{:s} #{:s}".format(lv.register.lower(), char_str)) - - def generate_assign_string_to_reg(self, lv: RegisterValue, rvalue: StringValue) -> None: - if lv.register not in ("AX", "AY", "XY"): - raise CodeError("need register pair AX, AY or XY for string address assignment", lv.register) - if rvalue.name: - self.p("\t\tld{:s} #<{:s}".format(lv.register[0].lower(), rvalue.name)) - self.p("\t\tld{:s} #>{:s}".format(lv.register[1].lower(), rvalue.name)) - else: - raise CodeError("cannot assign immediate string, it must be a string variable") - - def generate_assign_string_to_memory(self, lv: MemMappedValue, rvalue: StringValue) -> None: - if lv.datatype != DataType.WORD: - raise CodeError("need word memory type for string address assignment") - if rvalue.name: - assign_target = lv.name if lv.name else Parser.to_hex(lv.address) - self.p("\t\tlda #<{:s}".format(rvalue.name)) - self.p("\t\tsta " + assign_target) - self.p("\t\tlda #>{:s}".format(rvalue.name)) - self.p("\t\tsta {}+1".format(assign_target)) - else: - raise CodeError("cannot assign immediate string, it must be a string variable") diff --git a/python-prototype/il65/optimize.py b/python-prototype/il65/optimize.py deleted file mode 100644 index 978bf0919..000000000 --- a/python-prototype/il65/optimize.py +++ /dev/null @@ -1,326 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the optimizer that applies various optimizations to the parse tree, -eliminates statements that have no effect, optimizes calculations etc. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - - -from typing import List, no_type_check, Union -from .datatypes import DataType, VarType -from .plyparse import * -from .plylex import print_warning, print_bold -from .constantfold import ConstantFold - - -class Optimizer: - def __init__(self, mod: Module) -> None: - self.num_warnings = 0 - self.module = mod - self.optimizations_performed = False - self.constant_folder = ConstantFold(self.module) - - def optimize(self) -> None: - self.num_warnings = 0 - self.optimizations_performed = True - # keep optimizing as long as there were changes made - while self.optimizations_performed: - self.optimizations_performed = False - self._optimize() - # remaining optimizations that have to be done just once: - self.remove_unused_subroutines() - self.remove_empty_blocks() - - def _optimize(self) -> None: - self.constant_folder.fold_constants(True) # perform constant folding and simple expression optimization - # @todo expression optimization: reduce expression nesting / flattening of parenthesis - # @todo expression optimization: simplify logical expression when a term makes it always true or false - # @todo expression optimization: optimize some simple multiplications into shifts (A*=8 -> A<<3) - # @todo expression optimization: create augmented assignment from assignment that only refers to its lvalue (A=A+10, A=4*A, ...) - self.optimize_assignments() - self.remove_superfluous_assignments() - # @todo optimize addition with self into shift 1 (A+=A -> A<<=1) - self.optimize_goto_compare_with_zero() - self.join_incrdecrs() - # @todo remove gotos with conditions that are always false - # @todo remove loops with conditions that are always empty/false - # @todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) - - def join_incrdecrs(self) -> None: - def combine(incrdecrs: List[IncrDecr], scope: Scope) -> None: - # combine the separate incrdecrs - replaced = False - total = 0 - for i in incrdecrs: - if i.operator == "++": - total += i.howmuch - else: - total -= i.howmuch - if total == 0: - replaced = True - for x in incrdecrs: - scope.remove_node(x) - else: - is_float = False - if isinstance(target, SymbolName): - symdef = target.my_scope().lookup(target.name) - is_float = isinstance(symdef, VarDef) and symdef.datatype == DataType.FLOAT - elif isinstance(target, Dereference): - is_float = target.datatype == DataType.FLOAT - if is_float or -255 <= total <= 255: - replaced = True - for x in incrdecrs[1:]: - scope.remove_node(x) - incrdecr = self._make_incrdecr(incrdecrs[0], target, abs(total), "++" if total >= 0 else "--") - scope.replace_node(incrdecrs[0], incrdecr) - else: - # total is > 255 or < -255, make an augmented assignment out of it instead of an incrdecr - aug_assign = AugAssignment(operator="-=" if total < 0 else "+=", sourceref=incrdecrs[0].sourceref) # type: ignore - left = incrdecrs[0].target - right = LiteralValue(value=abs(total), sourceref=incrdecrs[0].sourceref) # type: ignore - left.parent = aug_assign - right.parent = aug_assign - aug_assign.nodes.append(left) - aug_assign.nodes.append(right) - aug_assign.mark_lhs() - replaced = True - for x in incrdecrs[1:]: - scope.remove_node(x) - scope.replace_node(incrdecrs[0], aug_assign) - if replaced: - self.optimizations_performed = True - self.num_warnings += 1 - print_warning("{}: merged a sequence of incr/decrs or augmented assignments".format(incrdecrs[0].sourceref)) - - for scope in self.module.all_nodes(Scope): - target = None - incrdecrs = [] # type: List[IncrDecr] - for node in list(scope.nodes): - if isinstance(node, IncrDecr): - if target is None: - target = node.target - incrdecrs.append(node) - continue - if self._same_target(target, node.target): - incrdecrs.append(node) - continue - if len(incrdecrs) > 1: - combine(incrdecrs, scope) # type: ignore - incrdecrs.clear() - target = None - if isinstance(node, IncrDecr): - # it was an incrdecr with a different target than what we had gathered so far. - if target is None: - target = node.target - incrdecrs.append(node) - if len(incrdecrs) > 1: - # combine remaining incrdecrs at the bottom of the block - combine(incrdecrs, scope) # type: ignore - - def _same_target(self, node1: Union[Register, SymbolName, Dereference], - node2: Union[Register, SymbolName, Dereference]) -> bool: - if isinstance(node1, Register) and isinstance(node2, Register) and node1.name == node2.name: - return True - if isinstance(node1, SymbolName) and isinstance(node2, SymbolName) and node1.name == node2.name: - return True - if isinstance(node1, Dereference) and isinstance(node2, Dereference): - if type(node1.operand) is not type(node2.operand): - return False - if isinstance(node1.operand, (SymbolName, LiteralValue, Register)): - return node1.operand == node2.operand - if not isinstance(node1, AstNode) or not isinstance(node2, AstNode): - raise TypeError("same_target called with invalid type(s)", node1, node2) - return False - - def remove_superfluous_assignments(self) -> None: - # remove consecutive assignment statements to the same target, only keep the last value (only if its a constant!) - # this is NOT done for memory mapped variables because these often represent a volatile register of some sort! - for scope in self.module.all_nodes(Scope): - prev_node = None # type: AstNode - for node in list(scope.nodes): - if isinstance(node, Assignment) and isinstance(prev_node, Assignment): - if isinstance(node.right, (LiteralValue, Register)) and self._same_target(node.left, prev_node.left): - if isinstance(node.left, SymbolName): - # only optimize if the symbol is not a memory mapped address (volatile memory!) - symdef = node.left.my_scope().lookup(node.left.name) - if isinstance(symdef, VarDef) and symdef.vartype == VarType.MEMORY: - continue - scope.remove_node(prev_node) - self.optimizations_performed = True - self.num_warnings += 1 - print_warning("{}: removed superfluous assignment".format(prev_node.sourceref)) - prev_node = node - - @no_type_check - def optimize_assignments(self) -> None: - # remove assignment statements that do nothing (A=A) - # remove augmented assignments that have no effect (x+=0, x-=0, x/=1, x//=1, x*=1) - # convert augmented assignments to simple incr/decr if value allows it (A+=10 => incr A by 10) - # simplify some calculations (x*=0, x**=0) to simple constant value assignment - # @todo remove or simplify logical aug assigns like A |= 0, A |= true, A |= false (or perhaps turn them into byte values first?) - for assignment in self.module.all_nodes(): - if isinstance(assignment, Assignment): - if self._same_target(assignment.left, assignment.right): - assignment.my_scope().remove_node(assignment) - self.optimizations_performed = True - self.num_warnings += 1 - print_warning("{}: removed statement that has no effect (left=right)".format(assignment.sourceref)) - elif isinstance(assignment, AugAssignment): - if isinstance(assignment.right, LiteralValue) and isinstance(assignment.right.value, (int, float)): - if assignment.right.value == 0: - if assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="): - self.num_warnings += 1 - print_warning("{}: removed statement that has no effect (aug.assign zero)".format(assignment.sourceref)) - assignment.my_scope().remove_node(assignment) - self.optimizations_performed = True - elif assignment.operator == "*=": - self.num_warnings += 1 - print_warning("{}: statement replaced by = 0".format(assignment.sourceref)) - new_assignment = self._make_new_assignment(assignment, 0) - assignment.my_scope().replace_node(assignment, new_assignment) - self.optimizations_performed = True - elif assignment.operator == "**=": - self.num_warnings += 1 - print_warning("{}: statement replaced by = 1".format(assignment.sourceref)) - new_assignment = self._make_new_assignment(assignment, 1) - assignment.my_scope().replace_node(assignment, new_assignment) - self.optimizations_performed = True - elif assignment.right.value >= 8 and assignment.operator in ("<<=", ">>="): - print("{}: shifting result is always zero".format(assignment.sourceref)) - new_stmt = Assignment(sourceref=assignment.sourceref) - new_stmt.nodes.append(assignment.left) - new_stmt.nodes.append(LiteralValue(value=0, sourceref=assignment.sourceref)) - assignment.my_scope().replace_node(assignment, new_stmt) - assignment.mark_lhs() - self.optimizations_performed = True - elif assignment.operator in ("+=", "-=") and 0 < assignment.right.value < 256: - howmuch = assignment.right - if howmuch.value not in (0, 1): - _, howmuch = coerce_constant_value(datatype_of(assignment.left, assignment.my_scope()), - howmuch, assignment.sourceref) - new_stmt = IncrDecr(operator="++" if assignment.operator == "+=" else "--", - howmuch=howmuch.value, sourceref=assignment.sourceref) - new_stmt.target = assignment.left - new_stmt.target.parent = new_stmt - assignment.my_scope().replace_node(assignment, new_stmt) - self.optimizations_performed = True - elif assignment.right.value == 1 and assignment.operator in ("/=", "//=", "*="): - self.num_warnings += 1 - print_warning("{}: removed statement that has no effect (aug.assign identity)".format(assignment.sourceref)) - assignment.my_scope().remove_node(assignment) - self.optimizations_performed = True - - @no_type_check - def _make_new_assignment(self, old_aug_assignment: AugAssignment, constantvalue: int) -> Assignment: - new_assignment = Assignment(sourceref=old_aug_assignment.sourceref) - new_assignment.parent = old_aug_assignment.parent - left = old_aug_assignment.left - left.parent = new_assignment - new_assignment.nodes.append(left) - value = LiteralValue(value=constantvalue, sourceref=old_aug_assignment.sourceref) - value.parent = new_assignment - new_assignment.nodes.append(value) - new_assignment.mark_lhs() - return new_assignment - - @no_type_check - def _make_aug_assign(self, old_assign: Assignment, target: Union[Register, SymbolName, Dereference], - value: Union[int, float], operator: str) -> AugAssignment: - assert isinstance(target, (Register, SymbolName, Dereference)) - a = AugAssignment(operator=operator, sourceref=old_assign.sourceref) - a.nodes.append(target) - target.parent = a - lv = LiteralValue(value=value, sourceref=old_assign.sourceref) - a.nodes.append(lv) - lv.parent = a - a.parent = old_assign.parent - a.mark_lhs() - return a - - @no_type_check - def _make_incrdecr(self, old_stmt: AstNode, target: Union[Register, SymbolName, Dereference], - howmuch: Union[int, float], operator: str) -> IncrDecr: - assert isinstance(target, (Register, SymbolName, Dereference)) - a = IncrDecr(operator=operator, howmuch=howmuch, sourceref=old_stmt.sourceref) - a.nodes.append(target) - target.parent = a - a.parent = old_stmt.parent - return a - - @no_type_check - def remove_unused_subroutines(self) -> None: - # some symbols are used by the emitted assembly code from the code generator, - # and should never be removed or the assembler will fail - never_remove = {"c64.FREADUY", "c64.FTOMEMXY", "c64.FADD", "c64.FSUB", - "c64flt.GIVUAYF", "c64flt.copy_mflt", "c64flt.float_add_one", "c64flt.float_sub_one", - "c64flt.float_add_SW1_to_XY", "c64flt.float_sub_SW1_from_XY"} - num_discarded = 0 - for sub in self.module.all_nodes(Subroutine): - usages = self.module.subroutine_usage[(sub.parent.name, sub.name)] - if not usages and sub.parent.name + '.' + sub.name not in never_remove: - sub.parent.remove_node(sub) - num_discarded += 1 - # if num_discarded: - # print("discarded {:d} unused subroutines".format(num_discarded)) - - @no_type_check - def optimize_goto_compare_with_zero(self) -> None: - # a conditional goto that compares a value with zero will be simplified - # the comparison operator and rvalue (0) will be removed and the if-status changed accordingly - for goto in self.module.all_nodes(Goto): - if isinstance(goto.condition, Expression): - pass # @todo optimize goto conditionals - # if cond and isinstance(cond.rvalue, (int, float)) and cond.rvalue.value == 0: - # simplified = False - # if cond.ifstatus in ("true", "ne"): - # if cond.comparison_op == "==": - # # if_true something == 0 -> if_not something - # cond.ifstatus = "not" - # cond.comparison_op, cond.rvalue = "", None - # simplified = True - # elif cond.comparison_op == "!=": - # # if_true something != 0 -> if_true something - # cond.comparison_op, cond.rvalue = "", None - # simplified = True - # elif cond.ifstatus in ("not", "eq"): - # if cond.comparison_op == "==": - # # if_not something == 0 -> if_true something - # cond.ifstatus = "true" - # cond.comparison_op, cond.rvalue = "", None - # simplified = True - # elif cond.comparison_op == "!=": - # # if_not something != 0 -> if_not something - # cond.comparison_op, cond.rvalue = "", None - # simplified = True - # if simplified: - # print("{}: simplified comparison with zero".format(stmt.sourceref)) - - def remove_empty_blocks(self) -> None: - # remove blocks without name and without address, or that are empty - for node in self.module.all_nodes(): - if isinstance(node, (Subroutine, Block)): - if not node.scope: - continue - if all(isinstance(n, Directive) for n in node.scope.nodes): - empty = True - for n in node.scope.nodes: - empty = empty and n.name not in {"asmbinary", "asminclude"} - if empty: - self.num_warnings += 1 - print_warning("ignoring empty block or subroutine", node.sourceref) - assert isinstance(node.parent, (Block, Module)) - node.my_scope().nodes.remove(node) - if isinstance(node, Block): - if not node.name and node.address is None: - self.num_warnings += 1 - print_warning("ignoring block without name and address", node.sourceref) - assert isinstance(node.parent, Module) - node.my_scope().nodes.remove(node) - - -def optimize(mod: Module) -> None: - opt = Optimizer(mod) - opt.optimize() - if opt.num_warnings: - print_bold("There are {:d} optimization warnings.".format(opt.num_warnings)) diff --git a/python-prototype/il65/plylex.py b/python-prototype/il65/plylex.py deleted file mode 100644 index 8431e5e0f..000000000 --- a/python-prototype/il65/plylex.py +++ /dev/null @@ -1,372 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the lexer of the IL65 code, that generates a stream of tokens for the parser. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import ast -import sys -import ply.lex -import attr - - -@attr.s(slots=True, frozen=True) -class SourceRef: - file = attr.ib(type=str) - line = attr.ib(type=int) - column = attr.ib(type=int, default=0) - - def __str__(self) -> str: - if self.column: - return "{:s}:{:d}:{:d}".format(self.file, self.line, self.column) - if self.line: - return "{:s}:{:d}".format(self.file, self.line) - return self.file - - -# token names - -tokens = ( - "INTEGER", - "FLOATINGPOINT", - "DOTTEDNAME", - "NAME", - "IS", - "CLOBBEREDREGISTER", - "REGISTER", - "COMMENT", - "DIRECTIVE", - "AUGASSIGN", - "EQUALS", - "NOTEQUALS", - "RARROW", - "RETURN", - "VARTYPE", - "SUB", - "DATATYPE", - "CHARACTER", - "STRING", - "BOOLEAN", - "GOTO", - "INCR", - "DECR", - "LT", - "GT", - "LE", - "GE", - "BITAND", - "BITOR", - "BITXOR", - "BITINVERT", - "SHIFTLEFT", - "SHIFTRIGHT", - "LOGICAND", - "LOGICOR", - "LOGICXOR", - "LOGICNOT", - "INTEGERDIVIDE", - "MODULO", - "POWER", - "LABEL", - "IF", - "PRESERVEREGS", - "INLINEASM", - "ENDL" -) - -literals = ['+', '-', '*', '/', '(', ')', '[', ']', '{', '}', '.', ',', '!', '?', ':'] - -# regex rules for simple tokens - -t_SHIFTLEFT = r"<<" -t_SHIFTRIGHT = r">>" -t_INTEGERDIVIDE = r"//" -t_BITAND = r"&" -t_BITOR = r"\|" -t_BITXOR = r"\^" -t_BITINVERT = r"~" -t_IS = r"=" -t_AUGASSIGN = r"\+=|-=|/=|//=|\*=|\*\*=|<<=|>>=|&=|\|=|\^=" -t_DECR = r"--" -t_INCR = r"\+\+" -t_EQUALS = r"==" -t_NOTEQUALS = r"!=" -t_LT = r"<" -t_GT = r">" -t_LE = r"<=" -t_GE = r">=" -t_IF = "if(_[a-z]+)?" -t_RARROW = r"->" -t_POWER = r"\*\*" - - -# ignore inline whitespace -t_ignore = " \t" -t_inlineasm_ignore = " \t\r\n" - - -# states for allowing %asm inclusion of raw assembly -states = ( - ('inlineasm', 'exclusive'), -) - -# reserved words -reserved = { - "sub": "SUB", - "var": "VARTYPE", - "memory": "VARTYPE", - "const": "VARTYPE", - "goto": "GOTO", - "return": "RETURN", - "true": "BOOLEAN", - "false": "BOOLEAN", - "not": "LOGICNOT", - "and": "LOGICAND", - "or": "LOGICOR", - "xor": "LOGICXOR", - "mod": "MODULO", - "AX": "REGISTER", - "AY": "REGISTER", - "XY": "REGISTER", - "SC": "REGISTER", - "SI": "REGISTER", - "SZ": "REGISTER", - "A": "REGISTER", - "X": "REGISTER", - "Y": "REGISTER", - "if": "IF", - "if_true": "IF", - "if_not": "IF", - "if_zero": "IF", - "if_ne": "IF", - "if_eq": "IF", - "if_cc": "IF", - "if_cs": "IF", - "if_vc": "IF", - "if_vs": "IF", - "if_ge": "IF", - "if_le": "IF", - "if_gt": "IF", - "if_lt": "IF", - "if_pos": "IF", - "if_neg": "IF", -} - - -# rules for tokens with some actions - -def t_inlineasm(t): - r"""%asm\s*\{[^\S\n]*""" - t.lexer.code_start = t.lexer.lexpos # Record start position - t.lexer.level = 1 # initial brace level - t.lexer.begin("inlineasm") # enter state 'inlineasm' - - -def t_inlineasm_lbrace(t): - r"""\{""" - t.lexer.level += 1 - - -def t_inlineasm_rbrace(t): - r"""\}""" - t.lexer.level -= 1 - # if closing brace, return code fragment - if t.lexer.level == 0: - t.value = t.lexer.lexdata[t.lexer.code_start:t.lexer.lexpos-1] - t.type = "INLINEASM" - t.lexer.lineno += t.value.count("\n") - t.lexer.begin("INITIAL") # back to normal lexing rules - return t - - -def t_inlineasm_comment(t): - r""";[^\n]*""" - pass - - -def t_inlineasm_string(t): - r"""(?x) # verbose mode - (?") - sref = SourceRef(filename, line, col) - if hasattr(t.lexer, "error_function"): - t.lexer.error_function(sref, "illegal character '{:s}'", t.value[0]) - else: - print("{}: illegal character '{:s}'".format(sref, t.value[0])) - t.lexer.skip(1) - - -def custom_error(t, message): - line, col = t.lineno, find_tok_column(t) - filename = getattr(t.lexer, "source_filename", "") - sref = SourceRef(filename, line, col) - if hasattr(t.lexer, "error_function"): - t.lexer.error_function(sref, message) - else: - print(sref, message) - t.lexer.skip(1) - - -def find_tok_column(token): - """Find the column of the token in its line.""" - last_cr = lexer.lexdata.rfind('\n', 0, token.lexpos) - chunk = lexer.lexdata[last_cr:token.lexpos] - return len(chunk.expandtabs()) - - -def print_warning(text: str, sourceref: SourceRef = None) -> None: - if sourceref: - print_bold("warning: {}: {:s}".format(sourceref, text)) - else: - print_bold("warning: " + text) - - -def print_bold(text: str) -> None: - if sys.stdout.isatty(): - print("\x1b[1m" + text + "\x1b[0m", flush=True) - else: - print(text) - - -lexer = ply.lex.lex() - - -if __name__ == "__main__": - ply.lex.runmain() diff --git a/python-prototype/il65/plyparse.py b/python-prototype/il65/plyparse.py deleted file mode 100644 index 034f48897..000000000 --- a/python-prototype/il65/plyparse.py +++ /dev/null @@ -1,1682 +0,0 @@ -""" -Programming Language for 6502/6510 microprocessors, codename 'Sick' -This is the parser of the IL65 code, that generates a parse tree. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import math -import builtins -import inspect -import enum -from collections import defaultdict -from typing import Union, Generator, Tuple, List, Optional, Dict, Any, no_type_check, Callable -import attr -from ply.yacc import yacc -from .plylex import SourceRef, tokens, lexer, find_tok_column, print_warning -from .datatypes import (DataType, VarType, REGISTER_SYMBOLS, REGISTER_BYTES, REGISTER_WORDS, - FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE, char_to_bytevalue) - - -__all__ = ["ProgramFormat", "ZpOptions", "math_functions", "builtin_functions", - "AstNode", "Directive", "Scope", "Block", "Module", "Label", "Expression", - "Register", "Subroutine", "LiteralValue", "AddressOf", "SymbolName", "Dereference", "IncrDecr", - "ExpressionWithOperator", "Goto", "SubCall", "VarDef", "Return", "Assignment", "AugAssignment", - "InlineAssembly", "BuiltinFunction", "TokenFilter", "parser", "connect_parents", "DatatypeNode", - "parse_file", "coerce_constant_value", "datatype_of", "check_symbol_definition", "scoped_name", - "NotCompiletimeConstantError", "ExpressionEvaluationError", "ParseError", "UndefinedSymbolError"] - - -class ProgramFormat(enum.Enum): - RAW = "raw" - PRG = "prg" - BASIC = "basicprg" - - -class ZpOptions(enum.Enum): - NOCLOBBER = "noclobber" - CLOBBER = "clobber" - CLOBBER_RESTORE = "clobber_restore" - - -math_functions = {name: func for name, func in vars(math).items() - if inspect.isbuiltin(func) and name != "pow" and not name.startswith("_")} -builtin_functions = {name: getattr(builtins, name) - for name in ['abs', 'bin', 'chr', 'divmod', 'hash', 'hex', 'len', 'oct', 'ord', 'pow', 'round']} -# @todo support more builtins 'all', 'any', 'max', 'min', 'sum' - - -class ParseError(Exception): - def __init__(self, message: str, sourceref: SourceRef) -> None: - super().__init__(message) - self.sourceref = sourceref - # @todo chain attribute, a list of other exceptions, so we can have more than 1 error at a time. - - def __str__(self): - return "{} {:s}".format(self.sourceref, self.args[0]) - - -class NotCompiletimeConstantError(TypeError): - pass - - -class ExpressionEvaluationError(ParseError): - pass - - -class UndefinedSymbolError(LookupError): - pass - - -start = "start" - - -@attr.s(repr=False, cmp=False, slots=True) -class AstNode: - # all ast nodes have: sourceref, parent, and nodes (=list of zero or more sub-nodes) - sourceref = attr.ib(type=SourceRef, init=True) - parent = attr.ib(type='AstNode', init=False, default=None) # will be hooked up later - nodes = attr.ib(type=List['AstNode'], init=False, default=attr.Factory(list)) - - @property - def lineref(self) -> str: - return "src l. " + str(self.sourceref.line) - - def my_scope(self) -> 'Scope': - # returns the closest Scope in the ancestry of this node, or raises LookupError if no scope is found - scope = self.parent - while scope: - if isinstance(scope, Scope): - return scope - scope = scope.parent - raise LookupError("no scope found in node ancestry", self) - - def all_nodes(self, *nodetypes: type) -> Generator['AstNode', None, None]: - if not self.nodes: - # this is the case when a node has been pruned away (nodes=None) or we don't have any child nodes - return - child_nodes = list(self.nodes) - if nodetypes: - for node in child_nodes: - if isinstance(node, nodetypes): - yield node - else: - yield from child_nodes - for node in child_nodes: - yield from node.all_nodes(*nodetypes) - - def remove_node(self, node: 'AstNode') -> None: - assert node.parent is self - self.nodes.remove(node) - node.parent = None - - def replace_node(self, oldnode: 'AstNode', newnode: 'AstNode') -> None: - assert oldnode.parent is self - assert isinstance(newnode, AstNode) - idx = self.nodes.index(oldnode) - self.nodes[idx] = newnode - newnode.parent = self - oldnode.parent = None - oldnode.nodes = None - - def add_node(self, newnode: 'AstNode', index: int = None) -> None: - assert isinstance(newnode, AstNode) - if index is None: - self.nodes.append(newnode) - else: - self.nodes.insert(index, newnode) - newnode.parent = self - - -@attr.s(cmp=False) -class Directive(AstNode): - name = attr.ib(type=str, default=None) - args = attr.ib(type=list, default=attr.Factory(list)) - # no subnodes. - - -@attr.s(cmp=False, slots=True, repr=False) -class Scope(AstNode): - # has zero or more subnodes - level = attr.ib(type=str, init=True) # type: ignore - nodes = attr.ib(type=list, init=True) # requires nodes in __init__ - symbols = attr.ib(init=False) - name = attr.ib(init=False) # will be set by enclosing block, or subroutine etc. - float_const_values = attr.ib(type=dict, default=attr.Factory(dict), init=False) # floatingpoint number -> float const name - _save_registers = attr.ib(type=bool, default=None, init=False) - - @property - def save_registers(self) -> bool: - if self._save_registers is not None: - return self._save_registers - try: - return self.my_scope().save_registers - except LookupError: - return False - - @save_registers.setter - def save_registers(self, save: bool) -> None: - self._save_registers = save - - @property - def parent_scope(self) -> Optional['Scope']: - parent_scope = self.parent - while parent_scope and not isinstance(parent_scope, Scope): - parent_scope = parent_scope.parent - return parent_scope # type: ignore - - def __attrs_post_init__(self): - # populate the symbol table for this scope for fast lookups via scope.lookup("name") or scope.lookup("dotted.name") - self.symbols = {} - for node in self.nodes: - assert isinstance(node, AstNode) - self._populate_symboltable(node) - - def _populate_symboltable(self, node: AstNode) -> None: - if isinstance(node, (Label, VarDef)): - if node.name in self.symbols: - raise ParseError("symbol '{}' already defined at {}".format(node.name, self.symbols[node.name].sourceref), node.sourceref) - self.symbols[node.name] = node - elif isinstance(node, (Subroutine, BuiltinFunction)): - if node.name in self.symbols: - raise ParseError("symbol '{}' already defined at {}".format(node.name, self.symbols[node.name].sourceref), node.sourceref) - self.symbols[node.name] = node - elif isinstance(node, (Block, Scope)): - if node.name: - if node.name != "ZP" and node.name in self.symbols: - raise ParseError("symbol '{}' already defined at {}" - .format(node.name, self.symbols[node.name].sourceref), node.sourceref) - self.symbols[node.name] = node - - @no_type_check - def define_builtin_functions(self) -> None: - for name, func in math_functions.items(): - f = BuiltinFunction(name=name, func=func, sourceref=self.sourceref) - self.add_node(f) - for name, func in builtin_functions.items(): - f = BuiltinFunction(name=name, func=func, sourceref=self.sourceref) - self.add_node(f) - - def define_float_constant(self, value: float) -> str: - if value in self.float_const_values: - return self.float_const_values[value] - name = "il65_float_const_" + str(1 + len(self.float_const_values)) - self.float_const_values[name] = value - return name - - def lookup(self, name: str) -> AstNode: - assert isinstance(name, str) - if '.' in name: - # look up the dotted name starting from the topmost scope - scope = self - node = self # type: AstNode - while node.parent: - if isinstance(node.parent, Scope): - scope = node.parent - node = node.parent - for namepart in name.split('.'): - if isinstance(scope, (Block, Subroutine)): - scope = scope.scope - if not isinstance(scope, Scope): - raise UndefinedSymbolError("undefined symbol: " + name) - scope = scope.symbols.get(namepart, None) - if not scope: - raise UndefinedSymbolError("undefined symbol: " + name) - return scope - else: - # find the name in nested scope hierarchy - if name in self.symbols: - return self.symbols[name] - parent_scope = self.parent - while parent_scope and not isinstance(parent_scope, Scope): - parent_scope = parent_scope.parent - if parent_scope: - assert isinstance(parent_scope, Scope) - return parent_scope.lookup(name) - raise UndefinedSymbolError("undefined symbol: " + name) - - def remove_node(self, node: AstNode) -> None: - if hasattr(node, "name"): - try: - del self.symbols[node.name] # type: ignore - except KeyError: - pass - super().remove_node(node) - - def replace_node(self, oldnode: AstNode, newnode: AstNode) -> None: - if hasattr(oldnode, "name"): - del self.symbols[oldnode.name] # type: ignore - super().replace_node(oldnode, newnode) - - def add_node(self, newnode: AstNode, index: int=None) -> None: - super().add_node(newnode, index) - self._populate_symboltable(newnode) - - -def validate_address(obj: AstNode, attrib: attr.Attribute, value: Optional[int]) -> None: - if value is None: - return - if isinstance(obj, Block) and obj.name == "ZP": - raise ParseError("zeropage block cannot have custom start {:s}".format(attrib.name), obj.sourceref) - if value < 0x0200 or value > 0xffff: - raise ParseError("invalid {:s} (must be from $0200 to $ffff)".format(attrib.name), obj.sourceref) - - -def dimensions_validator(obj: 'DatatypeNode', attrib: attr.Attribute, value: List[int]) -> None: - if not value: - return - dt = obj.to_enum() - if value and dt not in (DataType.MATRIX, DataType.WORDARRAY, DataType.BYTEARRAY): - raise ParseError("cannot use a dimension for this datatype", obj.sourceref) - if dt == DataType.WORDARRAY or dt == DataType.BYTEARRAY: - if len(value) == 1: - if value[0] <= 0 or value[0] > 256: - raise ParseError("array length must be 1..256", obj.sourceref) - else: - raise ParseError("array must have only one dimension", obj.sourceref) - if dt == DataType.MATRIX: - if len(value) < 2 or len(value) > 3: - raise ParseError("matrix must have two dimensions, with optional interleave", obj.sourceref) - if len(value) == 3: - if value[2] < 1 or value[2] > 256: - raise ParseError("matrix interleave must be 1..256", obj.sourceref) - if value[0] < 0 or value[0] > 128 or value[1] < 0 or value[1] > 128: - raise ParseError("matrix rows and columns must be 1..128", obj.sourceref) - - -@attr.s(cmp=False, repr=False) -class Block(AstNode): - # has one subnode: the Scope. - name = attr.ib(type=str, default=None) - address = attr.ib(type=int, default=None, validator=validate_address) - _unnamed_block_labels = {} # type: Dict[Block, str] - - @property - def scope(self) -> Scope: - return self.nodes[0] if self.nodes else None # type: ignore - - @scope.setter - def scope(self, scope: Scope) -> None: - assert isinstance(scope, Scope) - self.nodes.clear() - self.nodes.append(scope) - scope.name = self.name - - @property - def label(self) -> str: - if self.name: - return self.name - if self in self._unnamed_block_labels: - return self._unnamed_block_labels[self] - label = "il65_block_{:d}".format(len(self._unnamed_block_labels)) - self._unnamed_block_labels[self] = label - return label - - -@attr.s(cmp=False, repr=False) -class Module(AstNode): - # has one subnode: the Scope. - name = attr.ib(type=str, default=None) # filename - subroutine_usage = attr.ib(type=defaultdict, init=False, default=attr.Factory(lambda: defaultdict(set))) # will be populated later - format = attr.ib(type=ProgramFormat, init=False, default=ProgramFormat.PRG) # can be set via directive - address = attr.ib(type=int, init=False, default=0xc000, validator=validate_address) # can be set via directive - zp_options = attr.ib(type=ZpOptions, init=False, default=ZpOptions.NOCLOBBER) # can be set via directive - floats_enabled = attr.ib(type=bool, init=False, default=False) # can be set via directive - - @property - def scope(self) -> Scope: - return self.nodes[0] if self.nodes else None # type: ignore - - @no_type_check - def zeropage(self) -> Optional[Block]: - # return the zeropage block (if defined) - first_block = next(self.scope.all_nodes(Block)) - if first_block.name == "ZP": - return first_block - return None - - @no_type_check - def main(self) -> Optional[Block]: - # return the 'main' block (if defined) - for block in self.scope.all_nodes(Block): - if block.name == "main": - return block - return None - - -@attr.s(cmp=False) -class Label(AstNode): - name = attr.ib(type=str, default=None) - # no subnodes. - - -@attr.s(cmp=False, slots=True, repr=False) -class Expression(AstNode): - # just a common base class for the nodes that are an expression themselves: - # ExpressionWithOperator, AddressOf, LiteralValue, SymbolName, Register, SubCall, Dereference - is_lhs = attr.ib(type=bool, init=False, default=False) # left hand side of incrdecr/assignment/augassign? - - def is_compiletime_const(self) -> bool: - raise NotImplementedError("implement in subclass") - - def const_value(self) -> Union[int, float, bool, str]: - raise NotImplementedError("implement in subclass") - - -@attr.s(cmp=False, slots=True) -class Register(Expression): - name = attr.ib(type=str, validator=attr.validators.in_(REGISTER_SYMBOLS), default="???") - datatype = attr.ib(type=DataType, init=False) - # no subnodes. - - def __attrs_post_init__(self): - if self.name in REGISTER_BYTES: - self.datatype = DataType.BYTE - elif self.name in REGISTER_WORDS: - self.datatype = DataType.WORD - else: - self.datatype = None # register 'SC' etc. - - def __hash__(self) -> int: - return hash(self.name) - - def __eq__(self, other) -> bool: - if not isinstance(other, Register): - return NotImplemented - return self.name == other.name - - def __lt__(self, other) -> bool: - if not isinstance(other, Register): - return NotImplemented - return self.name < other.name - - def is_compiletime_const(self) -> bool: - return False - - def const_value(self) -> Union[int, float, bool, str]: - raise NotCompiletimeConstantError("register doesn't have a constant numeric value", self) - - -@attr.s(cmp=False) -class PreserveRegs(AstNode): - registers = attr.ib(type=str) # type: ignore - # no subnodes. - - -@attr.s(cmp=False, repr=False) -class InlineAssembly(AstNode): - # no subnodes. - assembly = attr.ib(type=str) # type: ignore - - -@attr.s(cmp=False, slots=True) -class DatatypeNode(AstNode): - # no subnodes. - name = attr.ib(type=str) # type: ignore - dimensions = attr.ib(type=list, default=None, validator=dimensions_validator) # if set, 1 or more dimensions (ints) - - def to_enum(self): - return { - "byte": DataType.BYTE, - "word": DataType.WORD, - "float": DataType.FLOAT, - "str": DataType.STRING, - "strp": DataType.STRING_P, - "strs": DataType.STRING_S, - "strps": DataType.STRING_PS, - "matrix": DataType.MATRIX, - "array": DataType.BYTEARRAY, - "wordarray": DataType.WORDARRAY - }[self.name] - - -@attr.s(cmp=False, repr=False) -class BuiltinFunction(AstNode): - # This is a pseudo-node that will be artificially injected in the top-most scope, - # to represent all supported built-in functions or math-functions. - # No child nodes. - name = attr.ib(type=str) # type: ignore - func = attr.ib(type=Callable) - - -@attr.s(cmp=False, repr=False) -class Subroutine(AstNode): - # one subnode: the Scope. - name = attr.ib(type=str) # type: ignore - param_spec = attr.ib(type=list) - result_spec = attr.ib(type=list) - address = attr.ib(type=int, default=None, validator=validate_address) - - @property - def scope(self) -> Scope: - return self.nodes[0] if self.nodes else None # type: ignore - - @scope.setter - def scope(self, scope: Scope) -> None: - assert isinstance(scope, Scope) - self.nodes.clear() - self.nodes.append(scope) - scope.name = self.name - if self.address is not None: - raise ValueError("subroutine must have either a scope or an address, not both") - - -@attr.s(cmp=True, slots=True, repr=False) -class LiteralValue(Expression): - # no subnodes. - value = attr.ib(default=None) - - def __repr__(self) -> str: - return "".format(self.value, self.sourceref) - - def const_value(self) -> Union[int, float, bool, str]: - return self.value - - def is_compiletime_const(self) -> bool: - return True - - -@attr.s(cmp=False) -class AddressOf(Expression): - # no subnodes. - name = attr.ib(type=str, validator=attr.validators._InstanceOfValidator(type=str), default="???") # type: ignore - - def is_compiletime_const(self) -> bool: - # address-of can be a compile time constant if the operand is a memory mapped variable or ZP variable - symdef = self.my_scope().lookup(self.name) - return isinstance(symdef, VarDef) and symdef.vartype == VarType.MEMORY \ - or getattr(symdef, "zp_address", None) is not None # type: ignore - - def const_value(self) -> Union[int, float, bool, str]: - symdef = self.my_scope().lookup(self.name) - if isinstance(symdef, VarDef): - if symdef.zp_address is not None: - return symdef.zp_address - if symdef.vartype == VarType.MEMORY: - return symdef.value.const_value() - raise NotCompiletimeConstantError("can only take constant address of a memory mapped variable", self) - raise NotCompiletimeConstantError("should be a vardef to be able to take its address", self) - - -@attr.s(cmp=True, slots=True) -class SymbolName(Expression): - # no subnodes. - name = attr.ib(type=str, default="???") - - def is_compiletime_const(self) -> bool: - symdef = self.my_scope().lookup(self.name) - return isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST - - def const_value(self) -> Union[int, float, bool, str]: - symdef = self.my_scope().lookup(self.name) - if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST: - return symdef.const_value() - raise NotCompiletimeConstantError("should be a const vardef to be able to take its constant numeric value", self) - - -@attr.s(cmp=False) -class Dereference(Expression): - # one subnode: operand (SymbolName, integer LiteralValue or Register (pair) ) - datatype = attr.ib(type=DataType, default=None) - size = attr.ib(type=int, default=None) - - @property - def operand(self) -> Union[SymbolName, LiteralValue, Register]: - return self.nodes[0] # type: ignore - - def __attrs_post_init__(self): - # convert datatype node to enum + size - if self.datatype is None: - assert self.size is None - self.size = 1 - self.datatype = DataType.BYTE - elif isinstance(self.datatype, DatatypeNode): - assert self.size is None - self.size = self.datatype.dimensions - if not self.datatype.to_enum().isnumeric(): - raise ParseError("dereference target value must be byte, word, float", self.datatype.sourceref) - self.datatype = self.datatype.to_enum() - if self.nodes and not isinstance(self.nodes[0], (SymbolName, LiteralValue, Register)): - raise TypeError("operand of dereference invalid type", self.nodes[0], self.sourceref) - - def is_compiletime_const(self) -> bool: - return False - - def const_value(self) -> Union[int, float, bool, str]: - raise NotCompiletimeConstantError("dereference is not a constant numeric value") - - -@attr.s(cmp=False) -class IncrDecr(AstNode): - # increment or decrement something by a small CONSTANT value (1..255) - # larger values will be treated/converted as an augmented assignment += or -=. - # one subnode: target (Register, SymbolName, or Dereference). - operator = attr.ib(type=str, validator=attr.validators.in_(["++", "--"])) # type: ignore - howmuch = attr.ib(default=1) - - @property - def target(self) -> Union[Register, SymbolName, Dereference]: - return self.nodes[0] # type: ignore - - @target.setter - def target(self, target: Union[Register, SymbolName, Dereference]) -> None: - if isinstance(target, Register): - if target.name not in REGISTER_BYTES | REGISTER_WORDS: - raise ParseError("cannot incr/decr that register", self.sourceref) - assert isinstance(target, (Register, SymbolName, Dereference)) - self.nodes.clear() - # the expression on the left hand side should be marked LHS to avoid improper constant folding/replacement. - target.is_lhs = True - self.nodes.append(target) - - def __attrs_post_init__(self): - # make sure the amount is always >= 0, flip the operator if needed - if self.howmuch < 0: - self.howmuch = -self.howmuch - self.operator = "++" if self.operator == "--" else "--" - - -@attr.s(cmp=False, slots=True, repr=False) -class ExpressionWithOperator(Expression): - # 2 nodes: left (Expression), right (not present if unary, Expression if not unary) - operator = attr.ib(type=str, default="???") - - @property - def unary(self) -> bool: - return len(self.nodes) == 1 - - @property - def left(self) -> Expression: - return self.nodes[0] # type: ignore - - @left.setter - def left(self, newleft: Expression) -> None: - if self.nodes: - self.nodes[0] = newleft - else: - self.nodes.append(newleft) - - @property - def right(self) -> Optional[Expression]: - return self.nodes[1] if len(self.nodes) == 2 else None # type: ignore - - @right.setter - def right(self, newright: Expression) -> None: - self.nodes[1] = newright - - def __attrs_post_init__(self): - assert self.operator not in ("++", "--"), "incr/decr should not be an expression" - if self.operator == "mod": - self.operator = "%" # change it back to the more common '%' - - def const_value(self) -> Union[int, float, bool, str]: - cv = [n.const_value() for n in self.nodes] # type: ignore - if self.unary: - if self.operator == "-": - return -cv[0] - elif self.operator == "+": - return cv[0] - elif self.operator == "~": - return ~cv[0] - elif self.operator == "not": - return not cv[0] - elif self.operator == "&": - raise NotCompiletimeConstantError("the address-of operator should have been parsed into an AddressOf node") - else: - raise ValueError("invalid unary operator: "+self.operator, self.sourceref) - else: - if self.operator == "-": - return cv[0] - cv[1] - elif self.operator == "+": - return cv[0] + cv[1] - elif self.operator == "*": - return cv[0] * cv[1] - elif self.operator == "/": - return cv[0] / cv[1] - elif self.operator == "**": - return cv[0] ** cv[1] - elif self.operator == "//": - return cv[0] // cv[1] - elif self.operator in ("%", "mod"): - return cv[0] % cv[1] - elif self.operator == "<<": - return cv[0] << cv[1] - elif self.operator == ">>": - return cv[0] >> cv[1] - elif self.operator == "|": - return cv[0] | cv[1] - elif self.operator == "&": - return cv[0] & cv[1] - elif self.operator == "^": - return cv[0] ^ cv[1] - elif self.operator == "==": - return cv[0] == cv[1] - elif self.operator == "!=": - return cv[0] != cv[1] - elif self.operator == "<": - return cv[0] < cv[1] - elif self.operator == ">": - return cv[0] > cv[1] - elif self.operator == "<=": - return cv[0] <= cv[1] - elif self.operator == ">=": - return cv[0] >= cv[1] - elif self.operator == "and": - return cv[0] and cv[1] - elif self.operator == "or": - return cv[0] or cv[1] - elif self.operator == "xor": - i1 = 1 if cv[0] else 0 - i2 = 1 if cv[1] else 0 - return bool(i1 ^ i2) - else: - raise ValueError("invalid operator: "+self.operator, self.sourceref) - - @no_type_check - def is_compiletime_const(self) -> bool: - if len(self.nodes) == 1: - return self.nodes[0].is_compiletime_const() - elif len(self.nodes) == 2: - return self.nodes[0].is_compiletime_const() and self.nodes[1].is_compiletime_const() - raise ValueError("should have 1 or 2 nodes") - - def evaluate_primitive_constants(self, sourceref: SourceRef) -> LiteralValue: - # make sure the lvalue and rvalue are primitives, and the operator is allowed - assert isinstance(self.left, LiteralValue) - assert isinstance(self.right, LiteralValue) - if self.operator not in {'+', '-', '*', '/', '//', '~', '|', '&', '%', '<<', '>>', '<', '>', '<=', '>=', '==', '!='}: - raise ValueError("operator", self.operator) - estr = "{} {} {}".format(repr(self.left.value), self.operator, repr(self.right.value)) - try: - lv = LiteralValue(value=eval(estr, {}, {}), sourceref=sourceref) # type: ignore # safe because of checks above - lv.parent = self.parent - return lv - except ZeroDivisionError: - raise ParseError("division by zero", sourceref) - except Exception as x: - raise ExpressionEvaluationError("expression error: " + str(x), self.sourceref) from None - - -@attr.s(cmp=False, repr=False) -class Goto(AstNode): - # one or two subnodes: target (SymbolName, integer LiteralValue, or Dereference) and optionally: condition (Expression) - # also the if_stmt itself can be a form of a condition (if_gt, ...) - if_stmt = attr.ib(default=None) - - @property - def target(self) -> Union[SymbolName, LiteralValue, Dereference]: - return self.nodes[0] # type: ignore - - @property - def condition(self) -> Optional[Expression]: - return self.nodes[1] if len(self.nodes) == 2 else None # type: ignore - - @property - def if_cond(self) -> str: - return self.if_stmt[3:] if self.if_stmt else "" - - -@attr.s(cmp=False, slots=True, repr=False) -class CallArgument(AstNode): - # one subnode: the value (Expression) - name = attr.ib(type=str, default=None) - - @property - def value(self) -> Expression: - return self.nodes[0] # type: ignore - - -@attr.s(cmp=False) -class CallArguments(AstNode): - # subnodes are zero or more subroutine call arguments (CallArgument) - nodes = attr.ib(type=list, init=True) # type: ignore # requires nodes in __init__ - - -@attr.s(cmp=False, repr=False) -class SubCall(Expression): - # has three subnodes: - # 0: target (Symbolname, integer LiteralValue, or Dereference), - # 1: preserve_regs (PreserveRegs) - # 2: arguments (CallArguments). - - @property - def target(self) -> Union[SymbolName, LiteralValue, Dereference]: - return self.nodes[0] # type: ignore - - @property - def preserve_regs(self) -> PreserveRegs: - return self.nodes[1] # type: ignore - - @property - def arguments(self) -> CallArguments: - return self.nodes[2] # type: ignore - - def is_compiletime_const(self) -> bool: - if isinstance(self.nodes[0], SymbolName): - symdef = self.nodes[0].my_scope().lookup(self.nodes[0].name) - if isinstance(symdef, BuiltinFunction): - return True - return False - - @no_type_check - def const_value(self) -> Union[int, float, bool, str]: - if isinstance(self.nodes[0], SymbolName): - symdef = self.nodes[0].my_scope().lookup(self.nodes[0].name) - if isinstance(symdef, BuiltinFunction): - arguments = [a.nodes[0].const_value() for a in self.nodes[2].nodes] - return symdef.func(*arguments) - raise NotCompiletimeConstantError("subroutine call is not a constant value", self) - - -@attr.s(cmp=False, slots=True, repr=False) -class VarDef(AstNode): - # zero or one subnode: value (Expression). - name = attr.ib(type=str) # type: ignore - vartype = attr.ib() - datatype = attr.ib() - size = attr.ib(type=list, default=None) - zp_address = attr.ib(type=int, default=None, init=False) # the address in the ZeroPage if this var is there, will be set later - - @property - def value(self) -> Expression: - return self.nodes[0] if self.nodes else None # type: ignore - - @value.setter - def value(self, value: Expression) -> None: - assert isinstance(value, Expression) - if self.nodes: - self.nodes[0] = value - else: - self.nodes.append(value) - - def const_value(self) -> Union[int, float, bool, str]: - if self.vartype != VarType.CONST: - raise NotCompiletimeConstantError("not a constant value", self) - if self.nodes and isinstance(self.nodes[0], Expression): - return self.nodes[0].const_value() - raise ValueError("no value", self) - - def __attrs_post_init__(self): - # convert vartype to enum - if self.vartype == "const": - self.vartype = VarType.CONST - elif self.vartype == "var": - self.vartype = VarType.VAR - elif self.vartype == "memory": - self.vartype = VarType.MEMORY - else: - raise ValueError("invalid vartype", self.vartype) - # convert datatype node to enum + size - if self.datatype is None: - assert self.size is None - self.size = [1] - self.datatype = DataType.BYTE - elif isinstance(self.datatype, DatatypeNode): - assert self.size is None - self.size = self.datatype.dimensions or [1] - self.datatype = self.datatype.to_enum() - if self.datatype == DataType.MATRIX and len(self.size) not in (2, 3): - raise ValueError("matrix size should be 2 dimensions with optional interleave", self) - if self.datatype.isarray() and sum(self.size) in (0, 1): - print("warning: {}: array/matrix with size 1, use normal byte/word instead".format(self.sourceref)) - if self.value is None and (self.datatype.isnumeric() or self.datatype.isarray()): - if self.vartype != VarType.CONST: - # leave None when it's a const, so we can check for uninitialized consts later and raise error. - self.value = LiteralValue(value=0, sourceref=self.sourceref) - self.value.parent = self - # if it's a matrix with interleave, it must be memory mapped - if self.datatype == DataType.MATRIX and len(self.size) == 3: - if self.vartype != VarType.MEMORY: - raise ParseError("matrix with interleave can only be a memory-mapped variable", self.sourceref) - # note: value coercion is done later, when all expressions are evaluated - - -@attr.s(cmp=False, repr=False) -class Return(AstNode): - # one, two or three subnodes: value_A, value_X, value_Y (all three Expression) - @property - def value_A(self) -> Optional[Expression]: - return self.nodes[0] if len(self.nodes) >= 1 else None # type: ignore - - @property - def value_X(self) -> Optional[Expression]: - return self.nodes[1] if len(self.nodes) >= 2 else None # type: ignore - - @property - def value_Y(self) -> Optional[Expression]: - return self.nodes[2] if len(self.nodes) >= 3 else None # type: ignore - - -@attr.s(cmp=False, slots=True, repr=False) -class Assignment(AstNode): - # has two subnodes: left (=Register/SymbolName/Dereference ) and right (=Expression) - @property - def left(self) -> Union[Register, SymbolName, Dereference]: - return self.nodes[0] # type: ignore - - @property - def right(self) -> Expression: - return self.nodes[1] # type: ignore - - @right.setter - def right(self, rvalue: Expression) -> None: - assert isinstance(rvalue, Expression) - self.nodes[1] = rvalue - - def mark_lhs(self): - # the expression on the left hand side should be marked LHS to avoid improper constant folding/replacement. - self.nodes[0].is_lhs = True - - -@attr.s(cmp=False, slots=True, repr=False) -class AugAssignment(AstNode): - # has two subnodes: left (=Register, SymbolName, or Dereference) and right (=Expression) - operator = attr.ib(type=str) # type: ignore - - @property - def left(self) -> Union[Register, SymbolName, Dereference]: - return self.nodes[0] # type: ignore - - @property - def right(self) -> Expression: - return self.nodes[1] # type: ignore - - @right.setter - def right(self, rvalue: Expression) -> None: - assert isinstance(rvalue, Expression) - self.nodes[1] = rvalue - - def mark_lhs(self): - # the expression on the left hand side should be marked LHS to avoid improper constant folding/replacement. - self.nodes[0].is_lhs = True - - -def datatype_of(targetnode: AstNode, scope: Scope) -> DataType: - # tries to determine the DataType of an assignment target node - if isinstance(targetnode, VarDef): - return DataType.WORD if targetnode.vartype == VarType.MEMORY else targetnode.datatype - elif isinstance(targetnode, (Dereference, Register)): - return targetnode.datatype - elif isinstance(targetnode, SymbolName): - symdef = scope.lookup(targetnode.name) - if isinstance(symdef, VarDef): - return symdef.datatype - raise TypeError("cannot determine datatype", targetnode) - - -def coerce_constant_value(datatype: DataType, value: Expression, - sourceref: SourceRef=None) -> Tuple[bool, Expression]: - # if we're a BYTE type, and the value is a single character, convert it to the numeric value - assert isinstance(value, Expression) - - def verify_bounds(pvalue: Union[int, float, str]) -> None: - # if the value is out of bounds, raise an overflow exception - if isinstance(pvalue, (int, float)): - if datatype == DataType.BYTE and not (0 <= pvalue <= 0xff): # type: ignore - raise OverflowError("value out of range for byte: " + str(pvalue)) - if datatype == DataType.WORD and not (0 <= pvalue <= 0xffff): # type: ignore - raise OverflowError("value out of range for word: " + str(pvalue)) - if datatype == DataType.FLOAT and not (FLOAT_MAX_NEGATIVE <= pvalue <= FLOAT_MAX_POSITIVE): # type: ignore - raise OverflowError("value out of range for float: " + str(pvalue)) - - if isinstance(value, LiteralValue): - if type(value.value) is str and len(value.value) == 1 and (datatype.isnumeric() or datatype.isarray()): - # convert a string of length 1 to its numeric character value - lv = LiteralValue(value=char_to_bytevalue(value.value), sourceref=value.sourceref) # type: ignore - lv.parent = value.parent - return True, lv - # if we're an integer value and the passed value is float, truncate it (and give a warning) - if datatype in (DataType.BYTE, DataType.WORD, DataType.MATRIX) and isinstance(value.value, float): - frac = math.modf(value.value) - if frac != 0: - print_warning("float value truncated ({} to datatype {})".format(value.value, datatype.name), sourceref=sourceref) - v2 = int(value.value) - verify_bounds(v2) - lv = LiteralValue(value=v2, sourceref=value.sourceref) # type: ignore - lv.parent = value.parent - return True, lv - if type(value.value) in (int, float): - verify_bounds(value.value) - if datatype == DataType.WORD: - if type(value.value) not in (int, float, str): - raise TypeError("cannot assign '{:s}' to {:s}".format(type(value.value).__name__, datatype.name.lower()), sourceref) - elif datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT): - if type(value.value) not in (int, float): - raise TypeError("cannot assign '{:s}' to {:s}".format(type(value.value).__name__, datatype.name.lower()), sourceref) - elif isinstance(value, (ExpressionWithOperator, SubCall)): - return False, value - elif isinstance(value, SymbolName): - symboldef = value.my_scope().lookup(value.name) - if isinstance(symboldef, VarDef) and symboldef.vartype == VarType.CONST: - return True, symboldef.value - elif isinstance(value, AddressOf): - try: - address = value.const_value() - lv = LiteralValue(value=address, sourceref=value.sourceref) # type: ignore - lv.parent = value.parent - return True, lv - except TypeError: - return False, value - if datatype == DataType.WORD and not isinstance(value, (LiteralValue, Dereference, Register, SymbolName, AddressOf)): - raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()), sourceref) - elif datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT) \ - and not isinstance(value, (LiteralValue, Dereference, Register, SymbolName, AddressOf)): - raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()), sourceref) - return False, value - - -def check_symbol_definition(name: str, scope: Scope, sref: SourceRef) -> Any: - try: - return scope.lookup(name) - except UndefinedSymbolError as x: - raise ParseError(str(x), sref) - - -@no_type_check -def scoped_name(node_with_name: AstNode, current_scope: Scope) -> str: - node_scope = node_with_name.my_scope() - return node_with_name.name if node_scope is current_scope else node_scope.name + "." + node_with_name.name - - -# ----------------- PLY parser definition follows ---------------------- - -def p_start(p): - """ - start : empty - | module_elements - """ - if p[1]: - scope = Scope(nodes=p[1], level="module", sourceref=_token_sref(p, 1)) - scope.name = "<" + p.lexer.source_filename + " global scope>" - p[0] = Module(name=p.lexer.source_filename, sourceref=_token_sref(p, 1)) - p[0].nodes.append(scope) - else: - scope = Scope(nodes=[], level="module", sourceref=_token_sref(p, 1)) - scope.name = "<" + p.lexer.source_filename + " global scope>" - p[0] = Module(name=p.lexer.source_filename, sourceref=SourceRef(lexer.source_filename, 1, 1)) - p[0].nodes.append(scope) - - -def p_module(p): - """ - module_elements : module_elt - | module_elements module_elt - """ - if len(p) == 2: - if p[1] is None: - p[0] = [] - else: - p[0] = [p[1]] - else: - if p[2] is None: - p[0] = p[1] - else: - p[0] = p[1] + [p[2]] - - -def p_module_elt(p): - """ - module_elt : ENDL - | directive - | block - """ - if p[1] != '\n': - p[0] = p[1] - - -def p_directive(p): - """ - directive : DIRECTIVE ENDL - | DIRECTIVE directive_args ENDL - """ - if len(p) == 3: - p[0] = Directive(name=p[1], sourceref=_token_sref(p, 1)) - else: - p[0] = Directive(name=p[1], args=p[2], sourceref=_token_sref(p, 1)) - - -def p_directive_args(p): - """ - directive_args : directive_arg - | directive_args ',' directive_arg - """ - if len(p) == 2: - p[0] = [p[1]] - else: - p[0] = p[1] + [p[3]] - - -def p_directive_arg(p): - """ - directive_arg : NAME - | INTEGER - | STRING - | BOOLEAN - """ - p[0] = p[1] - - -def p_block_name_addr(p): - """ - block : BITINVERT NAME INTEGER endl_opt scope - """ - p[0] = Block(name=p[2], address=p[3], sourceref=_token_sref(p, 2)) - p[0].scope = p[5] - - -def p_block_name(p): - """ - block : BITINVERT NAME endl_opt scope - """ - p[0] = Block(name=p[2], sourceref=_token_sref(p, 2)) - p[0].scope = p[4] - - -def p_block_address(p): - """ - block : BITINVERT INTEGER endl_opt scope - """ - p[0] = Block(address=p[2], sourceref=_token_sref(p, 2)) - p[0].scope = p[4] - - -def p_block(p): - """ - block : BITINVERT endl_opt scope - """ - p[0] = Block(sourceref=_token_sref(p, 1)) - p[0].scope = p[3] - - -def p_endl_opt(p): - """ - endl_opt : empty - | ENDL - """ - pass - - -def p_scope(p): - """ - scope : '{' scope_elements_opt '}' - """ - p[0] = Scope(nodes=p[2] or [], level="block", sourceref=_token_sref(p, 1)) - - -def p_scope_elements_opt(p): - """ - scope_elements_opt : empty - | scope_elements - """ - p[0] = p[1] - - -def p_scope_elements(p): - """ - scope_elements : scope_element - | scope_elements scope_element - """ - if len(p) == 2: - p[0] = [] if p[1] in (None, '\n') else [p[1]] - else: - if p[2] in (None, '\n'): - p[0] = p[1] - else: - p[0] = p[1] + [p[2]] - - -def p_scope_element(p): - """ - scope_element : ENDL - | label - | directive - | vardef - | subroutine - | inlineasm - | statement - """ - if p[1] != '\n': - p[0] = p[1] - else: - p[0] = None - - -def p_label(p): - """ - label : LABEL - """ - p[0] = Label(name=p[1], sourceref=_token_sref(p, 1)) - - -def p_inlineasm(p): - """ - inlineasm : INLINEASM ENDL - """ - p[0] = InlineAssembly(assembly=p[1], sourceref=_token_sref(p, 1)) - - -def p_vardef(p): - """ - vardef : VARTYPE type_opt NAME ENDL - """ - p[0] = VarDef(name=p[3], vartype=p[1], datatype=p[2], sourceref=_token_sref(p, 3)) - - -def p_vardef_value(p): - """ - vardef : VARTYPE type_opt NAME IS expression - """ - p[0] = VarDef(name=p[3], vartype=p[1], datatype=p[2], sourceref=_token_sref(p, 3)) - p[0].value = p[5] - - -def p_type_opt(p): - """ - type_opt : DATATYPE '(' dimensions ')' - | DATATYPE - | empty - """ - if len(p) == 5: - p[0] = DatatypeNode(name=p[1], dimensions=p[3], sourceref=_token_sref(p, 1)) - elif len(p) == 2 and p[1]: - p[0] = DatatypeNode(name=p[1], sourceref=_token_sref(p, 1)) - - -def p_dimensions(p): - """ - dimensions : INTEGER - | dimensions ',' INTEGER - """ - if len(p) == 2: - p[0] = [p[1]] - else: - p[0] = p[1] + [p[3]] - - -def p_literal_value(p): - """literal_value : INTEGER - | FLOATINGPOINT - | STRING - | CHARACTER - | BOOLEAN""" - tok = p.slice[-1] - if tok.type == "BOOLEAN": - p[1] = int(p[1]) # boolean literals are converted to integer form (true=1, false=0). - p[0] = LiteralValue(value=p[1], sourceref=_token_sref(p, 1)) - - -def p_subroutine(p): - """ - subroutine : SUB NAME '(' sub_param_spec ')' RARROW '(' sub_result_spec ')' subroutine_body ENDL - """ - body = p[10] - if isinstance(body, Scope): - p[0] = Subroutine(name=p[2], param_spec=p[4] or [], result_spec=p[8] or [], sourceref=_token_sref(p, 1)) - p[0].scope = body - elif type(body) is int: - p[0] = Subroutine(name=p[2], param_spec=p[4] or [], result_spec=p[8] or [], address=body, sourceref=_token_sref(p, 1)) - else: - raise TypeError("subroutine_body", p.slice) - - -def p_sub_param_spec(p): - """ - sub_param_spec : empty - | sub_param_list - """ - p[0] = p[1] - - -def p_sub_param_list(p): - """ - sub_param_list : sub_param - | sub_param_list ',' sub_param - """ - if len(p) == 2: - p[0] = [p[1]] - else: - p[0] = p[1] + [p[3]] - - -def p_sub_param(p): - """ - sub_param : LABEL REGISTER - | REGISTER - """ - if len(p) == 3: - p[0] = (p[1], p[2]) - elif len(p) == 2: - p[0] = (None, p[1]) - - -def p_sub_result_spec(p): - """ - sub_result_spec : empty - | '?' - | sub_result_list - """ - if p[1] == '?': - p[0] = ['A', 'X', 'Y'] # '?' means: all registers clobbered - else: - p[0] = p[1] - - -def p_sub_result_list(p): - """ - sub_result_list : sub_result_reg - | sub_result_list ',' sub_result_reg - """ - if len(p) == 2: - p[0] = [p[1]] - else: - p[0] = p[1] + [p[3]] - - -def p_sub_result_reg(p): - """ - sub_result_reg : REGISTER - | CLOBBEREDREGISTER - """ - p[0] = p[1] - - -def p_subroutine_body(p): - """ - subroutine_body : scope - | IS INTEGER - """ - if len(p) == 2: - p[0] = p[1] - else: - p[0] = p[2] - - -def p_statement(p): - """ - statement : assignment ENDL - | aug_assignment ENDL - | subroutine_call ENDL - | goto ENDL - | conditional_goto ENDL - | incrdecr ENDL - | return ENDL - """ - p[0] = p[1] - - -def p_incrdecr(p): - """ - incrdecr : assignment_target INCR - | assignment_target DECR - """ - p[0] = IncrDecr(operator=p[2], sourceref=_token_sref(p, 2)) - p[0].target = p[1] - - -def p_call_subroutine(p): - """ - subroutine_call : calltarget preserveregs_opt '(' call_arguments_opt ')' - """ - sref = _token_sref(p, 3) - p[0] = SubCall(sourceref=sref) - target = p[1] - if isinstance(target, int): - target = LiteralValue(value=target, sourceref=sref) - p[0].nodes.append(target) - p[0].nodes.append(p[2] or PreserveRegs(registers="", sourceref=sref)) - p[0].nodes.append(CallArguments(nodes=p[4] or [], sourceref=sref)) - - -def p_preserveregs_opt(p): - """ - preserveregs_opt : empty - | preserveregs - """ - p[0] = p[1] - - -def p_preserveregs(p): - """ - preserveregs : PRESERVEREGS - """ - p[0] = PreserveRegs(registers=p[1], sourceref=_token_sref(p, 1)) - - -def p_call_arguments_opt(p): - """ - call_arguments_opt : empty - | call_arguments - """ - p[0] = p[1] - - -def p_call_arguments(p): - """ - call_arguments : call_argument - | call_arguments ',' call_argument - """ - if len(p) == 2: - p[0] = [p[1]] - else: - p[0] = p[1] + [p[3]] - - -def p_call_argument(p): - """ - call_argument : expression - | register IS expression - | NAME IS expression - """ - if len(p) == 2: - p[0] = CallArgument(sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[1]) - elif len(p) == 4: - if isinstance(p[1], AstNode): - sref = p[1].sourceref - else: - sref = _token_sref(p, 2) - p[0] = CallArgument(name=p[1], sourceref=sref) - p[0].nodes.append(p[3]) - - -def p_return(p): - """ - return : RETURN - | RETURN expression - | RETURN expression ',' expression - | RETURN expression ',' expression ',' expression - """ - if len(p) == 2: - p[0] = Return(sourceref=_token_sref(p, 1)) - elif len(p) == 3: - p[0] = Return(sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[2]) # A - elif len(p) == 5: - p[0] = Return(sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[2]) # A - p[0].nodes.append(p[4]) # X - elif len(p) == 7: - p[0] = Return(sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[2]) # A - p[0].nodes.append(p[4]) # X - p[0].nodes.append(p[6]) # Y - - -def p_register(p): - """ - register : REGISTER - """ - p[0] = Register(name=p[1], sourceref=_token_sref(p, 1)) - - -def p_goto(p): - """ - goto : GOTO calltarget - """ - p[0] = Goto(sourceref=_token_sref(p, 1)) - target = p[2] - if isinstance(target, int): - target = LiteralValue(value=target, sourceref=p[0].sourceref) - p[0].nodes.append(target) - - -def p_conditional_goto_plain(p): - """ - conditional_goto : IF GOTO calltarget - """ - p[0] = Goto(if_stmt=p[1], sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[3]) - - -def p_conditional_goto_expr(p): - """ - conditional_goto : IF expression GOTO calltarget - """ - p[0] = Goto(if_stmt=p[1], sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[4]) - p[0].nodes.append(p[2]) - - -def p_calltarget(p): - """ - calltarget : symbolname - | INTEGER - | dereference - """ - p[0] = p[1] - - -def p_dereference(p): - """ - dereference : '[' dereference_operand ']' - """ - p[0] = Dereference(datatype=p[2][1], sourceref=_token_sref(p, 1)) - operand = p[2][0] - if isinstance(operand, int): - p[0].nodes.append(LiteralValue(value=operand, sourceref=p[0].sourceref)) - elif isinstance(operand, str): - p[0].nodes.append(Register(name=operand, sourceref=p[0].sourceref)) - elif isinstance(operand, SymbolName): - p[0].nodes.append(operand) - attr.validate(p[0]) - - -def p_dereference_operand(p): - """ - dereference_operand : symbolname type_opt - | REGISTER type_opt - | INTEGER type_opt - """ - p[0] = (p[1], p[2]) - - -def p_symbolname(p): - """ - symbolname : NAME - | DOTTEDNAME - """ - p[0] = SymbolName(name=p[1], sourceref=_token_sref(p, 1)) - - -def p_assignment(p): - """ - assignment : assignment_target IS expression - """ - p[0] = Assignment(sourceref=_token_sref(p, 2)) - p[0].nodes.append(p[1]) - p[0].nodes.append(p[3]) - p[0].mark_lhs() - - -def p_aug_assignment(p): - """ - aug_assignment : assignment_target AUGASSIGN expression - """ - p[0] = AugAssignment(operator=p[2], sourceref=_token_sref(p, 2)) - p[0].nodes.append(p[1]) - p[0].nodes.append(p[3]) - p[0].mark_lhs() - - -precedence = ( - # following the python operator precedence rules mostly; https://docs.python.org/3/reference/expressions.html#operator-precedence - ('left', 'LOGICOR'), - ('left', 'LOGICXOR'), - ('left', 'LOGICAND'), - ('right', 'LOGICNOT'), - ('left', "LT", "GT", "LE", "GE", "EQUALS", "NOTEQUALS"), - ('left', 'BITOR'), - ('left', 'BITXOR'), - ('left', 'BITAND'), - ('left', 'SHIFTLEFT', 'SHIFTRIGHT'), - ('left', '+', '-'), - ('left', '*', '/', 'INTEGERDIVIDE', 'MODULO'), - ('right', 'UNARY_MINUS', 'BITINVERT', "UNARY_ADDRESSOF"), - ('left', 'POWER'), - ('nonassoc', "COMMENT"), -) - - -def p_expression(p): - """ - expression : expression '+' expression - | expression '-' expression - | expression '*' expression - | expression '/' expression - | expression MODULO expression - | expression BITOR expression - | expression BITXOR expression - | expression BITAND expression - | expression SHIFTLEFT expression - | expression SHIFTRIGHT expression - | expression LOGICOR expression - | expression LOGICAND expression - | expression LOGICXOR expression - | expression POWER expression - | expression INTEGERDIVIDE expression - | expression LT expression - | expression GT expression - | expression LE expression - | expression GE expression - | expression EQUALS expression - | expression NOTEQUALS expression - """ - p[0] = ExpressionWithOperator(operator=p[2], sourceref=_token_sref(p, 2)) - p[0].nodes.append(p[1]) - p[0].nodes.append(p[3]) - - -def p_expression_uminus(p): - """ - expression : '-' expression %prec UNARY_MINUS - """ - p[0] = ExpressionWithOperator(operator=p[1], sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[2]) - - -def p_expression_addressof(p): - """ - expression : BITAND symbolname %prec UNARY_ADDRESSOF - """ - p[0] = AddressOf(name=p[2].name, sourceref=_token_sref(p, 1)) - - -def p_unary_expression_bitinvert(p): - """ - expression : BITINVERT expression - """ - p[0] = ExpressionWithOperator(operator=p[1], sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[2]) - - -def p_unary_expression_logicnot(p): - """ - expression : LOGICNOT expression - """ - p[0] = ExpressionWithOperator(operator=p[1], sourceref=_token_sref(p, 1)) - p[0].nodes.append(p[2]) - - -def p_expression_group(p): - """ - expression : '(' expression ')' - """ - p[0] = p[2] - - -def p_expression_expr_value(p): - """expression : expression_value""" - p[0] = p[1] - - -def p_expression_value(p): - """ - expression_value : literal_value - | symbolname - | register - | subroutine_call - | dereference - """ - p[0] = p[1] - - -def p_assignment_target(p): - """ - assignment_target : register - | symbolname - | dereference - """ - p[0] = p[1] - - -def p_empty(p): - """empty :""" - pass - - -def p_error(p): - if p: - sref = SourceRef(p.lexer.source_filename, p.lineno, find_tok_column(p)) - if p.value in ("", "\n"): - p.lexer.error_function(sref, "syntax error before end of line") - else: - p.lexer.error_function(sref, "syntax error before or at '{:.20s}'", str(p.value).rstrip()) - else: - lexer.error_function(None, "syntax error at end of input", lexer.source_filename) - # stack_state_str = ' '.join([symbol.type for symbol in parser.symstack][1:]) - # print('\n[ERROR DEBUG: parser state={:d} stack: {} . {} ]'.format(parser.state, stack_state_str, p)) - - -def _token_sref(p, token_idx): - """ Returns the coordinates for the YaccProduction object 'p' indexed - with 'token_idx'. The coordinate includes the 'lineno' and 'column', starting from 1. - """ - last_cr = p.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx)) - if last_cr < 0: - last_cr = -1 - chunk = p.lexer.lexdata[last_cr:p.lexpos(token_idx)] - column = len(chunk.expandtabs()) - return SourceRef(p.lexer.source_filename, p.lineno(token_idx), column) - - -class TokenFilter: - def __init__(self, lexer): - self.lexer = lexer - self.prev_was_EOL = False - assert "ENDL" in tokens - - def token(self): - # make sure we only ever emit ONE "ENDL" token in sequence - if self.prev_was_EOL: - # skip all EOLS that might follow - while True: - tok = self.lexer.token() - if not tok or tok.type != "ENDL": - break - self.prev_was_EOL = False - else: - tok = self.lexer.token() - self.prev_was_EOL = tok and tok.type == "ENDL" - return tok - - -parser = yacc(write_tables=True) - - -def connect_parents(node: AstNode, parent: AstNode) -> None: - node.parent = parent - for childnode in node.nodes: - if isinstance(childnode, AstNode): - connect_parents(childnode, node) - - -def parse_file(filename: str, lexer_error_func=None) -> Module: - lexer.error_function = lexer_error_func - lexer.lineno = 1 - lexer.source_filename = filename - tfilter = TokenFilter(lexer) - with open(filename, "rU") as inf: - sourcecode = inf.read() - result = parser.parse(input=sourcecode, tokenfunc=tfilter.token) - connect_parents(result, None) - return result diff --git a/python-prototype/mypy.ini b/python-prototype/mypy.ini deleted file mode 100644 index 15b82b2c3..000000000 --- a/python-prototype/mypy.ini +++ /dev/null @@ -1,10 +0,0 @@ -[mypy] -follow_imports = normal -ignore_missing_imports = True -incremental = True - -[mypy-il65/parsetab.*] -ignore_errors = True - -[mypy-il65/plyparser.*] -ignore_errors = True diff --git a/python-prototype/python.iml b/python-prototype/python.iml deleted file mode 100644 index 9770eaa13..000000000 --- a/python-prototype/python.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/python-prototype/requirements.txt b/python-prototype/requirements.txt deleted file mode 100644 index 326d1b007..000000000 --- a/python-prototype/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -attrs -ply -cbmcodecs >= 0.2.0 diff --git a/python-prototype/run_profile.py b/python-prototype/run_profile.py deleted file mode 100644 index b09de680a..000000000 --- a/python-prototype/run_profile.py +++ /dev/null @@ -1,12 +0,0 @@ -import cProfile -from il65.compile import PlyParser -from il65.optimize import optimize - - -def parse(): - parser = PlyParser(enable_floats=True) - parsed_module = parser.parse_file("testsource/large.ill") - optimize(parsed_module) - - -cProfile.run("parse()", filename="profile.dat") diff --git a/python-prototype/setup.cfg b/python-prototype/setup.cfg deleted file mode 100644 index ee290141b..000000000 --- a/python-prototype/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[pycodestyle] -max-line-length = 140 -exclude = .git,__pycache__,.tox,docs,tests,build,dist,parsetab.py diff --git a/python-prototype/tests/__init__.py b/python-prototype/tests/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/python-prototype/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/python-prototype/tests/test_codegen_mos6502.py b/python-prototype/tests/test_codegen_mos6502.py deleted file mode 100644 index 819074140..000000000 --- a/python-prototype/tests/test_codegen_mos6502.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest -from il65.datatypes import FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE -from il65.codegen.shared import to_hex, to_mflpt5 - - -def test_to_hex(): - assert to_hex(0) == "0" - assert to_hex(1) == "1" - assert to_hex(10) == "10" - assert to_hex(15) == "15" - assert to_hex(16) == "$10" - assert to_hex(255) == "$ff" - assert to_hex(256) == "$0100" - assert to_hex(20060) == "$4e5c" - assert to_hex(65535) == "$ffff" - with pytest.raises(OverflowError): - to_hex(-1) - with pytest.raises(OverflowError): - to_hex(65536) - - -def test_float_to_mflpt5(): - mflpt = to_mflpt5(1.0) - assert type(mflpt) is bytearray - assert b"\x00\x00\x00\x00\x00" == to_mflpt5(0) - assert b"\x82\x49\x0F\xDA\xA1" == to_mflpt5(3.141592653) - assert b"\x82\x49\x0F\xDA\xA2" == to_mflpt5(3.141592653589793) - assert b"\x90\x80\x00\x00\x00" == to_mflpt5(-32768) - assert b"\x81\x00\x00\x00\x00" == to_mflpt5(1) - assert b"\x80\x35\x04\xF3\x34" == to_mflpt5(0.7071067812) - assert b"\x80\x35\x04\xF3\x33" == to_mflpt5(0.7071067811865476) - assert b"\x81\x35\x04\xF3\x34" == to_mflpt5(1.4142135624) - assert b"\x81\x35\x04\xF3\x33" == to_mflpt5(1.4142135623730951) - assert b"\x80\x80\x00\x00\x00" == to_mflpt5(-.5) - assert b"\x80\x31\x72\x17\xF8" == to_mflpt5(0.69314718061) - assert b"\x80\x31\x72\x17\xF7" == to_mflpt5(0.6931471805599453) - assert b"\x84\x20\x00\x00\x00" == to_mflpt5(10) - assert b"\x9E\x6E\x6B\x28\x00" == to_mflpt5(1000000000) - assert b"\x80\x00\x00\x00\x00" == to_mflpt5(.5) - assert b"\x81\x38\xAA\x3B\x29" == to_mflpt5(1.4426950408889634) - assert b"\x81\x49\x0F\xDA\xA2" == to_mflpt5(1.5707963267948966) - assert b"\x83\x49\x0F\xDA\xA2" == to_mflpt5(6.283185307179586) - assert b"\x7F\x00\x00\x00\x00" == to_mflpt5(.25) - - -def test_float_range(): - assert b"\xff\x7f\xff\xff\xff" == to_mflpt5(FLOAT_MAX_POSITIVE) - assert b"\xff\xff\xff\xff\xff" == to_mflpt5(FLOAT_MAX_NEGATIVE) - with pytest.raises(OverflowError): - to_mflpt5(1.7014118346e+38) - with pytest.raises(OverflowError): - to_mflpt5(-1.7014118346e+38) - with pytest.raises(OverflowError): - to_mflpt5(1.7014118347e+38) - with pytest.raises(OverflowError): - to_mflpt5(-1.7014118347e+38) - assert b"\x03\x39\x1d\x15\x63" == to_mflpt5(1.7e-38) - assert b"\x00\x00\x00\x00\x00" == to_mflpt5(1.7e-39) - assert b"\x03\xb9\x1d\x15\x63" == to_mflpt5(-1.7e-38) - assert b"\x00\x00\x00\x00\x00" == to_mflpt5(-1.7e-39) diff --git a/python-prototype/tests/test_core.py b/python-prototype/tests/test_core.py deleted file mode 100644 index c5f0ca213..000000000 --- a/python-prototype/tests/test_core.py +++ /dev/null @@ -1,102 +0,0 @@ -import pytest -from il65.datatypes import DataType, STRING_DATATYPES, char_to_bytevalue -from il65.plyparse import coerce_constant_value, LiteralValue, Scope, SymbolName, VarDef -from il65.compile import ParseError -from il65.plylex import SourceRef - - -def test_datatypes(): - assert all(isinstance(s, DataType) for s in STRING_DATATYPES) - assert all(s.isstring() for s in STRING_DATATYPES) - assert not any(s.isarray() or s.isnumeric() for s in STRING_DATATYPES) - assert DataType.WORDARRAY.isarray() - assert not DataType.WORDARRAY.isnumeric() - assert not DataType.WORDARRAY.isstring() - assert not DataType.WORD.isarray() - assert DataType.WORD.isnumeric() - assert not DataType.WORD.isstring() - - -def test_sourceref(): - s = SourceRef("file", 99, 42) - assert str(s) == "file:99:42" - s = SourceRef("file", 99) - assert str(s) == "file:99" - - -def test_parseerror(): - p = ParseError("message", SourceRef("filename", 99, 42)) - assert p.args == ("message", ) - assert str(p) == "filename:99:42 message" - - -def test_char_to_bytevalue(): - assert char_to_bytevalue('a') == 65 - assert char_to_bytevalue('\n') == 13 - assert char_to_bytevalue('π') == 126 - assert char_to_bytevalue('▒') == 230 - assert char_to_bytevalue('\x00') == 0 - assert char_to_bytevalue('\xff') == 255 - with pytest.raises(AssertionError): - char_to_bytevalue('') - # screencodes not yet implemented: assert datatypes.char_to_bytevalue('a', False) == 65 - - -def test_coerce_value_novars(): - sref = SourceRef("test", 1, 1) - def lv(v) -> LiteralValue: - return LiteralValue(value=v, sourceref=sref) # type: ignore - assert coerce_constant_value(DataType.BYTE, lv(0)) == (False, lv(0)) - assert coerce_constant_value(DataType.BYTE, lv(255)) == (False, lv(255)) - assert coerce_constant_value(DataType.BYTE, lv('@')) == (True, lv(64)) - assert coerce_constant_value(DataType.WORD, lv(0)) == (False, lv(0)) - assert coerce_constant_value(DataType.WORD, lv(65535)) == (False, lv(65535)) - assert coerce_constant_value(DataType.WORD, lv('@')) == (True, lv(64)) - assert coerce_constant_value(DataType.FLOAT, lv(-999.22)) == (False, lv(-999.22)) - assert coerce_constant_value(DataType.FLOAT, lv(123.45)) == (False, lv(123.45)) - assert coerce_constant_value(DataType.FLOAT, lv('@')) == (True, lv(64)) - assert coerce_constant_value(DataType.BYTE, lv(5.678)) == (True, lv(5)) - assert coerce_constant_value(DataType.WORD, lv(5.678)) == (True, lv(5)) - assert coerce_constant_value(DataType.WORD, - lv("string")) == (False, lv("string")), "string (address) can be assigned to a word" - assert coerce_constant_value(DataType.STRING, lv("string")) == (False, lv("string")) - assert coerce_constant_value(DataType.STRING_P, lv("string")) == (False, lv("string")) - assert coerce_constant_value(DataType.STRING_S, lv("string")) == (False, lv("string")) - assert coerce_constant_value(DataType.STRING_PS, lv("string")) == (False, lv("string")) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.BYTE, lv(-1)) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.BYTE, lv(256)) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.BYTE, lv(256.12345)) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.WORD, lv(-1)) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.WORD, lv(65536)) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.WORD, lv(65536.12345)) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.FLOAT, lv(-1.7014118346e+38)) - with pytest.raises(OverflowError): - coerce_constant_value(DataType.FLOAT, lv(1.7014118347e+38)) - with pytest.raises(TypeError): - coerce_constant_value(DataType.BYTE, lv("string")) - with pytest.raises(TypeError): - coerce_constant_value(DataType.FLOAT, lv("string")) - - -def test_coerce_value_vars(): - sref = SourceRef("test", 1, 1) - scope = Scope(nodes=[], level="block", sourceref=sref) - vardef = VarDef(name="constantvar", vartype="const", datatype=None, sourceref=sref) - vardef.value = LiteralValue(value=99, sourceref=sref) - scope.add_node(vardef) - vardef = VarDef(name="varvar", vartype="var", datatype=None, sourceref=sref) - vardef.value = LiteralValue(value=42, sourceref=sref) - scope.add_node(vardef) - vardef = VarDef(name="memvar", vartype="memory", datatype=None, sourceref=sref) - vardef.value = LiteralValue(value=0xc000, sourceref=sref) - scope.add_node(vardef) - value = SymbolName(name="constantvar", sourceref=sref) - value.parent = scope - assert coerce_constant_value(DataType.BYTE, value) == (True, LiteralValue(value=99, sourceref=sref)) diff --git a/python-prototype/tests/test_optimizer.py b/python-prototype/tests/test_optimizer.py deleted file mode 100644 index 51d795094..000000000 --- a/python-prototype/tests/test_optimizer.py +++ /dev/null @@ -1,92 +0,0 @@ -import pytest -from il65.plyparse import IncrDecr, AugAssignment, VarDef, SymbolName -from il65.optimize import optimize -from .test_parser import parse_source - - -def test_incrdecr_joins_nonfloat(): - src = """~ test { - X ++ - X ++ - X += 10 - Y-- - Y-- - Y-=20 - }""" - result = parse_source(src) - testscope = result.scope.nodes[0].nodes[0] - assert len(testscope.nodes) == 6 - assert isinstance(testscope.nodes[0], IncrDecr) - assert testscope.nodes[0].howmuch == 1 - assert isinstance(testscope.nodes[1], IncrDecr) - assert testscope.nodes[1].howmuch == 1 - assert isinstance(testscope.nodes[2], AugAssignment) - assert testscope.nodes[2].right.value == 10 - assert isinstance(testscope.nodes[3], IncrDecr) - assert testscope.nodes[3].howmuch == 1 - assert isinstance(testscope.nodes[4], IncrDecr) - assert testscope.nodes[4].howmuch == 1 - assert isinstance(testscope.nodes[5], AugAssignment) - assert testscope.nodes[5].right.value == 20 - # now optimize the incrdecrs (joins them) - optimize(result) - testscope = result.scope.nodes[0].nodes[0] - assert len(testscope.nodes) == 2 # @todo broken optimization right now - assert isinstance(testscope.nodes[0], IncrDecr) - assert testscope.nodes[0].operator == "++" - assert testscope.nodes[0].howmuch == 12 - assert isinstance(testscope.nodes[1], IncrDecr) - assert testscope.nodes[1].operator == "--" - assert testscope.nodes[1].howmuch == 22 - - -def test_incrdecr_joins_float(): - src = """~ test { - var .float flt = 0 - flt ++ - flt ++ - flt += 10 - flt -- - flt -- - flt -- - flt -= 5 - }""" - result = parse_source(src) - testscope = result.scope.nodes[0].nodes[0] - assert len(testscope.nodes) == 8 - # now optimize the incrdecrs (joins them) - optimize(result) - testscope = result.scope.nodes[0].nodes[0] - assert len(testscope.nodes) == 2 - assert isinstance(testscope.nodes[0], VarDef) - assert isinstance(testscope.nodes[1], IncrDecr) - assert testscope.nodes[1].operator == "++" - assert testscope.nodes[1].howmuch == 4 - assert isinstance(testscope.nodes[1].target, SymbolName) - assert testscope.nodes[1].target.name == "flt" - - -def test_large_incrdecr_to_augassign(): - src = """~ test { - X ++ - X ++ - X += 255 - Y -- - Y -- - Y -= 255 - }""" - result = parse_source(src) - testscope = result.scope.nodes[0].nodes[0] - assert len(testscope.nodes) == 6 - # now optimize; joins the incrdecrs then converts to augassign because values are too large. - optimize(result) - testscope = result.scope.nodes[0].nodes[0] - assert len(testscope.nodes) == 2 - assert isinstance(testscope.nodes[0], AugAssignment) - assert testscope.nodes[0].left.name == "X" - assert testscope.nodes[0].operator == "+=" - assert testscope.nodes[0].right.value == 257 - assert isinstance(testscope.nodes[1], AugAssignment) - assert testscope.nodes[1].left.name == "Y" - assert testscope.nodes[1].operator == "-=" - assert testscope.nodes[1].right.value == 257 diff --git a/python-prototype/tests/test_parser.py b/python-prototype/tests/test_parser.py deleted file mode 100644 index a548c8025..000000000 --- a/python-prototype/tests/test_parser.py +++ /dev/null @@ -1,544 +0,0 @@ -import math -import pytest -from il65.plylex import lexer, tokens, find_tok_column, literals, reserved, SourceRef -from il65.plyparse import * -from il65.datatypes import DataType, VarType -from il65.constantfold import ConstantFold - - -def lexer_error(sourceref: SourceRef, fmtstring: str, *args: str) -> None: - print("ERROR: {}: {}".format(sourceref, fmtstring.format(*args))) - - -def parse_source(src: str) -> AstNode: - lexer.lineno = 1 - lexer.source_filename = "sourcefile" - tfilt = TokenFilter(lexer) - result = parser.parse(input=src, tokenfunc=tfilt.token) - connect_parents(result, None) - return result - - -lexer.error_function = lexer_error - - -def test_lexer_definitions(): - assert "ENDL" in tokens - assert "GOTO" in tokens - assert '+' in literals - assert ';' not in literals - assert "return" in reserved - assert "sub" in reserved - assert "A" in reserved - assert "if_cc" in reserved - - -test_source_1 = """ %output prg, sys - -; c1 - -; c2 - - -~ block $c000 { - %import a,b - - - ; comment - - var foo = 42+true - var .matrix(20,30) m = 9.234556 - ;comment2 - - - sub calculate () -> () { - return - } - - ;z - -} -""" - - -def test_lexer(): - lexer.input(test_source_1) - lexer.lineno = 1 - tokens = list(iter(lexer)) - token_types = list(t.type for t in tokens) - assert token_types == ['DIRECTIVE', 'NAME', ',', 'NAME', 'ENDL', 'ENDL', 'ENDL', - 'BITINVERT', 'NAME', 'INTEGER', '{', 'ENDL', - 'DIRECTIVE', 'NAME', ',', 'NAME', 'ENDL', 'ENDL', - 'VARTYPE', 'NAME', 'IS', 'INTEGER', '+', 'BOOLEAN', 'ENDL', - 'VARTYPE', 'DATATYPE', '(', 'INTEGER', ',', 'INTEGER', ')', 'NAME', 'IS', 'FLOATINGPOINT', 'ENDL', 'ENDL', - 'SUB', 'NAME', '(', ')', 'RARROW', '(', ')', '{', 'ENDL', 'RETURN', 'ENDL', '}', 'ENDL', 'ENDL', 'ENDL', 'ENDL', - '}', 'ENDL'] - directive_token = tokens[12] - assert directive_token.type == "DIRECTIVE" - assert directive_token.value == "import" - assert directive_token.lineno == 9 - assert directive_token.lexpos == lexer.lexdata.index("%import") - assert find_tok_column(directive_token) == 10 - bool_token = tokens[23] - assert bool_token.type == "BOOLEAN" - assert type(bool_token.value) is bool - assert bool_token.value == True - - -def test_lexer_strings(): - lexer.input(r"'hello\tbye\n\n' '\n'") - lexer.lineno = 1 - tokens = list(iter(lexer)) - assert len(tokens) == 2 - st = tokens[0] - assert st.type == "STRING" - assert st.value == "hello\tbye\n\n" - lexer.input(r"'hello\tbye\n\n'") - st = tokens[1] - assert st.type == "CHARACTER" - assert st.value == '\n' - - -def test_tokenfilter(): - lexer.input(test_source_1) - lexer.lineno = 1 - filter = TokenFilter(lexer) - tokens = [] - while True: - token = filter.token() - if not token: - break - tokens.append(token) - token_types = list(t.type for t in tokens) - assert token_types == ['DIRECTIVE', 'NAME', ',', 'NAME', 'ENDL', - 'BITINVERT', 'NAME', 'INTEGER', '{', 'ENDL', - 'DIRECTIVE', 'NAME', ',', 'NAME', 'ENDL', - 'VARTYPE', 'NAME', 'IS', 'INTEGER', '+', 'BOOLEAN', 'ENDL', - 'VARTYPE', 'DATATYPE', '(', 'INTEGER', ',', 'INTEGER', ')', 'NAME', 'IS', 'FLOATINGPOINT', 'ENDL', - 'SUB', 'NAME', '(', ')', 'RARROW', '(', ')', '{', 'ENDL', 'RETURN', 'ENDL', '}', 'ENDL', - '}', 'ENDL'] - - -def test_parser(): - result = parse_source(test_source_1) - assert isinstance(result, Module) - assert result.name == "sourcefile" - assert result.scope.name == "" - assert result.subroutine_usage == {} - assert result.scope.parent_scope is None - sub = result.scope.lookup("block.calculate") - assert isinstance(sub, Subroutine) - assert sub.name == "calculate" - block = result.scope.lookup("block") - assert isinstance(block, Block) - assert block.name == "block" - bool_vdef = block.scope.nodes[1] - assert isinstance(bool_vdef, VarDef) - assert isinstance(bool_vdef.value, ExpressionWithOperator) - assert isinstance(bool_vdef.value.right, LiteralValue) - assert isinstance(bool_vdef.value.right.value, int) - assert bool_vdef.value.right.value == 1 - assert block.address == 49152 - sub2 = block.scope.lookup("calculate") - assert sub2 is sub - assert sub2.lineref == "src l. 19" - all_nodes = list(result.all_nodes()) - assert len(all_nodes) == 14 - all_nodes = list(result.all_nodes(Subroutine)) - assert len(all_nodes) == 1 - assert isinstance(all_nodes[0], Subroutine) - assert isinstance(all_nodes[0].parent, Scope) - assert all_nodes[0] in all_nodes[0].parent.nodes - assert all_nodes[0].lineref == "src l. 19" - assert all_nodes[0].parent.lineref == "src l. 8" - - -def test_block_nodes(): - sref = SourceRef("file", 1, 1) - sub1 = Subroutine(name="subaddr", param_spec=[], result_spec=[], address=0xc000, sourceref=sref) - sub2 = Subroutine(name="subblock", param_spec=[], result_spec=[], sourceref=sref) - sub2.scope = Scope(nodes=[Label(name="start", sourceref=sref)], level="block", sourceref=sref) - assert sub1.scope is None - assert sub1.nodes == [] - assert sub2.scope is not None - assert len(sub2.scope.nodes) > 0 - - -def test_parser_2(): - src = """~ test { - 999(1,2) - [zz]() - } - """ - result = parse_source(src) - block = result.scope.nodes[0] - call = block.scope.nodes[0] - assert isinstance(call, SubCall) - assert len(call.arguments.nodes) == 2 - assert isinstance(call.target, LiteralValue) - assert call.target.value == 999 - call = block.scope.nodes[1] - assert isinstance(call, SubCall) - assert len(call.arguments.nodes) == 0 - assert isinstance(call.target, Dereference) - assert call.target.operand.name == "zz" - - -def test_typespec(): - src = """~ test { - [$c000.word] = 5 - [$c000 .byte] = 5 - [AX .word] = 5 - [AX .float] = 5 - } - """ - result = parse_source(src) - block = result.scope.nodes[0] - assignment1, assignment2, assignment3, assignment4 = block.scope.nodes - assert assignment1.right.value == 5 - assert assignment2.right.value == 5 - assert assignment3.right.value == 5 - assert assignment4.right.value == 5 - assert len(assignment1.left.nodes) == 1 - assert len(assignment2.left.nodes) == 1 - assert len(assignment3.left.nodes) == 1 - assert len(assignment4.left.nodes) == 1 - t1 = assignment1.left - t2 = assignment2.left - t3 = assignment3.left - t4 = assignment4.left - assert isinstance(t1, Dereference) - assert isinstance(t2, Dereference) - assert isinstance(t3, Dereference) - assert isinstance(t4, Dereference) - assert isinstance(t1.operand, LiteralValue) - assert isinstance(t2.operand, LiteralValue) - assert isinstance(t3.operand, Register) - assert isinstance(t4.operand, Register) - assert t1.operand.value == 0xc000 - assert t2.operand.value == 0xc000 - assert t3.operand.name == "AX" - assert t4.operand.name == "AX" - assert t1.datatype == DataType.WORD - assert t2.datatype == DataType.BYTE - assert t3.datatype == DataType.WORD - assert t4.datatype == DataType.FLOAT - assert t1.size is None - assert t2.size is None - assert t3.size is None - assert t4.size is None - - -def test_char_string(): - src = """~ test { - var x1 = '@' - var x2 = 'π' - var x3 = 'abc' - A = '@' - A = 'π' - A = 'abc' - } - """ - result = parse_source(src) - block = result.scope.nodes[0] - var1, var2, var3, assgn1, assgn2, assgn3, = block.scope.nodes - assert var1.value.value == '@' - assert var2.value.value == 'π' - assert var3.value.value == "abc" - assert assgn1.right.value == '@' - assert assgn2.right.value == 'π' - assert assgn3.right.value == "abc" - # note: the actual one-charactor-to-bytevalue conversion is done at the very latest, when issuing an assignment statement - - -def test_boolean_int(): - src = """~ test { - var x1 = true - var x2 = false - A = true - A = false - } - """ - result = parse_source(src) - block = result.scope.nodes[0] - var1, var2, assgn1, assgn2, = block.scope.nodes - assert type(var1.value.value) is int and var1.value.value == 1 - assert type(var2.value.value) is int and var2.value.value == 0 - assert type(assgn1.right.value) is int and assgn1.right.value == 1 - assert type(assgn2.right.value) is int and assgn2.right.value == 0 - - -def test_incrdecr_operators(): - sref = SourceRef("test", 1, 1) - with pytest.raises(ValueError): - IncrDecr(operator="??", sourceref=sref) - i = IncrDecr(operator="++", sourceref=sref) - assert i.howmuch == 1 - - -def test_symbol_lookup(): - sref = SourceRef("test", 1, 1) - var1 = VarDef(name="var1", vartype="const", datatype=DataType.WORD, sourceref=sref) - var1.value = LiteralValue(value=42, sourceref=sref) - var1.value.parent = var1 - var2 = VarDef(name="var2", vartype="const", datatype=DataType.FLOAT, sourceref=sref) - var2.value = LiteralValue(value=123.456, sourceref=sref) - var2.value.parent = var2 - label1 = Label(name="outerlabel", sourceref=sref) - label2 = Label(name="innerlabel", sourceref=sref) - scope_inner = Scope(nodes=[ - label2, - var2 - ], level="block", sourceref=sref) - scope_inner.name = "inner" - var2.parent = label2.parent = scope_inner - scope_outer = Scope(nodes=[ - label1, - var1, - scope_inner - ], level="block", sourceref=sref) - scope_outer.name = "outer" - var1.parent = label1.parent = scope_inner.parent = scope_outer - scope_topmost = Scope(nodes=[scope_outer], level="module", sourceref=sref) - scope_topmost.name = "topmost" - scope_outer.parent = scope_topmost - scope_topmost.define_builtin_functions() - assert scope_inner.parent_scope is scope_outer - assert scope_outer.parent_scope is scope_topmost - assert scope_topmost.parent_scope is None - assert label1.my_scope() is scope_outer - assert var1.my_scope() is scope_outer - assert scope_inner.my_scope() is scope_outer - assert label2.my_scope() is scope_inner - assert var2.my_scope() is scope_inner - assert scope_outer.my_scope() is scope_topmost - with pytest.raises(LookupError): - scope_topmost.my_scope() - with pytest.raises(UndefinedSymbolError): - scope_inner.lookup("unexisting") - with pytest.raises(UndefinedSymbolError): - scope_outer.lookup("unexisting") - assert scope_inner.lookup("innerlabel") is label2 - assert scope_inner.lookup("var2") is var2 - assert scope_inner.lookup("outerlabel") is label1 - assert scope_inner.lookup("var1") is var1 - with pytest.raises(UndefinedSymbolError): - scope_outer.lookup("innerlabel") - with pytest.raises(UndefinedSymbolError): - scope_outer.lookup("var2") - assert scope_outer.lookup("var1") is var1 - assert scope_outer.lookup("outerlabel") is label1 - math_func = scope_inner.lookup("sin") - assert isinstance(math_func, BuiltinFunction) - assert math_func.name == "sin" and math_func.func is math.sin - builtin_func = scope_inner.lookup("abs") - assert isinstance(builtin_func, BuiltinFunction) - assert builtin_func.name == "abs" and builtin_func.func is abs - # test dotted names: - with pytest.raises(UndefinedSymbolError): - scope_inner.lookup("noscope.nosymbol.nothing") - assert scope_inner.lookup("outer.inner.var2") is var2 - with pytest.raises(UndefinedSymbolError): - scope_inner.lookup("outer.inner.var1") - with pytest.raises(UndefinedSymbolError): - scope_inner.lookup("outer.var2") - assert scope_inner.lookup("outer.var1") is var1 - - -def test_const_numeric_expressions(): - src = """~ test { - A = 1+2+3+4+5 - X = 1+2*5+2 - Y = (1+2)*(5+2) - A = (((10+20)/2)+5)**3 - X = -10-11-12 - Y = 1.234 mod (0.9 / 1.2) - A = sin(1.234) - X = round(4.567)-2 - Y = 1+abs(-100) - A = ~1 - X = -1 - A = 4 << (9-3) - X = 5000 >> 2 - Y = 999//88 -} -""" - result = parse_source(src) - if isinstance(result, Module): - result.scope.define_builtin_functions() - assignments = list(result.all_nodes(Assignment)) - e = [a.nodes[1] for a in assignments] - assert all(x.is_compiletime_const() for x in e) - assert e[0].const_value() == 15 # 1+2+3+4+5 - assert e[1].const_value() == 13 # 1+2*5+2 - assert e[2].const_value() == 21 # (1+2)*(5+2) - assert e[3].const_value() == 8000 # (((10+20)/2)+5)**3 - assert e[4].const_value() == -33 # -10-11-12 - assert e[5].const_value() == 0.484 # 1.234 mod (0.9 / 1.2) - assert math.isclose(e[6].const_value(), 0.9438182093746337) # sin(1.234) - assert e[7].const_value() == 3 # round(4.567)-2 - assert e[8].const_value() == 101 # 1+abs(-100) - assert e[9].const_value() == -2 # ~1 - assert e[10].const_value() == -1 # -1 - assert e[11].const_value() == 256 # 4 << (9-3) - assert e[12].const_value() == 1250 # 5000 >> 2 - assert e[13].const_value() == 11 # 999//88 - - -def test_const_logic_expressions(): - src = """~ test { - A = true or false - X = true and false - Y = true xor false - A = false and false or true - X = (false and (false or true)) - Y = not (false or true) - A = 1 < 2 - X = 1 >= 2 - Y = 1 == (2+3) -} -""" - result = parse_source(src) - assignments = list(result.all_nodes(Assignment)) - e = [a.nodes[1] for a in assignments] - assert all(x.is_compiletime_const() for x in e) - assert e[0].const_value() == True - assert e[1].const_value() == False - assert e[2].const_value() == True - assert e[3].const_value() == True - assert e[4].const_value() == False - assert e[5].const_value() == False - assert e[6].const_value() == True - assert e[7].const_value() == False - assert e[8].const_value() == False - - -def test_const_other_expressions(): - src = """~ test { - memory memvar = $c123 - A = &memvar ; constant - X = &sin ; non-constant - Y = [memvar] ; non-constant -} -""" - result = parse_source(src) - if isinstance(result, Module): - result.scope.define_builtin_functions() - assignments = list(result.all_nodes(Assignment)) - e = [a.nodes[1] for a in assignments] - assert e[0].is_compiletime_const() - assert e[0].const_value() == 0xc123 - assert not e[1].is_compiletime_const() - with pytest.raises(TypeError): - e[1].const_value() - assert not e[2].is_compiletime_const() - with pytest.raises(TypeError): - e[2].const_value() - - -def test_vdef_const_folds(): - src = """~ test { - const cb1 = 123 - const cb2 = cb1 - const cb3 = cb1*3 -} -""" - result = parse_source(src) - if isinstance(result, Module): - result.scope.define_builtin_functions() - vd = list(result.all_nodes(VarDef)) - assert vd[0].name == "cb1" - assert vd[0].vartype == VarType.CONST - assert vd[0].datatype == DataType.BYTE - assert isinstance(vd[0].value, LiteralValue) - assert vd[0].value.value == 123 - assert vd[1].name == "cb2" - assert vd[1].vartype == VarType.CONST - assert vd[1].datatype == DataType.BYTE - assert isinstance(vd[1].value, SymbolName) - assert vd[1].value.name == "cb1" - assert vd[2].name == "cb3" - assert vd[2].vartype == VarType.CONST - assert vd[2].datatype == DataType.BYTE - assert isinstance(vd[2].value, ExpressionWithOperator) - cf = ConstantFold(result) - cf.fold_constants() - vd = list(result.all_nodes(VarDef)) - assert vd[0].name == "cb1" - assert vd[0].vartype == VarType.CONST - assert vd[0].datatype == DataType.BYTE - assert isinstance(vd[0].value, LiteralValue) - assert vd[0].value.value == 123 - assert vd[1].name == "cb2" - assert vd[1].vartype == VarType.CONST - assert vd[1].datatype == DataType.BYTE - assert isinstance(vd[1].value, LiteralValue) - assert vd[1].value.value == 123 - assert vd[2].name == "cb3" - assert vd[2].vartype == VarType.CONST - assert vd[2].datatype == DataType.BYTE - assert isinstance(vd[2].value, LiteralValue) - assert vd[2].value.value == 369 - - -def test_vdef_const_expressions(): - src = """~ test { - var bvar = 99 - var .float fvar = sin(1.2-0.3) - var .float flt2 = -9.87e-6 - - bvar ++ - fvar ++ - flt2 ++ - bvar += 2+2 - fvar += 2+3 - flt2 += 2+4 - bvar = 2+5 - fvar = 2+6 - flt2 = 2+7 -} -""" - result = parse_source(src) - if isinstance(result, Module): - result.scope.define_builtin_functions() - cf = ConstantFold(result) - cf.fold_constants() - vd = list(result.all_nodes(VarDef)) - assert len(vd)==3 - assert vd[0].name == "bvar" - assert isinstance(vd[0].value, LiteralValue) - assert vd[0].value.value == 99 - assert vd[1].name == "fvar" - assert isinstance(vd[1].value, LiteralValue) - assert type(vd[1].value.value) is float - assert math.isclose(vd[1].value.value, math.sin(0.9)) - assert vd[2].name == "flt2" - assert isinstance(vd[2].value, LiteralValue) - assert math.isclose(-9.87e-6, vd[2].value.value) - # test incrdecr assignment target - nodes = list(result.all_nodes(IncrDecr)) - assert len(nodes) == 3 - assert isinstance(nodes[0].target, SymbolName) - assert nodes[0].target.name == "bvar" - assert isinstance(nodes[1].target, SymbolName) - assert nodes[1].target.name == "fvar" - assert isinstance(nodes[2].target, SymbolName) - assert nodes[2].target.name == "flt2" - # test augassign assignment target - nodes = list(result.all_nodes(AugAssignment)) - assert len(nodes) == 3 - assert isinstance(nodes[0].left, SymbolName) - assert nodes[0].left.name == "bvar" - assert isinstance(nodes[1].left, SymbolName) - assert nodes[1].left.name == "fvar" - assert isinstance(nodes[2].left, SymbolName) - assert nodes[2].left.name == "flt2" - # test assign assignment target - nodes = list(result.all_nodes(Assignment)) - assert len(nodes) == 3 - assert isinstance(nodes[0].left, SymbolName) - assert nodes[0].left.name == "bvar" - assert isinstance(nodes[1].left, SymbolName) - assert nodes[1].left.name == "fvar" - assert isinstance(nodes[2].left, SymbolName) - assert nodes[2].left.name == "flt2" diff --git a/python-prototype/tests/test_vardef.py b/python-prototype/tests/test_vardef.py deleted file mode 100644 index 022f3abea..000000000 --- a/python-prototype/tests/test_vardef.py +++ /dev/null @@ -1,112 +0,0 @@ -import pytest -from il65.datatypes import DataType, VarType -from il65.plyparse import (LiteralValue, VarDef, DatatypeNode, ExpressionWithOperator, - Scope, AddressOf, SymbolName, UndefinedSymbolError) -from il65.plylex import SourceRef - - -def test_creation(): - sref = SourceRef("test", 1, 1) - v = VarDef(name="v1", vartype="const", datatype=None, sourceref=sref) - assert v.name == "v1" - assert v.vartype == VarType.CONST - assert v.datatype == DataType.BYTE - assert v.size == [1] - assert v.value is None - assert v.zp_address is None - v = VarDef(name="v2", vartype="memory", datatype=None, sourceref=sref) - assert v.vartype == VarType.MEMORY - assert isinstance(v.value, LiteralValue) - assert v.value.value == 0 - dt = DatatypeNode(name="float", sourceref=sref) - v = VarDef(name="v2", vartype="var", datatype=dt, sourceref=sref) - assert v.vartype == VarType.VAR - assert v.datatype == DataType.FLOAT - assert isinstance(v.value, LiteralValue) - assert v.value.value == 0 - dt = DatatypeNode(name="matrix", sourceref=sref) - with pytest.raises(ValueError): - VarDef(name="v2", vartype="var", datatype=dt, sourceref=sref) - dt.dimensions = [2, 3] - v = VarDef(name="v2", vartype="var", datatype=dt, sourceref=sref) - assert v.vartype == VarType.VAR - assert v.datatype == DataType.MATRIX - assert v.size == [2, 3] - assert isinstance(v.value, LiteralValue) - assert v.value.value == 0 - dt = DatatypeNode(name="str", sourceref=sref) - v = VarDef(name="v2", vartype="var", datatype=dt, sourceref=sref) - assert v.vartype == VarType.VAR - assert v.datatype == DataType.STRING - assert v.size == [1] - assert v.value is None - - -def test_set_value(): - sref = SourceRef("test", 1, 1) - v = VarDef(name="v1", vartype="var", datatype=DatatypeNode(name="word", sourceref=sref), sourceref=sref) - assert v.datatype == DataType.WORD - assert v.value.value == 0 - v.value = LiteralValue(value=42, sourceref=sref) - assert v.value.value == 42 - v = VarDef(name="v1", vartype="var", datatype=DatatypeNode(name="str", sourceref=sref), sourceref=sref) - assert v.datatype == DataType.STRING - assert v.value is None - v.value = LiteralValue(value="hello", sourceref=sref) - assert v.value.value == "hello" - e = ExpressionWithOperator(operator="-", sourceref=sref) - e.left = LiteralValue(value=42, sourceref=sref) - v.value = e - assert v.value is e - - -def test_const_value(): - sref = SourceRef("test", 1, 1) - scope = Scope(nodes=[], level="block", sourceref=sref) - vardef = VarDef(name="constvar", vartype="const", datatype=None, sourceref=sref) - vardef.value = LiteralValue(value=43, sourceref=sref) - scope.add_node(vardef) - vardef = VarDef(name="varvar", vartype="var", datatype=None, sourceref=sref) - vardef.value = LiteralValue(value=44, sourceref=sref) - scope.add_node(vardef) - vardef = VarDef(name="memvar", vartype="memory", datatype=None, sourceref=sref) - vardef.value = LiteralValue(value=45, sourceref=sref) - scope.add_node(vardef) - v = VarDef(name="v1", vartype="var", datatype=DatatypeNode(name="word", sourceref=sref), sourceref=sref) - with pytest.raises(TypeError): - v.const_value() - v = VarDef(name="v1", vartype="memory", datatype=DatatypeNode(name="word", sourceref=sref), sourceref=sref) - with pytest.raises(TypeError): - v.const_value() - v = VarDef(name="v1", vartype="const", datatype=DatatypeNode(name="word", sourceref=sref), sourceref=sref) - with pytest.raises(ValueError): - v.const_value() - v.value = LiteralValue(value=42, sourceref=sref) - assert v.const_value() == 42 - v = VarDef(name="v1", vartype="const", datatype=DatatypeNode(name="float", sourceref=sref), sourceref=sref) - with pytest.raises(ValueError): - v.const_value() - v.value = LiteralValue(value=42.9988, sourceref=sref) - assert v.const_value() == 42.9988 - e = ExpressionWithOperator(operator="-", sourceref=sref) - e.left = LiteralValue(value=42, sourceref=sref) - v.value = e - assert v.const_value() == -42 - s = SymbolName(name="unexisting", sourceref=sref) - s.parent = scope - v.value = s - with pytest.raises(UndefinedSymbolError): - v.const_value() - s = SymbolName(name="constvar", sourceref=sref) - s.parent = scope - v.value = s - assert v.const_value() == 43 - a = AddressOf(name="varvar", sourceref=sref) - a.parent = scope - v.value = a - with pytest.raises(TypeError): - v.const_value() - a = AddressOf(name="memvar", sourceref=sref) - a.parent = scope - v.value = a - assert v.const_value() == 45 diff --git a/python-prototype/tests/test_vmcore.py b/python-prototype/tests/test_vmcore.py deleted file mode 100644 index a6047565f..000000000 --- a/python-prototype/tests/test_vmcore.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest -from tinyvm.core import Memory - - -def test_memory_unsigned(): - m = Memory() - m.set_byte(1000, 1) - m.set_byte(1001, 2) - m.set_byte(1002, 3) - m.set_byte(1003, 4) - m.set_byte(2000, 252) - m.set_byte(2001, 253) - m.set_byte(2002, 254) - m.set_byte(2003, 255) - assert 1 == m.get_byte(1000) - assert 2 == m.get_byte(1001) - assert 3 == m.get_byte(1002) - assert 4 == m.get_byte(1003) - assert 252 == m.get_byte(2000) - assert 253 == m.get_byte(2001) - assert 254 == m.get_byte(2002) - assert 255 == m.get_byte(2003) - assert b"\x01\x02\x03\x04" == m.get_bytes(1000, 4) - assert 0x0201 == m.get_word(1000) - assert 0xfffe == m.get_word(2002) - m.set_word(2002, 40000) - assert 40000 == m.get_word(2002) - assert 0x40 == m.get_byte(2002) - assert 0x9c == m.get_byte(2003) - - -def test_memory_signed(): - m = Memory() - m.set_byte(1000, 1) - m.set_byte(1001, 2) - m.set_byte(1002, 3) - m.set_byte(1003, 4) - m.set_byte(2000, 252) - m.set_byte(2001, 253) - m.set_byte(2002, 254) - m.set_byte(2003, 255) - assert 1 == m.get_sbyte(1000) - assert 2 == m.get_sbyte(1001) - assert 3 == m.get_sbyte(1002) - assert 4 == m.get_sbyte(1003) - assert -4 == m.get_sbyte(2000) - assert -3 == m.get_sbyte(2001) - assert -2 == m.get_sbyte(2002) - assert -1 == m.get_sbyte(2003) - assert 0x0201 == m.get_sword(1000) - assert -2 == m.get_sword(2002) - m.set_sword(2002, 30000) - assert 30000 == m.get_sword(2002) - assert 0x30 == m.get_sbyte(2002) - assert 0x75 == m.get_sbyte(2003) - m.set_sword(2002, -30000) - assert -30000 == m.get_sword(2002) - assert 0x8ad0 == m.get_word(2002) - assert 0xd0 == m.get_byte(2002) - assert 0x8a == m.get_byte(2003) diff --git a/python-prototype/tests/test_zp.py b/python-prototype/tests/test_zp.py deleted file mode 100644 index f1bddf0e8..000000000 --- a/python-prototype/tests/test_zp.py +++ /dev/null @@ -1,83 +0,0 @@ -import pytest -from il65.compile import Zeropage, CompileError -from il65.plyparse import ZpOptions, VarDef -from il65.plylex import SourceRef -from il65.datatypes import DataType - - -def test_zp_names(): - sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.NOCLOBBER, False) - with pytest.raises(AssertionError): - zp.allocate(VarDef(name="", vartype="memory", datatype=DataType.BYTE, sourceref=sref)) - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - zp.allocate(VarDef(name="varname", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - with pytest.raises(AssertionError): - zp.allocate(VarDef(name="varname", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - zp.allocate(VarDef(name="varname2", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - - -def test_zp_noclobber_allocation(): - sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.NOCLOBBER, True) - assert zp.available() == 9 - with pytest.raises(CompileError): - # in regular zp there aren't 5 sequential bytes free - zp.allocate(VarDef(name="impossible", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) - for i in range(zp.available()): - loc = zp.allocate(VarDef(name="bvar"+str(i), vartype="var", datatype=DataType.BYTE, sourceref=sref)) - assert loc > 0 - assert zp.available() == 0 - with pytest.raises(CompileError): - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - with pytest.raises(CompileError): - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) - - -def test_zp_float_enable(): - sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.CLOBBER, False) - with pytest.raises(TypeError): - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) - zp = Zeropage(ZpOptions.CLOBBER, True) - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) - - -def test_zp_clobber_allocation(): - sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.CLOBBER, True) - assert zp.available() == 239 - loc = zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) - assert loc > 3 and loc not in zp.free - num, rest = divmod(zp.available(), 5) - for _ in range(num-3): - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) - assert zp.available() == 19 - with pytest.raises(CompileError): - # can't allocate because no more sequential bytes, only fragmented - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) - for _ in range(14): - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) - with pytest.raises(CompileError): - zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) - assert zp.available() == 1 - zp.allocate(VarDef(name="last", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - with pytest.raises(CompileError): - zp.allocate(VarDef(name="impossible", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - - -def test_zp_efficient_allocation(): - # free = [0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa] - sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.NOCLOBBER, False) - assert zp.available() == 9 - assert 0x2a == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - assert 0x52 == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - assert 0x04 == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) - assert 0xf7 == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) - assert 0x06 == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) - assert 0xf9 == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) - assert zp.available() == 0 diff --git a/python-prototype/testsource/calls.ill b/python-prototype/testsource/calls.ill deleted file mode 100644 index 48b40034e..000000000 --- a/python-prototype/testsource/calls.ill +++ /dev/null @@ -1,159 +0,0 @@ -; call tests - -~ foo { - - - - var word var1 = 99 - memory word mem1 = $cff0 - - var byte varb1 = 99 - memory byte memb1 = $cff0 - const word constw = $2355 - const byte constb = $23 - const float constf = 3.4556677 - const str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - -bar2: - -bar: goto $c000 - goto var1 ; jumps to the address in var1 - goto mem1 ; jumps to the address in mem1 - goto [var1] - goto [$c000.word] - goto [var1] - goto [mem1] - - ; ---- - - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 ("hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - goto [var1] - goto [mem1] ; comment - goto $c000 - goto 64738 - 64738(1,2) ; @todo should be jsr $64738 - return 9999() ; @todo should be jmp 9999 ? - return [AX]() - return [var1] () ; comment - return [mem1] () - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - goto [$c2.word] - return 33 - return [$c2.word] - return [$c2.word] (4) - return [$c2.word] (4) - return [$c2.word] (4) - return [$c2.word] (4) - return [$c2dd.word] ( ) - goto [$c2dd.word] - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! ("hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - - sub3 (81) - sub3 !(81) - sub3 !A (81) - sub3 !X (81) - sub3 !Y (81) - sub3 !XY (81) - sub3 !AXY (81) - - bar!() - bar !() - [XY]! () - [XY] ! () - [var1]!() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 ("hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - A= [$c2.word] - A= [$c2dd.word ] - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() - return - -} - - -~ main { - -start: - foo.bar() - return - -sub unused_sub ()->() { - A=X - X=Y - Y=A - return -} - - -} diff --git a/python-prototype/testsource/conditionals.ill b/python-prototype/testsource/conditionals.ill deleted file mode 100644 index fbd28ae38..000000000 --- a/python-prototype/testsource/conditionals.ill +++ /dev/null @@ -1,141 +0,0 @@ -%output basic - -%import c64lib - - -~ main { - - var word value - memory word memvalue = $8000 - - -start: - A = 100 - - - ; conditional if, without conditional expression. needs explicit if status. - if_not goto label - if_true goto label - if_zero goto label - if_cc goto label - if_lt goto label - if_le goto label - if_ge goto label - if_gt goto label - if_cc goto value - ;if_cc goto memvalue - ;if_cc goto #memvalue - if_cc goto [value] - ;if_cc goto [memvalue] - ;if_cc goto $c000 - ;if_cc goto [$c000.word] - -label: - ; conditional if with a single 'truth' value (register) - if_true A goto label2 - if_not A goto label2 - if_zero A goto label2 - if A goto label2 - if_true X goto label2 - if_not X goto label2 - if_zero X goto label2 - if X goto label2 - if_true Y goto label2 - if_not Y goto label2 - if_zero Y goto label2 - if Y goto label2 - if_true XY goto label2 - if_not XY goto label2 - if_zero XY goto label2 - if XY goto label2 - -label2: - ; conditional if with a single 'truth' value (variable) - if_true value goto label3 - if_not value goto label3 - if_zero value goto label3 - if value goto label3 - if_true memvalue goto label3 - if_not memvalue goto label3 - if_zero memvalue goto label3 - if memvalue goto label3 - -label3: - ; conditional if with a single 'truth' value (indirect address) - if_true [$c000] goto label4 - if_not [$c000] goto label4 - if_zero [$c000] goto label4 - if [$c000] goto label4 - if_true [XY] goto label4 - if_true [AY] goto label4 - if_true [AX] goto label4 - -label4: - return -} - - - -~ conditionals { - var bytevar = 22 + 23 - var str name = "?"*80 - var bytevar2 = 23 - var word wordvar = 22345 - - -start: - c64.init_system() - - A = 0 -printloop: - c64scr.print_byte_decimal(A) - c64.CHROUT('\n') - A++ - if A <20 goto printloop - return - -label1: - if_true A==123 goto label1 - if_true A == 123 goto label1 - if_true A!=0 goto label1 - if_not X!=0 goto label1 - - if_true A <= 1 goto label1 - if_true A==1 goto label1 - if_true A!=1 goto label1 - if_not X < 1 goto label1 - if_not Y>1 goto label1 - if_not X!=1 goto label1 - - if_true 22<=Y goto label1 - if_true A<=22 goto label1 - if_true AY goto label1 - if_true X>A goto label1 - if A<=22 goto label1 - if A <= 22 goto label1 - if_zero A goto label1 - if_zero X goto label1 - if Y goto label1 - if_true XY goto label1 - if_not XY goto label1 - if A goto label1 - if_not goto label1 - - if_true bytevar<=A goto label2 - if_true bytevar<=22 goto label2 - if bytevar<=22 goto label2 - if bytevar<=22 goto label2 - if bytevar goto label2 - if bytevar>bytevar2 goto label2 - if_zero bytevar goto label2 - if_not wordvar goto label2 - if_zero wordvar goto label2 - if_true wordvar goto label2 - - -label2: - return -} - - diff --git a/python-prototype/testsource/dtypes.ill b/python-prototype/testsource/dtypes.ill deleted file mode 100644 index 1d6327d00..000000000 --- a/python-prototype/testsource/dtypes.ill +++ /dev/null @@ -1,311 +0,0 @@ -; var definitions and immediate primitive data type tests - -%output raw -%zp clobber - - -%import c64lib - -~ ZP { - ; ZeroPage block definition: - ; base address is set to $04 (because $00 and $01 are used by the hardware, and $02/$03 are scratch) - ; everything here ends up in the zeropage, as long as there is space. - ; you can NOT put subroutines in here (yet). -} - -~ ZP { - var zpvar1 - var zpvar2 - memory zpmem1 = $f0 - const zpconst = 1.234 - var word zpvarw1 - var word zpvarw2 - var float zpvarflt1 = 11.11 - var float zpvarflt2 = 22.22 - var float zpvarflt3 - -} - -~ ZP { - ; will be merged with other ZP blocks! - var zpvar1b = $88 -} - - -~ foo { - var foovar1 - memory foomem1 = $f0f0 - const fooconst = 1.234 - return -} - - -~ main { - ; variables - var uninitbyte1 - var byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var byte initbyte2 = $12 - var byte initbyte2b = false - var byte initbyte3 = 99.876 - var initchar1 = '@' - var byte initchar2 = '@' - var word uninitword - var word initword1 = $1234 - var word initword1b = true - var word initword2 = false - var word initword3 = 9876.554321 - var word initword5 = 20 - var float uninitfloat - var float initfloat1 = 0 - var float initfloat1b = true - var float initfloat2 = -1.234e-14 - var float initfloat3 = 9.87e+14 - var float initfloat4 = 1.70141183e+38 - var float initfloat5 = -1.70141183e+38 - var float initfloat6 = 1.234 - - var wordarray( 256 ) uninit_wordarray - var wordarray(10) init_wordarray = $1234 - var wordarray(10) init_wordarrayb = true - var array( 256) uninit_bytearray - var array(10 ) init_bytearray =$12 - var array(10 ) init_bytearrayb =true - var array(10 ) init_bytearrayc ='@' - - var str text = "hello-null" - var strp ptext = 'hello-pascal' - var strs stext = 'screencodes-null' - var strps pstext = "screencodes-pascal" - - var matrix( 2, 128 ) uninitmatrix - var matrix(10, 20) initmatrix1 = $12 - var matrix(10, 20) initmatrix1b = true - var matrix(10, 20) initmatrix1c = '@' - var matrix(10, 20) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory byte membyte2 = $c222 - memory word memword1 = $cf03 - memory float memfloat = $cf04 - memory array(10 ) membytes = $cf05 - memory wordarray( 10) memwords = $cf06 - memory matrix( 10, 20 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const byte cbyte2 = 1 - const byte cbyte3 = '@' - const byte cbyte4 = true - const word cword1 = false - const word cword2 = $1234 - const word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const float cfloat2 = 2.3456 - const float cfloat2b = cfloat2*3.44 - const float cfloat3 = true - const str ctext3 = "constant-text" - const strp ctext4 = "constant-ptext" - const strs ctext5 = "constant-stext" - const strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var word vmemaddr1 = &membyte1 - var word vmemaddr2 = &memword1 - var word vmemaddr3 = &memfloat - var word vmemaddr4 = &membytes - var word vmemaddr5 = &memwords - var word vmemaddr6 = &memmatrix - var word vmemaddr8 = 100*sin(cbyte1) - var word vmemaddr9 = cword2+$5432 - var word vmemaddr10 = cfloat2b - - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - var word initword0a = &ZP.zpmem1 - var initbytea0 = &ZP.zpmem1 - - - ; (constant) expressions - var word expr_byte1b = -1-2-3-4-$22+$80+ZP.zpconst - var byte expr_fault2 = 1 + (8/3)+sin(33) + len("abc") - - -sin: - return - -max: - return - - -start: - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = membyte2 - XY = memword1 - XY = sin - XY = &sin ; @todo not yet implemented - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 ; @todo not yet implemented - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = sin - [$c333.word] = &sin ; @todo not yet implemented - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = sin - initword1 = &sin ; @todo not yet implemented - - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = sin - memword1 = &sin ; @todo not yet implemented - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 - - return -} - -~ footer { - XY = "text-immediate" ; reuses existing - AY = "text-immediate" ; reuses existing - AX = "another" - AX = "" - [$c000.word] = "another" ; must reuse string - [$c100.word] = "text-immediate" ; must reuse string - [$c200.word] = "" ; must reuse string - return -} diff --git a/python-prototype/testsource/floats.ill b/python-prototype/testsource/floats.ill deleted file mode 100644 index 851da6b82..000000000 --- a/python-prototype/testsource/floats.ill +++ /dev/null @@ -1,148 +0,0 @@ -; floating point tests - -%output basic - -%import c64lib - -~ main_testing { -start: - var float myfloat1 = 1234.56789 - var float myfloat2 = 9876.54321 - var float myfloatneg1 = -555.666 - var float myfloat_large1 = 987654.1111 - var float myfloat_large2 = -123456.2222 - var str myfloatstr = "1234.998877" - var float myfloatzero = 0 - var float myfloatsmall1 = 1.234 - var float myfloatsmall2 = 2.6677 - - return -} - - -~ main { - - byte bbvar - - float@ - - var float flt_pi = 3.141592653589793 - var float flt_minus32768 = -32768 - var float flt_1 = 1 - var float flt_half_sqr2 = 0.7071067811865476 - var float flt_sqr2 = 1.4142135623730951 - var float flt_minus_half = -.5 - var float flt_log_2 = 0.6931471805599453 - var float flt_10 = 10 - var float flt_1e9 = 1000000000 - var float flt_half = .5 - var float flt_one_over_log_2 = 1.4426950408889634 - var float flt_half_pi = 1.5707963267948966 - var float flt_double_pi = 6.283185307179586 - var float flt_point25 = .25 - var float my_float - memory .word some_address = $ccdd - memory .byte some_addressb = $ccee - var .byte bytevar = $cc - var .word wordvar = $cdef - - -sub printflt (float: AY) -> (?) { - c64.MOVFM!(AY) - goto c64.FPRINTLN - ; c64.FOUT!() - ; c64scr.print_string!(AY) - ;goto c64.CHROUT('\n') -} - - -start: - ; assign some float values to the memory - AY = &flt_pi - some_address = & flt_pi - some_address = 4123.2342342222 - [$c000.word] = & flt_pi - - my_float ++ - my_float += 1 - my_float += 0.5 - my_float += 999.222 - my_float -- - my_float -= 1 - my_float -= 0.5 - my_float -= 999.222 - - ; print some floating points from source and compare them with ROM - - printflt(&flt_pi) - printflt(&c64.FL_PIVAL) - - printflt(&flt_minus32768) - printflt(&c64.FL_N32768) - - printflt(&flt_1) - printflt(&c64.FL_FONE) - - printflt(&flt_half_sqr2) - printflt( & c64.FL_SQRHLF) - - printflt(&flt_sqr2) - printflt(&c64.FL_SQRTWO) - - printflt(&flt_minus_half) - printflt(&c64.FL_NEGHLF) - - printflt(&flt_log_2) - printflt(&c64.FL_LOG2) - - printflt(&flt_10) - printflt(&c64.FL_TENC) - - printflt(&flt_1e9) - printflt(&c64.FL_NZMIL) - - printflt(&flt_half) - printflt(&c64.FL_FHALF) - - printflt(&flt_one_over_log_2) - printflt(&c64.FL_LOGEB2) - - printflt(&flt_half_pi) - printflt(&c64.FL_PIHALF) - - printflt(&flt_double_pi) - printflt(&c64.FL_TWOPI) - - printflt(& flt_point25) - printflt(&c64.FL_FR4) - -reg_to_float: - c64.CHROUT!('\n') - - A=33 - X=44 - Y=55 - - my_float = A - printflt(&my_float) - - my_float = X - printflt(&my_float) - - my_float = Y - printflt(&my_float) - - XY = 11122 - my_float = XY - printflt(&my_float) - - AX = 33344 - my_float = AX - printflt(&my_float) - - AY = 55566 - my_float = AY - printflt(&my_float) - - return -} diff --git a/python-prototype/testsource/included.binary b/python-prototype/testsource/included.binary deleted file mode 100644 index 82df4219c..000000000 Binary files a/python-prototype/testsource/included.binary and /dev/null differ diff --git a/python-prototype/testsource/included.source b/python-prototype/testsource/included.source deleted file mode 100644 index 7e410005b..000000000 --- a/python-prototype/testsource/included.source +++ /dev/null @@ -1,2 +0,0 @@ -; this assembly code is included as-is - nop diff --git a/python-prototype/testsource/input.ill b/python-prototype/testsource/input.ill deleted file mode 100644 index e661ebd15..000000000 --- a/python-prototype/testsource/input.ill +++ /dev/null @@ -1,51 +0,0 @@ -%output basic - -%import c64lib - -~ main { - var .str name = "????????????????????????????????????????????????????????????????????????????????" ; 80 - var .word orig_irq - -start: - c64.init_system() - - orig_irq = c64.CINV - SI = 1 - c64.CINV = &irq_handler - SI = 0 - - - c64scr.print_string("enter your name: ") - c64scr.input_chars(name) - c64.CHROUT('\n') - -blop: - return - %breakpoint - return - - ; yeah! - - c64scr.print_string("thank you, mr or mrs: ") - c64scr.print_string(name) - c64.CHROUT('\n') - - SI = 1 - c64.CINV = orig_irq - SI = 0 - - return - - -irq_handler: - %asm { - lda c64.SFDX - cmp #$40 ; nothing pressed? - beq + - inc c64.EXTCOL ; otherwise change color -+ jmp c64.IRQDFRT - } - - - -} diff --git a/python-prototype/testsource/large.ill b/python-prototype/testsource/large.ill deleted file mode 100644 index 8d4f30944..000000000 --- a/python-prototype/testsource/large.ill +++ /dev/null @@ -1,7135 +0,0 @@ -; var definitions and immediate primitive data type tests - -%output basic -%zp clobber - -%import c64lib - -~ main { -start: - [$d020]=0 - return - blockdtypes1.max() - blockdtypes2.max() - blockdtypes3.max() - blockdtypes4.max() - blockdtypes5.max() - blockdtypes6.max() - blockdtypes7.max() - blockdtypes8.max() - blockdtypes9.max() - blockdtypes10.max() - blockdtypes11.max() - blockdtypes12.max() - blockdtypes13.max() - blockdtypes14.max() - blockdtypes15.max() - blockdtypes16.max() - blockdtypes17.max() - blockdtypes18.max() - blockdtypes19.max() - blockdtypes20.max() - blockcalls1.bar() - blockcalls2.bar() - blockcalls3.bar() - blockcalls4.bar() - blockcalls5.bar() - blockcalls6.bar() - blockcalls7.bar() - blockcalls8.bar() - blockcalls9.bar() - blockcalls10.bar() - blockcalls11.bar() - blockcalls12.bar() - blockcalls13.bar() - blockcalls14.bar() - blockcalls15.bar() - blockcalls16.bar() - blockcalls17.bar() - blockcalls18.bar() - blockcalls19.bar() - blockcalls20.bar() - - return -} - -~ blockdtypes1 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls1 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() - %noreturn -} - -~ blockdtypes2 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls2 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes3 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls3 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes4 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls4 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes5 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls5 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes6 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls6 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes7 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls7 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes8 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls8 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes9 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls9 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes10 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls10 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes11 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls11 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes12 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls12 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes13 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls13 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes14 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls14 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes15 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls15 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes16 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls16 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes17 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls17 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes18 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls18 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes19 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls19 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - -~ blockdtypes20 { - ; variables - var uninitbyte1 - var .byte uninitbyte2 - var initbyte1 = $12 - var initbyte1b = true - var .byte initbyte2 = $12 - var .byte initbyte2b = false - var .byte initbyte3 = 99.876 - var initchar1 = '@' - var .byte initchar2 = '@' - var .word uninitword - var .word initword1 = $1234 - var .word initword1b = true - var .word initword2 = false - var .word initword3 = 9876.554321 - var .word initword5 = 20 - var .float uninitfloat - var .float initfloat1 = 0 - var .float initfloat1b = true - var .float initfloat2 = -1.234e-14 - var .float initfloat3 = 9.87e+14 - var .float initfloat4 = 1.70141183e+38 - var .float initfloat5 = -1.70141183e+38 - var .float initfloat6 = 1.234 - var .array( 10) uninit_bytearray - var .array(10 ) init_bytearray =$12 - var .array(10 ) init_bytearrayb =true - var .array(10 ) init_bytearrayc ='@' - var .wordarray( 10 ) uninit_wordarray - var .wordarray(10) init_wordarray = $1234 - var .wordarray(10) init_wordarrayb = true - - var .str text = "hello"+"-null" - var .strp ptext = 'hello-pascal' - var .strs stext = 'screencodes-null' - var .strps pstext = "screencodes-pascal" - - var .matrix( 3, 4 ) uninitmatrix - var .matrix( 3, 4 ) initmatrix1 = $12 - var .matrix( 3, 4 ) initmatrix1b = true - var .matrix( 3, 4 ) initmatrix1c = '@' - var .matrix( 3, 4 ) initmatrix1d = 123.456 - - ; memory-mapped variables - memory membyte1 = $cf01 - memory .byte membyte2 = $c222 - memory .word memword1 = $cf03 - memory .float memfloat = $cf04 - memory .array(10 ) membytes = $cf05 - memory .wordarray( 10) memwords = $cf06 - memory .matrix( 3, 4 ) memmatrix = $cf07 - - ; constants (= names for constant values, can never occur as lvalue) - const cbyte1 = 1 - const cbyte1b = false - const .byte cbyte2 = 1 - const .byte cbyte3 = '@' - const .byte cbyte4 = true - const .word cword1 = false - const .word cword2 = $1234 - const .word cword5 = 9876.5432 - const cfloat1 = 1.2345 - const .float cfloat2 = 2.3456 - const .float cfloat2b = cfloat2*3.44 - const .float cfloat3 = true - const .str ctext3 = "constant-text" - const .strp ctext4 = "constant-ptext" - const .strs ctext5 = "constant-stext" - const .strps ctext6 = "constant-pstext" - - ; taking the address of various things: - var .word vmemaddr1 = &membyte1 - var .word vmemaddr2 = &memword1 - var .word vmemaddr3 = &memfloat - var .word vmemaddr4 = &membytes - var .word vmemaddr5 = &memwords - var .word vmemaddr6 = &memmatrix - var .word vmemaddr8 = 100*sin(cbyte1) - var .word vmemaddr9 = cword2+$5432 - var .word vmemaddr10 = cfloat2b - ; taking the address of things from the ZP will work even when it is a var - ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - - -max: - return - - -; --- immediate primitive value assignments ---- - - A = [$99] - A = [$aabb] - A = $99 - A = [cbyte3] - A = 0 - A = '@' - A = 1.2345 - A = true - A = false - A = 255 - A = X - A = [$99] - A = [$c020.byte] - A = [$c020] - A = cbyte3 - A = membyte2 - A = uninitbyte1 - - - XY = 0 - XY = '@' - XY = 1.2345 - XY = 456.66 - XY = 65535 - XY = true - XY = false - XY = text - XY = cbyte3 - XY = [cbyte3] - XY = [cword2] - XY = uninitbyte1 - XY = "text-immediate" - AY = "text-immediate" - ; AX = &"text-immediate" ; equivalent to simply assigning the string directly - ; AX = & "text-immediate" ; equivalent to simply assigning the string directly - AX = ctext3 - AX = "" - AX = XY - AX = Y - XY = membyte2 - XY = &membyte2 - XY = memword1 - XY = max - XY = &max - - - [$c000] = A - [$c000] = 255 - [$c000] = '@' - [$c000] = true - [$c000] = false - [$c000] = cbyte3 - [$c000] = uninitbyte1 - [$c000] = membyte2 - [$c000] = cbyte2 - [$c000] = [cword2] - - [$c000.word] = A - [$c000.word] = AX - [$c000.word] = cbyte3 - [$c000.word] = cword2 - [$c000.word] = ctext3 - [$c000.word] = 65535 - [$c000.word] = "text" - [$c000.word] = "" - [$c000.word] = uninitbyte1 - [$c000.word] = membyte2 - [$c000.word] = &membyte2 - [$c000.word] = [cword2] - [$c000.word] = memword1 - [$c000.float] = 65535 - [$c000.float] = 456.66 - [$c000.float] = 1.70141183e+38 - [$c000.float] = cbyte3 - [$c000.float] = cword2 - - [$c001] = [$c002] - [$c111.word] = [$c222] - [$c112.word] = [$c223.byte] - [$c222.word] = [$c333.word] - [$c333.word] = max - [$c333.word] = &max - - - SC = 0 - SC = 1 - SC = false - SI = 1 - SI = 0 - SI = false - - uninitbyte1 = 99 - uninitbyte1 = 1.234 - uninitbyte1 = '@' - initbyte1 = 99 - initbyte1 = 1.234 - initbyte1 = '@' - initbyte1 = A - initbyte1 = cbyte3 - uninitword = 99 - uninitword = 5.6778 - uninitword = "test" - uninitword = '@' - uninitword = A - uninitword = XY - uninitword = ctext3 - initword1 = cbyte3 - initword1 = cword2 - initfloat1 = 99 - initfloat1 = 9.8765 - initfloat1 = '@' - initfloat1 = cbyte3 - initfloat1 = cword2 - uninitfloat = 99 - uninitfloat = 9.8765 - uninitfloat = '@' - initword1 = max - initword1 = &max - - membyte1 = A - membyte1 = cbyte3 - memword1 = A - memword1 = AX - memword1 = cbyte3 - memword1 = cword2 - memword1 = ctext3 - - - membyte1 = 22 - memword1 = 2233 - memfloat = 3.4567 - memword1 = max - memword1 = &max - - membyte1 = A - memword1 = A - memword1 = XY - memfloat = cbyte3 - memfloat = cword2 - - ; float assignments that require ROM functions from c64lib: - memfloat = Y - memfloat = XY - uninitfloat = Y - uninitfloat = XY - initfloat2 = Y - initfloat2 = XY - initfloat2 = initbyte2 - initfloat2 = initword2 - initfloat1 = uninitfloat - initfloat1 = initfloat2 -%noreturn -} - - -~ blockcalls20 { - - var .word var1 = 99 - memory .word mem1 = $cff0 - - var .byte varb1 = 99 - memory .byte memb1 = $cff0 - const .word constw = $2355 - const .byte constb = $23 - const .float constf = 3.4556677 - const .str constt = "derp" - - sub sub1 () -> (X?) = $ffdd - sub sub2 (A) -> (Y?) = $eecc - sub sub3 (thing: XY) -> (Y?) = $ddaa - sub sub4 (string: XY, other : A) -> (Y?) = $dd22 - - -bar: - goto sub1 - return sub2 (1 ) - return sub3 (3) - return sub3 (thing="hello") - return sub3 ("hello, there") - return sub4 (string="hello, there", other = 42) - return sub4 ("hello", 42) - return sub4 ("hello", other= 42) - return sub4 (string="hello", other = 42) - return bar () - goto [AX] - return [AX] () - goto [var1] - return [var1] () ; comment - goto [mem1] ; comment - return [mem1] () - goto [$c2.word] - return [$c2.word] () - goto [$c2dd.word] - return [$c2dd.word] ( ) - goto $c000 - return $c000 ( ) - goto $c2 - return $c2() - - %asm { - nop - nop - nop - nop - } - - sub1!() - sub2!(11) - sub3 !(3) - sub3! (thing="hello") - sub3! ("hello, there") - sub4! ("hello", 42) - sub4! ("hello", other=42) - sub4! (string="hello", other = 42) - sub4! (string="hello, there", other = 42) - bar!() - [XY] ! () - [var1] !() - [mem1]!() - [$c2.word]!() - [$c2dd.word]!() - $c000!() - $c2!() - - %asm { - nop - nop - nop - nop - } - - sub1() - sub2(11) - sub3 (3) - sub3 (thing="hello") - sub3 ("hello, there") - sub4 ("hello", 42) - sub4 ("hello", other= 42) - sub4 (string="hello", other = 42) - sub4 (string="hello, there", other = 42) - bar () - [AX]() - [var1] ( ) - [mem1] () - [$c2.word]() - [$c2dd.word]() - $c000() - $c2() - - - %asm { - nop - nop - nop - nop - } - - constw() - sub1() - main.start() -%noreturn -} - diff --git a/python-prototype/testsource/numbergame.ill b/python-prototype/testsource/numbergame.ill deleted file mode 100644 index 514774503..000000000 --- a/python-prototype/testsource/numbergame.ill +++ /dev/null @@ -1,106 +0,0 @@ -%output basic -%import c64lib -%import mathlib - -~ main { - var .str name = '?' * 80 - var .str guess = '?' * 80 - var secretnumber - var attempts_left = 10 - - -start: - c64.init_system() - - A = c64.VMCSB - A |= 2 - c64.VMCSB = A - c64.VMCSB |= 2 ; @todo when this works it replaces the three lines above - - ; greeting - c64scr.print_string("Enter your name: ") - Y = c64scr.input_chars(name) - c64.CHROUT('\n') - c64.CHROUT('\n') - c64scr.print_string("Hello, ") - c64scr.print_string(name) - c64.CHROUT('.') - c64.CHROUT('\n') - - ; create a secret random number from 1-100 - c64.RNDA(0) ; fac = rnd(0) - c64.MUL10() ; fac *= 10 - c64.MUL10() ; .. and now *100 - c64.FADDH() ; add 0.5.. - c64.FADDH() ; and again, so +1 total - AY = c64flt.GETADRAY() - secretnumber=A - ;A=math.randbyte() - ;A+=c64.RASTER - ;A-=c64.TIME_LO - ;X,secretnumber=math.divmod_bytes(A, 99) - - c64scr.print_string("I am thinking of a number from 1 to 100!You'll have to guess it!\n") - -printloop: - c64scr.print_string("\nYou have ") - c64scr.print_byte_decimal(attempts_left) - c64scr.print_string(" guess") - - ; @todo comparison expression so we can do if attempts_left>0 ... - A = attempts_left - A-- - if_zero A goto ask_guess - c64scr.print_string("es") -ask_guess: - c64scr.print_string(" left.\nWhat is your next guess? ") - Y = c64scr.input_chars(guess) - c64.CHROUT('\n') - [$22.word] = guess - c64.FREADSTR(A) - AY = c64flt.GETADRAY() - A -= secretnumber ; @todo condition so we can do if guess > secretnumber.... # @todo "removed statement that has no effect" is WRONG!! - A -= secretnumber ; # @todo "removed statement that has no effect" is WRONG!! - if_zero goto correct_guess - if_gt goto too_high - c64scr.print_string("That is too ") - c64scr.print_string("low!\n") - goto continue - -correct_guess: - c64scr.print_string("\nThat's my number, impressive!\n") - goodbye() - return - -too_high: - c64scr.print_string("That is too ") - c64scr.print_string("high!\n") - -continue: - attempts_left-- - if_zero attempts_left goto game_over - goto printloop - -game_over: - c64scr.print_string("\nToo bad! It was: ") - c64scr.print_byte_decimal(secretnumber) - c64.CHROUT('\n') - return goodbye() - goodbye() ; @todo fix subroutine usage tracking, it doesn't register this one - return - return - return - return - return - return - return - return - return - return - -sub goodbye ()->() { - c64scr.print_string("\nThanks for playing. Bye!\n") - return -} - -} diff --git a/python-prototype/testsource/source1.ill b/python-prototype/testsource/source1.ill deleted file mode 100644 index 3c35297e1..000000000 --- a/python-prototype/testsource/source1.ill +++ /dev/null @@ -1,227 +0,0 @@ -; source IL file -; these are comments -; line 2 comment -; line 3 comment - - -%output basic ; create a c-64 program with basic SYS call to launch it -%zp restore , clobber ; clobber over the zp memory normally used by basic/kernal rom, frees up more zp - -options - - -%blocks - - -~ main -{ - ; this is the main block with the start routine. - - memory screen = $d021 - memory border = $d020 - memory cursor = 646 - const cyan = 3 - const .word redw = $2002 - var uninitbyte1 - var .byte uninitbyte2 - var .word uninitword1 - var .byte initbyte33 = 33 - var .word initword9876 = $9876 - var .array(10) bytes2 = $44 - var .wordarray(10) words2 = $bbcc - ] - - -start: ;foo - X = 55 - Y = $77 - Y = X - Y = X - X = 66 - screen = 0 - screen = 66 - border = 66 - cursor = 66 - X = 66 - Y = 66 - A = 66 - X = 66 - Y = 66 - A = 66 - border = 66 - cursor = 66 - border = 66 - cursor = 66 - border = false - border = true - border = 0 - border = 0 - [$d020] = 0 - screen = X - cursor = Y - [646] = Y - uninitbyte1 = 123 - uninitword1 = 12345 - uninitword1 = A - initbyte33 = 133 - initword9876 = 9999 - initword9876 = 1 - initword9876 = Y - - initbyte33 ++ - initword9876 ++ - - Y = 0 - Y = 1 - Y = 2 - Y = true - Y = false - A = 0 - Y = 0 - X = 0 - - ; [646,Y] = [$d020,X] - - -_loop: block2.zpw1 ++ - A ++ - X ++ - Y ++ - [$d020] ++ - A -- - X -- - Y-- - [$d020]-- - block2.zpw2 = 99 - fidget.subroutine() - goto _loop - return 155,2,%00000101 ; will end up in A, X, Y -} - - - -~ block2 { - - return - -sub memsub () -> () = $fff2 - -sub customsub (Y)->() { - - %asm { - nop - nop - lda #99 - nop - nop - } - - A=2 - X=33 - return fidget.subroutine() - -} - - -somelabel1222: - - customsub(2) - return - - var zp1 - var zp2 - var .word zpw1 - var .word zpw2 - var .array(100) some_array1 = $aa ; initialized with $aa bytes - const white = 1 - const red = 2 - const .word border2 = $d020 - const .word screen2 = $d021 - return -} - -~ block3 { -somelabel1: - %asm { - nop - nop - nop - } - - goto somelabel1 - goto block2.somelabel1222 - A=99 - X=99 - Y=99 - A=99 - X=99 - Y=99 - A=99 - X=99 - Y=99 - [$d020]=55 - [$d021]=55 - [$d020]=55 - [$d021]=55 - - A=1 - X=1 - [$d020]=1 - [$aa]=1 - [$bb]=1 - Y=1 - X=1 - A=1 - [$d021]=1 - A=2 - [$cc]=1 - [$cd]=1 - [$ce]=1 - [$cf]=1 - [$cf00]=1 - [$cf01]=2 - [$cf02]=1 - Y=2 - A=2 - X=1 - - return -} - -; comments before block 4 - -~ block4 { - A=1 - A=2 - A=3 - A=4 - A=5 - X=8 - X=9 - Y=10 - [$cc]=1 - [$cc]=2 - [$cc]=3 - [$cc]=4 - [$dd]=A - [$dd]=X - [$dd]=Y - return -} - - -~ fidget $0c00 -{ - -subroutine: - -; subroutine (A, X, Y) - ;[border2] = red - ;[screen2] = white - return $99 - -} - - -; comment at end -; another one diff --git a/python-prototype/testsource/source2.ill b/python-prototype/testsource/source2.ill deleted file mode 100644 index 267f8ffbc..000000000 --- a/python-prototype/testsource/source2.ill +++ /dev/null @@ -1,27 +0,0 @@ -%output prg -%address $c000 - -~ test { - var .byte localvar = 33 - return -} - -~ main { -start: - ;A=0 - ;[$d020]=A - ;X=false - ;Y=true - ;return - - ;included_assembly - ;%asminclude " included.sourcelynx walla derp ", test_include - ;%asminclude " included.sourcelynx walla derp ", "test_include" - - ;included_binary - ;%asmbinary " included.binary 234 " - ;%asmbinary " included.binary", $40 - %asmbinary "included.binary", $40, $200 - - return -} diff --git a/python-prototype/testsource/source3.ill b/python-prototype/testsource/source3.ill deleted file mode 100644 index c525196fc..000000000 --- a/python-prototype/testsource/source3.ill +++ /dev/null @@ -1,76 +0,0 @@ - -%output basic ; create a c-64 program with basic SYS call to launch it - - -%import c64lib ; searched in several locations and with .ill file extension added - -~ main -{ - memory screen2 = $0401 - memory screen3 = $0402 - memory .word screenw = $0500 - - ; ascii to petscii, 0 terminated - var .str hello = "hello everyone out there." - - ; ascii to petscii, with length as first byte - var .strp hellopascalstr = "Hello!\0x00\x01\x02d ag\0x01." - - ; ascii to screen codes, 0 terminated - var .strs hello_screen = "Hello!\n guys123." - - ; ascii to screen codes, length as first byte - var .strps hellopascal_screen = "Hello! \n." - - var .str hello2 = "@@\f\b\n\r\t@@" - -start: - global2.make_screen_black() - - A='?' - [$d020] = '?' - [$d021] = '?' - [$d022] = '?' - [$d023] = 'q' - c64.BGCOL0 = 'a' - screen2 = 'a' - screen3 = 'a' - screenw = '2' - A='?' - X='?' - Y='?' - A='\002' - X=A - A='\xf2' - X=A - c64.CHROUT('A') - A='\f' - X=A - A='\b' - X=A - A='\n' - X=A - A='\r' - X=A - A='\t' - A='0' - c64.CHROUT('0') - c64.CHROUT('1') - c64.CHROUT('2') - c64scr.print_string(hello) - return c64.CHROUT('!') - - - return -} - -~ global2 { - -make_screen_black: - c64.EXTCOL = 0 - c64.BGCOL0 = 0 - c64.COLOR = 3 - Y = true - return - -} diff --git a/python-prototype/testsource/source4.ill b/python-prototype/testsource/source4.ill deleted file mode 100644 index 320b95c1f..000000000 --- a/python-prototype/testsource/source4.ill +++ /dev/null @@ -1,61 +0,0 @@ -%output basic ; create a c-64 program with basic SYS to() launch it - - %import "c64lib.ill" - - ~ main -{ - var .str greeting = "hello world!\r12345678 is a big number.\r" - var .strp p_greeting = "hello world!\r12345678 is a big number.\r" - const .word BORDER = $d020 - -start: - c64scr.print_pimmediate() ; this prints the pstring immediately following it - %asm { - .strp "hello-pimmediate!{cr}" - } - - c64scr.print_byte_decimal0 (19) - c64.CHROUT (13) - c64scr.print_byte_decimal (19) - c64.CHROUT (13) - - - c64scr.print_word_decimal0 ($0102) - c64.CHROUT (13) - c64scr.print_word_decimal ($0102) - c64.CHROUT (13) - return - -start2: - global2.make_screen_black() - c64.CLEARSCR() - c64scr.print_string(greeting) - c64scr.print_pstring(p_greeting) - c64scr.print_byte_decimal(0) - c64scr.print_byte_hex(0, 0) - c64.CHROUT(13) - c64scr.print_byte_decimal(13) - c64scr.print_byte_hex(0, 13) - c64.CHROUT(13) - c64scr.print_byte_decimal(255) - c64scr.print_byte_hex(0, 254) - c64scr.print_byte_hex(0, 129) - c64.CHROUT(13) - - c64.CHROUT(13) - c64scr.print_word_decimal($0100) - c64.CHROUT(13) - return - - -} - -~ global2 { - -make_screen_black: - c64.EXTCOL = 0 - c64.BGCOL0 = 0 - c64.COLOR = 3 - return - -} diff --git a/python-prototype/testsource/todo.ill b/python-prototype/testsource/todo.ill deleted file mode 100644 index 6445a8d00..000000000 --- a/python-prototype/testsource/todo.ill +++ /dev/null @@ -1,59 +0,0 @@ -~ main { - var .float flt1 = 9.87e-21 - var .float flt = -9.87e-21 - const .word border = $0099 - var counter = 1 - -start: - counter ++ - main.counter ++ - ; @todo float augassign - flt += 1000.1 - flt *= 2.34 - flt *= flt - - ;[border] &= 2 ; @todo augassign on dereference - - flt = flt+ 1 ; @todo optimize into augassign - XY*=3 ; @todo operator - XY=XY/0 ; @todo zerodiv (during expression to code generation) @todo operator - XY=XY//0 ; @todo zerodiv (during expression to code generation) @todo operator - - ; @todo incr by more than 1 - [AX]++ - [AX]++ - A=0 - ; @todo decr by more than 1 - [AX]-- - [AX]-- - - - block2.b2var++ - block2.start() - return 44.123 - -sub goodbye ()->() { - var xxxxxx ; @todo vars in sub? - memory y = $c000 ; @todo memvars in sub? - const q = 22 ; @todo const in sub? - - y++ - return -} - - -sub derp ()->() { - const q = 22 - A = q *4 ; @todo fix scope not found error - return -} -} - - - -~ block2 { - var b2var = 0 - -start: - return -} diff --git a/python-prototype/testsource/todo2.ill b/python-prototype/testsource/todo2.ill deleted file mode 100644 index cba3d60fc..000000000 --- a/python-prototype/testsource/todo2.ill +++ /dev/null @@ -1,87 +0,0 @@ -~ main { - - var b1 - var .word w1 - var .float f1 - memory .byte border = $d020 - -start: - X++ - X++ - return ; dummy - X+=2 - return ; dummy - X+=255 - return ; dummy - X-- - X-- - return ; dummy - X-=2 - return ; dummy - X-=255 - return ; dummy - XY++ - XY++ - return ; dummy - XY+=2 - return ; dummy - XY+=255 - return ; dummy - XY-- - XY-- - return ; dummy - XY-=2 - return ; dummy - XY-=255 - return ; dummy - b1++ - b1++ - w1++ - w1++ - f1++ - f1++ - b1-- - b1-- - w1-- - w1-- - f1-- - f1-- - b1+=255 - w1+=255 - f1+=255 - b1-=255 - w1-=255 - f1-=255 - - [$c000]++ - [$c000]++ - [$c000]-- - [$c000]-- - [border]++ - [border]++ - [border]-- - [border]-- - - X ++ - X ++ - X += 255 - Y-- - Y-- - Y-=255 - XY ++ - XY ++ - XY += 255 - - ;[$c000]+=2 - ;[$c000]+=255 - ;[$c000]+=255 - ;[$c000 .word]+=255 - ;[$c000]-=2 - ;[$c000]-=255 - ;[$c000 .word]-=255 - ;[border]+=2 - ;[border]+=255 - ;[border]-=2 - ;[border]-=255 - %noreturn -} diff --git a/python-prototype/tinyvm/__init__.py b/python-prototype/tinyvm/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/python-prototype/tinyvm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/python-prototype/tinyvm/core.py b/python-prototype/tinyvm/core.py deleted file mode 100644 index 4d7ec040f..000000000 --- a/python-prototype/tinyvm/core.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Simplistic 8/16 bit Virtual Machine to execute a stack based instruction language. -Core data structures and definitions. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import enum -import struct -import array -from typing import Callable, Union -from il65.codegen.shared import mflpt5_to_float, to_mflpt5 - - -class DataType(enum.IntEnum): - BOOL = 1 - BYTE = 2 - SBYTE = 3 - WORD = 4 - SWORD = 5 - FLOAT = 6 - ARRAY_BYTE = 7 - ARRAY_SBYTE = 8 - ARRAY_WORD = 9 - ARRAY_SWORD = 10 - MATRIX_BYTE = 11 - MATRIX_SBYTE = 12 - - @staticmethod - def guess_datatype_for(value: Union[bool, int, float, bytearray, array.array]) -> 'DataType': - if isinstance(value, int): - if 0 <= value <= 255: - return DataType.BYTE - if -128 <= value <= 127: - return DataType.SBYTE - if 0 <= value <= 65535: - return DataType.WORD - if -32768 <= value <= 32767: - return DataType.SWORD - raise OverflowError("integer value too large for byte or word", value) - if isinstance(value, bool): - return DataType.BOOL - if isinstance(value, float): - return DataType.FLOAT - if isinstance(value, bytearray): - return DataType.ARRAY_BYTE - if isinstance(value, array.array): - if value.typecode == "B": - return DataType.ARRAY_BYTE - if value.typecode == "H": - return DataType.ARRAY_WORD - if value.typecode == "b": - return DataType.ARRAY_SBYTE - if value.typecode == "h": - return DataType.ARRAY_SWORD - raise ValueError("invalid array typecode", value.typecode) - raise TypeError("invalid value type", value) - - -class ExecutionError(Exception): - pass - - -class TerminateExecution(SystemExit): - pass - - -class MemoryAccessError(Exception): - pass - - -class Memory: - def __init__(self): - self.mem = bytearray(65536) - self.readonly = bytearray(65536) - self.mmap_io_charout_addr = -1 - self.mmap_io_charout_callback = None - self.mmap_io_charin_addr = -1 - self.mmap_io_charin_callback = None - - def mark_readonly(self, start: int, end: int) -> None: - self.readonly[start:end+1] = [1] * (end-start+1) - - def memmapped_io_charout(self, address: int, callback: Callable) -> None: - self.mmap_io_charout_addr = address - self.mmap_io_charout_callback = callback - - def memmapped_io_charin(self, address: int, callback: Callable) -> None: - self.mmap_io_charin_addr = address - self.mmap_io_charin_callback = callback - - def get_byte(self, index: int) -> int: - if self.mmap_io_charin_addr == index: - self.mem[index] = self.mmap_io_charin_callback() - return self.mem[index] - - def get_bytes(self, startindex: int, amount: int) -> bytearray: - return self.mem[startindex: startindex+amount] - - def get_sbyte(self, index: int) -> int: - if self.mmap_io_charin_addr == index: - self.mem[index] = self.mmap_io_charin_callback() - return struct.unpack("b", self.mem[index:index+1])[0] - - def get_word(self, index: int) -> int: - return self.mem[index] + 256 * self.mem[index+1] - - def get_sword(self, index: int) -> int: - return struct.unpack(" float: - return mflpt5_to_float(self.mem[index: index+5]) - - def set_byte(self, index: int, value: int) -> None: - if self.readonly[index]: - raise MemoryAccessError("read-only", index) - self.mem[index] = value - if self.mmap_io_charout_addr == index: - self.mmap_io_charout_callback(value) - - def set_sbyte(self, index: int, value: int) -> None: - if self.readonly[index]: - raise MemoryAccessError("read-only", index) - self.mem[index] = struct.pack("b", bytes([value]))[0] - if self.mmap_io_charout_addr == index: - self.mmap_io_charout_callback(self.mem[index]) - - def set_word(self, index: int, value: int) -> None: - if self.readonly[index] or self.readonly[index+1]: - raise MemoryAccessError("read-only", index) - self.mem[index], self.mem[index + 1] = struct.pack(" None: - if self.readonly[index] or self.readonly[index+1]: - raise MemoryAccessError("read-only", index) - self.mem[index], self.mem[index + 1] = struct.pack(" None: - if any(self.readonly[index:index+5]): - raise MemoryAccessError("read-only", index) - self.mem[index: index+5] = to_mflpt5(value) diff --git a/python-prototype/tinyvm/examples/printiovm.txt b/python-prototype/tinyvm/examples/printiovm.txt deleted file mode 100644 index 86b589460..000000000 --- a/python-prototype/tinyvm/examples/printiovm.txt +++ /dev/null @@ -1,26 +0,0 @@ -; source code for a tinyvm program; memory mapped I/O -%block b1 -%vardefs -const byte chr_i 105 -const byte chr_r 114 -const byte chr_m 109 -const byte chr_e 101 -const byte chr_n 110 -const byte chr_EOL 10 -const word chrout 53248 -const word chrin 53249 -%end_vardefs -%instructions -loop: - push chrin - syscall memread_byte - push chrout - swap - syscall memwrite_byte - push chrout - push chr_EOL - syscall memwrite_byte - syscall delay - jump loop -%end_instructions -%end_block ;b1 diff --git a/python-prototype/tinyvm/examples/testvm-timer.txt b/python-prototype/tinyvm/examples/testvm-timer.txt deleted file mode 100644 index bb51b9824..000000000 --- a/python-prototype/tinyvm/examples/testvm-timer.txt +++ /dev/null @@ -1,22 +0,0 @@ -; source code for a tinyvm program for the timer -%block b1_timer -%vardefs -var byte teller_timer 32 -const word screenloc_timer 1028 -const byte one_timer 1 -%end_vardefs - -%instructions - push teller_timer - push one_timer - add - dup - pop teller_timer - push screenloc_timer - swap - syscall memwrite_word - return 0 -%end_instructions -%subblocks -%end_subblocks -%end_block ;b1_timer diff --git a/python-prototype/tinyvm/examples/testvm.txt b/python-prototype/tinyvm/examples/testvm.txt deleted file mode 100644 index 7582f89ef..000000000 --- a/python-prototype/tinyvm/examples/testvm.txt +++ /dev/null @@ -1,39 +0,0 @@ -; source code for a tinyvm program -%block b1 -%vardefs -var word teller 0 -var word numbertoprint 0 -const byte one 1 -const word thousand 1000 -const byte space_chr 32 -const word screenstart 1024 -%end_vardefs - -%instructions -back: - push teller - push one - add - dup - dup - dup - push screenstart - swap - syscall memwrite_word - pop teller - call 1 printnumber - push thousand - cmp_lt - jump_if_true back - return 0 -printnumber: - syscall decimalstr_unsigned - syscall printstr - push space_chr - syscall printchr - return 0 -%end_instructions - -%subblocks -%end_subblocks -%end_block ;b1 diff --git a/python-prototype/tinyvm/main.py b/python-prototype/tinyvm/main.py deleted file mode 100644 index a4910bc97..000000000 --- a/python-prototype/tinyvm/main.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Simplistic 8/16 bit Virtual Machine to execute a stack based instruction language. -Main entry point to launch a VM to execute the given programs. - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import sys -from .parse import Parser -from .vm import VM - -mainprogram = None -timerprogram = None - -if len(sys.argv) >= 2: - source = open(sys.argv[1]).read() - parser = Parser(source) - mainprogram = parser.parse() - -if len(sys.argv) == 3: - source = open(sys.argv[2]).read() - parser = Parser(source) - timerprogram = parser.parse() - -if len(sys.argv) not in (2, 3): - raise SystemExit("provide 1 or 2 program file names as arguments") - -# ZeroPage and hardware stack of a 6502 cpu are off limits for now -VM.readonly_mem_ranges = [(0x00, 0xff), (0x100, 0x1ff)] -vm = VM(mainprogram, timerprogram) -vm.enable_charscreen(0x0400, 40, 25) -vm.run() diff --git a/python-prototype/tinyvm/parse.py b/python-prototype/tinyvm/parse.py deleted file mode 100644 index 31bf5f94c..000000000 --- a/python-prototype/tinyvm/parse.py +++ /dev/null @@ -1,218 +0,0 @@ -""" -Simplistic 8/16 bit Virtual Machine to execute a stack based instruction language. -Parser for the simplistic text based program representation - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import array -from typing import Optional, List, Tuple, Dict, Any -from .program import Opcode, Program, Block, Variable, Instruction, Value -from .vm import StackValueType -from .core import DataType - - -class ParseError(Exception): - pass - - -class Parser: - def __init__(self, source: str) -> None: - self.source = source.splitlines() - self.lineno = 0 - - def parse(self) -> Program: - blocklist = [] - while self.lineno < len(self.source): - self.skip_empty() - blocklist.append(self.parse_block(None)) - return Program(blocklist) - - def skip_empty(self) -> None: - while self.lineno < len(self.source): - line = self.source[self.lineno].strip() - if not line or line.startswith(";"): - self.lineno += 1 - else: - break - - def parse_block(self, parent: Optional[Block]) -> Block: - assert self.source[self.lineno].startswith("%block") - blockname = self.source[self.lineno].split()[1] - variables = [] # type: List[Variable] - instructions = [] # type: List[Instruction] - labels = {} # type: Dict[str, Instruction] - self.lineno += 1 - self.skip_empty() - if self.source[self.lineno].startswith("%vardefs"): - variables = self.parse_vardefs() - self.skip_empty() - if self.source[self.lineno].startswith("%instructions"): - instructions, labels = self.parse_instructions() - block = Block(blockname, parent, variables, instructions, labels, []) - self.skip_empty() - if self.source[self.lineno].startswith("%subblocks"): - block.blocks = self.parse_subblocks(block) - self.skip_empty() - assert self.source[self.lineno].startswith("%end_block") - self.lineno += 1 - return block - - def get_array_type(self, dtype: DataType) -> str: - return { - DataType.ARRAY_BYTE: 'B', - DataType.ARRAY_SBYTE: 'b', - DataType.ARRAY_WORD: 'H', - DataType.ARRAY_SWORD: 'h', - DataType.MATRIX_BYTE: 'B', - DataType.MATRIX_SBYTE: 'b' - }[dtype] - - def parse_vardefs(self) -> List[Variable]: - assert self.source[self.lineno].startswith("%vardefs") - self.lineno += 1 - variables = [] - while not self.source[self.lineno].startswith("%"): - vartype, datatype, name, argstr = self.source[self.lineno].split(maxsplit=3) - dtype = DataType[datatype.upper()] - length = height = 0 - value = None # type: StackValueType - if dtype in (DataType.BYTE, DataType.WORD, DataType.SBYTE, DataType.SWORD): - value = Value(dtype, int(argstr)) - elif dtype == DataType.FLOAT: - value = Value(dtype, float(argstr)) - elif dtype == DataType.BOOL: - value = Value(dtype, argstr.lower() not in ("0", "false")) - elif dtype in (DataType.ARRAY_BYTE, DataType.ARRAY_SBYTE, DataType.ARRAY_WORD, DataType.ARRAY_SWORD): - args = argstr.split(maxsplit=1) - length = int(args[0]) - valuestr = args[1] - typecode = self.get_array_type(dtype) - if valuestr[0] == '[' and valuestr[-1] == ']': - value = Value(dtype, array.array(typecode, [int(v) for v in valuestr[1:-1].split()])) - else: - value = Value(dtype, array.array(typecode, [int(valuestr)]) * length) - elif dtype in (DataType.MATRIX_BYTE, DataType.MATRIX_SBYTE): - args = argstr.split(maxsplit=2) - length = int(args[0]) - height = int(args[1]) - valuestr = args[2] - typecode = self.get_array_type(dtype) - if valuestr[0] == '[' and valuestr[-1] == ']': - value = Value(dtype, array.array(typecode, [int(v) for v in valuestr[1:-1].split()])) - else: - value = Value(dtype, array.array(typecode, [int(valuestr)] * length * height)) - else: - raise TypeError("weird dtype", dtype) - variables.append(Variable(name, dtype, value, vartype == "const")) - self.lineno += 1 - self.skip_empty() - assert self.source[self.lineno].startswith("%end_vardefs") - self.lineno += 1 - return variables - - def parse_instructions(self) -> Tuple[List[Instruction], Dict[str, Instruction]]: - assert self.source[self.lineno].startswith("%instructions") - self.lineno += 1 - instructions = [] - labels = {} # type: Dict[str, Instruction] - - def parse_instruction(ln: str) -> Instruction: - parts = ln.split(maxsplit=1) - opcode = Opcode[parts[0].upper()] - args = [] # type: List[Any] - if len(parts) == 2: - args = parts[1].split() - else: - args = [] - if opcode in (Opcode.CALL, Opcode.RETURN): - args[0] = int(args[0]) # the number of arguments/parameters - return Instruction(opcode, args, None, None) - - while not self.source[self.lineno].startswith("%"): - line = self.source[self.lineno].strip() - if line.endswith(":"): - # a label that points to an instruction - label = line[:-1].rstrip() - self.lineno += 1 - line = self.source[self.lineno] - next_instruction = parse_instruction(line) - labels[label] = next_instruction - instructions.append(next_instruction) - self.lineno += 1 - else: - instructions.append(parse_instruction(line)) - self.lineno += 1 - self.skip_empty() - assert self.source[self.lineno].startswith("%end_instructions") - self.lineno += 1 - return instructions, labels - - def parse_subblocks(self, parent: Block) -> List[Block]: - assert self.source[self.lineno].startswith("%subblocks") - self.lineno += 1 - blocks = [] - while not self.source[self.lineno].startswith("%end_subblocks"): - self.lineno += 1 - while True: - if self.source[self.lineno].startswith("%block"): - blocks.append(self.parse_block(parent)) - else: - break - self.skip_empty() - self.lineno += 1 - return blocks - - -if __name__ == "__main__": - src = """ -%block b1 -%vardefs -var byte v1 0 -var word w1 2222 -var sword ws -3333 -const byte c1 99 -const sword cws -5444 -var array_byte ba 10 33 -var array_byte ba2 10 [1 2 3 4 5 6 7 8 9 10] -var matrix_byte mxb 4 5 33 -var matrix_byte mxb2 3 2 [1 2 3 4 5 6] -%end_vardefs - -%instructions - nop - nop -l1: - nop - push c1 - push2 c1 cws - call 3 l1 - return 2 -%end_instructions - -%subblocks - -%block b2 -%vardefs -%end_vardefs -%end_block ; b2 - -%end_subblocks -%end_block ;b1 - - - -%block b3 -%vardefs -%end_vardefs -%instructions - nop - nop -l1: - nop - return 99 -%end_instructions -%end_block ; b3 -""" - parser = Parser(src) - program = parser.parse() diff --git a/python-prototype/tinyvm/program.py b/python-prototype/tinyvm/program.py deleted file mode 100644 index 0d32b31a6..000000000 --- a/python-prototype/tinyvm/program.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -Simplistic 8/16 bit Virtual Machine to execute a stack based instruction language. -These are the program/instruction definitions that make up a program for the vm - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -import enum -import array -import operator -from typing import List, Dict, Optional, Union, Callable, Any -from .core import DataType - - -class Opcode(enum.IntEnum): - TERMINATE = 0 - NOP = 1 - PUSH = 10 - PUSH2 = 11 - PUSH3 = 12 - POP = 13 - POP2 = 14 - POP3 = 15 - DUP = 16 - DUP2 = 17 - SWAP = 18 - ADD = 50 - SUB = 51 - MUL = 52 - DIV = 53 - AND = 70 - OR = 71 - XOR = 72 - NOT = 73 - TEST = 100 - CMP_EQ = 101 - CMP_LT = 102 - CMP_GT = 103 - CMP_LTE = 104 - CMP_GTE = 105 - CALL = 200 - RETURN = 201 - SYSCALL = 202 - JUMP = 203 - JUMP_IF_TRUE = 204 - JUMP_IF_FALSE = 205 - JUMP_IF_STATUS_ZERO = 206 - JUMP_IF_STATUS_NE = 207 - JUMP_IF_STATUS_EQ = 208 - JUMP_IF_STATUS_CC = 209 - JUMP_IF_STATUS_CS = 210 - JUMP_IF_STATUS_VC = 211 - JUMP_IF_STATUS_VS = 212 - JUMP_IF_STATUS_GE = 213 - JUMP_IF_STATUS_LE = 214 - JUMP_IF_STATUS_GT = 215 - JUMP_IF_STATUS_LT = 216 - JUMP_IF_STATUS_POS = 217 - JUMP_IF_STATUS_NEG = 218 - - -class Value: - __slots__ = ["dtype", "value", "length", "height"] - - def __init__(self, dtype: DataType, value: Union[int, float, bytearray, array.array], length: int=0, height: int=0) -> None: - self.dtype = dtype - self.value = value - self.length = length - self.height = height - - def __str__(self): - return repr(self) - - def __repr__(self): - return "".format(self.dtype.name, self.value) - - def number_arithmetic(self, v1: 'Value', oper: Callable, v2: 'Value') -> 'Value': - if v1.dtype != DataType.FLOAT and v2.dtype == DataType.FLOAT: - raise TypeError("cannot use a float in arithmetic operation on an integer", v1, oper.__name__, v2) - if v1.dtype == DataType.BYTE: - return Value(DataType.BYTE, oper(v1.value, v2.value) & 255) - if v1.dtype == DataType.SBYTE: - result = oper(v1.value, v2.value) - if result < -128 or result > 127: - raise OverflowError("sbyte", result) - return Value(DataType.SBYTE, result) - if v1.dtype == DataType.WORD: - return Value(DataType.WORD, oper(v1.value, v2.value) & 65535) - if v1.dtype == DataType.SWORD: - result = oper(v1.value, v2.value) - if result < -32768 or result > 32767: - raise OverflowError("sword", result) - return Value(DataType.SWORD, result) - if v1.dtype == DataType.FLOAT: - return Value(DataType.FLOAT, oper(v1.value, v2.value)) - raise TypeError("cannot {} {}, {}".format(oper.__name__, v1, v2)) - - def number_comparison(self, v1: 'Value', oper: Callable, v2: 'Value') -> bool: - if v1.dtype != DataType.FLOAT and v2.dtype == DataType.FLOAT: - raise TypeError("cannot use a float in logical operation on an integer", v1, oper.__name__, v2) - return oper(v1.value, v2.value) - - def __add__(self, other: 'Value') -> 'Value': - return self.number_arithmetic(self, operator.add, other) - - def __sub__(self, other: 'Value') -> 'Value': - return self.number_arithmetic(self, operator.sub, other) - - def __mul__(self, other: 'Value') -> 'Value': - return self.number_arithmetic(self, operator.sub, other) - - def __truediv__(self, other: 'Value') -> 'Value': - return self.number_arithmetic(self, operator.truediv, other) - - def __floordiv__(self, other: 'Value') -> 'Value': - return self.number_arithmetic(self, operator.floordiv, other) - - def __eq__(self, other: Any) -> bool: - if not isinstance(other, Value): - return False - return self.number_comparison(self, operator.eq, other) - - def __lt__(self, other: 'Value') -> bool: - return self.number_comparison(self, operator.lt, other) - - def __le__(self, other: 'Value') -> bool: - return self.number_comparison(self, operator.le, other) - - def __gt__(self, other: 'Value') -> bool: - return self.number_comparison(self, operator.gt, other) - - def __ge__(self, other: 'Value') -> bool: - return self.number_comparison(self, operator.ge, other) - - -class Variable: - __slots__ = ["name", "value", "dtype", "length", "height", "const"] - - def __init__(self, name: str, dtype: DataType, value: Value, const: bool=False) -> None: - self.name = name - self.value = value - self.dtype = dtype - self.const = const - - def __str__(self): - return repr(self) - - def __repr__(self): - return "".format(self.name, self.value, self.const) - - -class Instruction: - __slots__ = ["opcode", "args", "next", "alt_next"] - - def __init__(self, opcode: Opcode, args: List[Union[Value, int, str]], - nxt: Optional['Instruction']=None, alt_next: Optional['Instruction']=None) -> None: - self.opcode = opcode - self.args = args - self.next = nxt # regular next statement, None=end - self.alt_next = alt_next # alternate next statement (for condition nodes, and return instruction for call nodes) - - def __str__(self): - return repr(self) - - def __repr__(self): - return "".format(self.opcode.name, self.args) - - -class Block: - def __init__(self, name: str, parent: Optional['Block'], - variables: List[Variable] = None, - instructions: List[Instruction] = None, - labels: Dict[str, Instruction] = None, # named entry points - subblocks: List['Block'] = None) -> None: - self.name = name - self.parent = parent - self.variables = variables or [] - self.blocks = subblocks or [] - self.instructions = instructions or [] - self.labels = labels or {} - - def __str__(self): - return repr(self) - - def __repr__(self): - if self.parent: - return "".format(self.name, self.parent.name) - return "".format(self.name) - - -class Program: - def __init__(self, blocks: List[Block]) -> None: - self.blocks = blocks diff --git a/python-prototype/tinyvm/vm.py b/python-prototype/tinyvm/vm.py deleted file mode 100644 index 8a718604d..000000000 --- a/python-prototype/tinyvm/vm.py +++ /dev/null @@ -1,761 +0,0 @@ -""" -Simplistic 8/16 bit Virtual Machine to execute a stack based instruction language. -This is the VM itself (execution engine) - -Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 -""" - -# 8/16 bit virtual machine - -# machine specs: - -# MEMORY: 64K bytes, treated as one single array, indexed per byte, ONLY DATA - NO CODE -# elements addressable as one of three elementary data types: -# 8-bit byte (singed and unsigned), -# 16-bit words (two 8-bit bytes, signed and unsigned) (stored in LSB order), -# 5-byte MFLPT floating point -# addressing is possible via byte index (for the $0000-$00ff range) or via an unsigned word. -# there is NO memory management at all; all of the mem is globally shared and always available in full. -# certain blocks of memory can be marked as read-only (write attempts will then crash the vm) -# -# MEMORY ACCESS: via explicit load and store instructions, -# to put a value onto the stack or store the value on the top of the stack, -# or in one of the dynamic variables. -# -# I/O: either via programmed I/O routines: -# write [byte/bytearray to text output/screen] : syscall_printstr / syscall_printchr, -# read [byte/bytearray from keyboard] : syscall_input / syscall_getchr (both blocking) -# or via memory-mapped I/O (text screen matrix, keyboard scan register) -# -# CPU: single threaded, stack based execution, -# no registers, but unlimited dynamic variables (v0, v1, ...) that have a value and a type. -# types: -# 1-bit boolean, -# 8-bit byte (singed and unsigned), -# 16-bit words (two 8-bit bytes, signed and unsigned), -# floating point, -# array of bytes (signed and unsigned), -# array of words (signed and unsigned), -# matrix (2-dimensional array) of bytes (signed and unsigned). -# all of these can have the flag CONST as well which means they cannot be modified. -# -# CPU INSTRUCTIONS: -# stack manipulation mainly: -# nop -# push var / push2 var1, var2 -# pop var / pop2 var1, var2 -# various arithmetic operations, logical operations, boolean test and comparison operations -# jump label -# jump_if_true label, jump_if_false label -# jump_if_status_XX label special system dependent status register conditional check such as carry bit or overflow bit) -# call function (arguments are on stack) -# return (return values on stack) -# syscall function (special system dependent implementation) -# -# TIMER 'INTERRUPT': triggered around each 1/60th of a second. -# executes on a DIFFERENT stack and with a different PROGRAM LIST, -# but with access to ALL THE SAME DYNAMIC VARIABLES. -# This suspends the main program until the timer program RETURNs! -# - -import time -import itertools -import collections -import array -import threading -import pprint -import tkinter -import tkinter.font -from typing import Dict, List, Tuple, Union, no_type_check -from .program import Instruction, Variable, Block, Program, Opcode, Value -from .core import Memory, DataType, TerminateExecution, ExecutionError - - -class CallFrameMarker: - __slots__ = ["returninstruction"] - - def __init__(self, instruction: Instruction) -> None: - self.returninstruction = instruction - - def __str__(self) -> str: - return repr(self) - - def __repr__(self) -> str: - return "".format(str(self.returninstruction)) - - -StackValueType = Union[Value, CallFrameMarker] - - -class Stack: - def __init__(self): - self.stack = [] - self.pop_history = collections.deque(maxlen=10) - - def debug_peek(self, size: int) -> List[StackValueType]: - return self.stack[-size:] - - def size(self) -> int: - return len(self.stack) - - def pop(self) -> StackValueType: - x = self.stack.pop() - self.pop_history.append(x) - return x - - def pop2(self) -> Tuple[StackValueType, StackValueType]: - x, y = self.stack.pop(), self.stack.pop() - self.pop_history.append(x) - self.pop_history.append(y) - return x, y - - def pop3(self) -> Tuple[StackValueType, StackValueType, StackValueType]: - x, y, z = self.stack.pop(), self.stack.pop(), self.stack.pop() - self.pop_history.append(x) - self.pop_history.append(y) - self.pop_history.append(z) - return x, y, z - - def pop_under(self, number: int) -> StackValueType: - return self.stack.pop(-1-number) - - def push(self, item: StackValueType) -> None: - self._typecheck(item) - self.stack.append(item) - - def push2(self, first: StackValueType, second: StackValueType) -> None: - self._typecheck(first) - self._typecheck(second) - self.stack.append(first) - self.stack.append(second) - - def push3(self, first: StackValueType, second: StackValueType, third: StackValueType) -> None: - self._typecheck(first) - self._typecheck(second) - self._typecheck(third) - self.stack.extend([first, second, third]) - - def push_under(self, number: int, value: StackValueType) -> None: - self.stack.insert(-number, value) - - def peek(self) -> StackValueType: - return self.stack[-1] if self.stack else None - - def swap(self) -> None: - x = self.stack[-1] - self.stack[-1] = self.stack[-2] - self.stack[-2] = x - - def _typecheck(self, value: StackValueType): - if not isinstance(value, (Value, CallFrameMarker)): - raise TypeError("invalid item type pushed", value) - - -# noinspection PyPep8Naming,PyUnusedLocal,PyMethodMayBeStatic -class VM: - str_encoding = "iso-8859-15" - str_alt_encoding = "iso-8859-15" - readonly_mem_ranges = [] # type: List[Tuple[int, int]] - timer_irq_resolution = 1/30 - charout_address = 0xd000 - charin_address = 0xd001 - - def __init__(self, program: Program, timerprogram: Program=None) -> None: - for opcode in Opcode: - if opcode not in self.dispatch_table: - raise NotImplementedError("missing opcode dispatch for " + opcode.name) - for oc in Opcode: - if oc not in self.dispatch_table: - raise NotImplementedError("no dispatch entry in table for " + oc.name) - self.memory = Memory() - self.memory.memmapped_io_charout(self.charout_address, self.memmapped_charout) - self.memory.memmapped_io_charin(self.charin_address, self.memmapped_charin) - for start, end in self.readonly_mem_ranges: - self.memory.mark_readonly(start, end) - self.main_stack = Stack() - self.timer_stack = Stack() - self.main_program, self.timer_program, self.variables, self.labels = self.flatten_programs(program, timerprogram or Program([])) - self.connect_instruction_pointers(self.main_program) - self.connect_instruction_pointers(self.timer_program) - self.program = self.main_program - self.stack = self.main_stack - self.pc = None # type: Instruction - self.charscreen_address = 0 - self.charscreen_width = 0 - self.charscreen_height = 0 - self.keyboard_scancode = 0 - self.system = System(self) - assert all(i.next for i in self.main_program - if i.opcode != Opcode.TERMINATE), "main: all instrs next must be set" - assert all(i.next for i in self.timer_program - if i.opcode not in (Opcode.TERMINATE, Opcode.RETURN)), "timer: all instrs next must be set" - assert all(i.alt_next for i in self.main_program - if i.opcode in (Opcode.CALL, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE)), "main: alt_nexts must be set" - assert all(i.alt_next for i in self.timer_program - if i.opcode in (Opcode.CALL, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE)), "timer: alt_nexts must be set" - print("[TinyVM starting up.]") - - def enable_charscreen(self, screen_address: int, width: int, height: int) -> None: - self.charscreen_address = screen_address - self.charscreen_width, self.charscreen_height = width, height - - def flatten_programs(self, main: Program, timer: Program) \ - -> Tuple[List[Instruction], List[Instruction], Dict[str, Variable], Dict[str, Instruction]]: - variables = {} # type: Dict[str, Variable] - labels = {} # type: Dict[str, Instruction] - instructions_main = [] # type: List[Instruction] - instructions_timer = [] # type: List[Instruction] - for block in main.blocks: - flat = self.flatten(block, variables, labels) - instructions_main.extend(flat) - instructions_main.append(Instruction(Opcode.TERMINATE, [], None, None)) - for block in timer.blocks: - flat = self.flatten(block, variables, labels) - instructions_timer.extend(flat) - return instructions_main, instructions_timer, variables, labels - - def flatten(self, block: Block, variables: Dict[str, Variable], labels: Dict[str, Instruction]) -> List[Instruction]: - def block_prefix(b: Block) -> str: - if b.parent: - return block_prefix(b.parent) + "." + b.name - else: - return b.name - prefix = block_prefix(block) - instructions = block.instructions - for ins in instructions: - if ins.opcode == Opcode.SYSCALL: - continue - if ins.args: - newargs = [] # type: List[Union[str, int, Value]] - for a in ins.args: - if isinstance(a, str): - newargs.append(prefix + "." + a) - else: - newargs.append(a) # type: ignore - ins.args = newargs - for vardef in block.variables: - vname = prefix + "." + vardef.name - assert vname not in variables - variables[vname] = vardef - for name, instr in block.labels.items(): - name = prefix + "." + name - assert name not in labels - labels[name] = instr - for subblock in block.blocks: - instructions.extend(self.flatten(subblock, variables, labels)) - del block.instructions - del block.variables - del block.labels - return instructions - - def connect_instruction_pointers(self, instructions: List[Instruction]) -> None: - i1, i2 = itertools.tee(instructions) - next(i2, None) - for i, nexti in itertools.zip_longest(i1, i2): - if i.opcode in (Opcode.JUMP_IF_TRUE, Opcode.JUMP_IF_FALSE): - i.next = nexti # normal flow target - i.alt_next = self.labels[i.args[0]] # conditional jump target - elif i.opcode == Opcode.JUMP: - i.next = self.labels[i.args[0]] # jump target - elif i.opcode == Opcode.CALL: - i.next = self.labels[i.args[1]] # call target - i.alt_next = nexti # return instruction - else: - i.next = nexti - - def run(self) -> None: - if self.charscreen_address: - threading.Thread(target=ScreenViewer.create, - args=(self, self.charscreen_address, self.charscreen_width, self.charscreen_height), - name="screenviewer", daemon=True).start() - time.sleep(0.05) - - self.pc = self.program[0] # first instruction of the main program - self.stack.push(CallFrameMarker(None)) # enter the call frame so the timer program can end with a RETURN - try: - counter = 0 - previous_timer_irq = time.perf_counter() - while self.pc is not None: - next_pc = self.dispatch_table[self.pc.opcode](self, self.pc) - if next_pc: - self.pc = self.pc.next - counter += 1 - if self.charscreen_address and counter % 1000 == 0: - time.sleep(0.001) # allow the tkinter window to update - time_since_irq = time.perf_counter() - previous_timer_irq - if time_since_irq > 1/60: - self.timer_irq() - previous_timer_irq = time.perf_counter() - except TerminateExecution as x: - why = str(x) - print("[TinyVM execution terminated{:s}]\n".format(": "+why if why else ".")) - return - except Exception as x: - print("EXECUTION ERROR") - self.debug_stack(5) - raise - else: - print("[TinyVM execution ended.]") - - def timer_irq(self) -> None: - # This is the timer 'irq' handler. It is called to run the timer program at a certain interval. - # During the execution the main program is halted - if self.timer_program: - previous_pc = self.pc - previous_program = self.program - previous_stack = self.stack - self.stack = self.timer_stack - self.program = self.timer_program - self.pc = self.program[0] - self.stack.push(CallFrameMarker(None)) # enter the call frame so the timer program can end with a RETURN - while self.pc is not None: - next_pc = self.dispatch_table[self.pc.opcode](self, self.pc) - if next_pc: - self.pc = self.pc.next - self.pc = previous_pc - self.program = previous_program - self.stack = previous_stack - - def debug_stack(self, size: int=5) -> None: - stack = self.stack.debug_peek(size) - if len(stack) > 0: - print("** stack (top {:d}):".format(size)) - for i, value in enumerate(reversed(stack), start=1): - print(" {:d}. {:s} {:s}".format(i, type(value).__name__, str(value))) - else: - print("** stack is empty.") - if self.stack.pop_history: - print("** last {:d} values popped from stack (most recent on top):".format(self.stack.pop_history.maxlen)) - pprint.pprint(list(reversed(self.stack.pop_history)), indent=2, compact=True, width=20) # type: ignore - if self.pc is not None: - print("* instruction:", self.pc) - - def memmapped_charout(self, value: int) -> None: - string = self.system.decodestr(bytearray([value])) - print(string, end="") - - def memmapped_charin(self) -> int: - return self.keyboard_scancode - - def assign_variable(self, variable: Variable, value: Value) -> None: - assert not variable.const, "cannot modify a const" - assert isinstance(value, Value) - variable.value = value - - def opcode_NOP(self, instruction: Instruction) -> bool: - # do nothing - return True - - def opcode_TERMINATE(self, instruction: Instruction) -> bool: - raise TerminateExecution() - - def opcode_PUSH(self, instruction: Instruction) -> bool: - value = self.variables[instruction.args[0]].value # type: ignore - self.stack.push(value) - return True - - def opcode_DUP(self, instruction: Instruction) -> bool: - self.stack.push(self.stack.peek()) - return True - - def opcode_DUP2(self, instruction: Instruction) -> bool: - x = self.stack.peek() - self.stack.push(x) - self.stack.push(x) - return True - - def opcode_SWAP(self, instruction: Instruction) -> bool: - value2, value1 = self.stack.pop2() - self.stack.push2(value2, value1) - return True - - @no_type_check - def opcode_PUSH2(self, instruction: Instruction) -> bool: - value1 = self.variables[instruction.args[0]].value - value2 = self.variables[instruction.args[1]].value - self.stack.push2(value1, value2) - return True - - @no_type_check - def opcode_PUSH3(self, instruction: Instruction) -> bool: - value1 = self.variables[instruction.args[0]].value - value2 = self.variables[instruction.args[1]].value - value3 = self.variables[instruction.args[2]].value - self.stack.push3(value1, value2, value3) - return True - - @no_type_check - def opcode_POP(self, instruction: Instruction) -> bool: - value = self.stack.pop() - variable = self.variables[instruction.args[0]] - self.assign_variable(variable, value) - return True - - @no_type_check - def opcode_POP2(self, instruction: Instruction) -> bool: - value1, value2 = self.stack.pop2() - variable = self.variables[instruction.args[0]] - self.assign_variable(variable, value1) - variable = self.variables[instruction.args[1]] - self.assign_variable(variable, value2) - return True - - @no_type_check - def opcode_POP3(self, instruction: Instruction) -> bool: - value1, value2, value3 = self.stack.pop3() - variable = self.variables[instruction.args[0]] - self.assign_variable(variable, value1) - variable = self.variables[instruction.args[1]] - self.assign_variable(variable, value2) - variable = self.variables[instruction.args[2]] - self.assign_variable(variable, value3) - return True - - @no_type_check - def opcode_ADD(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(first + second) - return True - - @no_type_check - def opcode_SUB(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(first - second) - return True - - @no_type_check - def opcode_MUL(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(first * second) - return True - - @no_type_check - def opcode_DIV(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(first / second) - return True - - def opcode_AND(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(first and second) - return True - - def opcode_OR(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(first or second) - return True - - def opcode_XOR(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - ifirst = 1 if first else 0 - isecond = 1 if second else 0 - self.stack.push(Value(DataType.BOOL, bool(ifirst ^ isecond))) - return True - - def opcode_NOT(self, instruction: Instruction) -> bool: - self.stack.push(Value(DataType.BOOL, not self.stack.pop())) - return True - - def opcode_TEST(self, instruction: Instruction) -> bool: - self.stack.push(Value(DataType.BOOL, bool(self.stack.pop()))) - return True - - def opcode_CMP_EQ(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(Value(DataType.BOOL, first == second)) - return True - - @no_type_check - def opcode_CMP_LT(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(Value(DataType.BOOL, first < second)) - return True - - @no_type_check - def opcode_CMP_GT(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(Value(DataType.BOOL, first > second)) - return True - - @no_type_check - def opcode_CMP_LTE(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(Value(DataType.BOOL, first <= second)) - return True - - @no_type_check - def opcode_CMP_GTE(self, instruction: Instruction) -> bool: - second, first = self.stack.pop2() - self.stack.push(Value(DataType.BOOL, first >= second)) - return True - - def opcode_CALL(self, instruction: Instruction) -> bool: - # arguments are already on the stack - num_args = instruction.args[0] - assert isinstance(num_args, int) - self.stack.push_under(num_args, CallFrameMarker(instruction.alt_next)) - return True - - def opcode_RETURN(self, instruction: Instruction) -> bool: - num_returnvalues = instruction.args[0] - assert isinstance(num_returnvalues, int) - callframe = self.stack.pop_under(num_returnvalues) - assert isinstance(callframe, CallFrameMarker), callframe - self.pc = callframe.returninstruction - return False - - def opcode_SYSCALL(self, instruction: Instruction) -> bool: - syscall = instruction.args[0] - assert isinstance(syscall, str) - call = getattr(self.system, "syscall_" + syscall, None) - if call: - return call() - else: - raise RuntimeError("no syscall method for " + syscall) - - def opcode_JUMP(self, instruction: Instruction) -> bool: - return True # jump simply points to the next instruction elsewhere - - def opcode_JUMP_IF_TRUE(self, instruction: Instruction) -> bool: - result = self.stack.pop() - assert isinstance(result, Value) - if result.value: - self.pc = self.pc.alt_next # alternative next instruction - return False - return True - - def opcode_JUMP_IF_FALSE(self, instruction: Instruction) -> bool: - result = self.stack.pop() - if result.value: # type: ignore - return True - self.pc = self.pc.alt_next # alternative next instruction - return False - - def opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG(self, instruction: Instruction) -> bool: - raise ExecutionError("unsupported conditional jump", instruction) # @todo implement hardware specific status register flags - - dispatch_table = { - Opcode.TERMINATE: opcode_TERMINATE, - Opcode.NOP: opcode_NOP, - Opcode.PUSH: opcode_PUSH, - Opcode.PUSH2: opcode_PUSH2, - Opcode.PUSH3: opcode_PUSH3, - Opcode.POP: opcode_POP, - Opcode.POP2: opcode_POP2, - Opcode.POP3: opcode_POP3, - Opcode.DUP: opcode_DUP, - Opcode.DUP2: opcode_DUP2, - Opcode.SWAP: opcode_SWAP, - Opcode.ADD: opcode_ADD, - Opcode.SUB: opcode_SUB, - Opcode.MUL: opcode_MUL, - Opcode.DIV: opcode_DIV, - Opcode.AND: opcode_AND, - Opcode.OR: opcode_OR, - Opcode.XOR: opcode_XOR, - Opcode.NOT: opcode_NOT, - Opcode.TEST: opcode_TEST, - Opcode.CMP_EQ: opcode_CMP_EQ, - Opcode.CMP_LT: opcode_CMP_LT, - Opcode.CMP_GT: opcode_CMP_GT, - Opcode.CMP_LTE: opcode_CMP_LTE, - Opcode.CMP_GTE: opcode_CMP_GTE, - Opcode.CALL: opcode_CALL, - Opcode.RETURN: opcode_RETURN, - Opcode.SYSCALL: opcode_SYSCALL, - Opcode.JUMP: opcode_JUMP, - Opcode.JUMP_IF_TRUE: opcode_JUMP_IF_TRUE, - Opcode.JUMP_IF_FALSE: opcode_JUMP_IF_FALSE, - Opcode.JUMP_IF_STATUS_ZERO: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_NE: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_EQ: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_CC: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_CS: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_VC: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_VS: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_GE: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_LE: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_GT: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_LT: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_POS: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - Opcode.JUMP_IF_STATUS_NEG: opcode_JUMP_IF_STATUS_UNSUPPORTED_FLAG, - } - - -class System: - def __init__(self, vm: VM) -> None: - self.vm = vm - - def encodestr(self, string: str, alt: bool=False) -> bytearray: - return bytearray(string, self.vm.str_alt_encoding if alt else self.vm.str_encoding) - - def decodestr(self, bb: Union[bytearray, array.array], alt: bool=False) -> str: - return str(bb, self.vm.str_alt_encoding if alt else self.vm.str_encoding) # type: ignore - - def syscall_printstr(self) -> bool: - value = self.vm.stack.pop() - assert isinstance(value, Value) - if value.dtype == DataType.ARRAY_BYTE: - print(self.decodestr(value.value), end="") # type: ignore - return True - else: - raise TypeError("printstr expects bytearray", value) - - def syscall_printchr(self) -> bool: - charactervalue = self.vm.stack.pop() - assert isinstance(charactervalue, Value) - if charactervalue.dtype == DataType.BYTE: - print(self.decodestr(bytearray([charactervalue.value])), end="") # type: ignore - return True - else: - raise TypeError("printchr expects BYTE", charactervalue) - - def syscall_input(self) -> bool: - self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(input()))) - return True - - def syscall_getchr(self) -> bool: - self.vm.stack.push(Value(DataType.BYTE, self.encodestr(input() + '\n')[0])) - return True - - def syscall_decimalstr_signed(self) -> bool: - value = self.vm.stack.pop() - assert isinstance(value, Value) - if value.dtype in (DataType.SBYTE, DataType.SWORD): - self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value)))) - return True - else: - raise TypeError("decimalstr_signed expects signed int", value) - - def syscall_decimalstr_unsigned(self) -> bool: - value = self.vm.stack.pop() - assert isinstance(value, Value) - if value.dtype in (DataType.BYTE, DataType.WORD): - self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value)))) - return True - else: - raise TypeError("decimalstr_signed expects unsigned int", value) - - def syscall_hexstr_signed(self) -> bool: - value = self.vm.stack.pop() - if type(value) is int: - if value >= 0: # type: ignore - strvalue = "${:x}".format(value) - else: - strvalue = "-${:x}".format(-value) # type: ignore - self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(strvalue))) - return True - else: - raise TypeError("hexstr expects int", value) - - def syscall_memwrite_byte(self) -> bool: - value, address = self.vm.stack.pop2() - assert isinstance(value, Value) and isinstance(address, Value) - assert value.dtype == DataType.BYTE and address.dtype == DataType.WORD - self.vm.memory.set_byte(address.value, value.value) # type: ignore - return True - - def syscall_memwrite_sbyte(self) -> bool: - value, address = self.vm.stack.pop2() - assert isinstance(value, Value) and isinstance(address, Value) - assert value.dtype == DataType.SBYTE and address.dtype == DataType.WORD - self.vm.memory.set_sbyte(address.value, value.value) # type: ignore - return True - - def syscall_memwrite_word(self) -> bool: - value, address = self.vm.stack.pop2() - assert isinstance(value, Value) and isinstance(address, Value) - assert value.dtype in (DataType.WORD, DataType.BYTE) and address.dtype == DataType.WORD - self.vm.memory.set_word(address.value, value.value) # type: ignore - return True - - def syscall_memwrite_sword(self) -> bool: - value, address = self.vm.stack.pop2() - assert isinstance(value, Value) and isinstance(address, Value) - assert value.dtype in (DataType.SWORD, DataType.SBYTE, DataType.BYTE) and address.dtype == DataType.WORD - self.vm.memory.set_sword(address.value, value.value) # type: ignore - return True - - def syscall_memwrite_float(self) -> bool: - value, address = self.vm.stack.pop2() - assert isinstance(value, Value) and isinstance(address, Value) - assert value.dtype == DataType.FLOAT and address.dtype == DataType.WORD - self.vm.memory.set_float(address.value, value.value) # type: ignore - return True - - def syscall_memwrite_str(self) -> bool: - strbytes, address = self.vm.stack.pop2() - assert isinstance(strbytes, Value) and isinstance(address, Value) - assert strbytes.dtype == DataType.ARRAY_BYTE and address.dtype == DataType.WORD - for i, b in enumerate(strbytes.value): # type: ignore - self.vm.memory.set_byte(address+i, b) # type: ignore - return True - - def syscall_memread_byte(self) -> bool: - address = self.vm.stack.pop() - assert isinstance(address, Value) - assert address.dtype == DataType.WORD - self.vm.stack.push(Value(DataType.BYTE, self.vm.memory.get_byte(address.value))) # type: ignore - return True - - def syscall_smalldelay(self) -> bool: - time.sleep(1/100) - return True - - def syscall_delay(self) -> bool: - time.sleep(0.1) - return True - - -class ScreenViewer(tkinter.Tk): - def __init__(self, vm: VM, screen_addr: int, screen_width: int, screen_height: int) -> None: - super().__init__() - self.title("IL65 tinyvm") - self.fontsize = 16 - self.vm = vm - self.address = screen_addr - self.width = screen_width - self.height = screen_height - self.monospace = tkinter.font.Font(self, family="Courier", weight="bold", size=self.fontsize) # type: ignore - cw = self.monospace.measure("x")*self.width+8 - self.canvas = tkinter.Canvas(self, width=cw, height=self.fontsize*self.height+8, bg="blue") - self.canvas.pack() - self.bind("", self.keypress) - self.bind("", self.keyrelease) - self.after(10, self.update_screen) - - def keypress(self, e) -> None: - key = e.char or e.keysym - if len(key) == 1: - self.vm.keyboard_scancode = self.vm.system.encodestr(key)[0] - elif len(key) > 1: - code = 0 - if key == "Up": - code = ord("w") - elif key == "Down": - code = ord("s") - elif key == "Left": - code = ord("a") - elif key == "Right": - code = ord("d") - self.vm.keyboard_scancode = code - else: - self.vm.keyboard_scancode = 0 - - def keyrelease(self, e) -> None: - self.vm.keyboard_scancode = 0 - - def update_screen(self) -> None: - self.canvas.delete(tkinter.ALL) - lines = [] - for y in range(self.height): - line = self.vm.system.decodestr(self.vm.memory.get_bytes(self.address+y*self.width, self.width)) - lines.append("".join(c if c.isprintable() else " " for c in line)) - for y, line in enumerate(lines): - self.canvas.create_text(4, self.fontsize*y, text=line, fill="white", font=self.monospace, anchor=tkinter.NW) - self.after(30, self.update_screen) - - @classmethod - def create(cls, vm: VM, screen_addr: int, screen_width: int, screen_height: int) -> None: - viewer = cls(vm, screen_addr, screen_width, screen_height) - viewer.mainloop()