Skip to content
Max Langhof edited this page Nov 11, 2016 · 22 revisions

Notepad++ syntax highlighting

We made a Notepad++ language definition that you can use!
asmLang.xml
It looks like this (indentation not included).

All instructions are bold and color-coded:

  • Blue means the stack grows.
  • Orange means the stack shrinks with this instruction.
  • Black means the amount of elements on the stack doesn't change.
  • Red (io) means "stack impact depends on the context".

It also recognizes the IO constants and valid numeric constants.

Tips and tricks

General

  • Needless to say, favor simple solutions and algorithms. Your program can only have < 256 instructions!

  • Find a way to keep your program organized. Since you only have jumps (i.e. the infamous gotos) to move around in the program, you can quickly end up with spaghetti code. The amount of labels (and comments) you can create is not limited, so use them to your advantage and keep them as descriptive as possible!

  • It may be worth it to allow your robot to do some dumb or imprecise thing (as long as it doesn't tend to get itself killed that way) by omitting edge case handling in favor of other, more substantial logic.

Coding

  • "Variables" (or "registers", depending on how you look at it) can be created by just labeling a part of the memory: variableName: db8 #0 or floatVariable: dbf 0.0 (or whatever initial values you would like). You can create as many as you like, but be aware that they (especially float ones) do take up your memory. Also:

  • Since all functions and operations only interact with the stack, you shouldn't primarily rely on variables for your biddings. Compare these:

; Computes the sum of two bytes on the stack
sumFromStack:
 add8           ; Yep, that's it! One byte of instruction(s).

a: db8 #0
b: db8 #0
; Computes the sum of two variables and stores it in the first
sumFromVariables:
 push8 a        ; 2 bytes
 push8 b        ; 2 bytes
 add8           ; 1 byte
 pop8 a         ; 2 bytes
; This took 7 bytes in total!

Only use variables when it's necessary. The stack is your friend.

  • It can be a powerful technique to use indirect jumps and label addresses as "function pointers" of sorts:
howToReact: db8 #0

...

eventReaction1:
<code>

eventReaction2:
<code>

... ; Determine which reaction you want to happen
push8 &eventReaction1
pop8 howToReact

... ; do stuff
jmp [howToReact]
  • You will probably branch and jump around a lot to create your desired program flow and decision making. Be very aware of what the stack looks like at every jump! If you jump back to your main loop/program start but left something on the stack, then your stack will slowly (or not so slowly) grow over time, until it spills into your program code and breaks things. Worse, if you pop too many things off the stack, your stack will "underflow" into the start of your program and quickly turn your entire memory into a mess. Finding the place where you went wrong can be annoying, so it's best to find a way to keep track of what you expect the stack to look like around every jump (or instruction). The Notepad++ language highlighting may help with this.

Saving program space:

  • Once you start running low on program space, consider using relative instructions where possible: push8r, pop8r, jnzr. These take only 1 byte instead of 2, but they only work with addresses within 31 bits of themselves (in both directions).

    • For the sake of saving space, it may be useful to rearrange your code in such a way that (byte) variables are close to their usage (so you can push8r/pop8r from/to them) and that branching jumps target nearby addresses (jnzr). This may potentially make the code a lot harder to understand though, since the program flow may be harder to recognize.
    • On this note, your loops are a great place to use jnzr.
    • A byte constant takes just as much space when used directly (push8 #VALUE) as if it's extracted into a (nearby) labelled memory cell (CONSTANT_LABEL: db8 #VALUE) and accessed via push8r CONSTANT_LABEL. The latter both eliminates a "magic number" and allows reusing the value. Remember, you can give the value as many names as you want (e.g. b00001100: TWELVE: MAX_STEPS: db8 #12)! While you have to keep them close to their usage, you're never losing anything compared to the "direct usage" way.
    • Creating a new byte variable (1 byte) near its usage with relative commands (1 byte per instruction) is likely cheaper than using one that is further away and needs absolute addressing (2 bytes per instruction).
    • If you want to push a float constant that is not predefined (i.e. one of the c_f functions) and can be approximated by a byte, using push8 with the closest byte and then b2f needs only 3 bytes of code instead of 5, it does take two instructions instead of one though.
    • Also consider working in bytes where possible instead of floats to save variable size and stack space.

Clone this wiki locally