[Oberon] Standalone BootLoader format

Paul Reed paulreed at paddedcell.com
Thu May 14 16:34:54 CEST 2020


Hi Tomas,

> Yes, and I try setting SB pointing to pc when storing this array to
> file. This mimics what RISC.Mod emulator does R[13] := pc*4 prior
> execution.

Unfortunately, as has already been pointed out, this won't be
sufficient, because the instructions loading SB are special.  They are
laid down by the Oberon-0 compiler in a linked list in OSG:

   PROCEDURE GetSB;
   BEGIN
     IF curSB = 1 THEN Put2(Ldw, SB, 0, pc-fixorgD); fixorgD := pc-1; 
curSB := 0 END
   END GetSB;

The offsets are used as a linked list (a common technique in Prof.
Wirth's compilers).  At the end of compilation, fixorgD will end up
pointing at the last instruction which needs to be patched, which
contains the pc value of the previous one, and so on.  You will need
to follow this chain back while patching the instructions.  OSG.fix()
can be used to do this.  You stop when the link is 0.

(If you are doing your work in a module outside OSG, you will need
to change OSG to export OSG.fixorgD and procedure OSG.fix.  At the
moment the only variables of relevance here which are exported are
OSG.pc and OSG.code - but the others are used, for example, in
OSG.Close.

All this comes about because the Oberon-0 compiler generates normal
module code which is intended to run inside the Oberon System, ie it
is not "standalone" anymore.  The full Oberon-07 compiler does both
standalone [MODULE* ] and a normal module.  So quite some changes
are necessary to do what you want.)


>> the first
>> instruction is a branch to the body over any procedures
>> there are  a further seven instructions reserved, e.g. for a branch
>> when using interrupts in a von-Neumann (as opposed to modified >
>> Harvard) setup.
> 
> Actually I thought we are branching over boot limits, did not know 
> about
> interrupts in a von-Neumann
> 
>  0 A branch instruction to the initializing body of module Modules (*
> set by OLR.Link *)
> 12 The limit of available memory
> 16 The address of the end of the module space loaded
> 20 The current root of the links of loaded modules
> 24 The current limit of the module area

The only variable set by the bootloader is at 12, the limit of memory, 
so
you definitely need to load your code before or later than that.  The
others are created by the Oberon boot linker and loaded when the boot
loader loads the Oberon Inner Core from disk.  They are not relevant 
here
and you can ignore them.


>> Note that the serial bootfile format is different from
>> See BootLoad.Mod for details.
> 
> Yes, now it works. But I have two jumps, not neat, but with more
> understanding, I can improve further, to have a single branch.

You will always need two jumps in general because you need a jump to
somewhere where you set up SB, then you need to jump to the body of
the module.  The body is not always the beginning because procedures
come first, and you can't insert because the displacements to
procedures will be wrong.

Also note the bootloader has set the stack to 80000H (StkOrg) and
this grows downwards.

Mainly I think, you need a more advanced Storeboot procedure - you
still don't seem to be creating a correct bootfile, I think this is
where some confusion occurs.

Your first word of the bootfile should be the length of the first
section, the second word should be the address, and then the subsequent
words should be the data you want to put at that address.  Any 
subsequent
section must also be like this.  You need to finish with a section with
a word len of zero.

 From section 14.1 of the book:

   BootFile = {block}.
   block = size address {byte}. (size and address are words)

and size = 0 indicates the end of the file.  {} means possibly repeated.

This is read by the code in BootLoad.Mod:

   PROCEDURE RecInt(VAR x: INTEGER);
     VAR z, y, i: INTEGER;
   BEGIN z := 0;  i := 4;
     REPEAT i := i-1;
       REPEAT UNTIL SYSTEM.BIT(rsCtrl, 0);     <--- wait for RS232 byte
       SYSTEM.GET(rsData, y);                  <--- read RS232 byte
       z := ROR(z+y, 8)             <--- rollup byte into 4-byte integer
     UNTIL i = 0;
     x := z
   END RecInt;

   PROCEDURE LoadFromLine;
     VAR len, adr, dat: INTEGER;
   BEGIN RecInt(len);
     WHILE len > 0 DO
       RecInt(adr);
       REPEAT RecInt(dat); SYSTEM.PUT(adr, dat); adr := adr + 4; len := 
len - 4 UNTIL len = 0;
       RecInt(len)
     END
   END LoadFromLine;


So an example bootfile with three sections might be

   04 00 00 00   size = 4  (first section)
   00 00 00 00   address = 0
   xx yy 00 E7   E700yyxx  B start  <--- need to calculate this 
displacement

   08 00 00 00   size = 8  (second section)
   aa bb cc dd   address = ddccbbaa
   0C 00 00 4D   4D00000C  MOV SB 0000000C
   uu vv 00 E7   E700vvuu  B body   <--- need to calculate this 
displacement

   pp qq 00 00   size = 0000qqpp  pc * 4  (third section)
   ee ff gg hh   address = hhggffee
   00 00 00 40   40000000  MOV R0 0  <--- body (happens to be at 
beginning if no procedures)
   08 00 D0 A0   A0D00008  STW R0 SB 8
   ...           rest of module code

   00 00 00 00   size = 0  (fourth section, end of file marker)


> In the OS[P].Mod there is a `dc := 0' what is the meaning; Code 
> displacement???

Probably "declaration counter" or something.  It tracks the displacement 
from SB of each declared variable.  Interestingly this is finalised 
before code is output, because in Oberon, module variable declarations 
have to come before procedures and the body.

But you shouldn't need to change this because you just need to load SB 
with a suitable value at startup.  In the above I have suggested 12, 
because for free you will get the size of memory in the first declared 
(integer) variable, in case you are interested in it.

HTH,
Paul


More information about the Oberon mailing list