aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md88
1 files changed, 17 insertions, 71 deletions
diff --git a/README.md b/README.md
index 5aff6a7..9603775 100644
--- a/README.md
+++ b/README.md
@@ -367,19 +367,19 @@ Since you need to be as optimal as possible, sometimes using just registers will
suffice, while other times using the memory might be needed. In any case:
1. Be consistent on your calling convention.
-2. **Documentation is important**. Don't be afraid to
- clearly denote which registers or memory addresses might be touched after
- calling this function
+2. **Documentation is important**. Don't be afraid to clearly denote which
+ registers or memory addresses might be touched after calling a given function
3. Do not be afraid to hint on the function's name how you can call it. This is
better explained in the following sections.
-## Registers are to be saved by the caller
+## Don't expect resources to be preserved across calls
-As a general rule, as a caller don't expect values on registers to survive to a
-function call. If there is important information to be kept or updated across
-calls, make sure to shadow it on memory addresses which are guaranteed to not be
-touched (on that check on the function's documentation!). Another option is to
-use the stack, but do so with caution as stack overflows are a real danger here.
+As a general rule, as a caller don't expect values on registers or memory
+regions to survive to a function call. If there is important information to be
+kept or updated across calls, make sure to shadow it on memory addresses which
+are guaranteed to not be touched (on that check on the function's
+documentation!). Another option is to use the stack, but do so with caution as
+stack overflows are a real danger here.
## Reserve return values and arguments into zeropage
@@ -390,60 +390,14 @@ example, one might reserve `$00-$04` to variables named like `zp_arg0` to
1. It's up to the caller to save values from these addresses if they are to be
preserved across calls. The called function might change these values during its
execution.
-2. The called function will save to `zp_arg0` the returned value, and also to
- `zp_arg1` if the returned value is 16 bit (little endian).
-3. From 1. and 2.; you can infer that if you are passing things through
- `zp_argX`, everything is done in memory: you cannot mix registers and memory
- arguments.
-
-As for the parameters being accepted, one might follow the same convention as
-it's done in places like the Linux kernel, in which equivalent C signatures are
-given as documentation for an assembly function. Moreover, it could also be a
-good idea to embed this information into the function's name. This can come at
-the cost of a darn ugly name, but at least there won't be any misunderstandings
-when using this function. So all in all, you could end up with something like:
+2. The called function can save to `zp_arg0` the returned value, and also to
+ `zp_arg1` if the returned value is 16 bit (little endian). In any case, and
+ as always, read the documentation for it.
-``` assembly
-;; Get a thing given the passed argument. It expects the value on memory and it
-;; will return a byte. You can assume this function to have the equivalent C
-;; signature:
-;;
-;; uint8_t get_thing_from_arg(uint8_t value)
-;;
-;; NOTE: `a` register is modified.
-.proc get_thing_from_arg
- lda zp_arg0
- ;; Do something with it
-
- sta zp_arg0
- rts
-.endproc
-```
-
-## Register-only strategies
-
-Sometimes just passing a value on the register is enough. Consider the following
-patterns.
+## Using the `x` or `y` registers for code that has to "select"
-### Using the `a` register as both input and output
-
-``` assembly
-lda #m_value
-jsr compute_next_value
-clc
-adc #m_base
-```
-
-Here `compute_next_value` expects only one argument, to be set in the `a`
-register. The result is also left in `a`. This is useful on pure functions that
-will not overwrite on other registers or on many memory addresses. That being
-said, the function should always state if it's touching other registers or other
-memory addresses. Moreover, in assembly the function signature is tightly
-coupled to how it will be called. Hence, consider how code elsewhere is calling
-this function as pulling data into the `a` register just to call a function can
-defeat the purpose of this optimization.
-
-### Using the `x` or `y` registers for code that has to "select"
+One handy convention is to use the `x` or `y` registers as selectors. Refer to
+something like this:
``` assembly
ldx #0
@@ -458,16 +412,8 @@ for example, as a way to index the bank to be selected. This can be further
documented by adding a suffix into the function's name, as it's done in the
above example.
-## Memory arguments first, optimize with registers later
-
-In a similar fashion than the principle of "don't optimize early": prefer the
-calling convention through memory addresses first if you are not sure how a
-given function is going to be used in the end. You can later optimize to the
-register-only strategies if you are sure that you can make this optimization.
-This allows for the flexibility of memory addresses at first without the
-possible gotchas from register-only calls. Moreover, if you anticipate that a
-given function will be a core library one, it will probably be a good idea to
-stick with memory arguments, as they fit a more generic approach.
+In this example, adding a suffix like `_x` is already telling the caller that
+the `x` register is involved on this scenario.
# Flow of control