"Hello!" to whoever wrote all this stuff!
A section of code in the bootrom uses another scheme. I call it "ODD" in my bootrom annotation, just because I need something to call it by. The idea is to place the return address into the fp register, and then use a jmp instruction to return. Here is a typical example that calls a printf routine of this flavor:
fefe73f4: 49fa 037e lea %pc@(0xfefe7774),%a4 fefe73f8: 4dfa 0008 lea %pc@(0xfefe7402),%fp fefe73fc: 60ff 0000 2fd6 bral 0xfefea3d4 fefe7402: 6000 0002 braw 0xfefe7406The first instruction loads the address of the string we want "printf" to print into the a4 register. The second loads the address that we want to return to into the fp register. Both of these use PC relative addressing to generate those addresses. This is neither here nor there really, and in the first case would be done that way even with the usual "jsr" style calls. Then we do a branch to the routine we want to call. Note that the address placed into fp is the address of the instruction we want to return to.
Why take these pains? The reason is to avoid using the stack. This code is used during the testing of RAM memory, and the bootrom wants to test all of it, and that would conflict with use of some of it for the stack.
Note also that these scheme does not allow subroutines to be recursive. In fact it requires all subroutines to be leaf routines, and we cannot call other subroutines from a subroutine. The strings used by this section of code always have "\r\n" in them. The normal printf only will have "\n" and uses a recursion to expand it to "\r\n" as is often done.
The return from subroutines coded to use this scheme look like:
fefe9628: 4ed6 jmp %fp@A jump to the address saved in the "fp" register.
fefe9c22: 46fc 3700 movew #0x3700,%sr fefe9c26: 2e4e moveal %fp,%sp fefe9c28: 46fc 2700 movew #0x2700,%sr fefe9c2c: 4ed6 jmp %fp@The "sr" register is the "status register" and contains a bit (along with many others) to control whether the processor is in master or interrupt mode. The bit in question is 0x1000. This routine first sets this bit, putting the processor into master mode. Then it copies the value in fp into sp (which is now the master mode stack pointer). Then it clears the M/I bit putting the processor back into interrupt mode. Apparently the bootrom normally runs in interrupt mode.
The value in fp that gets thus saved is the return address for this call. Why would it be worthwhile to save this for some kind of future use? It ends up getting used like this:
fefe9eee: 46fc 3700 movew #0x3700,%sr ; select master state fefe9ef2: 204f moveal %sp,%a0 fefe9ef4: 46fc 2700 movew #0x2700,%sr ; select interrupt state fefe9ef8: 003c 0001 orib #1,%ccr fefe9efc: 4ed0 jmp %a0@ ; return to stashed value
The address is indeed used as a sort of "longjmp" from code that I don't yet fully understand.
A quick side note. I have sometimes wondered about the convention used by my disassembler of placing a "%" in front of all register names. It does make it very handy when I do searches with my editor. If I search for "sr", I find a variety of things I don't care about. Searching for "%sr" gives me exactly what I want.
volatile in count = 100000; while ( count-- ) ;We find something like this in the bootrom code:
fefe29b4: 2e3c 0000 0640 movel #1600,%d7 ; loop ; fefe29ba: 2239 feff 5490 movel 0xfeff5490,%d1 ; fefe29c0: e2a7 asrl %d1,%d7 fefe29c2: 5387 subql #1,%d7 ; delay loop fefe29c4: 4a87 tstl %d7 fefe29c6: 6efa bgts 0xfefe29c2Ignore the two instructions I have commented out -- these fetch a constant and shift the delay count by the value fetched. This looks like code compiled from C and is a straightforward translation. The loop subtacts, tests, and conditionally branches.
The following code can be written by the knowledgable m68k assembly language programmer.
fefe9ff2: 223c 0000 0180 movel #384,%d1 ; delay fefe9ff8: 51c9 fffe dbf %d1,0xfefe9ff8Now this is a delay loop, and the object is to waste time, so you can't argue that this somehow has merit for being more efficient. It is better in terms of how much code space it uses (the loop body has 2 words, and the above has 3). The big advantage is in "coolness" if you will pardon me saying so.
The "dbf" instruction is "decrement and branch".
It decrements the d1 register until it goes negative and then quits branching.
It can also terminate on a condition.
The condition here is "f" (false), so it never terminates on the condition.
(It would terminate if the condition was true, but it is always false).
A cute instruction, and we like it.