; Program: TYP4LDR.Z80 ; Authors: Joe Wright, Bridger Mitchell, Jay Sage ; Date: June 18, 1988 ; Version: 1.0 vers equ 11 ; August 11, 1988, Bruce Morgen. ; Saved a byte in the file size ; calculation and another in ; the relocator module. ;vers equ 10 ; 20 Jun 88 jww ; Checks file size against memory allocation earlier ; and therefore eliminates two calls to ADJADDR. ; Shorten ADJADDR routine. ; Place record count in DE before LOAD: no equ 0 yes equ not no prl equ yes ; If not PRL, DRI SPR is assumed align equ no ; If yes, force page alignment for load test equ no ; If yes, no .phase/.dephase tbuff equ 80h ; Always executes from the default buffer ; This file contains the overlay loader code that is placed into the page-0 ; header of a PRL or SPR program file to make a type-4 Z-System program that ; will be loaded automatically by the ZCPR34 or Z3PLUS command processor to ; the highest possible address in the TPA. SPR and PRL files are much the ; same except that the code segment in PRL files is relative to 0100h while ; in SPR files it is relative to 0000h. Assuming that the program has been ; assembled and then linked to PRL by either DRI's LINK or SLR's SLRNK+, the ; type-4 program would be created using Ron Fowler's MLOAD (version 2.1 or ; later) with the command: ; ; MLOAD [.COM]=.PRL,TYP4LDR[.HEX] ; ; where is the name of the program and the items in square brackets ; are optional. ; The header in a PRL or SPR file occupies one page or two records. TYP4LDR ; comprises two pieces of code, one in the first record of the file and one ; in the second record. These two pieces of code are executed by the ; command processor from the default buffer at 80h. ; The command processor first calls the routine in record 0. This code ; calculates where the actual program code beginning with record 2 should be ; loaded and reports the result back to the command processor. The command ; processor then loads the second routine in record 1 into the default ; buffer at 80h and the actual program code beginning with record 2 ; (including the bitmap) to the calculated load address. The command ; processor then returns to the second TYP4LDR routine, which performs the ; relocation of the program code and then returns to the CCP. ;-------------------------------------------------- ; PRTVAL macro(s) to print text and value during assembly prtval2 macro m1,v1,m2 ; \ .printx m1 v1 m2 ; +- this is the print value macro endm ; / prtval macro r,msg1,val,msg2 ; \ .radix r ; passing the radix value prtval2 ,%val, ; requires the uses of 2 macros endm ; / ;============================================================================= ; Record 0 Routine -- Load Address Calculator ; Record 0 of the loader code begins with a Z-System header that identifies ; the program to the command processor as a type-4 program. The first byte ; contains a RST 0 instruction that will cause a warmboot if an attempt is ; made to execute the program with a command processor that is not ; compatible with ZCPR34 or later. The next two bytes would normally ; contain the entry address to the program code. In the PRL and SPR file ; format, they contain the length of the program. This value is used to ; locate the bitmap and to compute the size of the program. ; ZCPR34 calls the code in this record at location TBUFF+9. It is called ; from a point inside the command processor's MLOAD3 routine where the load ; address is being computed. On entry, the registers contain the following ; information: ; HL = Z3ENV, environment descriptor address ; DE = ENTRY, address of beginning of CCP ; BC = PROGSIZE, program size from program header in record 2 ; A = FULLGET flag ; The value in HL is not used here but could be used, for example, by a ; different loader designed to load a routine into the RCP module. The ; Z3ENV address would be needed to determine the address and size of the RCP ; buffer. ; The value in DE is used to determine the address of the top of the TPA. ; The value reported in BC is extracted by the CCP from the load-address ; word of the type-3 header in the actual program code. This will normally ; be 0100h for a PRL file or 0000h for an SPR file. However, if the program ; uses dynamically allocated buffer space after the end of its own code, ; then the type-4 program must be loaded to a correspondingly lower address. ; In that case, the value at offset 11 (0Bh) in record 2 should be patched ; to contain the address of the end of the required buffer space relative to ; the nominal load address (100h for PRL, 000h for SPR). The value thus ; includes the code segment, data segment, and the dynamic buffer space, and ; the nominal load address. ; The value in A is the FULLGET flag that tells whether or not the command ; processor supports the fullget option. If it does not, the CCP always ; performs a test to make sure that code is never loaded to an address above ; the beginning of the CCP. Because it does not know in advance how big a ; file is, it actually has to protect one extra page below the CCP. If the ; CCP supports the fullget option, this checking is disabled. Since during ; type-4 loading we know the size of the code, we can load it one page ; higher in memory. ; When the loader routine here is called, the top of the stack contains the ; return address to the point in MLOAD3 from which the loader was called. org 100h ; ORG HEX file at 100h if not test .phase tbuff ; Program actually runs at 80h endif rec0: rst 0 ; Only a ZCPR34-compatible CCP ; ..can execute this file length: ds 2 ; Length of the code module (SPR or PRL) db 'Z3ENV' db 4 ; A Type 4 program start: ex (sp),hl ; Get return address from stack ; ..and put Z3ENV on the stack ld (load+1),hl ; Set in-line jump to the return address ld hl,(length) ; Length of this PRL/SPR code push hl ; Save on stack for sector 1 routine ;(+2) push de ; Save CCP address ;(+4) or a ; Test FULLGET push af ; Save result ;(+6) if prl ; If PRL file, we have to adjust PROGSIZE dec b ; ..for the 100h address offset endif ; We now calculate the size of the file to be loaded by the CCP. It will be ; the length of the program plus the length of the bitmap (1/8 the code size) ; adjusted to an integer number of records. ex de,hl ; PRL length into DE ld hl,7 ; Make sure we count any fractional byte add hl,de ld a,3 ; Divisor (2^3=8) div: srl h ; 0 to H7, H0 to carry rr l ; Carry to L7 dec a jr nz,div ; Divide by 8 add hl,de ; PRL/8 + PRL to HL ld de,127 add hl,de ld a,128 and l ld l,a ; If fullget is false, the CCP file loader will perform tests to prevent the ; load from overwriting either the CCP or an RSX. Because the CCP cannot ; detect the end of a file until an attempt to read the next record fails, ; no attempted read is allowed in the top page of memory. To account for this, ; the effective file size is taken to be 101h bytes longer than the actual ; size. pop af ; Get fullget status back ;(+4) pop de ; CCP address (+2) push hl ; Save file size (+4) push de ; Save CCP address (+6) push af ; Save fullget again ;(+8) jr nz,noadj1 ; If fullget is true, no adjustment needed ld de,101h add hl,de noadj1: ex de,hl ; Load size to DE ; Place the larger of Load size and Memory size in DE ld a,e sub c ld a,d sbc b jr nc,noadj2 ; DE is larger ld d,b ld e,c ; Calculate possible load address assuming RSX defines top of TPA and DE ; defines memory required. If fullget is false, a further adjustment ; is required because the CCP file load checking code uses the RSX lower ; page boundary as the upper address. noadj2: ld hl,(6) ; DOS/RSX pointer to HL pop af ; We need fullget test again ;(+6) jr nz,noadj3 ld l,0 ; If fullget false, use beginning of page noadj3: push de ; Save memory size ;(+8) call adjaddr ; Calculate address with DOS/RSX ; Now calculate possible load address assuming CCP defines top of TPA pop de ; Memory size into DE ;(+6) pop hl ; CCP address ;(+4) call adjaddr ; Calculate address and keep lowest ; Get ready to return to CCP loadaddr equ $+1 ld de,-1 ; Get final value of load address if align ld e,a ; Force page alignment (A=0) endif pop hl ; Get file size ;(+2) push de ; Pass load address to record-1 routine ;(+4) push de ; Hold a copy on the stack for now ;(+6) add hl,hl ld e,h ld d,a ; A remains zero from last call to ADJADDR: ld hl,tbuff ; Set for for CCP to call second record ex (sp),hl ; Load address to HL, tbuff to top of stack if test rst 38h ; DDT breakpoint endif load: jp 0 ; Return to CCP (hot-patched above) ; Command processor loads record 1 to tbuff ; ..and records 2+ to load address, then ; ..'returns' to tbuff ;-------------------------------------------------- ; This subroutine takes the address of the top of TPA in HL and the amount of ; memory required in DE and computes the proper load address. It then compares ; it to the value stored at LOADADDR and replaces the value there if the new ; value is lower. adjaddr: xor a ; Clear carry flag sbc hl,de ; Compute lower memory address ex de,hl ; ..and put it in DE ld hl,(loadaddr) ; Get previously computed load address sbc hl,de ; Compare HL and DE ret c ; HL is lower, nothing to do ld (loadaddr),de ; New lower address ret space defl 80h - ($-rec0) ; Space remaining in this record .printx ; Skip line on screen if space < 80h prtval 10,,space,bytes rept space db 0 ; Zero fill endm else ; Code too long space defl -space prtval 10,<1st routine too long by>,space,bytes endif if not test .dephase endif ;============================================================================= ; Record 1 Routine -- Code Relocator ; This code is loaded to tbuff by the command processor. It is executed ; after the command processor has loaded the program code/data to the load ; address and relogged the current DU. On entry, the stack contains the ; following data: ; ; (sp) load address ; (sp+2) PRL/SPR code size ; (sp+4) Z3ENV address ; (sp+6) return address of command processor routine ; that called MLOAD3 if not test .phase tbuff endif rec1: pop de ; Load address to DE pop bc ; PRL/SPR code size to BC pop hl ; Toss out Z3ENV (not needed here) ; The loaded module may have been assembled as a Type 3. ; We change it here to Type 4. ld hl,8 ; Offset to type byte add hl,de ; Add the offset ld (hl),4 ; Make it type 4 ; Bridger Mitchell's Word-wide relocator starts here.. push de ; Save load address on stack exx ; Select alternate registers pop de ; Load address to DE' if prl dec d ; -100h if PRL assembly endif exx ; Select main registers ld ix,0 ; Clear IX add ix,sp ; Save SP in IX ex de,hl ; Load address to HL ; The relocator uses SP as a memory pointer and so we must disable ; the interrupt system until we get it right again. di ; Disable interrupt system ld sp,hl ; Sp -> start of code, lag 1 byte dec sp ; ..because prl marks the high byte add hl,bc ; Add code length, hl ->prl bitmap ld e,1 ; Init the rotation byte ; It will set CY every 8 bytes ; Main relocation loop.. rloop: ld a,b ; Check byte count or c jr z,rdone ; Return to MLOAD caller dec bc ; Reduce byte count rrc e ; Every 8 bits the CY is set jr nc,same ; ..not set ld d,(hl) ; Set d = next byte from bitmap inc hl ; And advance bitmap pointer same: rlc d ; Shift bitmap byte left into CY jr nc,noof ; No relocation needed exx ; Alternate registers pop hl ; Get word to relocate from 'stack' add hl,de ; Add the load address in DE' push hl ; Put it back exx ; Main registers noof: inc sp ; -> next byte of code jr rloop rdone: ld sp,ix ; Restore the stack pointer ei ; And permit interrupts again if test rst 38h ; DDT breakpoint else ret ; Return to MLOAD3's caller in ZCPR34 endif space defl 80h - ($-rec1) ; Space remaining in this record if space < 80h prtval 10,,space,bytes if space > 16 ; Include ID if there is room db ' TYP4LDR Ver ',vers/10+'0','.',vers mod 10+'0' space defl space - 16 endif rept space db 0 ; Zero fill endm else ; Code too long space defl -space prtval 10,<2nd routine too long by>,space,bytes endif .printx ; Skip line on screen if not test .dephase ; Good Housekeeping endif end ; End of TYP4LDR.Z80 Relocator