Using machine code in BBC BASIC is best suited with allocating dynamic memory with DIM and then storing (assembling) the code to that area. If you don't assemble but have code ready, eg. loaded directly with PROC_lbytes, it is necessary that your machine code is relocatable, ie. contains no absolute address references (the machine code assumes that it is located at a certain ORG position in memory). You can relocatable code by omitting CALL and JP instructions and only use JR and DJNZ (jump relative) instruction. However, your program cannot be very large since relative jumps only range +/- 128 bytes in either direction from the instruction.

You can make truly relocatable machine code with the Mpm assembler, which is an integrated part of the Z88 Assembler Workbench. By using the '-R' option a small header is generated together with your code. The header contains a relocater routine and a relocation table. When your code is executed, it is automatically relocated to the current position in memory (just once). Subsequent calls to the code will just execute your code and not the relocater. With this option you can always store your machine code utilities with PROC_sbytes and at a later time allocate space with DIM for your code, and just load it into appropriate BBC BASIC memory. The code may be placed anywhere (in RAM).

The Mpm assembler generates the relocation table, when parsing the source code for compilation of the binary executable code. The relocator routine that is used by Mpm is made available here for convenience:

 MODULE relocatorXDEF relocator, SIZEOF_relocator

; *************************************************************************
;
; This routine will relocate a machine code program.
; A relocation table is placed immediatly after the relocation routine.
;
; The program that is to be relocated and executed are placed immediately
; after the relocation table.
;
; The relocation routine expects IY to point at the start address, entry
; of the relocater. The relocater then calculates the correct origin for
; the relocated program. The BBC BASIC on the Z88 also setups the IY register
; during a BBC BASIC CALL command to execute local machine code.
;
; After completed relocation, the entry of the relocater is patched to
; JP  which jumps directly to the executing program, if the relocated
; code is called again.
;
; The following registers are affected by the initial relocation process:
;         AFBCDEHL/IX../........ same
;         ......../..IY/afbcdehl different
;
; As indicated above the outside world cannot use the alternate registers
; as parameter interface to the relocated program - they are smashed by the
; relocater.
;
.relocator          ex   af,af'                   ; preserve AF
                    exx                           ; preserve BC, DE, HL
                    push iy
                    pop  hl
                    ld   bc, #end_relocator-relocator

                    add  hl,bc                    ; absolute address of relocation table

                    ld   e,(hl)
                    inc  hl
                    ld   d,(hl)
                    push de                       ; DE = total of relocation offset elements
                    inc  hl
                    ld   c,(hl)
                    inc  hl
                    ld   b,(hl)                   ; total size of relocation offset elements
                    inc  hl
                    push hl                       ; preserve pointer to first relocation offset element
                    add  hl,bc
                    ld   b,h                      ; HL = pointer to current relocation address
                    ld   c,l                      ; BC = program ORG, first byte of program

.relocate_loop           ex   (sp),hl                  ; HL = pointer to relocation offset element
                         ld   a,(hl)
                         inc  hl                       ; ready for next relocation offset pointer
                         or   a
                         jr   nz, byte_offset
.extended_offset         ld   e,(hl)
                         inc  hl
                         ld   d,(hl)                   ; DE = extended offset pointer to next relocation address
                         inc  hl                       ; ready for next relocation offset pointer
                         jr   relocate_address

.byte_offset             ld   d,0
                         ld   e,a                      ; offset pointer to next relocation address
.relocate_address        ex   (sp),hl                  ; HL = pointer to current relocation address
                         add  hl,de                    ; new pointer at memory that contains relocation address
                         ld   e,(hl)
                         inc  hl
                         ld   d,(hl)
                         ex   de,hl
                         add  hl,bc                    ; HL = address relocated to program ORG in BC
                         ex   de,hl
                         ld   (hl),d
                         dec  hl
                         ld   (hl),e                   ; update relocated address back to memory

                         pop  de                       ; DE = pointer to relocation offset
                         ex   (sp),hl                  ; HL = index counter
                         dec  hl                       ; update index counter
                         ld   a,h
                         or   l                        ; all addresses relocated?
                         ex   (sp),hl                  ; index counter back on stack
                         push de                       ; pointer to relocation offset back on stack
                    jr   nz, relocate_loop
                    pop  af
                    pop  af                            ; remove redundant variables

; relocation of program completed. Patch the entry of the relocater to JP  that
; jumps directly to the executing program, if the loaded program is executed again.
; Finish with restoring main registers and then execute program.
;
.relocation_finished
                    ld   (iy+0),$C3
                    ld   (iy+1),c
                    ld   (iy+2),b                 ; patch entry to JP , the relocated program
                    exx                           ; swap back to main BC, DE, HL
                    ex   af,af'                   ; and main AF
                    jp   (iy)                     ; execute relocated program...
.end_relocator

     DEFC SIZEOF_relocator = end_relocator - relocator

; ******************************************************************************
;
; The relocation table is placed here by the Mpm assembler.
; The format of the generated table is:
;
;    total_elements    ds.w 1
;    sizeof_table      ds.w 1
;    patchpointer_0    ds.b 1  --+
;    patchpointer_1    ds.b 1    |
;    ....                        |  sizeof_table
;    ....                        |
;    patchpointer_n    ds.b 1  --+
;
; The first patch pointer is an offset from the start of the program (.routine)
; to the first position of a location that contains a relocatable address.
; The following patchpointers are relative offsets from the current relocated
; address to the next.
; If the offset distance is larger than 255 bytes between two relocatable
; addresses, the following patchpointer is used:
;
;         0,<16bit patchpointer>
;
; which denotes the an offset from the current relocated address to the next.
; The 16 bit patch pointer is stored in the low byte, high byte order.
;

.routine
; the machine code to be relocated is placed immediately after the relocation table

 

     MODULE relocator
     XDEF relocator, SIZEOF_relocator

; *************************************************************************;; This routine will relocate a machine code program.; A relocation table is placed immediatly after the relocation routine.;; The program that is to be relocated and executed are placed immediately; after the relocation table.;; The relocation routine expects IY to point at the start address, entry; of the relocater. The relocater then calculates the correct origin for; the relocated program. The BBC BASIC on the Z88 also setups the IY register; during a BBC BASIC CALL command to execute local machine code.;; After completed relocation, the entry of the relocater is patched to; JP <nn> which jumps directly to the executing program, if the relocated; code is called again.;; The following registers are affected by the initial relocation process:;         AFBCDEHL/IX../........ same;         ......../..IY/afbcdehl different;; As indicated above the outside world cannot use the alternate registers; as parameter interface to the relocated program - they are smashed by the; relocater.;.relocator          ex   af,af'                   ; preserve AF                    exx                           ; preserve BC, DE, HL                    push iy                    pop  hl                    ld   bc, #end_relocator-relocator
                    add  hl,bc                    ; absolute address of relocation table
                    ld   e,(hl)                    inc  hl                    ld   d,(hl)                    push de                       ; DE = total of relocation offset elements                    inc  hl                    ld   c,(hl)                    inc  hl                    ld   b,(hl)                   ; total size of relocation offset elements                    inc  hl                    push hl                       ; preserve pointer to first relocation offset element                    add  hl,bc                    ld   b,h                      ; HL = pointer to current relocation address                    ld   c,l                      ; BC = program ORG, first byte of program
.relocate_loop           ex   (sp),hl                  ; HL = pointer to relocation offset element                         ld   a,(hl)                         inc  hl                       ; ready for next relocation offset pointer                         or   a                         jr   nz, byte_offset.extended_offset         ld   e,(hl)                         inc  hl                         ld   d,(hl)                   ; DE = extended offset pointer to next relocation address                         inc  hl                       ; ready for next relocation offset pointer                         jr   relocate_address
.byte_offset             ld   d,0                         ld   e,a                      ; offset pointer to next relocation address.relocate_address        ex   (sp),hl                  ; HL = pointer to current relocation address                         add  hl,de                    ; new pointer at memory that contains relocation address                         ld   e,(hl)                         inc  hl                         ld   d,(hl)                         ex   de,hl                         add  hl,bc                    ; HL = address relocated to program ORG in BC                         ex   de,hl                         ld   (hl),d                         dec  hl                         ld   (hl),e                   ; update relocated address back to memory
                         pop  de                       ; DE = pointer to relocation offset                         ex   (sp),hl                  ; HL = index counter                         dec  hl                       ; update index counter                         ld   a,h                         or   l                        ; all addresses relocated?                         ex   (sp),hl                  ; index counter back on stack                         push de                       ; pointer to relocation offset back on stack                    jr   nz, relocate_loop                    pop  af                    pop  af                            ; remove redundant variables
; relocation of program completed. Patch the entry of the relocater to JP <nn> that; jumps directly to the executing program, if the loaded program is executed again.; Finish with restoring main registers and then execute program.;.relocation_finished                    ld   (iy+0),$C3                    ld   (iy+1),c                    ld   (iy+2),b                 ; patch entry to JP <nn>, the relocated program                    exx                           ; swap back to main BC, DE, HL                    ex   af,af'                   ; and main AF                    jp   (iy)                     ; execute relocated program....end_relocator
     DEFC SIZEOF_relocator = end_relocator - relocator
; ******************************************************************************;; The relocation table is placed here by the Mpm assembler.; The format of the generated table is:;;    total_elements    ds.w 1;    sizeof_table      ds.w 1;    patchpointer_0    ds.b 1  --+;    patchpointer_1    ds.b 1    |;    ....                        |  sizeof_table;    ....                        |;    patchpointer_n    ds.b 1  --+;; The first patch pointer is an offset from the start of the program (.routine); to the first position of a location that contains a relocatable address.; The following patchpointers are relative offsets from the current relocated; address to the next.; If the offset distance is larger than 255 bytes between two relocatable; addresses, the following patchpointer is used:;;         0,<16bit patchpointer>;; which denotes the an offset from the current relocated address to the next.; The 16 bit patch pointer is stored in the low byte, high byte order.;
.routine; the machine code to be relocated is placed immediately after the relocation table