I'm assuming at present that most people who take an interest in 680x0 coding do not need to be told about the usefulness of adopting understandable variable names and so on. It seemed wisest in fact to concentrate on two areas where my coding arrangements, particularly with larger projects, do tend to differ from what might be considered the norm...
When a number of libraries have to be opened I've found it worthwhile to code the opening and closing operations using loops. The approach that I use most often works like this: Pointers to the start of the library names (ie the first name) and the first library base are loaded into address registers (a2 and a3 in the following example fragments) whilst a data register (d3 is being used here) is loaded with a count one less than the number of libraries to be opened (because the automated dbeq instruction counts down to -1 if the loop goes to completion). Here are some typical setting up operations...
lea lib_names,a2
lea lib_base_start,a3
move.w #LIBRARY_COUNT-1,d3 loop counter
and here's the loop code that would open the libraries...
openloop move.l (a2)+,a1 library name pointer
moveq #0,d0 any version will do
CALLSYS OpenLibrary,_SysBase
move.l d0,(a3)+ store returned base
dbeq d3,openloop
Irrespective of the number of libraries being opened the loop terminates either with d0 holding the last valid open library pointer and d3 holding -1 or, if an OpenLibrary() call failed, with d0 holding 0 and d3 holding a loop count value. The important point with all this, which you'll see if you trace through the loop code of the examples that you'll find from time to time in my published code, is that as soon as a library open error occurs the loop quits with register (a3) pointing to the base of the library that failed to open!
To close any previously successfully opened libraries then all I need to do is use a backward reading loop to collect the valid library pointers already stored in the library base variables. The library closing loop, incidentally, is usually written as a subroutine. This is because the code can normally be called under two different situations - when the program has run without error and all libraries need to be closed, or when there has been a library opening error and fewer libraries need to be closed. By testing the zero flag at the end of the library opening loop we can tell whether an error occurred and so a conditional beq instruction allows us to select either a normal or an error pathway like this...
beq error_exit
;other program code goes here!
normal_exit lea lib_base_end,a3
moveq #LIBRARY_COUNT,d2 library count
jsr CloseLibs close libraries
moveq #0,d0 clear d0 for O/S
movem.l (sp)+,d3/a2-a3/a5 restore registers
rts and quit
error_exit moveq #LIBRARY_COUNT-1,d2
sub d3,d2
jsr CloseLibs close libraries
moveq #0,d0 clear d0 for O/S
movem.l (sp)+,d3/a2-a3/a5 restore registers
rts and quit
Chances are that these ideas are best examined in conjunction with any of the 680x0 projects of mine that have used this approach!
In any large program many operations need to be carried out and particularly with system resource allocation various system calls may fail. My 680x0 approach is similar to the method that I use in my C programs. It's called 'dynamic resource allocation' and the idea is to code all allocation and opening operations as separate subroutines that try to carry out their intended job and then either fail and return an error code, or succeed and push the address of a corresponding deallocation routine onto a function pointer stack.
Under normal (non-error) termination conditions program closedown occurs by reading any existing pointers from the function pointer stack and executing the corresponding deallocation routines. If during any of the allocation attempts an error occurs the same stack emptying routine gets called but, only those pointers that have been placed onto the stack prior to the occurrence of the error will be retrieved and executed. Hence, one deallocation loop handles both error and non- error condition closedown perfectly safely.
Now this may seem like an unnecessarily complicated way of doing things but believe me it has not been used to make life difficult for you. Far from it - because this technique makes systematic program closedown a piece of cake. Just look how easy the deallocation loop is to code...
closedown move.l (a5)+,d0 retrieve pointer
beq.s lib_normal_exit
move.l d0,a0
jsr (a0) execute routine
bra.s closedown
Just five instructions are needed. We pull the function pointer off the stack and providing that it is non-zero, execute the function. Do note incidentally that a data register (and I've arbitrarily chosen d0) has been used within the loop and this is primarily to ensure that the move instruction affects the processor's status flags. We could not use the 680x0's move instruction to load an address register directly anyway - but we could have used a movea instruction instead and moved the contents of the longword pointed to by register a5 directly into the a0 register. This however would NOT affect the processor flags and so it would still be necessary to test a0 both before the beq instruction and before performing the indirect subroutine call.
You'll notice incidentally when you examine the magazine/book examples which use this approach that I set up a null address right at the top of the function pointer stack. This is specifically to allow a zero address test to be used to indicate when all items have been removed from the stack and used.
There are a number of benefits with this stack based approach but the driving force behind its development was that the deallocation/closedown routines automatically get executed in the reverse order to that used in the initial allocation/opening stages of the program. This provides a nice safe way of deallocating things and it avoids having to use loads of 'spaghetti code' jumps and branches to provide error pathways when operations fail. In addition to this it not only becomes possible to work on individual resource allocation/deallocation operations in relative isolation but, since such routines tend to take on a standardised format, code becomes less prone to the silly slips we all seem to make at times.
| GO BACK TO THE NEXT HIGHEST MENU LEVEL! | GO DIRECTLY TO THE INITIAL MENU! |