Sega CD Development

Home Sega CD SLO 32X Transfer ConvSCD

Part V: Subroutines, Exceptions and the Stack

Once your done with this one, you'll have all the basics of 68k assembly. There's still a lot more to learn, but for that you really need to write some code yourself and get cozy with the M68K manual. So this will probably be the last 68K tutorial for a while; however, do not fear. I will be writing some Sega CD programming tutorials soon so you can get some real world experience. Now, on to the stack.

A stack is a data structure, that means that it's made of a bunch of different values in RAM instead of just one like a byte or word variable. It's very appropriately named since it can be easily likened to a stack of books. With books, you can take one off the top of the stack, and you can put one back on, but you can't easily take a book out from the middle. A stack in the 68K works in much the same way. You can put a pice of data on top of the stack and you can get the piece of data that's on the top of the stack, but getting data from the middle of the stack is a bit trickier.

Typically, a stack occupies a number of sequential address in RAM, and on the 68K most stacks build down. This means that the first item put on the stack has the highest address, the next piece of data would have the next lowest address. So for example if the top address in the stack is $FFFFFE, and that location stores a word, if you were to put another value on the stack, it would be stored at $FFFFC. The next at $FFFFFA, and so on. If you put the numbers 1, 2, and 3 on the stack in that order, it would look like this:

$FFFFFE: 1
$FFFFFC: 2
$FFFFFA: 3

If you were to then pull the numbers back off the stack, you'd get 3, 2 and 1 in that order. For those of you who were paying attention, you would have noticed that the numbers come off the stack in the reverse order that they came. This reveals one of the most basic uses of a stack, to reverse the order of a set of values. However, this is not The most useful task a stack can accomplish. Another use is found in subroutines.

Subroutines are like small programs that can be called again and again by the main program (or by other subroutines). They're incredibly useful because they allow you to write the code for a certain task once,and then call it whenver you need it. This also allows you to make your program smaller since you only need one copy of the code, instead of having one for each time it would be used.

Subroutines work in a similar manner to branches. There are two mnemonics used to call subroutines, bsr and jsr. Like bra, bsr can only use PC relative addressing and like jmp, jsr can use just about any mode except relative addressing. So the following would all be valid, assuming that "MySubRoutine" was a valid label.

bsr MySubRoutine ;jump to the subroutine at MySubRoutine using relative addressing
jsr MySubRoutine ;do the same using absolute addressing
lea MySubRoutine(PC), a0
jsr (a0) ;jump to subroutine using pointer

There is one distinct difference between bsr and jsr, and their branch counterparts. bsr and jsr save the current value of PC so you can easily return to the point you called the function from. This is done with the rts instruction. Here's an example.

bsr MySubRoutine ;branch to MySubRoutine
moveq #0, d0 ;execution resumes here as soon as the subroutine is done

MySubRoutine:
moveq #3, d1
rts ;Go back to where the subroutine was called from

This allows you to use a subroutine multiple places within your program without having to duplicate the code. For instance, look at the following code:

bsr MySubRoutine
... ;do stuff
bsr MySubRoutine
... ;do more stuff
bsr MySubRoutine

This would cause the subroutine to be executed three times with some extra stuff in the middle. You may be wondering what this has to do with stacks. PC is saved onto something called the system stack. You may remember that register a7 is a special address register. It's the "system stack pointer." The system stack, is a stack like the one I described above. What makes it special is that it's used to store the contents of PC when a subroutine call is made. So when such a call is made, the following happens:

a7 is decremented by 4 to make room on the stack, so if a7 was equal to FFFF04 it would then equal FFFF00
PC is put into the memory location pointed to by a7, so in our example PC would be stored at FFFF00
jump to the subroutine

And when the corresponding rts is executed the following happens:

PC is read from the memory location pointed to by a7, in our example this location is FFFF00
a7 is incremented so it points to it's old location
processor jumps to the original location

Now for exceptions. Exceptions on the 68K occur for a variety of reasons. There's one that occurs at startup, there are others that occur when there are errors in processing (illegal instructions, bus errors, etc.), and then there are external interrupts. The latter is incredibly useful for integrating with hardware. Without interrupts, you have to keep reading from a piece of hardware waiting for an event to happen. The time used for this could have been used for some other processing task, but instead you're forced to waste your time waiting for the hardware. Interrupts allow the hardware to interrupt the processor from what it's doing so it can handle hardware access.

This works in a similar way to subroutine calls, PC is pushed onto the stack and the processor jumps to another location in memory. When the interrupt routine is done, it returns to where it left off. Of course, since you don't explicitly call a interrupt handler, the processor obviously can't get the location to jump to in the normal matter. This is where the vector table comes in. The vector table is just a bunch of numbers at the very beginning of the 68K's memory space. It spans from address 0 to address 99h. Each entry contains an address of an exception handler. The section for interrupts starts at 64h with the address of the level 1 interrupt handler. 68h holds the handler for the level 2 interrupt handler and so on. On the Genesis the only interrupts used are 2, 4, and 6 with their entries in the vector table located at 68h, 70h, and 78h respectively.

There is another difference between bsr/jsr and exceptions and that is what's stored on the stack. bsr/jsr just store PC, but when an exception handler is called another piece of information is also stored on the stack and that's the processor flags. I'm not going to explain why this is important, but just keep in mind that it happens and that you have to use a different instruction to return from an exception handler than from a subroutine. That instruction is rte. It pops both the flags and the PC value of the stack when executed.

Well that raps up Part V. I don't think I did a terribly good job with it, so feel free to ask questions if something isn't clear. My first Sega CD tutorial should be done soon. It will cover the basics of how to use the assembler and how to get a simple SLO program up and running.


Part IV Sega CD Home