! B-tree system-dependent module (internal filesystem version).
! Compatibility version: believes in new-style mapped databases and old-style
! unmapped ones.  New databases will be created new-style, but the old-style
! code has to remain for the foreseeable future....
! Note that the tree and data modules are not affected by whether or not
! this layer implements transactions, as they just read and write logical
! blocks using the routines provided here...

%externalstring(47) copyright %alias "GDMR_(C)_TREE" = %c
   "Copyright (C) 1987 George D.M. Ross"

%option "-low-nonstandard-nocheck-nodiag-noline"
!%option "-low-nonstandard"

%constinteger initial size = 4

%constinteger data header site = 1

%include "GDMR_H:FSysAcc.Inc"
%include "GDMR_H:FSys.Inc"

%constinteger auto truncate flag = 1

!! %include "GDMR_H:Dump.Inc"

%include "Moose:Mouse.Inc"
%systemroutinespec phex(%integer i)


! Old-style (unmapped) header, contains root block pointer, free list, and some
! stuff used during testing.  >>>NB<<< it is essential that the "epoch" field
! of the new-style header is reserved and ZERO in the old-style header.

%constinteger old free size = 256 - 8
%recordformat old header fm((%short checksum, 
                             %short root, used, flags, free count,
                             %shortarray free(1 : old free size),
                             %short should have, seed) %c
                            %or %bytearray b(0 : 511))


! New-style mapped database.

! A two-level indirectory is used to convert from logical page number to
! physical page number.  The following two records describe the upper and
! lower levels of the map respectively.  To save space, the tree root block
! is kept in the master page.
!
! The update strategy for the database is that when a page (data, tree or
! map) is written for the first time a new physical page is allocated for it
! and the map is updated accordingly.  The database is commited iff the master
! page has been written out successfully to the alternate site, the newer of the
! two pages being used when the database is first opened.

%constinteger map limit = 255 - 1 - 1 - 2
%recordformat master fm(%short checksum,
                        %shortarray map(0 : map limit),
                        %short tree root,
                        %integer epoch)

%constinteger page map limit = 254
%recordformat page map fm(%short checksum,
                          %shortarray page(0 : page map limit))


! The following record holds the state of the open database.  If the old header
! field is non-nil then the database is assumed to be an old-style one with
! no indirectory map.  Otherwise (for databases with maps) the active master
! page and the most recent lower-level map page are cached, together with a
! used and a modified bitmap giving the status of the physical pages in the
! database: allocations of physical pages take place via the used map, while
! the modified map determines whether a new site is required or not (on first
! write to a page).

%constinteger bitmap size = 128  { * 8 to get blocks }
%recordformat tree access fm(%record(*)%name access,
                             %integer file token, access mode, file size,
                             %record(old header fm)%name old header,
                             %record(master fm)%name master,
                             %integer master site,
                             %integer master page changed,
                             %record(page map fm)%name page map,
                             %integer last page map site,
                             %integer page map changed,
                             %bytearray physical used(0 : bitmap size),
                             %bytearray logical used(0 : bitmap size),
                             %bytearray modified(0 : bitmap size))
                             

! Error codes

%constinteger checksum error = -500
%constinteger dud mode       = -505


! Checksum stuff

%predicate check checksum(%record(*)%name b)
!  %short c
!  %shortname p
!  %integer i
!     c = 0;  p == shortinteger(addr(b))
!     %for i = 1, 1, 256 %cycle
!        c <- c + p;  p == p [1]
!     %repeat
!     %false %if c # 0
!     %true
   %label L, F
      A0 = addr(b)
      D0 = 0;  D1 = 255
   L: *add.w (A0)+, D0
      *dbra D1, L
      *tst.w D0
      *bne F
      %true
   F: %false
%end

%routine set checksum(%record(*)%name b)
   %short c
   %shortname p
   %integer i
      c = 0;  p == shortinteger(addr(b) + 2)
      %for i = 2, 1, 256 %cycle
         c <- c + p;  p == p [1]
      %repeat
      shortinteger(addr(b)) = -c
%end


! I/O stuff proper

%routine zap block(%record(*)%name block)
   D0 = 127
L: *clr.l (A0)+
   *dbra D0, L
%end

%integerfn read physical block(%integer access token, block,
                               %record(*)%name buffer)
   %record(tree access fm)%name a
   %integer bytes, status
      a == record(access token)
      status = fsys read file block(a_access, a_file token,
                                    block, bytes, buffer)
      %result = status %if status # 0
      %result = 0 %if check checksum(buffer)
      %result = checksum error
%end

%integerfn write physical block(%integer access token, block,
                                %record(*)%name buffer)
   %record(tree access fm)%name a
      a == record(access token)
      set checksum(buffer)
      %result = fsys write file block(a_access, a_file token,
                                      block, 512, buffer)
%end

%integerfn write unreadable(%record(tree access fm)%name ta,
                            %integer block)
   %recordformat block fm(%integerarray x(0 : 127))
   %record(block fm) b
   %integer i, status
      !! printstring("Write unreadable: block ");  write(block, 0)
      !! printstring(", size ");  write(ta_file size, 0);  newline
      b_x(i) = 16_ABABABAB %for i = 0, 1, 127
      status = fsys write file block(ta_access, ta_file token,
                                     block, 512, b)
      %if status # 0 %start
         printstring("Write unreadable: ");  write(status, 0)
         newline
         %result = -10001
      %finish
      ta_file size = block
      %result = 0
%end


! Map manipulation routines: read and write map pages, obtain and release
! logical and physical pages.

!! %routine show bitmap(%bytename map)
!!    %integer n, i
!!       %for n = 1, 1, 8 %cycle
!!          space
!!          %for i = 0, 1, 7 %cycle
!!             %if map & (1 << i) = 0 %then print symbol('0') %c
!!                                    %else print symbol('1')
!!          %repeat
!!          map == map [1]
!!       %repeat
!! %end

%integerfn translate for read(%record(tree access fm)%name ta,
                              %integer logical, %integername physical)
   ! Performs a map lookup to convert the logical block address to a
   ! physical address.  Returns zero physical address if the page is unmapped.
   %integer map page, map offset, status
      !! printstring("Translate for read: ");  write(logical, 0);  newline
      map page = logical // page map limit
      map offset = logical - map page * page map limit
      map page = ta_master_map(map page)
      %if ta_last page map site # map page %start
         %if ta_page map changed # 0 %start
            !! printstring("Previous map page at ")
            !! write(ta_last page map site, 0)
            !! printstring(" changed, flushing")
            !! newline
            status = write physical block(addr(ta), ta_last page map site,
                                          ta_page map)
            %result = status %if status # 0
         %finish
         ta_page map changed = 0
         %if map page = 0 %start
            !! printstring("New page, zapping");  newline
            zap block(ta_page map)
         %else
            ta_last page map site = -1
            !! printstring("Reading from ");  write(map page, 0)
            !! newline
            status = read physical block(addr(ta), map page, ta_page map)
            %result = status %if status # 0
         %finish
         ta_last page map site = map page
      %finish
      physical = ta_page map_page(map offset)
      !! printstring("-> ");  write(physical, 0);  newline
      %result = 0
%end

%integerfn new physical page(%record(tree access fm)%name ta)
   %integer bit, byte, mask, block, status
      !! printstring("Map:");  show bitmap(ta_physical used(0));  newline
      %for byte = 0, 1, bitmap size %cycle
         %if ta_physical used(byte) # 255 %start
            %for bit = 0, 1, 7 %cycle
               mask = 1 << bit
               %if ta_physical used(byte) & mask = 0 %start
                  ta_physical used(byte) = ta_physical used(byte) ! mask
                  block = byte << 3 + bit
                  !! printstring("New physical page ")
                  !! write(block, 0);  printstring(", size ")
                  !! write(ta_file size, 0);  newline
                  %if block >= ta_file size %start
                     status = write unreadable(ta, block)
                     %result = -10002 %if status # 0
                  %finish
                  %result = block
               %finish
            %repeat
         %finish
      %repeat
      !! printstring("No new physical page");  newline
      %result = -10003
%end

%integerfn translate for write(%record(tree access fm)%name ta,
                               %integer logical, %integername physical)
   ! Perform a map lookup.  Allocate a new physical page if this is the first
   ! time the logical page has been modified.
   %integer bit, byte, status, map page, map offset, new physical map page
      ! First pull in the map page and translate the current physical
      ! address.  We'll have to check whether or not it has been updated this
      ! time round.
      !! printstring("Translate for write: ");  write(logical, 0);  newline
      status = translate for read(ta, logical, physical)
      %result = status %if status # 0
      bit = physical & 7;  byte = physical >> 3
      %if byte > bitmap size %start
         printstring("Translate for write -- ""modified"" bitmap size error: ")
         write(byte, 0);  newline
      %finish
      %if (byte # 0 %or bit # 0) %and ta_modified(byte) & (1 << bit) # 0 %start
         ! Already updated
         !! printstring("Already updated to ");  write(physical, 0);  newline
         %result = 0
      %finish
      ! Since we know now that this is a first-time update for this
      ! logical page, we'll have to allocate a new physical page and update 
      ! the corresponding map page.
      physical = new physical page(ta)
      !! printstring("First update, mapping to ");  write(physical, 0);  newline
      %result = -10004 %if physical <= 0
      map page = logical // page map limit
      map offset = logical - map page * page map limit
      ! The map page is in store.  Update it.  If necessary obtain a new
      ! physical page for it.
      ta_page map_page(map offset) = physical
      ta_page map changed = 1
      !! printstring("MM");  show bitmap(ta_modified(0));  newline
      bit = ta_last page map site & 7;  byte = ta_last page map site >> 3
      %if byte > bitmap size %start
         printstring("Translate for write -- ""modified"" bitmap size error: ")
         write(byte, 0);  newline
      %finish
      %if bit = 0 = byte %or ta_modified(byte) & (1 << bit) = 0 %start
         ! First modification for map page.  Get a new physical page.
         new physical map page = new physical page(ta)
         !! printstring("New map page: ");  write(new physical map page, 0)
         !! newline
         %result = -10005 %if new physical map page < 0
         ta_master_map(map page) = new physical map page
         ta_last page map site = new physical map page 
         ta_master page changed = 1
         bit = new physical map page & 7;  byte = new physical map page >> 3
         %if byte > bitmap size %start
            printstring("Translate for write -- ""modified"" bitmap size error: ")
            write(byte, 0);  newline
         %finish
         ta_modified(byte) = ta_modified(byte) ! (1 << bit)
      %finish
      ! Now mark the new physical page as changed
      bit = physical & 7;  byte = physical >> 3
      %if byte > bitmap size %start
         printstring("Translate for write -- ""modified"" bitmap size error: ")
         write(byte, 0);  newline
      %finish
      ta_modified(byte) = ta_modified(byte) ! (1 << bit)
      %result = 0
%end

! Logical block I/O.  For unmapped trees these just call the corresponding
! physical I/O routines.  Mapped trees require an indirection.

%integerfn read block(%integer access token, block, %record(*)%name buffer)
   %record(tree access fm)%name ta
   %integer status, physical
      ta == record(access token)
      %result = read physical block(access token, block, buffer) %c
         %if ta_old header ## nil
      !! printstring("Read (mapped) block ");  write(block, 0);  newline
      status = translate for read(ta, block, physical)
      %result = status %if status # 0
      !! printstring("Translated to ");  write(physical, 0);  newline
      zap block(buffer) %and %result = 0 %if physical <= 0;  ! Unmapped
      %result = read physical block(access token, physical, buffer)
%end

%integerfn write block(%integer access token, block, %record(*)%name buffer)
   %record(tree access fm)%name ta
   %integer status, physical
      ta == record(access token)
      %result = dud mode %if ta_access mode = 0
      %result = write physical block(access token, block, buffer) %c
         %if ta_old header ## nil
      !! printstring("Write (mapped) block ");  write(block, 0);  newline
      status = translate for write(ta, block, physical)
      %result = status %if status # 0
      !! printstring("Translated to ");  write(physical, 0);  newline
      %result = write physical block(access token, physical, buffer)
%end


! Free block list management.  For non-mapped databases the free list is held
! in the header block, while the bitmap and map pages themselves are used for
! mapped databases.

%integerfn get new block(%integer access token)
   %record(tree access fm)%name ta
   %record(old header fm)%name old header
   %integer it, i, bit, byte, mask
      ta == record(access token)
      %result = dud mode %if ta_access mode = 0
      %if ta_old header ## nil %start
         old header == ta_old header
         %if old header_free count = 0 %start
            ! Nothing spare.  Should extend.
            old header_used = 1 %if old header_used = 0;  ! Skip data header
            old header_used = old header_used + 1
            i = write block(access token, 0, old header)
            %result = i %if i < 0
            %result = old header_used
         %else
            it = old header_free(1);  ! Use the first
            old header_free count = old header_free count - 1
            %if old header_free count > 0 %start
               ! More than one free.  Shuffle down the rest.
               old header_free(i) = old header_free(i + 1) %c
                  %for i = 1, 1, old header_free count
            %finish
            i = write block(access token, 0, old header)
            %result = i %if i < 0
            %result = it
         %finish
      %finish
      ! New-style header....
      %for byte = 0, 1, bitmap size %cycle
         %if ta_logical used(byte) # 255 %start
            %for bit = 0, 1, 7 %cycle
               mask = 1 << bit
               %if ta_logical used(byte) & mask = 0 %start
                  ta_logical used(byte) = ta_logical used(byte) ! mask
                  !! printstring("Logical block ")
                  !! write(byte << 3 + bit, 0)
                  !! printstring(" allocated");  newline
                  %result = byte << 3 + bit
               %finish
            %repeat
         %finish
      %repeat
      %result = -10006
%end

%integerfn free block(%integer access token, which)
   %record(tree access fm)%name ta
   %record(old header fm)%name old header
   %integer i, pos, bit, byte, status, physical
   %integer map page slot, map page, map offset, new physical map page
      ta == record(access token)
      %result = dud mode %if ta_access mode = 0
      %if ta_old header ## nil %start
         old header == ta_old header
         %result = 0 %if old header_free count >= old free size;  ! Throw it away meantime
         %if old header_free count = 0 %start
            old header_free(1) = which
            old header_free count = 1
         %else
            %for pos = 1, 1, old header_free count %cycle
               %if old header_free(pos) > which %start
                  ! Found insertion position
                  old header_free(i + 1) = old header_free(i) %c
                     %for i = old header_free count, -1, pos
                  old header_free(pos) = which
                  old header_free count = old header_free count + 1
                  -> write it
               %finish
            %repeat
            ! Muat be last of all
            old header_free count = old header_free count + 1
            old header_free(old header_free count) = which
         %finish
   write it:
         %result = write block(access token, 0, old header)
      %finish

      ! New-style header....
      !! printstring("Freeing logical block ");  write(which, 0);  newline
      bit = which & 7;  byte = which >> 3
      %if byte > bitmap size %start
         printstring("Free block bitmap error: ");  write(byte, 0)
         newline
      %finish
      ta_logical used(byte) = ta_logical used(byte) & (\ (1 << bit))
      ! As well as marking the logical block free we have to zap its entry
      ! in the translation map and note it as unmodified.
      map page slot = which // page map limit
      map offset = which - map page slot * page map limit
      map page = ta_master_map(map page slot)
      %result = -10014 %if map page = 0;  ! Unmapped as yet??
      %if ta_last page map site # map page %start
         %if ta_page map changed # 0 %start
            !! printstring("Previous map page at ")
            !! write(ta_last page map site, 0)
            !! printstring(" changed, flushing")
            !! newline
            status = write physical block(addr(ta), ta_last page map site,
                                          ta_page map)
            %result = status %if status # 0
         %finish
         ta_page map changed = 0
         ta_last page map site = -1
         !! printstring("Reading from ");  write(map page, 0)
         !! newline
         status = read physical block(addr(ta), map page, ta_page map)
         %result = status %if status # 0
         ta_last page map site = map page
      %finish
      physical = ta_page map_page(map offset)
      !! printstring("-> ");  write(physical, 0);  newline
      ta_page map_page(map offset) = 0
      ta_page map changed = 1
      ! Now, have we modified this map page before?  If not, we'll have
      ! to allocate a new physical block for it and update the master....
      !! printstring("MM");  show bitmap(ta_modified(0));  newline
      bit = map page & 7;  byte = map page >> 3
      %if byte > bitmap size %start
         printstring("Free block -- ""modified"" bitmap size error: ")
         write(byte, 0);  newline
      %finish
      %if ta_modified(byte) & (1 << bit) = 0 %start
         ! First modification for map page.  Get a new physical page.
         new physical map page = new physical page(ta)
         !! printstring("New map page: ");  write(new physical map page, 0)
         !! newline
         %result = -10015 %if new physical map page < 0
         ta_master_map(map page slot) = new physical map page
         ta_last page map site = new physical map page 
         ta_master page changed = 1
         bit = new physical map page & 7;  byte = new physical map page >> 3
         %if byte > bitmap size %start
            printstring("Free block -- ""modified"" bitmap size error: ")
            write(byte, 0);  newline
         %finish
         ta_modified(byte) = ta_modified(byte) ! (1 << bit)
      %finish
      ! Now mark the new physical page as free and unchanged
      bit = physical & 7;  byte = physical >> 3
      %if byte > bitmap size %start
         printstring("Translate for write -- ""modified"" bitmap size error: ")
         write(byte, 0);  newline
      %finish
      ta_modified(byte) = ta_modified(byte) & (\ (1 << bit))
      %result = 0
%end


! Tree root determination.  Rather than take up an entire block in a known
! location to store the pointer to the tree's root, we decide instead to keep
! it in the database indirectory master page.
! Non-mapped databases hold it in the tree header.

%integerfn find root(%integer access token)
   %record(tree access fm)%name ta
      ta == record(access token)
      %result = ta_old header_root %if ta_old header ## nil
      %result = ta_master_tree root
%end

%integerfn set root(%integer access token, new root)
   %record(tree access fm)%name ta
   %record(old header fm)%name old header
      ta == record(access token)
      %result = dud mode %if ta_access mode = 0
      %if ta_old header ## nil %start
         old header == ta_old header
         old header_root = new root
         %result = write block(access token, 0, old header)
      %finish
      ta_master_tree root = new root
      ta_master page changed = 1
      %result = 0
%end


! Bitmap construction

%routine bitmap set(%integer bit, %bytename map)
   %integer byte, offset
      byte = bit >> 3;  offset = bit & 7
      map [byte] = map [byte] ! (1 << offset)
%end

%integerfn build bitmaps(%record(tree access fm)%name ta)
   %integer status, map page, map offset, map physical, map logical
      ta_physical used(0) = 2_00000011;  ! Two master pages
      ta_logical used(0)  = 2_00000011;  ! Reserved + data header
      ta_modified(0)      = 2_00000011;  ! Two master pages (??)
      %for map page = 0, 1, map limit %cycle
         map physical = ta_master_map(map page)
         %if map physical # 0 %start
            ! Map page exists, so note it and scan it
            !! printstring("Note map page ");  write(map physical, 0)
            !! newline
            bitmap set(map physical, ta_physical used(0))
            status = read physical block(addr(ta), map physical, ta_page map)
            %result = status %if status # 0
            ta_last page map site = map physical
            %for map offset = 0, 1, page map limit %cycle
               map physical = ta_page map_page(map offset)
               %if map physical # 0 %start
                  ! Database page exists.  Note physical site, then
                  ! calculate and note logical address.
                  !! printstring("Note physical database page ")
                  !! write(map physical, 0);  newline
                  bitmap set(map physical, ta_physical used(0))
                  map logical = map page * page map limit + map offset
                  !! printstring("Note logical database page ")
                  !! write(map logical, 0);  newline
                  bitmap set(map logical, ta_logical used(0))
               %finish
            %repeat
         %finish
      %repeat
      !! printstring("PU");  show bitmap(ta_physical used(0));  newline
      !! printstring("LU");  show bitmap(ta_logical  used(0));  newline
      !! printstring("M ");  show bitmap(ta_modified     (0));  newline
      %result = 0
%end


! Tree module proper

%include "GDMR_H:Tree.Core"
%include "GDMR_H:Data.Core"


! open/close/create

%owninteger open CPU = 0, open real = 0

%externalintegerfn B tree open by ID(%record(fsys access fm)%name access,
                                     %integer request flags,
                                     %integer ID, mode,
                                     %integername token, flags)
   %record(tree access fm)%name a == nil
   %record(master fm)%name other master
   %integer status, x, access mode, compatible mode
      !! printstring("Open ");  phex(ID)
      !! printstring(", mode ");  write(mode, 0);  newline
      !! open CPU = CPU time;  open real = real time
      %if mode = 0 %start
         access mode = read access
         compatible mode = read access
      %else
         access mode = read access ! modify access
         compatible mode = 0
      %finish
      a == new(a);  a = 0
      !! printstring("'a' at ");  phex(addr(a))
      !! printstring(", heap ");  phex(integer(addr(a) - 4));  newline
      a_access == access;  a_access mode = mode
      status = fsys open file(access, ID, access mode, compatible mode,
                              request flags, a_file token, a_file size, flags)
      !! printstring("Open status: ");  write(status, 0);  newline
      dispose(a) %and %result = status %if status # 0
      a_file size = a_file size >> 9;  ! bytes -> blocks
      a_master == new(a_master)
      !! printstring("'a_master' at ");  phex(addr(a_master))
      !! printstring(", heap ");  phex(integer(addr(a_master) - 4));  newline
      status = read physical block(addr(a), 0, a_master)
      %if status # 0 %start
         !! printstring("Read pyhysical status: ");  write(status, 0);  newline
         x = fsys close file(nil, a_file token, 0)
         !! printstring("Close status: ");  write(x, 0);  newline
         dispose(a_master)
         dispose(a)
         !! printstring("Returning ");  write(status, 0);  newline
         %result = status
      %finish
      %if a_master_epoch = 0 %start
         !! printstring("Unmapped tree ");  phex(ID);  newline
         a_old header == record(addr(a_master))
         a_master == nil
      %else
         other master == new(other master)
         !! printstring("'other master' at ");  phex(addr(other master))
         !! printstring(", heap ");  phex(integer(addr(other master) - 4));  newline
         !! printstring("B-tree ");  phex(ID)
         !! printstring(": master 0 epoch ");  phex(a_master_epoch)
         !! newline
         status = read physical block(addr(a), 1, other master)
         %result = status %if status # 0
         !! spaces(17);  printstring("master 1 epoch ")
         !! phex(other master_epoch);  newline
         !! dump(512, byteinteger(addr(a_master)))
         !! dump(512, byteinteger(addr(other master)))
         %if other master_epoch > a_master_epoch %start
            dispose(a_master)
            a_master == other master
            a_master site = 0;  ! Always the *target* site
         %else
            dispose(other master)
            a_master site = 1;  ! Always the *target* site
         %finish
         a_old header == nil
         a_page map == new(a_page map);  a_page map = 0
         !! printstring("'a_page map' at ");  phex(addr(a_page map))
         !! printstring(", heap ");  phex(integer(addr(a_page map) - 4));  newline
         %if a_access mode # 0 %start
            status = build bitmaps(a)
            %result = status %if status # 0
         %finish
      %finish
      token = addr(a)
      !! printstring("Opened as ");  phex(token)
      !! printstring(", size is ");  write(a_file size, 0);  newline
      %result = 0
%end


%externalintegerfn B tree close(%integer token, abandon)
   %record(tree access fm)%name a
   %integer status, close CPU, close real
      a == record(token)
      %if a_old header ## nil %start
         dispose(a_old header)
      %else
         ! Write out the maps, then dispose them
         !! printstring("Closing: abandon ");  write(abandon, 0)
         !! printstring(", mpc ");  write(a_master page changed, 0)
         !! printstring(", ms ");  write(a_master site, 0)
         !! printstring(", pmc ");  write(a_page map changed, 0)
         !! printstring(", lpms ");  write(a_last page map site, 0)
         !! newline
         %if a_access mode # 0 %and abandon = 0 %start
            %if a_master page changed # 0 %start
               %if a_page map changed # 0 %start
                  !! printstring("Writing last modified map to ")
                  !! write(a_last page map site, 0);  newline
                  status = write physical block(token, a_last page map site,
                                                a_page map)
                  %result = status %if status # 0
               %finish
               a_master_epoch = a_master_epoch + 1
               !! printstring("Writing master page to ")
               !! write(a_master site, 0);  newline
               status = write physical block(token, a_master site, a_master)
               %result = status %if status # 0
            %finish
         %finish
         dispose(a_page map)
         dispose(a_master)
      %finish
      status = fsys close file(a_access, a_file token, auto truncate flag)
      !! %if status < 0 %start
      !!    printstring("Close tree failed: ");  write(status, 0)
      !!    newline
      !! %finish
      dispose(a)
      !! close CPU = CPU time;  close real = real time
      !! printstring("Tree: ");  write(close CPU - open CPU, 0)
      !! printstring(" CPU, ");  write(close real - open real, 0)
      !! printstring(" real");  newline
      %result = 0
%end


%externalintegerfn B tree create(%record(fsys access fm)%name access,
                                 %integer request flags,
                                 %string(31) name, %integer partition,
                                 %integer benefactor ID,
                                 %integername ID)
   %record(master fm) master
   %integer status, token, size, flags
      status = fsys create file(access, name, partition, benefactor ID,
                                request flags, initial size, ID)
      %result = status %if status # 0
      status = fsys open file(nil, ID, modify mode, 0, request flags,
                              token, size, flags)
      %result = status %if status # 0
      ! Initialise the two master pages with epoch 1 & everything else
      ! zero -- this means that the database is implicitly zero.
      master = 0
      master_epoch = 1
      master_checksum = -1
      status = fsys write file block(access, token, 0, 512, master)
      %result = status %if status # 0
      status = fsys write file block(access, token, 1, 512, master)
      %result = status %if status # 0
      %result = fsys close file(access, token, 0)
%end

%end %of %file
