Programming with Ophis | ||
---|---|---|
<<< Previous | Structured Programming | Next >>> |
All programming languages are designed around the concept of procedures.[1] Procedures let you break a computation up into different parts, then use them independently. However, compilers do a lot of work for you behind the scenes to let you think this. Consider the following assembler code. How many times does the loop execute?
loop: ldx #$10 jsr do'stuff dex bne loop |
The correct answer is "I don't know, but it should be 16." The reason we don't know is because we're assuming here that the do'stuff routine doesn't change the value of the X register. If it does, than all sorts of chaos could result. For major routines that aren't called often but are called in places where the register state is important, you should store the old registers on the stack with code like this:
do'stuff: pha txa pha tya pha ;; Rest of do'stuff goes here pla tay pla tax pla rts |
(Remember, the last item pushed onto the stack is the first one pulled off, so you have to restore them in reverse order.) That's three more bytes on the stack, so you don't want to do this if you don't absolutely have to. If do'stuff actually doesn't touch X, there's no need to save and restore the value. This technique is called callee-save.
The reverse technique is called caller-save and pushes important registers onto the stack before the routine is called, then restores them afterwards. Each technique has its advantages and disadvantages. The best way to handle it in your own code is to mark at the top of each routine which registers need to be saved by the caller. (It's also useful to note things like how it takes arguments and how it returns values.)
[1] | Yes, all of them. Functional languages just let you do more things with them, logic programming has implicit calls to query procedures, and object-oriented "methods" are just normal procedures that take one extra argument in secret. |
<<< Previous | Home | Next >>> |
The stack | Up | Variables |