diff --git a/codeCore/test/prog8tests/codecore/TestZeropageFreeList.kt b/codeCore/test/prog8tests/codecore/TestZeropageFreeList.kt index 79c6cdbd1..703a4d165 100644 --- a/codeCore/test/prog8tests/codecore/TestZeropageFreeList.kt +++ b/codeCore/test/prog8tests/codecore/TestZeropageFreeList.kt @@ -19,7 +19,7 @@ import prog8.code.target.zp.PETZeropage /** - * Comprehensive tests for Zeropage free list configurations across all targets. + * Tests for Zeropage free list configurations across all targets. * * Tests verify that for each target and ZeropageType combination: * - The exact list of free locations is correct diff --git a/codeGenM68k/src/prog8/codegen/m68k/AsmGen.kt b/codeGenM68k/src/prog8/codegen/m68k/AsmGen.kt index 9bde3f8ea..bb4265ff5 100644 --- a/codeGenM68k/src/prog8/codegen/m68k/AsmGen.kt +++ b/codeGenM68k/src/prog8/codegen/m68k/AsmGen.kt @@ -38,13 +38,13 @@ internal fun IRInstruction.forwardableImmediateFor(register: VirtualRegister, ty * It may contain registers that are NOT call arguments. * For every call argument register, the loaded immediate value is forwarded * directly into the argument's hardware register instead of going through the - * register file (move #imm,dX instead of move p8_regfile+N,dX, and - * fmovecr or a const-pool load into fpX instead of moving through p8_fregfile). + * register file. That is move #imm,dX instead of move p8_regfile+N,dX, and + * fmovecr or a const-pool load into fpX instead of moving through p8_fregfile. * * [deadRegisters] are the registers from [loads] that are not read anywhere - * else in the subroutine (their only use is the forwarded call argument); - * their register-file stores are omitted entirely. Note that this can also - * remove stores for registers that are not call arguments at all. + * else in the subroutine; their only use is the forwarded call argument, so + * their register-file stores are omitted entirely. This can also remove + * stores for registers that are not call arguments at all. * * The read check covers the subroutine's whole flattened instruction list * rather than only the part after the call (see `isRegisterReadElsewhere`), diff --git a/codeGenM68k/test/TestInstructionSelectionOptimizations.kt b/codeGenM68k/test/TestInstructionSelectionOptimizations.kt index 8e2c3a4dd..5a711951c 100644 --- a/codeGenM68k/test/TestInstructionSelectionOptimizations.kt +++ b/codeGenM68k/test/TestInstructionSelectionOptimizations.kt @@ -1255,7 +1255,7 @@ class TestInstructionSelectionOptimizations : FunSpec({ lines.any { it == "move.w #1234,var" } shouldBe true } - // === extb.l gate robustification === + // === extb.l gate hardening === test("M68020 sign-extends byte to long with extb.l") { val lines = generateAsm( diff --git a/codeGenNew6502/src/prog8/codegen/new6502/InstrControl.kt b/codeGenNew6502/src/prog8/codegen/new6502/InstrControl.kt index 19faeacca..c34cc54e3 100644 --- a/codeGenNew6502/src/prog8/codegen/new6502/InstrControl.kt +++ b/codeGenNew6502/src/prog8/codegen/new6502/InstrControl.kt @@ -494,7 +494,7 @@ internal val CallResult.hardwareSlot: CallingConventionSlot? /** * The slot-based arguments, in the order they must be loaded to avoid register clobbering. * Order matches the old 6502 codegen: paired regs first (AX/AY/XY), then single regs (Y, X, A), - * then float regs, then status flags. This ensures that loading a paired register + * then float regs, then status flags. That way, loading a paired register * (which clobbers two hardware regs) doesn't overwrite a value needed for a later argument. * Within each group the original argument order is kept. */ diff --git a/codeGenNew6502/src/prog8/codegen/new6502/InstrLoadStore.kt b/codeGenNew6502/src/prog8/codegen/new6502/InstrLoadStore.kt index 8f0732b3e..0a5f8e7b4 100644 --- a/codeGenNew6502/src/prog8/codegen/new6502/InstrLoadStore.kt +++ b/codeGenNew6502/src/prog8/codegen/new6502/InstrLoadStore.kt @@ -1104,8 +1104,9 @@ internal fun AsmGen.copyRegisterLong(dstReg: Int, srcReg: Int) { emitLine("bpl -") } -/** the address of a [base] (+ [displacement], for a Symbol base only - matches the previous behavior - * of ignoring any offset that came together with a fixed numeric address) */ +/** the address of a [base], plus [displacement]. The displacement is only added + * for a Symbol base; this matches the previous behavior of ignoring any offset + * that came together with a fixed numeric address. */ internal fun AsmGen.resolveAddress(base: AddressBase, displacement: Int = 0): String { return when (base) { is AddressBase.Symbol -> { diff --git a/compiler/test/codegeneration/TestBooleanLiteralsNoOpt.kt b/compiler/test/codegeneration/TestBooleanLiteralsNoOpt.kt index 3bcd11f4d..e031ac935 100644 --- a/compiler/test/codegeneration/TestBooleanLiteralsNoOpt.kt +++ b/compiler/test/codegeneration/TestBooleanLiteralsNoOpt.kt @@ -10,7 +10,7 @@ import prog8tests.helpers.compileText /** * Tests for bugs that only occurred when compiling with -noopt (optimizations disabled). * - * These tests ensure that boolean literals in logical expressions are handled correctly + * These tests verify that boolean literals in logical expressions are handled correctly * when the optimizer doesn't remove them. */ class TestBooleanLiteralsNoOpt: FunSpec({ diff --git a/compiler/test/codegeneration/TestPointerFieldAugmentedAssignment6502.kt b/compiler/test/codegeneration/TestPointerFieldAugmentedAssignment6502.kt index 034b51422..3f2f4c755 100644 --- a/compiler/test/codegeneration/TestPointerFieldAugmentedAssignment6502.kt +++ b/compiler/test/codegeneration/TestPointerFieldAugmentedAssignment6502.kt @@ -9,9 +9,9 @@ import java.nio.file.Files /** * These tests execute compiled 6502 code in the ksim65 simulator to verify that * in-place modifications (augmented assignments) on struct fields reached via - * a pointer are correct, especially when the pointer variable is *not* located - * in the zeropage (so its effective address must live in the shared scratch - * pointer that evaluation of the source expression may clobber). + * a pointer are correct. This is especially tricky when the pointer variable is + * *not* located in the zeropage: its effective address must live in the shared + * scratch pointer, which evaluation of the source expression may clobber. */ class TestPointerFieldAugmentedAssignment6502 : FunSpec({ val outputDir = Files.createTempDirectory("prog8test") diff --git a/docs/source/comparing.rst b/docs/source/comparing.rst index c5a838591..8d39ba827 100644 --- a/docs/source/comparing.rst +++ b/docs/source/comparing.rst @@ -105,7 +105,7 @@ Pointers and Structs A variable that can hold a :ref:`pointer ` for the target can be used as a pointer to a memory location, so the value it points to is always a single byte. This is similar to ``uint8_t*`` from C. You have to deal with the pointer manually if the object it points to is something different. -- Note that there is the ``peekw`` builtin function that *does* allow you to directly obtain the *word* value at the given memory location. +- There is the ``peekw`` builtin function that *does* allow you to directly obtain the *word* value at the given memory location. So if you use this, you can use uword pointers as pointers to word values without much hassle. - "dereferencing" an untyped pointer is done via array indexing ``ptr[index]`` or via the memory read operator ``@(ptr)``, or ``peek/peekw(ptr)``. - Pointers don't have to be a variable, you can immediately access the value of a given memory location using ``@($d020)`` for instance. diff --git a/docs/source/compiling.rst b/docs/source/compiling.rst index ee141e9bd..9ff2838f3 100644 --- a/docs/source/compiling.rst +++ b/docs/source/compiling.rst @@ -53,7 +53,8 @@ Mac OS (and Linux, and WSL2 on Windows): The Gradle build system is used to build the compiler. You will also need at least Java version 17 or higher to build it. The most interesting gradle commands to run are probably the ones listed below. -(Note: if you have a recent gradle installed on your system already, you can probably replace the ``./gradlew`` wrapper commands with just the regular ``gradle`` command.) +If you have a recent Gradle installation already, you can probably replace the ``./gradlew`` wrapper commands +with the regular ``gradle`` command. ``./gradlew build`` Builds the compiler code and runs all available checks and unit-tests. @@ -93,7 +94,7 @@ For normal use, the ``installDist`` task should suffice and after successful com 3. rebuild the full project. Alternatively you can also use the Makefile in the antlr directory to generate the parser, but for development the - Antlr4 plugin provides several extremely handy features so you'll probably want to have it installed anyway. + Antlr4 plugin provides several useful features so you'll probably want to have it installed anyway. .. image:: _static/antlrparser.png :alt: Generating the Antlr4 parser files @@ -371,10 +372,10 @@ One or more .p8 module files Tells the assembler to issue warning messages about symbol shadowing. These *can* be problematic, but usually aren't because prog8 has different scoping rules than the assembler has. - You may want to watch out for shadowing of builtin names though. Especially 'a', 'x' and 'y' - as those are the cpu register names and if you shadow those, the assembler might - interpret certain instructions differently and produce unexpected opcodes (like LDA X getting - turned into TXA, or not, depending on the symbol 'x' being defined in your own assembly code or not) + You may want to watch out for shadowing of builtin names though. Especially 'a', 'x' and 'y', + as those are the cpu register names. If you shadow those, the assembler might interpret certain + instructions differently and produce unexpected opcodes, like LDA X getting turned into TXA. + Whether that actually happens depends on whether the symbol 'x' is defined in your own assembly code. ``-watch`` Enables continuous compilation mode (watches for file changes). @@ -382,7 +383,7 @@ One or more .p8 module files almost instant compilation times (less than a second) can be achieved in this mode. The compiler will compile your program and then instead of exiting, it waits for any changes in the module source files. As soon as a change happens, the program gets compiled again. - Note that it is possible to use the watch mode with multiple modules as well, but it will + It is possible to use the watch mode with multiple modules as well, but it will recompile everything in that list even if only one of the files got updated. *Combined with the Emulator's Host-FS or real hardware Calypso's network drive:* this enables you to edit prog8 source files with an editor on the X16 itself (such as XVI, XEdit). When saving the source file, diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 99de8e7e1..58ee4e70e 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -103,9 +103,9 @@ Language :index:`Features` - ``when`` statement to avoid if-else chains - ``on .. goto`` statement for fast jump tables - ``in`` expression for concise and efficient multi-value/containment test -- ``defer`` statement with program-wide unwinding to help write concise and robust resource cleanup logic +- ``defer`` statement with program-wide unwinding to help write reliable resource cleanup code - Several specialized built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror`` -- Various powerful built-in libraries to do I/O, number conversions, graphics and more +- Built-in libraries for I/O, number conversions, graphics and more - Floating point math is supported on most cbm-compatible compiler targets. - Provides access to most Kernal ROM routines as external subroutine definitions you can call normally. - Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents. diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index b85d201c4..e33506c02 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -188,9 +188,8 @@ Miscellaneous .. note:: The arguments are in 'natural' left to right reading order that is first the msb then the lsb. - Don't get confused by how the system actually stores this 16-bit word value in memory (which is - in little-endian format, so lsb first then msb, on the 6502 targets; the m68k targets store it - big-endian, msb first) + The system stores this 16-bit word value in memory in little-endian format on the 6502 targets + (lsb first, then msb); the m68k targets store it big-endian (msb first). :index:`mklong` (msb, b2, b1, lsb) Efficiently create a long value from four bytes (the msb, second, first and finally the lsb). Avoids multiplication and shifting. @@ -198,9 +197,8 @@ Miscellaneous .. note:: The arguments are in 'natural' left to right reading order that is first the msb then the lsb. - Don't get confused by how the system actually stores this 32-bit word value in memory (which is - in little-endian format, so lsb first then b1, b2 and finally the msb, on the 6502 targets; - the m68k targets store it big-endian, msb first) + The system stores this 32-bit word value in memory in little-endian format on the 6502 targets + (lsb first, then b1, b2 and finally the msb); the m68k targets store it big-endian (msb first). :index:`mklong2` (msw, lsw) Efficiently create a long value from two words (the msw, and the lsw). Avoids multiplication and shifting. @@ -274,9 +272,9 @@ Miscellaneous Rotate the bits in x (byte or word) one position to the left. This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag, while the highest bit will become the new Carry flag value. - (essentially, it is a 9-bit or 17-bit rotation) - On M68k targets, the extend (X) bit substitutes the carry flag for rotates - (because M68k has separate C and X flags; ``set_carry`` and ``clear_carry`` manage both). + Essentially, it is a 9-bit or 17-bit rotation. + On M68k targets, the extend (X) bit substitutes the carry flag for rotates, because M68k has + separate C and X flags; ``set_carry`` and ``clear_carry`` manage both. Modifies in-place, doesn't return a value (so can't be used in an expression). You can rol a memory location directly by using the direct memory access syntax, so like ``rol(@($5000))`` You can use ``if_cc`` or ``if_cs`` after a rol to act on the new carry bit, if required. @@ -291,9 +289,9 @@ Miscellaneous Rotate the bits in x (byte or word) one position to the right. This uses the CPU's rotate semantics: the highest bit will be set to the current value of the Carry flag, while bit 0 will become the new Carry flag value. - (essentially, it is a 9-bit or 17-bit rotation) - On M68k targets, the extend (X) bit substitutes the carry flag for rotates - (because M68k has separate C and X flags; ``set_carry`` and ``clear_carry`` manage both). + Essentially, it is a 9-bit or 17-bit rotation. + On M68k targets, the extend (X) bit substitutes the carry flag for rotates, because M68k has + separate C and X flags; ``set_carry`` and ``clear_carry`` manage both. Modifies in-place, doesn't return a value (so can't be used in an expression). You can ror a memory location directly by using the direct memory access syntax, so like ``ror(@($5000))`` You can use ``if_cc`` or ``if_cs`` after a ror to act on the new carry bit, if required. @@ -1443,7 +1441,7 @@ For non-blocking sequenced playback (driven by an IRQ handler): ``sub set_gap(ubyte ticks)`` Set the number of silence ticks between notes (default 1). - ``0`` means no silence gap at all — notes flow into each other seamlessly. + ``0`` means no silence gap at all — notes flow into each other without a break. The note-on time is automatically adjusted: ``note = duration - gap``, minimum 1 tick. This means changing the gap does **not** affect the overall tempo. Call this before ``song()``. diff --git a/docs/source/profiling.rst b/docs/source/profiling.rst index 76c17cf08..2c1d7f86f 100644 --- a/docs/source/profiling.rst +++ b/docs/source/profiling.rst @@ -21,7 +21,7 @@ or :source:`online here `). This script cross-references the memory stats file with an assembly listing of the program, produced by the Prog8 compiler with the ``-asmlist`` option. It then prints the top N lines in your (assembly) program source that perform the most reads and writes, which you can use to identify possible hot spots/bottlenecks/variables that should be better placed in zeropage etc. -Note that the profiler simply works with the total number of accesses to memory locations. +The profiler works with the total number of accesses to memory locations. This is *not* the same as the most run-time (cpu instructions cycle times aren't taken into account at all)! Here is an example of the output it generates: diff --git a/docs/source/programming.rst b/docs/source/programming.rst index cd7a3db01..4a4995801 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -96,7 +96,7 @@ Identifiers Naming things in Prog8 is done via valid *identifiers*. They start with a letter, and after that, a combination of letters, numbers, or underscores. -Note that any Unicode Letter symbol is accepted as a letter! +Any Unicode Letter symbol is accepted as a letter! Examples of valid identifiers:: a @@ -114,7 +114,7 @@ Examples of valid identifiers:: .. index:: pair: Identifiers; Scoped names Sometimes called "qualified names" or "dotted names", a scoped name is a sequence of identifiers separated by a dot. -They are used to reference symbols in other scopes. Note that unlike many other programming languages, +They are used to reference symbols in other scopes. Unlike many other programming languages, scoped names always need to be fully scoped (because they always start in the global scope). Also see :ref:`blocks`:: main.start ; the entrypoint subroutine @@ -179,7 +179,7 @@ the enclosing scope is searched for it, and so on, up to the top level block, un If the symbol was not found the compiler will issue an error message. **:index:`Subroutines`** create a new scope. All variables inside a subroutine are hoisted up to the -scope of the subroutine they are declared in. Note that you can define **nested subroutines** in Prog8, +scope of the subroutine they are declared in. You can define **nested subroutines** in Prog8, and such a nested subroutine has its own scope! This also means that you have to use a fully qualified name to access a variable from a nested subroutine:: @@ -281,7 +281,7 @@ Directives Level: not at module scope. Tells the assembler to continue assembling on the given alignment interval. For example, ``%align $100`` will insert an assembler command to align on the next page boundary. - Note that this has no impact on variables following this directive! Prog8 reallocates all variables + This has no impact on variables following this directive! Prog8 reallocates all variables using different rules. If you want to align a specific variable (array or string), you should use one of the alignment tags for variable declarations instead. Valid intervals are from 2 to 65536. @@ -295,7 +295,7 @@ Directives Level: not at module scope. Declares that a piece of *assembly code* is inside the curly braces. This code will be copied as-is into the generated output assembly source file. - Note that the start and end markers are both *double curly braces* to minimize the chance + The start and end markers are both *double curly braces* to minimize the chance that the assembly code itself contains either of those. If it does contain a ``}}``, it will confuse the parser. @@ -308,7 +308,7 @@ Directives .. caution:: Avoid using single-letter symbols in included assembly code, as they could be confused with CPU registers. - Also, note that all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`. + Also, all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`. .. index:: pair: Directives; %asmbinary @@ -318,7 +318,7 @@ Directives This directive can only be used inside a block. The assembler itself will include the file as binary bytes at this point, prog8 will not process this at all. This means that the filename must be spelled exactly as it appears on your computer's file system. - Note that this filename may differ in case compared to when you chose to load the file from disk from within the + This filename may differ in case compared to when you chose to load the file from disk from within the program code itself (for example on the C64 and X16 there's the PETSCII encoding difference). The file is located relative to the current working directory! The optional offset and length can be used to select a particular piece of the file. @@ -344,7 +344,7 @@ Directives .. caution:: Avoid using single-letter symbols in included assembly code, as they could be confused with CPU registers. - Also, note that all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`. + Also, all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`. Here is a small example program to show how to use labels to reference the included contents from prog8 code:: @@ -665,7 +665,8 @@ value. Range expressions used outside a direct ``for`` loop still require a con .. attention:: The value of the loop variable after executing the loop *is undefined* - you cannot rely on it to be the last value in the range for instance! The value of the variable should only be used inside the for loop body. - (this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value) + This is an optimization issue, to avoid having to deal with mostly useless post-loop logic to adjust + the loop variable's value. Do not modify the loop variable inside the loop body. The compiler assumes exclusive control over the loop variable; changing it can cause incorrect iteration counts or infinite loops. If you need to alter the iteration, use a ``while`` loop instead. @@ -968,7 +969,7 @@ on .. goto / on .. call statement (jump table) .. index:: single: Conditionals; on goto/call statement The ``on goto / call`` statement is suitable to create a fast call of a subroutine from a list based on an index value. -it selects a function to jump to in O(1) whereas a similar when-statement, runs in O(n) because that one checks each index value. +It selects a function to jump to in O(1), whereas a similar when-statement runs in O(n) because that one checks each index value. The ``on goto / call`` instead simply gets the correct entry from an array of function pointers and jumps to it directly. The index value that is used is 0-based; 0 will jump to the first entry in the list, 1 to the second, and so on. If the value is too large (i.e. outside the list of functions), no call is performed, and execution continues. @@ -1008,7 +1009,7 @@ compile-time constant integers (bytes or words). They also have to be the same datatype as the when-value, otherwise no efficient comparison can be done. You can explicitly put a list of numbers that all should result in the same case, or even use any *range expression* as long as it denotes a constant list of numbers. -Be aware that every number is compared individually so using long lists of numbers and/or +Be careful: every number is compared individually so using long lists of numbers and/or many choice cases will result in poor performance. If you need to call a certain function based on some sequential index number, look at the ``on .. goto / call`` statement instead. @@ -1173,7 +1174,7 @@ The ``swap`` keyword swaps the values in two variables without the need of a tem swap(var1, var2) -Supports booleans and all other numeric datatypes including pointers. Note that complicated expressions +Supports booleans and all other numeric datatypes including pointers. Complicated expressions that you want to swap may not be implemented yet. To avoid such errors you'll have to swap them in the old fashioned way (using a temporary variable), until an optimized code path gets implemented in a future Prog8 version. @@ -1260,17 +1261,17 @@ arithmetic: ``+`` ``-`` ``*`` ``/`` ``%`` ``/`` is division (will result in integer division when using on integer operands, and a floating point division when at least one of the operands is a float) ``%`` is the remainder operator: ``25 % 7`` is 4. Be careful: without a space after the %, it will be parsed as a binary number. So ``25 %10`` will be parsed as the number 25 followed by the binary number 2, which is a syntax error. - Note that remainder is only supported on integer operands (not floats). + Remainder is only supported on integer operands (not floats). bitwise arithmetic: ``&`` ``|`` ``^`` ``~`` ``<<`` ``>>`` ``&`` is bitwise and, ``|`` is bitwise or, ``^`` is bitwise xor, ``~`` is bitwise invert (this one is an unary operator) ``<<`` is bitwise left shift and ``>>`` is bitwise right shift (both will not change the datatype of the value) - While the operands can be signed integers (the expression will just consider the underlying bit patterns), - the result value of a bitwise expression is always unsigned. + The operands can be signed integers; the expression just considers the underlying bit patterns. + The result value of a bitwise expression is always unsigned. assignment: ``=`` Sets the target on the LHS (left hand side) of the operator to the value of the expression on the RHS (right hand side). - Note that an assignment sometimes is not possible or supported. + An assignment is sometimes not possible or supported. It's possible to chain assignments like ``x = y = z = 42`` as a shorthand for the three assignments with the same value. augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=`` @@ -1331,7 +1332,7 @@ address of: ``&``, ``&<``, ``&>``, ``&&`` in the :ref:`variables` chapter. ``&<`` and ``&>`` are for use on split word arrays, they give you the address of the LSB byte array - and MSB byte array separately, respectively. Note that ``&<`` is just the same as ``&`` in this case. + and MSB byte array separately, respectively. ``&<`` is the same as ``&`` in this case. For more details on split word arrays, see :ref:`arrayvars`. Split word arrays (and with them the ``&>`` operator) don't exist on the m68k targets; there ``&>`` is an error. @@ -1345,7 +1346,7 @@ address of: ``&``, ``&<``, ``&>``, ``&&`` type cast: ``as`` Explicitly convert an expression to another data type. - Note that ``as`` has very low precedence, lower than most other operators. + ``as`` has very low precedence, lower than most other operators. This means that ``a + b as long`` is parsed as ``(a + b) as long``. If you want to cast an operand before an operation, use parentheses: ``(a as long) * b``. @@ -1584,7 +1585,7 @@ flag such as Carry (Pc). .. note:: Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted - directly instead of a call to them. Note that it is literal copy-paste of code that is done, + directly instead of a call to them. It is literal copy-paste of code that is done, so make sure the assembly is actually written to behave like such - which probably means you don't want a ``rts`` or ``jmp`` or ``bra`` in it! Inlining may increase code size significantly and can only be used in limited scenarios @@ -1593,8 +1594,9 @@ flag such as Carry (Pc). The **sixteen 'virtual' 16-bit registers** from the Commander X16 can also be specified as ``R0`` .. ``R15`` . This means you don't have to set them up manually before calling a subroutine that takes one or more parameters in those 'registers'. You can just list the arguments directly. - *This also works on the other compilation targets!* (however they might not be as efficient there as on the X16, - because on most other targets such as the C64, these registers are not placed in zeropage due to lack of space) + *This also works on the other compilation targets!* However, they might not be as efficient there as on + the X16, because on most other targets such as the C64, these registers are not placed in zeropage due + to lack of space. In both regular **prog8** *and* **assembly** code these 'registers' are directly accessible too via: - ``cx16.r0`` - ``cx16.r15`` (memory-mapped **uword** values, most often these are used) @@ -1652,7 +1654,7 @@ and returning stuff in several registers as well. The ``clobbers`` clause is use are clobbered by the call instead of being unchanged or returning a meaningful result value. This register clobber information currently is only for documentation purposes. -Note that the address ($ffd5 in the example above) can actually be an expression as long as it is a compile time constant. This can +The address ($ffd5 in the example above) can actually be an expression as long as it is a compile time constant. This can make it easier to define jump tables for example, like this:: const uword APIBASE = $8000 @@ -1692,7 +1694,7 @@ Otherwise the compiler will warn you about discarding the result of the call. There is no guarantee of a left-to-right or right-to-left evaluation of the call arguments. .. caution:: - Note that due to the way parameters are processed by the compiler, + Due to the way parameters are processed by the compiler, subroutines are *non-reentrant*. This means you cannot create *recursive calls* (routines calling themselves) without doing some manual work to save and restore the variables that need to retain their value between calls. If you really need a recursive algorithm, there are a few options: @@ -1775,11 +1777,11 @@ Deferred ("cleanup") code .. index:: pair: Subroutines; Deferred code Usually when a subroutine exits, it has to clean up things that it worked on. For example, it has to close -a file that it opened before to read data from, or it has to free a piece of memory that it allocated via -a dynamic memory allocation library, etc. -Every spot where the subroutine exits (return statement, jump, or the end of the routine) you have to take care -of doing the cleanups required. This can get tedious, and the cleanup code is separated from the place where -the resource allocation was done at the start. +a file it opened earlier to read data from, or free a piece of memory it allocated via a dynamic memory +allocation library. +Every exit of the subroutine (a return statement, a jump, or the end of the routine) needs the required +cleanups. This can get tedious, and the cleanup code is separated from the place where the resource +allocation was done at the start. The ``defer`` keyword can be used to schedule a statement (or block of statements) to be executed just before exiting of the current subroutine. That can be via a return statement or a jump to somewhere else, diff --git a/docs/source/structpointers.rst b/docs/source/structpointers.rst index 3d81d1424..20e6962cb 100644 --- a/docs/source/structpointers.rst +++ b/docs/source/structpointers.rst @@ -94,7 +94,7 @@ So the syntax for declaring typed pointers looks like this: ``^^type[size]``: array with size size containing pointers to a type. So for example; ``^^word[100] values`` declares values to be an array of 100 pointers to words. - Note that an array of pointers (regardless of the type they point to) is always a split word array. + An array of pointers (regardless of the type they point to) is always a split word array. (this is the most efficient way to access the pointers, and they need to be copied to zeropage first to be able to use them anyway. It also allows for arrays of up to 256 pointers instead of 128.) @@ -129,11 +129,13 @@ For example, to advance a pointer to the next value, you can use ``pointer++``. To make it point to the preceding value, you can use ``pointer--``. **Adding or subtracting X to a pointer will change the pointer by X times the size of the value it points at (the same as the C language does it), instead of simply adding or subtracting the value from the pointer address value.** -(that is what Prog8 still does for untyped uword pointers, or pointers to a type that just takes up a single byte of memory). +That is what Prog8 still does for untyped uword pointers, or pointers to a type that just takes up +a single byte of memory. That special pointer arithmetic is also performed for pointers to struct types: the compiler knows the memory storage size of the whole struct type and advances or rewinds -the pointer value (memory address) by the appropriate number of bytes (X times the size of the struct). More info about structs can be found below. +the pointer value (memory address) by the appropriate number of bytes (X times the size of the struct). +More info about structs can be found below. You can subtract two pointers of the **same** type from each other. The result is the number of elements between them (C-style), using the same scaling rules as adding or subtracting an integer. The result has the target's address type (``uword`` on 16-bit targets, ``long`` on 32-bit targets). @@ -318,7 +320,7 @@ Typical uses: - **Hardware or memory-mapped registers:** one location that the hardware interprets as a byte or a word depending on how it is accessed. - **Overlays:** reinterpret a buffer, such as the raw bytes of a ``float``, for debugging or serialization, or reuse the same scratch space between different phases of a program. -Note that unions do not (yet) help save space inside structs, because nesting a union (or struct) as a field is not allowed; see above. +Unions do not (yet) help save space inside structs, because nesting a union (or struct) as a field is not allowed; see above. Because all fields overlap, static initialization with values is not allowed. The only valid initializer is the empty form ``[]``, which places a zero-initialized union instance in BSS:: @@ -380,9 +382,12 @@ Initializing byte-array fields with a string For a sized ``ubyte`` (or ``byte``) array field, a string literal may be used in place of an explicit byte array. The string is encoded using the current ``%encoding`` and then filled into the array using -C-style semantics: if the encoded string is shorter than the array, the remaining bytes are filled with -zero (the first zero acts as the implicit C string terminator); if it is exactly the array length, no -extra terminator is added; and if it is longer than the array, a compilation error is reported. +C-style semantics: + +- if the encoded string is shorter than the array, the remaining bytes are filled with zero + (the first zero acts as the implicit C string terminator) +- if it is exactly the array length, no extra terminator is added +- if it is longer than the array, a compilation error is reported For example, given ``ubyte[4] name``, the initializers ``"abcd"``, ``"abc"`` and ``"a"`` are all accepted and produce ``[a,b,c,d]``, ``[a,b,c,0]`` and ``[a,0,0,0]`` respectively, while ``"abcde"`` diff --git a/docs/source/targetsystem.rst b/docs/source/targetsystem.rst index 4058f3b9a..75df6a92c 100644 --- a/docs/source/targetsystem.rst +++ b/docs/source/targetsystem.rst @@ -56,7 +56,7 @@ If it matches one of the existing built-in compilation targets, the internal lib if the user supplied library location doesn't contain a replacement library file for anything that might get imported. The path you need to provide for the ``library`` variable can be relative (to the current working directory where you launch the compiler from) and you can use a tilde ``~`` in it like in a shell path, to refer to a user's home directory. -Note that library modules not unique to a specific compilation target (for example, `buffers`, `sorting` or `strings`) will +Library modules not unique to a specific compilation target (for example, `buffers`, `sorting` or `strings`) will be picked up from the internal library files just fine as was always the case. You can still provide custom versions of them in your own library folder of course, like you already could with using the ``-srcdirs`` compiler flag. @@ -151,9 +151,9 @@ A minimal script for a custom target looks like this:: ``ENTRY`` should point at ``prog8_program_start``, the label the compiler emits for the start of the program. The ``.data`` and ``.bss`` sections are deliberately merged into a single output -section so that the linker places them in one load segment: if ``.bss`` were its own output section, +section so that the linker places them in one load segment. If ``.bss`` were its own output section, the linker would compute that segment's address as "end of the previous segment + alignment" instead -of honouring the address in the script, and ``prog8_program_end`` would no longer point at the true +of honouring the address in the script. Then ``prog8_program_end`` would no longer point at the true end of the loaded image. For the same reason ``prog8_program_end`` is defined by the linker script rather than by the generated assembly. Startup code that needs to clear the bss, such as the ``qemu68k`` bootinfo support, references that symbol, so leave it in. @@ -165,9 +165,9 @@ Source level restrictions ^^^^^^^^^^^^^^^^^^^^^^^^^ The m68k code generator rejects a number of directives and options that only make sense for a -6502 target: the ``%launcher``, ``%address``, ``%memtop`` and ``%varsaddress`` directives, register -annotations on ordinary subroutine parameters (they are only used in ``asmsub``), split word arrays -written as ``&>``, and specific block addresses. The ``-varsgolden``, ``-varshigh`` and +6502 target: the ``%launcher``, ``%address``, ``%memtop`` and ``%varsaddress`` directives, split word arrays +written as ``&>``, and specific block addresses. Register annotations on ordinary subroutine parameters +are rejected as well; those are only used in ``asmsub``. The ``-varsgolden``, ``-varshigh`` and ``-varsaddress`` command line options are rejected as well, so where the variables are placed is decided by the linker script instead. @@ -470,17 +470,16 @@ Other targets may use a different vector or may not support bare handlers at all .. caution:: It is advised to **not use floating point calculations** inside IRQ handler routines. - Beside them being very slow, there are intricate requirements such as having the - correct ROM bank enabled to be able to successfully call them (and making sure the correct - ROM bank is reset at the end of the handler), and the possibility - of corrupting variables and floating point calculations that are being executed - in the interrupted main program. These memory locations should be backed up - and restored at the end of the handler, further increasing its execution time... + They are very slow, and the correct ROM bank must be enabled to call them. Make sure the correct + ROM bank is reset at the end of the handler as well. + There is also the possibility of corrupting variables and floating point calculations + that are being executed in the interrupted main program. These memory locations should be + backed up and restored at the end of the handler, further increasing its execution time... .. caution:: The Commander X16's sixteen 'virtual registers' R0-R15 *are not preserved* in the IRQ handler! (On any system!) So you should make sure that the handler routine does NOT use these registers, or do some sort of saving/restoring yourself - of the ones that you do need in the IRQ handler. Note that Prog8 itself may also use these registers, so be very careful. + of the ones that you do need in the IRQ handler. Prog8 itself may also use these registers, so be very careful. This is not a X16 specific thing; these registers also exist on the other compiler targets, and the same issue holds there. @@ -495,7 +494,7 @@ Commander X16 specific IRQ handling =================================== .. index:: single: Targets; Commander X16 IRQ Handling -Note that for the CommanderX16 the set_rasterirq() will disable VSYNC irqs and never call the system IRQ handler regardless +Be careful: for the CommanderX16, set_rasterirq() will disable VSYNC irqs and never call the system IRQ handler regardless of the return value of the user handler routine. This also means the default sys.wait() routine won't work anymore, when using this handler. @@ -529,7 +528,7 @@ Look at the examples/cx16/multi-irq-new.p8 example to see how these routines can Here they are, all available in ``cx16``: ``disable_irqs ()`` - Disables all Vera IRQ sources. Note that the CPU irq disable flag is not changed by this routine. + Disables all Vera IRQ sources. The CPU irq disable flag is not changed by this routine; you can manipulate that via ``sys.set_irqd()`` and ``sys.clear_irqd()`` as usual. ``enable_irq_handlers (bool disable_all_irq_sources)`` @@ -569,4 +568,4 @@ And a utility method to set the VIA1 timer1 to trigger IRQs at regular intervals Set VIA1 timer1 to trigger after the given delay (cycles). Enables VIA timer1 irqs if delay>0, otherwise disables it. If keeprunning then the timer keeps triggering, otherwise it stops after a single trigger. - Note that the speed of the timer depends on the clock speed of the X16 (controlled by a jumper on the motherboard, usually 8 MHz). + The speed of the timer depends on the clock speed of the X16 (controlled by a jumper on the motherboard, usually 8 MHz). diff --git a/docs/source/technical.rst b/docs/source/technical.rst index ef4cbd9b8..6f75f2757 100644 --- a/docs/source/technical.rst +++ b/docs/source/technical.rst @@ -103,7 +103,7 @@ What they are intended for Without these IDs, the bank selector would only know that *some* routine is being called, but it wouldn't know which one unless you had a separate manager routine for every single banked subroutine. -Note that the same banked subroutine called from multiple different places in your code will currently +The same banked subroutine called from multiple different places in your code will currently result in the *same* ID being passed to the banking routine, as the ID is unique per ``extsub`` declaration. Viewing assigned IDs @@ -167,7 +167,7 @@ The bank number is not translated into assembly (only as a comment):: On the C128, it means programs can use ~41 Kb of contiguous RAM at $1c00 to $c000 (exclusive). However, if your program uses floats, Prog8 *does* need the Basic ROM for the floating point routines, and it won't be banked out. Such programs are limited to the regular size of about 38 Kb on the C64, and less on the C128. - Be aware that the bank setting is only done if you are *not* using ``%option no_sysinit`` because the + Be aware: the bank setting is only done if you are *not* using ``%option no_sysinit`` because the program's bootstrap code is what initializes the memory bank configuration. @@ -245,7 +245,7 @@ Regular subroutines For *single* byte, word, and pointer arguments (not long or float), the values are simply loaded in cpu registers by the caller before calling the subroutine. *The subroutine itself will take care of putting the values into the parameter variables.* This saves on code size because otherwise all callers would have to store the values in those variables themselves. -Note that this convention is also still used for subroutines that specify parameters to be put into +This convention is also still used for subroutines that specify parameters to be put into one of the *virtual registers* R0-R15, as those are in the end just variables too (see :ref:`reusevirtualregs_params`) The rules are as follows: @@ -295,7 +295,7 @@ In case of *multiple* return values: These are kernal (ROM) routines or low-level assembly routines, that get their arguments via specific registers. Sometimes even via a processor status flag such as the Carry flag. -Note that word values can be put in a "CPU register pair" such as AY (meaning A+Y registers) but also +Word values can be put in a "CPU register pair" such as AY (meaning A+Y registers) but also in one of the 16 'virtual' 16 bit registers introduced by the Commander X16, R0-R15. Float values can be put in the FAC1 or FAC2 floating point 'registers'. The return values also get returned via designated registers, or via processor status flags again. @@ -336,7 +336,7 @@ Some notes and references into the compiler's source code modules: is more or less a machine code language for a virtual machine - and indeed this is what the built-in prog8 VM will execute if you use the 'virtual' compilation target and use ``-emu`` to launch the VM. (``intermediate`` and ``codeGenIntermediate`` modules, and ``virtualmachine`` module for the VM related stuff) - Note that this IR is still *targeted to one specific compilation target only*; various properties and all library + This IR is still *targeted to one specific compilation target only*; various properties and all library code for the selected target machine is encoded into the IR. It is *not possible* to eventually create a C64 program from an IR file created for the CommanderX16 target. diff --git a/docs/source/variables.rst b/docs/source/variables.rst index badd421f4..c88845cde 100644 --- a/docs/source/variables.rst +++ b/docs/source/variables.rst @@ -223,7 +223,7 @@ by the compiler everywhere you refer to the constant (and no memory is allocated for the constant itself). Only the simple numeric types (byte, word, long, float) and pointer types can be defined as a constant. If something is defined as a constant, very efficient code can usually be generated from it. Variables on the other hand can't be optimized as much, need memory, and more code to manipulate them. -Note that a subset of the library routines in the ``math``, ``strings`` and ``floats`` modules are recognised in +A subset of the library routines in the ``math``, ``strings`` and ``floats`` modules are recognised in compile time expressions. For example, the compiler knows what ``math.sin8u(12)`` is and replaces it with the computed result. The type of a constant can be omitted. When omitted, the type is inferred from the initializer expression: @@ -310,7 +310,7 @@ Integers (bytes, words, longs) Integers are 8, 16 or 32 bit numbers and can be written in normal decimal notation, in hexadecimal and in binary notation. There is no octal notation. Hexadecimal has the '$' prefix, -binary has the '%' prefix. Note that ``%`` is also the remainder operator so be careful: if you want to take the remainder +binary has the '%' prefix. ``%`` is also the remainder operator so be careful: if you want to take the remainder of something with an operand starting with 1 or 0, you'll have to add a space in between, otherwise the parser thinks you've typed an invalid binary number. @@ -390,8 +390,8 @@ Floating point support is available on the c64 and cx16 (and virtual) compiler t On the cbm-compatible systems, the rom routines are used for floating point operations, so on both systems the correct rom banks have to be banked in to make this work. Although the C128 shares the same floating point format, Prog8 currently doesn't support -using floating point on that system (because the c128 fp routines require the fp variables -to be in another ram bank than the program, which Prog8 doesn't support yet). +using floating point on that system. The c128 fp routines require the fp variables to be in +another ram bank than the program, which Prog8 doesn't support yet. Also your code needs to import the ``floats`` library to enable floating point support in the compiler, and to gain access to the floating point routines. @@ -415,9 +415,10 @@ Arrays ^^^^^^ .. index:: pair: Data Types; Arrays -Arrays can be created from a list of booleans, bytes, words, floats, addresses of other variables -(such as explicit address-of expressions, strings, or other array variables), pointers, struct initializers, +Arrays can be created from a list of booleans, bytes, words, floats, pointers, struct initializers, or ``memory()`` calls (either directly or via ``const`` variables holding ``memory()`` results). +Addresses of other variables are also accepted, such as explicit address-of expressions, strings, +or other array variables. The values in an array literal always have to be constants. A trailing comma is allowed, sometimes this is easier when copying values or when adding more stuff to the array later. Here are some examples of arrays:: @@ -439,10 +440,14 @@ or when adding more stuff to the array later. Here are some examples of arrays:: .. note:: On the 6502-family targets (c64, c128, cx16, pet32, virtual) the 8-bit index register limits an array to be - indexable by a single byte. This means byte/string arrays are limited to 256 elements (255 characters for a - string, because of the terminating null byte), word arrays to 256 elements as well (if split, which is the - default; when not split the maximum length is 128; see below for details about this distinction), and - float arrays to 51 elements. + indexable by a single byte. This means: + + - byte/string arrays are limited to 256 elements (255 characters for a string, because of the + terminating null byte) + - word arrays to 256 elements as well, if split (which is the default); when not split the maximum + length is 128. See below for details about this distinction. + - float arrays to 51 elements + On the m68k targets (amiga500, amiga1200, qemu68k) the total array size limit is **32768 bytes**. There the byte/bool array limit is 32768 elements, the word array limit is 16384 elements, and the long, float and string array limits are 8192 elements each (a ``str`` array holds 4-byte pointers on these targets). @@ -460,7 +465,7 @@ need this you have to write it out depending on the use case: you can copy the m ``sys.memcopy(sourcearray, targetarray, sizeof(targetarray))``. Or perhaps use ``sys.memset`` instead to set it all to the same value, or maybe even simply assign the individual elements. -Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.) +The various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.) can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte`` for instance. The same applies to the names of the builtin functions (such as ``memory``, ``mkword``, ``peek`` and ``poke``): they cannot be redefined as a variable, block, subroutine, struct or label. @@ -575,12 +580,12 @@ code that expects the words to be sequentially in memory (such as the cx16.FB_se you have to create a normal array variable first and then pass that to the subroutine. .. caution:: - Be aware that the default is to split word arrays. Normal array access is taken care of by Prog8, so you won't + The default is to split word arrays. Normal array access is taken care of by Prog8, so you won't notice this optimization. However if you are accessing the array's values using other ways (for example via a pointer, and then using ``peekw`` to get the value) you have to be aware of this. In that ``peekw`` example you have - to make sure to use ``@nosplit`` on the word array so that the words stay sequentially in memory which is what ``peekw`` needs. - Also be careful when passing arrays to library routines (this is via a pointer!): you have to make sure - the library routine can deal with the split array otherwise you have to use ``@nosplit`` as well. + to use ``@nosplit`` on the word array so that the words stay sequentially in memory which is what ``peekw`` needs. + Also be careful when passing arrays to library routines (this is via a pointer!): the library routine + has to be able to deal with the split array, otherwise you have to use ``@nosplit`` as well. .. _encodings: @@ -648,9 +653,9 @@ There are several escape sequences available to put special characters into your Characters like ^, _, \\, {, } and | (that have no direct PETSCII counterpart) are still accepted and converted to the closest PETSCII equivalents. (Make sure you save the source file in UTF-8 encoding if you use this.) Using the ``in`` operator you can easily check if a character is present in a string, -example: ``if '@' in email_address {....}`` (however this gives no clue about the location -in the string where the character is present, if you need that, use the ``strings.find()`` -library function instead) +example: ``if '@' in email_address {....}``. However this gives no clue about the location +in the string where the character is present; if you need that, use the ``strings.find()`` +library function instead. **Caution:** This checks *all* elements in the string with the length as it was initially declared. Even when a string was changed and is terminated early with a 0-byte early, @@ -774,7 +779,7 @@ without defining a memory-mapped location, you can do so by enclosing the addres @(vic+$20) = 6 ; you can also use expressions to 'calculate' the address You can actually also use the array indexing notation for this. It will be silently converted into -the direct memory access expression as explained above. Note that unlike regular arrays, +the direct memory access expression as explained above. Unlike regular arrays, the index is not limited to an ubyte value. You can use a larger integer type to index a pointer variable like this:: pointervar[999] = 0 ; set memory byte to zero at location pointervar + 999. diff --git a/intermediate/src/prog8/intermediate/IRCalls.kt b/intermediate/src/prog8/intermediate/IRCalls.kt index 4ec5a6f9f..6b30299de 100644 --- a/intermediate/src/prog8/intermediate/IRCalls.kt +++ b/intermediate/src/prog8/intermediate/IRCalls.kt @@ -43,7 +43,7 @@ sealed interface CallTarget { /** where an argument comes from, or where a result is delivered */ sealed interface CallLocation { - /** no explicit location: value simply lives in the given virtual register */ + /** no explicit location: value lives in the given virtual register */ data object Default : CallLocation /** the value is passed via the memory location of a subroutine parameter variable */ diff --git a/virtualmachine/test/TestVmOperandSchema.kt b/virtualmachine/test/TestVmOperandSchema.kt index df87914f5..263c13a34 100644 --- a/virtualmachine/test/TestVmOperandSchema.kt +++ b/virtualmachine/test/TestVmOperandSchema.kt @@ -13,8 +13,8 @@ import prog8.vm.VirtualMachine /** * Every VM instruction handler must read exactly the operands that the OpcodeSchema declares for * that (opcode, type) combination. This test builds a canonical instruction for every combination - * straight from the schema and executes it, so a handler that reaches for an operand slot the - * instruction doesn't have (or casts a memory reference to the wrong kind) fails here. + * straight from the schema and executes it. A handler that reaches for an operand slot the + * instruction doesn't have, or casts a memory reference to the wrong kind, fails here. */ class TestVmOperandSchema: FunSpec({ @@ -167,7 +167,7 @@ class TestVmOperandSchema: FunSpec({ val instruction = buildInstruction(opcode, type) try { runSingle(instruction) - } catch (ex: ProgramExitException) { + } catch (_: ProgramExitException) { // fine: return/exit style instruction ran to completion } catch (ex: NullPointerException) { operandProblems += "$instruction -> NPE (operand assumed present): ${ex.message}"