Now that you have typed the above code into a file, I shall explain what is happening. The code begins at the label 'start' and sets A1 to the address of the label 'exceptions' within the program. This is where the LEA instruction is useful - when writing position independant programs. These are programs that can be run at any address and are a requirement if you want to write good QDOS programs.
The 'exceptions' label identifies the start of the 19 long words of data that hold the addresses of the 19 redefined exception vectors as detailed above. At the moment the table contains random garbage and needs to be initialised BEFORE we tell QDOS to use the new vectors.
The address of the routine to handle address exceptions, 'x_address', is loaded into A2 - again using position independant methods, and then placed in the table at the first location. You will note that 'address register with post-increment' addressing is used here. This means that A1 is automatically incremented by the correct amount - 4 in the case of the long sized move - ready for the next vector to be loaded.
This process is repeated for the illegal, divide by zero, CHK, TRAPV, privilege violation, trace and interrupt level 7 vectors.
There are 11 vectors left in the table for TRAP #5 through to TRAP #15. Rather than give each of these a single handler, we point them all to the same one as we intend to ignore these instructions when they occur. To set these 11 vectors up, we run through a small loop which counts D0 down from 10 to -1 setting the vector for each of the 11 TRAP exceptions to be the single routine at address x_trap.
Our exceptions table has now been defined and all we have to do is tell QDOS that we want to use it. Once again, A1 is set to the start address of the exceptions table as required by QDOS, D1 is then set to -1 which implies 'the current job' to QDOS. This is used in many of the QDOS routines which require a job ID, passing -1 means 'me'. As we are executing this code directly from SuperBasic, that is what the current job will be. Once the vectors have been set up for any job, all other jobs created by it will use the same vector table.
This means that as the initiating job is SuperBasic, and as most other jobs are created by SuperBasic, this means that we have effectively created a protection mechanism for every job in the system created FROM THIS POINT ONWARDS ! If this is the first code loaded on your system, then every single job created will be protected by this code.
Trap #1 is called with D0 set to the value MT_TRAPV - a fancy way of saying 7 - and we return to SuperBasic with any error codes that may arise. As there appears to be only 'invalid job' returned, it is unlikely that there will be any as we are using the current job's own id.
Now that the initialisation has been carried out, the exception handlers will just sit there until such time as they are activated.
Most of the handler code is the same - we simply trap the exception, print a warning message to channel #0 and attempt to carry on - but the Address and illegal exception handlers do additional processing.
In the case of an address error, there is an extra 8 bytes of data on the stack on top of the 'standard' stack frame as discussed above. These need to be cleared off before we execute the RTE instruction.
this is only true of a QL with 128K or a Trump Card etc. If you use QXL or some other card with an upgraded processor, then the stack is different and this code won't work properly.
An Illegal instruction also manipulates the stack, but this time, it adds 2 to the address of the failed instruction. This prevents it from trying to execute it again when we exit the routine. Of course this may not always be successful and can cause further errors along the way - if the instruction was followed by a word of data for example. Trying to execute the data could lead to another exception and so on. What would you rather have, a message telling you about it or a lock up with no indications ?
The messages are defined in the standard QDOS manner of a size word followed by the bytes of the message. The appropriate message has its address loaded into A1 by the exception handler, and a brach is made to the sub-routine MESSAGE_0 which will attempt to display a message to channel #0. If this fails, it will try #1 before giving up.
If you have a QDOS manual and you look up UT_ERR0 (that's a zero by the way !) you will see that it takes an error code in D0 as its only parameter. We are using it slightly differently as we are defining our own messages and not using the Sinclair defined ones such as 'invalid channel id' or 'bad parameter' etc.In order to do this, we load D0 with the address of the message but set bit 31 of D0 so that QDOS knows that it is an address and not an error code.
The UT_ERR0 routine lives in the ROM somewhere, I don't know where it lives in all ROMs as it could have been moved between ROM releases. Because of this, there is a vector table in the ROM at a standard position. To get the address of the routine, we simply read the contents of the vector table into an address register and JSR to that address. (This will be explained later in the series when I cover QDOS).
So now that we have assembled the code all we do is LRESPR it (or RESPR(512), LBYTES and then CALL) and that is it. Whenever any exceptions occur, the above code will handle them and, most importantly, tell you what has happened. Your QL may still be hung - but at least you should know why !