; ANY4.Z80, from ANYWHERE.ASM by Bridger Mitchell ; (ref. The Computer Journal, issue #33, pp.11,14-15) ; July 18, 1988 mods by Bruce Morgen to avoid lots of ; "LD (HL),nn" opcodes - at first it turned out to be ; a byte longer and a little slower! Anyway, it's a ; brain-teaser to do it that way and I wound up with ; a combination of the two approaches that seems ; reasonably efficient and compact. The register ; recovery feature is also sketched out - all primary ; registers except BC and IY can be passed to the ; program by PUSHing at ANYWHERE+3 and POPping at ; RDONE+3. After the initial relocate-&-move run, ; of course, any register can be passed. ; July 15, 1988 mods by Bruce Morgen to to "pull" the ; relocated code down over the Where-Am-I and relocator ; routines as suggested by Bridger in the TCJ piece. ; July 14, 1988 mods by Bruce Morgen to allow easier ; creation of relocatable programs using MLOAD instead ; of a debugger/save sequence. Sample procedure: ; ZAS ANY4 H or Z80ASM ANY4/H ; Assemble to a HEXfile ; (M80/L80 can't handle this correctly, ; use the M80 equate and the RELHEX ; utility if you must use M80.) ; MLOAD PROGNAME=PROGNAME.PRL,ANY4 ; MLOAD v2.1 or later required prl equ 1 ; Could also use SPR files, but ; the DSEG handling could be ; tricky - PRL preferred. m80 equ 1 ; Make this a 1 if using the ; M80/RELHEX method if m80 .z80 aseg endif org 100h ; Code is position-independent, ; but MLOAD needs this anyway anywhere: db 01h ; "LD BC" opcode ds 2 ; PRL format code length here ; (proper HEXfile won't overlay) ; Bridger's Where-Am-I routine ; We could save registers other than BC & IY here for later recovery ; e.g.: ; push hl ; push de ld hl,0000 ; Zero out HL push hl ; Use it to clear IX & IY first pop ix push hl pop iy add ix,sp ; Store incoming SP in IX ld a,(hl) ; Get byte at 0000h di ; Disable interrupt system ld (hl),0c9h ; Poke a RET at 0000h rst 0 ; "CALL" it me: ld (hl),a ; Replace the original byte dec sp ; Adjust SP dec sp ei ; Interrupts OK again pop hl ; Get ANYWHERE+ME from stack ld de,anywhere-me ; Signed difference in DE add hl,de ; HL pts to ANYWHERE ; Bridger's neat word-wide PRL/SPR relocator begins here, ; slightly modified to save the byte count and bitmap start reloc: push bc ; Byte count to stack - b/m push hl ; ANYWHERE to stack exx pop de ; Set DE' to relocation base of code pop bc ; Set BC' to byte count - b/m if prl dec d ; -100H for PRL version endif exx inc h ; Adjust HL to code start - b/m di ld sp,hl dec sp add hl,bc ld e,00000001b ; Init. rotation counter byte ex de,hl ; Bitmap start to DE add iy,de ; Get bitmap start in IY - b/m ex de,hl ; Bitmap start back to HL rloop: ld a,b ; Anything left to process? or c jr z,rdone ; Branch ahead if done dec bc ; Decrement 16-bit byte counter rrc e ; Rotate right, bit 7 = carry jr nc,rsame ; If bit reset, same bitmap byte ld d,(hl) ; Otherwise get byte from bitmap inc hl ; Point to the next one rsame: rlc d ; Rotate left, bit 0 = carry jr nc,noof ; No carry means no relocation exx ; Go to alt. regs. pop hl ; Get value in HL' add hl,de ; Add in relocation base push hl ; Put back relocated value exx ; Back to primary regs. noof: inc sp ; Increment pointer to code jr rloop ; Loop around rdone: ld sp,ix ; Recover incoming SP ei ; Now overlay the bitmap area with our move & run routine - b/m ; We could recover saved registers other than BC & IY here ; e.g.: ; pop de ; pop hl exx ; Go to alt. registers push iy ; Get bitmap start into HL' pop hl if prl inc d ; Adjust runtime origin if PRL endif ld (hl),0edh ; Move LDIR to (HL') inc hl ld (hl),0b0h inc hl ld (hl),0d9h ; EXX opcode next inc hl ld (hl),0c3h ; JP opcode next inc hl ld (hl),e ; Then ANYWHERE, as JP's operand inc hl ld (hl),d ld h,d ; Copy LDIR dest. in DE' to HL' ld l,e inc h ; "Compute" LDIR source jp (iy) ; Branch to MOVER & the program ; This code gets moved to (and executed in) the bitmap area ; ;mover: ldir ; Move source to dest. ; exx ; To primary regs. ; jp "real"_anywhere ; Branch to the program end