Difference between revisions of "Code header"

From BeebWiki
Jump to: navigation, search
(Fuller information on ARM header.)
m (Tidied some formatting.)
 
(4 intermediate revisions by the same user not shown)
Line 14: Line 14:
 
==Header==
 
==Header==
 
The general layout of the code header is the following:
 
The general layout of the code header is the following:
 
+
<div class="mw-widebody">
 
   Start+0:  JMP Entry                              ; Code entry address or entry point
 
   Start+0:  JMP Entry                              ; Code entry address or entry point
 
   Start+3:  JMP Service                            ; Service entry point if a ROM
 
   Start+3:  JMP Service                            ; Service entry point if a ROM
Line 29: Line 29:
 
             ALIGN                                  ; If the code needs to be aligned
 
             ALIGN                                  ; If the code needs to be aligned
 
   Entry:
 
   Entry:
 +
</div>
 +
The title, version and copyright strings should be formatted as shown. The
 +
binary version number should be either &0x for version x.yz or &xy for
 +
version x.yz.
  
The title, version and copyright strings should be formatted as shown. The binary
+
Theoretically, the copyright offset can be any value up to &FF, and the
version number should be either &0x for version x.yz or &xy for version x.yz.
+
copyright string can be any length, resulting in the relocation addresses
 
+
being at any arbitary distance from the start of the file. In practice, the
Theoretically, the copyright offset can be any value up to &FF, and the copyright string
+
header should be written so it fits into a maximum of 256 bytes.
can be any length, resulting in the relocation addresses being at any arbitary distance
 
from the start of the file. In practice, the header should be written so it fits into
 
a maximum of 256 bytes.
 
  
 
===ROM type byte===
 
===ROM type byte===
 
The ROM type byte at Start+6 indicates what CPU the code is written for.
 
The ROM type byte at Start+6 indicates what CPU the code is written for.
 
+
<div class="mw-widebody">
 
   bit 7 - Service entry for ROMs, ignored when loaded
 
   bit 7 - Service entry for ROMs, ignored when loaded
 
   bit 6 - Contains code, if clear will generate an error similar to 'This is not a language'
 
   bit 6 - Contains code, if clear will generate an error similar to 'This is not a language'
Line 50: Line 51:
 
             2 6502              6 -          10 -          14 -
 
             2 6502              6 -          10 -          14 -
 
             3 6800/6809/68000    7 PDP11      11 80186      15 -
 
             3 6800/6809/68000    7 PDP11      11 80186      15 -
 
+
</div>
 
Code in a ROM will normally have a ROM type byte of &E0+cpu, code loaded
 
Code in a ROM will normally have a ROM type byte of &E0+cpu, code loaded
 
from a file that is not also a ROM image will normally have a ROM type byte
 
from a file that is not also a ROM image will normally have a ROM type byte
Line 83: Line 84:
  
 
===65x2 - ROM type 0,1,2===
 
===65x2 - ROM type 0,1,2===
 +
<div class="mw-widebody">
 
   Start+0:  JMP  Entry                            ; Code entry point
 
   Start+0:  JMP  Entry                            ; Code entry point
 
   Start+3:  JMP  Service                          ; Service entry point
 
   Start+3:  JMP  Service                          ; Service entry point
Line 94: Line 96:
 
   ...
 
   ...
 
   Entry:                                            ; Code start
 
   Entry:                                            ; Code start
 
+
</div>
 
===6809 - ROM type 3===
 
===6809 - ROM type 3===
 +
<div class="mw-widebody">
 
   Start+0:  BRA  Entry:EQUB 0                      ; If position-independent code
 
   Start+0:  BRA  Entry:EQUB 0                      ; If position-independent code
 
           or JMP  >Entry                            ; If not position-independent code
 
           or JMP  >Entry                            ; If not position-independent code
Line 108: Line 111:
 
   ...
 
   ...
 
   Entry:                                            ; Code start
 
   Entry:                                            ; Code start
 
+
</div>
 
===PDP11 - ROM type 7===
 
===PDP11 - ROM type 7===
 +
<div class="mw-widebody">
 
   Start+0:  BR  Entry:EQUB 0                      ; Branch to code
 
   Start+0:  BR  Entry:EQUB 0                      ; Branch to code
 
   Start+3:  EQUB &4C:EQUW Service                  ; 6502 jump to ROM service handler
 
   Start+3:  EQUB &4C:EQUW Service                  ; 6502 jump to ROM service handler
Line 123: Line 127:
 
             ALIGN                                  ; Align to 16-bit words
 
             ALIGN                                  ; Align to 16-bit words
 
   Entry:                                            ; Code start
 
   Entry:                                            ; Code start
 
+
</div>
 
===Z80 - ROM type 8===
 
===Z80 - ROM type 8===
 +
<div class="mw-widebody">
 
   Start+0:  JR  Entry:EQUB 0                      ; If position-independent code
 
   Start+0:  JR  Entry:EQUB 0                      ; If position-independent code
 
           or JP  Entry                            ; If not position-independent code
 
           or JP  Entry                            ; If not position-independent code
Line 137: Line 142:
 
   ...
 
   ...
 
   Entry:                                            ; Code start
 
   Entry:                                            ; Code start
 
+
</div>
 
===32000 - ROM type 9===
 
===32000 - ROM type 9===
Even if the ROM type byte bit 5 is clear the entry offset at Reloc+4 must be present.
+
Even if the ROM type byte bit 5 is clear the entries at Reloc+0 and Reloc+4 must be present.
 
+
<div class="mw-widebody">
 
   Start+0:  EQUB &60:EQUW 0                        ; 6502 RTS
 
   Start+0:  EQUB &60:EQUW 0                        ; 6502 RTS
 
           or EQUB &4C:EQUW Language65              ; 6502 jump to 6502 language stub
 
           or EQUB &4C:EQUW Language65              ; 6502 jump to 6502 language stub
Line 154: Line 159:
 
   ...
 
   ...
 
   Entry:
 
   Entry:
 
+
</div>
 
===80x86 - ROM type 11===
 
===80x86 - ROM type 11===
 +
<div class="mw-widebody">
 
   Start+0:  BR  Entry:EQUB 0                      ; Code is entered here
 
   Start+0:  BR  Entry:EQUB 0                      ; Code is entered here
 
           or JMP  Entry                            ;
 
           or JMP  Entry                            ;
Line 166: Line 172:
 
   Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 
   Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 
   Reloc+0:  EQUD Start                            ; Load address if ROMtype bit 5 is set
 
   Reloc+0:  EQUD Start                            ; Load address if ROMtype bit 5 is set
 +
  ...
 
   Entry:
 
   Entry:
 
+
</div>
 
===ARM - ROM type 13===
 
===ARM - ROM type 13===
 
See also notes on ARM headers.
 
See also notes on ARM headers.
 
+
<div class="mw-widebody">
 
   If ROM type byte is &4D or &8D:
 
   If ROM type byte is &4D or &8D:
 
   Start+0:  EQUD Entry                            ; RomFS entry address
 
   Start+0:  EQUD Entry                            ; RomFS entry address
Line 196: Line 203:
 
   ...
 
   ...
 
   End:
 
   End:
 
+
</div>
 
==Extracting load/exec address==
 
==Extracting load/exec address==
 
File load and execution addresses can be extracted from the code header with
 
File load and execution addresses can be extracted from the code header with
the follow pseudo-code. The execution address is the same as the load address
+
the following code. The execution address is the same as the load address as
as the client code calculates the entry point as an offset from the execution
+
the client code calculates the entry point as an offset from the execution
 
address, which is the first byte of the header.
 
address, which is the first byte of the header.
 +
 +
=== BASIC code ===
 +
  REM Check for file header, using BASIC V or equivalent
 +
  in%=OPENIN(A$):IF in% THEN
 +
    FOR A%=0 TO 255:IF EOF#in%:buffer%?A%=0 ELSE buffer%?A%=BGET#in%
 +
    NEXT A%:CLOSE#in%:in%=0            :REM Read header
 +
    IF buffer%!(buffer%?7)=&29432800 THEN
 +
      A%=buffer%?6                    :REM Code type
 +
      load%=&FFFF8000                  :REM Sideways ROM
 +
      IF A% AND &40:load%=&8000        :REM Language ROM
 +
      IF A% AND &20 THEN
 +
        A%=buffer%?7:REPEAT A%=A%+1:UNTIL buffer%?A%=0 OR A%>247
 +
        IF A%<248:load%=buffer%!(A%+1) :REM Relocation address
 +
      ENDIF
 +
      exec%=load%
 +
    ENDIF
 +
  ENDIF
 +
 +
=== C code ===
 +
  // Check for file header
 +
  if(handle=fopen(pathname, "r")) {
 +
    memset(buffer, 0, 256);
 +
    fread(buffer, 1, 256, handle);
 +
    fclose(handle);
 +
    off=buffer[7];
 +
    if (buffer[off+0]==0 && buffer[off+1]=='(' && buffer[off+2]=='C' && buffer[off+3]==')') {
 +
      type=buffer[6];
 +
      load=0xFFFF8000;                          // Sideways ROM
 +
      if (type & 0x40) load=0x8000;            // Language ROM
 +
      if (type & 0x20) {                        // Language relocation address
 +
        while (buffer[++off] && off<248);      // Step past copyright string
 +
        if (++off<249) {
 +
          load=(buffer[off+0]&0xFF) | ((buffer[off+1]&0xFF)<<8) |
 +
              ((buffer[off+2]&0xFF)<<16) | ((buffer[off+3]&0xFF)<<24);
 +
        }
 +
      }
 +
      exec=load;
 +
    }
 +
  }
 +
 +
<!--
 +
File load and execution addresses can be extracted from the code header with
 +
the follow pseudo-code. The execution address is the same as the load
 +
address as the client code calculates the entry point as an offset from the
 +
execution address, which is the first byte of the header.
  
 
   load=unknown; exec=load
 
   load=unknown; exec=load
Line 222: Line 274:
 
   exit
 
   exit
  
 +
-->
 
Clients calculate the entry point with the following pseudo-code.
 
Clients calculate the entry point with the following pseudo-code.
 
+
<div class="mw-widebody">
 
   entry=start
 
   entry=start
 
   if start[7] does not point to &00,"(C)", then enter code        // Raw code
 
   if start[7] does not point to &00,"(C)", then enter code        // Raw code
Line 239: Line 292:
 
   }
 
   }
 
   enter code
 
   enter code
 
+
</div>
 
==ARM header notes==
 
==ARM header notes==
 
The ARM ROM header is complicated by having been implemented differently on
 
The ARM ROM header is complicated by having been implemented differently on
 
different platforms, the Acorn ARM Evaluation System, the Arthur/RISC OS
 
different platforms, the Acorn ARM Evaluation System, the Arthur/RISC OS
RomFS, and the Sprow ARM CoProcessor.
+
RomFS, and the Sprow ARM CoProcessor. (And there is also the RISC OS module
 +
format which most ARM CoPro clients can't *RUN.)
  
 
* There is always a relocation address, regardless of the setting of ROMtype bit 5.
 
* There is always a relocation address, regardless of the setting of ROMtype bit 5.
Line 253: Line 307:
 
an unconditional branch to the main code. All ARM instructions take four
 
an unconditional branch to the main code. All ARM instructions take four
 
bytes, so this overlaps into the ROM Service entry. However, an ARM branch
 
bytes, so this overlaps into the ROM Service entry. However, an ARM branch
instruction is &EAxxxxxx and &EA is a 6502 NOP instruction, so a null service
+
instruction is &EAxxxxxx and &EA is a 6502 NOP instruction, so a null
entry point can be made with:
+
service entry point can be made with:
  
 
   Start+0 xx B Entry    ; Jump to ARM code
 
   Start+0 xx B Entry    ; Jump to ARM code
Line 276: Line 330:
 
===Sprow ARM CoPro===
 
===Sprow ARM CoPro===
 
The Sprow ARM CoPro puts the ARM entry point in the 16-bit address field of
 
The Sprow ARM CoPro puts the ARM entry point in the 16-bit address field of
a 6502 JMP instructon, leaving space for a ROM service handler JMP at Start+3:
+
a 6502 JMP instructon, leaving space for a ROM service handler JMP at
 +
Start+3:
  
 
   Start+0 4C EQUB &4C  ; 6502 JMP
 
   Start+0 4C EQUB &4C  ; 6502 JMP
Line 292: Line 347:
  
 
===Identifying ARM header===
 
===Identifying ARM header===
It is not possible to create a single file that will be recognised correctly by
+
It is not possible to create a single file that will be recognised correctly
all ARM platforms, but it is possible to examine the header and decide which
+
by all ARM platforms, but it is possible to examine the header and decide
ARM platform is is intended for, and consequently, what load and entry
+
which ARM platform is is intended for, and consequently, what load and entry
 
addresses it has.
 
addresses it has.
  

Latest revision as of 01:29, 16 February 2023

Files have load and execution addresses that specify where they are loaded to and executed. These specify I/O memory by being &FFxxxxxx and language memory by being <>&FFxxxxxx. The I/O processor is always a 65x2, but the language processor can be one of many CPUs. Files can have a header which specifies what processor the code is written for so if it is run on the wrong processor the client code can generate an error.

The header is the same as the ROM header as ROMs also need to specify what CPU the code is written for and where the code should be copied to in the language memory. The code header also allows files to be loaded from filesystems that don't have load/exec addresses, such as DOS/Windows.

Header

The general layout of the code header is the following:

 Start+0:   JMP Entry                              ; Code entry address or entry point
 Start+3:   JMP Service                            ; Service entry point if a ROM
 Start+6:   EQUB ROMtype                           ; Code type byte
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Version number and Title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string (optional)
 Copyright: EQUB &00:EQUS "(C)J.G.Harston"         ; Copyright string
            EQUB &00
 Reloc+0:   EQUD Start                             ; Load address if ROMtype bit 5 is set
 Reloc+4:   EQUD Offset                            ; Offsets if specified by the ROMtype
 etc:       ....                                   ; Possibly other data
            ...
            ALIGN                                  ; If the code needs to be aligned
 Entry:

The title, version and copyright strings should be formatted as shown. The binary version number should be either &0x for version x.yz or &xy for version x.yz.

Theoretically, the copyright offset can be any value up to &FF, and the copyright string can be any length, resulting in the relocation addresses being at any arbitary distance from the start of the file. In practice, the header should be written so it fits into a maximum of 256 bytes.

ROM type byte

The ROM type byte at Start+6 indicates what CPU the code is written for.

 bit 7 - Service entry for ROMs, ignored when loaded
 bit 6 - Contains code, if clear will generate an error similar to 'This is not a language'
 bit 5 - Contains a relocation address, if clear the code loads to &8000
 bit 4 - Electron key expansion
 bit 3-0 indicates the CPU type:
           0 6502 BASIC         4 -            8 Z80         12 80286
           1 Turbo6502          5 -            9 32016       13 ARM
           2 6502               6 -           10 -           14 -
           3 6800/6809/68000    7 PDP11       11 80186       15 -

Code in a ROM will normally have a ROM type byte of &E0+cpu, code loaded from a file that is not also a ROM image will normally have a ROM type byte of &60+cpu. If the ROM type byte is &40+cpu or &C0+cpu there is no relocation address and the code loads to &8000.

If the code is run on a CPU that does not match the CPU type an error similar to 'This is not Z80 code' is generated.

Some clients always expect the relocation data to be present even if bit 5 is clear, so a full header should always be used with the flag byte being &60+cpu or &E0+cpu.

Entry point

The entry point at Start+0 must be an unconditional branch to the code's start point in the machine code for the CPU. If the ROM type byte bit 7 is set then Start+3 contains the ROM service entry point, so the entry point at Start+0 must fit into three bytes. If the ROM type byte bit 7 is clear the code at Start+0 can be up to six bytes long, and some CPUs require this.

If ROMtype bit 7 is set then the entry point at Start+3 must be a 6502 unconditional branch to the ROM's service code, or a 6502 RTS.

The code is entered at the entry point with the registers set to a defined state. This is normally:

 Primary register:   0=raw code (no header), 1=code with a header
 Secondary register: points to command line tail
 Flags:              CC=entered at reset, CS=entered otherwise, eg a *command

Example headers

65x2 - ROM type 0,1,2

 Start+0:   JMP  Entry                             ; Code entry point
 Start+3:   JMP  Service                           ; Service entry point
 Start+6:   EQUB ROMtype                           ; flags+&00, flags+&01, flags+&02
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Binary version number and title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string
 Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 Reloc+0:   EQUW Start                             ; Load address if ROMtype bit 5 is set
 Reloc+2:   EQUW RelocateTable                     ; Normally 0, only supported by MOS 3.50
 ...
 Entry:                                            ; Code start

6809 - ROM type 3

 Start+0:   BRA  Entry:EQUB 0                      ; If position-independent code
         or JMP  >Entry                            ; If not position-independent code
 Start+3:   EQUB &4C:EQUW Service                  ; 6502 jump to ROM service handler
         or EQUB &60:EQUW 0                        ; 6502 RTS if no ROM service handler
 Start+6:   EQUB flags+&03                         ; ROM type byte
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Binary version number and title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string
 Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 Reloc+0:   EQUD Start                             ; Load address if ROMtype bit 5 is set
 ...
 Entry:                                            ; Code start

PDP11 - ROM type 7

 Start+0:   BR   Entry:EQUB 0                      ; Branch to code
 Start+3:   EQUB &4C:EQUW Service                  ; 6502 jump to ROM service handler
         or EQUB &60:EQUW 0                        ; 6502 RTS if no ROM service handler
 Start+6:   EQUB flags+&07                         ; ROM type byte
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Binary version number and title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string
 Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 Reloc+0:   EQUD Start                             ; Load address if ROMtype bit 5 is set
 Reloc+4:   EQUD Entry-Start                       ; Offset to entry if ROMtype bit 5 set
 ...
            ALIGN                                  ; Align to 16-bit words
 Entry:                                            ; Code start

Z80 - ROM type 8

 Start+0:   JR   Entry:EQUB 0                      ; If position-independent code
         or JP   Entry                             ; If not position-independent code
 Start+3:   EQUB &4C:EQUW Service                  ; 6502 jump to ROM service handler
         or EQUB &60:EQUW 0                        ; 6502 RTS if no ROM service handler
 Start+6:   EQUB flags+&08                         ; ROM type byte
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Binary version number and title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string
 Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 Reloc+0:   EQUD Start                             ; Load address if ROMtype bit 5 is set
 ...
 Entry:                                            ; Code start

32000 - ROM type 9

Even if the ROM type byte bit 5 is clear the entries at Reloc+0 and Reloc+4 must be present.

 Start+0:   EQUB &60:EQUW 0                        ; 6502 RTS
         or EQUB &4C:EQUW Language65               ; 6502 jump to 6502 language stub
 Start+3:   EQUB &4C:EQUW Service                  ; 6502 jump to ROM service handler
         or EQUB &60:EQUW 0                        ; 6502 RTS if no ROM service handler
 Start+6:   EQUB flags+&09                         ; ROM type byte
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Binary version number and title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string
 Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 Reloc+0:   EQUD Start                             ; Load address, even if ROMtype bit 5 is not set
 Reloc+4:   EQUD Entry-Start                       ; Offset to entry, even if ROMtype bit 5 is not set
 ...
 Entry:

80x86 - ROM type 11

 Start+0:   BR   Entry:EQUB 0                      ; Code is entered here
         or JMP  Entry                             ;
 Start+3:   EQUB &4C:EQUW Service                  ; 6502 jump to ROM service handler
         or EQUB &60:EQUW 0                        ; 6502 RTS if no ROM service handler
 Start+6:   EQUB flags+&0B                         ; ROM type byte
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Binary version number and title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string
 Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 Reloc+0:   EQUD Start                             ; Load address if ROMtype bit 5 is set
 ...
 Entry:

ARM - ROM type 13

See also notes on ARM headers.

 If ROM type byte is &4D or &8D:
 Start+0:   EQUD Entry                             ; RomFS entry address
 Start+4:   EQUW 0
 
 If other ROM type byte values:
 Start+0:   EQUW (Entry-Start-8)/4:EQUB 0:EQUB &EA ; ARM branch instruction, overlaps Start+3
 Start+4:   EQUB &D0:EQUB Service-Start-6          ; 6502 BNE to ROM service handler
         or EQUB &60:EQUB 0                        ; 6502 RTS if no ROM service handler
 or:
 Start+0:   EQUB &4C:EQUW Entry                    ; &4C, followed by Entry address
 Start+3:   EQUB &4C:EQUW Service                  ; 6502 JMP to ROM service handler
         or EQUB &60:EQUW 0                        ; 6502 RTS if no ROM service handler
 
 Start+6:   EQUB flags+&0D                         ; ROM type byte
 Start+7:   EQUB Copyright-Start                   ; Offset to copyright string
 Start+8:   EQUB Ver:EQUS "Title"                  ; Binary version number and title string
            EQUB &00:EQUS "0.00 (01 Jan 2001)"     ; Version string
 Copyright: EQUB &00:EQUS "(C)J.G.Harston":EQUB 0  ; Copyright string
 Reloc+0:   EQUD Start                             ; Load address, even if ROMtype bit 5 is not set
 Reloc+4:   EQUD End-Entry                         ; Size of code, even if ROMtype bit 5 is not set
 ...
            ALIGN                                  ; Align to 32-bit words
 Entry:
 ...
 End:

Extracting load/exec address

File load and execution addresses can be extracted from the code header with the following code. The execution address is the same as the load address as the client code calculates the entry point as an offset from the execution address, which is the first byte of the header.

BASIC code

 REM Check for file header, using BASIC V or equivalent
 in%=OPENIN(A$):IF in% THEN
   FOR A%=0 TO 255:IF EOF#in%:buffer%?A%=0 ELSE buffer%?A%=BGET#in%
   NEXT A%:CLOSE#in%:in%=0            :REM Read header
   IF buffer%!(buffer%?7)=&29432800 THEN
     A%=buffer%?6                     :REM Code type
     load%=&FFFF8000                  :REM Sideways ROM
     IF A% AND &40:load%=&8000        :REM Language ROM
     IF A% AND &20 THEN
       A%=buffer%?7:REPEAT A%=A%+1:UNTIL buffer%?A%=0 OR A%>247
       IF A%<248:load%=buffer%!(A%+1) :REM Relocation address
     ENDIF
     exec%=load%
   ENDIF
 ENDIF

C code

 // Check for file header
 if(handle=fopen(pathname, "r")) {
   memset(buffer, 0, 256);
   fread(buffer, 1, 256, handle);
   fclose(handle);
   off=buffer[7];
   if (buffer[off+0]==0 && buffer[off+1]=='(' && buffer[off+2]=='C' && buffer[off+3]==')') {
     type=buffer[6];
     load=0xFFFF8000;                          // Sideways ROM
     if (type & 0x40) load=0x8000;             // Language ROM
     if (type & 0x20) {                        // Language relocation address
       while (buffer[++off] && off<248);       // Step past copyright string
       if (++off<249) {
         load=(buffer[off+0]&0xFF) | ((buffer[off+1]&0xFF)<<8) |
              ((buffer[off+2]&0xFF)<<16) | ((buffer[off+3]&0xFF)<<24);
       }
     }
     exec=load;
   }
 }

Clients calculate the entry point with the following pseudo-code.

 entry=start
 if start[7] does not point to &00,"(C)", then enter code        // Raw code
 romtype=start[6]
 if rombyte bit 6 is clear, then error "This is not code"        // No code
 if (rombyte AND 15)<>mycpu, then error "This is not MyCPU code" // Wrong CPU
 switch (rombyte AND 15) {
   when 7,9:                                                     // PDP11, 32000
     point to the relocation address after the zero
       byte at the end of the copyright string
     entry=start+reload[4..7]                                    // Offset to entry point
   when 13:                                                      // ARM
     if start[3]=&EA, then entry=start                           // ARM branch instruction
                      else entry=start[1..2]                     // Embedded address
 }
 enter code

ARM header notes

The ARM ROM header is complicated by having been implemented differently on different platforms, the Acorn ARM Evaluation System, the Arthur/RISC OS RomFS, and the Sprow ARM CoProcessor. (And there is also the RISC OS module format which most ARM CoPro clients can't *RUN.)

  • There is always a relocation address, regardless of the setting of ROMtype bit 5.
  • The data at Start+0 may be an ARM branch instruction, a 32-bit entry address or a &4C byte followed by a 16-bit entry address.
  • The address at Reloc+0 is the load address of the data after the header in RomFS and is the load address of Start+0 on other systems.

ARM Evaluation System

The ARM Evaluation System simply jumps to Start+0, so the first word must be an unconditional branch to the main code. All ARM instructions take four bytes, so this overlaps into the ROM Service entry. However, an ARM branch instruction is &EAxxxxxx and &EA is a 6502 NOP instruction, so a null service entry point can be made with:

 Start+0 xx B Entry    ; Jump to ARM code
         xx
         xx
 Start+3 EA            ; 6502 NOP
         60 EQUB &60   ; 6502 RTS
         00 EQUB &00

As the ROM service handler is always entered with NE set from testing the ROM type byte a two-byte BNE instruction can be used to jump to an actual service handler:

 Start+0 xx B Entry    ; Jump to ARM code
         xx
         xx
 Start+3 EA            ; 6502 NOP
         D0 EQUB &D0   ; 6502 BNE
         xx EQUB Service-Start-6

Sprow ARM CoPro

The Sprow ARM CoPro puts the ARM entry point in the 16-bit address field of a 6502 JMP instructon, leaving space for a ROM service handler JMP at Start+3:

 Start+0 4C EQUB &4C   ; 6502 JMP
         xx EQUW Entry
         xx
 Start+3 4C EQUB &4C
         xx EQUW Service
         xx

ARM RomFS

RomFS simply stores a 32-bit entry address at Start+0, overlapping the ROM service handler at Start+3, so the ROM type byte must have bit 7 clear. The relocation address at reloc[0...3] and the entry address at start[0..3] are actually used as load/exec addresses for the data that starts at reloc+8.

Identifying ARM header

It is not possible to create a single file that will be recognised correctly by all ARM platforms, but it is possible to examine the header and decide which ARM platform is is intended for, and consequently, what load and entry addresses it has.

 Start+
 00 01 02  03  04 05 06
 .. .. ..  ..  .. .. 0D Raw code
 .. .. ..  ..  .. .. 2D Raw code
 ee ee ee  ee  .. .. 4D RomFS file
 dd dd dd  EA  .. .. 6D ARM Evaluation System
 .. ee ee <>EA .. .. 6D Sprow ARM CoPro
 
 ee ee ee  ee  .. .. 8D RomFS directory
 .. .. ..  ..  .. .. AD Raw code
 dd dd dd  EA  .. .. CD ARM Evaluation System
 .. ee ee <>EA .. .. CD Sprow ARM CoPro
 dd dd dd  EA  .. .. ED ARM Evaluation System
 .. ee ee <>EA .. .. ED Sprow ARM CoPro

Jgharston (talk) 02:56, 23 January 2017 (UTC)