Once we have determined the highest memory address available we can copy our loader code into high memory.
* * RELOCATE LOADER TO HIGH MEMORY * LA R2,LOADER ADDRESS OF ROUTINE TO RELOCATE LA R3,LOADERLN LENGTH OF ROUTINE TO RELOCATE LR R4,R9 HIGH MEM ADDRESS SR R4,R3 BACK UP LENGTH OF CODE S R4,M32K LEAVE A 32K BUFFER N R4,MASK START ON A CLEAN BOUNDRY LR R8,R4 SAVE START ADDRESS IN R8 * LA R1,256 MAX MVC LENGTH
We start by loading the address of our loader routine into Register 2 and the length of the routine into Register 3. We copy the highest memory address into Register 4 and then subtract the length of the code. Because we use the storage immediately following our loader code for the disk I/O buffer we subtract out another 32k leaving more then plenty of room. Finally we use a logical AND to start loading on an even boundary (we at least have to start on a double word boundary to avoid alignment errors). Finally save the beginning load address into Register 8 (so we can branch to it when relocation is completed) and we set Register 1 to 256 or the maximum length for a MVC instruction.
MCOPY DS 0H CR R3,R1 CHECK FOR MAX LEGNTH BL MCOPY010 NO - SHORT MOVE * MVC 0(256,R4),0(R2) MOVE 256 BYTES AR R2,R1 NEXT AREA AR R4,R1 TO MOVE SR R3,R1 ADJUST LENGTH B MCOPY LOOP BACK * * MCOPY010 DS 0H LTR R3,R3 CHECK FOR ANYTHING TO MOVE BZ MCOPY020 ALL DONE * BCTR R3,0 SUBTRACT ONE FOR EXECUTE EX R3,MVC COPY LAST OF DATA * MCOPY020 DS 0H LR R15,R8 ADDRESS OF RELOCATED LOADER BR R15 BRANCH TO IT
The copy routine is pretty straight forward. First we check to see if we have at least 256 bytes left to copy. If not we branch to MCOPY010 to handle a short move. We then copy 256 bytes of data, increment our source and target pointers, and then decrement the remaining length.
When we finally have less than 256 bytes to copy we must first check that there is at least one byte to move. We then subtract one from the length and use an Executed Move Characters to copy the remainder of the data.
Finally we branch to the routine we just moved.
* M32K DC A(1024*32) * CONSTANT 32K MASK DC A(X'00FFF000') * BOUNDARY MASK MVC MVC 0(1,R4),0(R2) * EXECUTED INSTRUCTION
Finally we define our constants.
It is important to point out that we don’t do any relocation of pointers for the routine we copy to high memory. It is important that when we write the code for our loader we avoid using any relocatable constants. For example if we used something like:
PTR DC A(BUFFER)
we would generate a relocatable constant. Once we copied the routine to high memory the address pointer would still refer to the original location in low memory. Instead we need to calculate the pointer address at run time and instead use something like:
LA R2,BUFFER ST R2,PTR PTR DC A(0)
This will give us the proper value in our pointer.
The Assembler output will help us determine if we have used any relocatable addresses in our program. Near the end of the assembly listing we find the Relocation Dictionary. It provides us with a list of all the relocatable symbols in the program and the address where it is located. If we find any relocatable symbols that are contained within our code to be relocated we can then go back to the source and correct the problem.
RELOCATION DICTIONARY POS.ID REL.ID FLAGS ADDRESS 0001 0001 08 000005 0001 0001 08 000D81 0001 0001 08 000D89 0001 0001 08 000D91 0001 0001 08 000D99
[Next – Nucleus Load Module Loader]