/* $NetBSD$ */

/*
 * Copyright (c) 1994-1996 Mark Brinicombe.
 * Copyright (c) 1994-1996 Brini.
 * All rights reserved.
 *
 * This code is derived from software written for Brini by Mark Brinicombe
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Mark Brinicombe.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * RiscBSD kernel project
 *
 * bsdbooter.c
 *
 * RiscBSD boot loader
 *
 * Created      : 12/09/94
 * Last updated : 12/07/96
 *
 * Based on kate/boot/boot.c
 *
 *    $Id$
 */

/* Include standard header files */

#include <stdarg.h>

/* Include local headers */

#include "katelib.h"
#include "swiv.h"
#include "swis.h"
#include "arm.h"
#include "stand.h"

#include "bootconfig.h"

/*
 * Declare global variables
 */

# define VERSION "2.87"

# define USE_MODULEAREA

# define KERNAREA 512
# define KERNBASE 0xf0000000

# define TABLEAREA 513
# define LOADAREA 514

# define SCRATCHSIZE 0xc000


# define OS_DynamicArea 0x66
# define OS_Memory      0x68
# define OS_MMUControl  0x6b

# define FASTBOOT_FILENAME "<BtRiscBSD$Dir>.booter.fastboot"

/*
 * Declare external variables
 */

extern char *__cli;

/*
 * Local vars. Some of these have to be global because they are used after
 * the processor has been switches to SVC mode local variables would be
 * lost as they would be on the USR mode stack.
 */

BootConfig bootconfig;
int in[3], out[3];
char kernelname[1024];
unsigned char *buffer;
static aout_t aout;
unsigned int stringtablesize;
unsigned int kernelsize;
unsigned int logical;
unsigned int physical;
unsigned int filesize;
unsigned int copysize;
unsigned int ptr;
/*
 * Local function prototypes
 */

void fatal(struct Error *error);
void fatal1(int error, char *string);
unsigned char *locate_memory_blocks(void);
void _bsdboot(BootConfig *bootconfig, unsigned int address);

/* Now for the main code */

extern int main (int, char **);

void __exit(int);

void _main(void)
  {
    __exit (main (0, (char **)0));	/* ... ignition */

/* not reached */
  }

/* The main booter code */

int main(int argc, char *argv[])
  {
    char *cliptr;
    int loop;
    int filehandle;
    int filetype;
    unsigned char *arrangementtable;
    struct Error *error;

/* Analyse the command line */

    cliptr = __cli;

/* Skip the command name */

    while (*cliptr != ' ' && *cliptr != 0)
      ++cliptr;

/* Skip any spaces */

    while (*cliptr == ' ')
      ++cliptr;

/* Check for another parameter */

    if (*cliptr != 0)
      {
        for (loop = 0; *cliptr != ' ' && *cliptr != 0; ++loop,++cliptr)
          {
            kernelname[loop] = *cliptr;
          }
        kernelname[loop] = 0;
      }
    else
      strcpy(kernelname, "netbsd");

    strcpy(bootconfig.kernelname, kernelname);

/* Write the command line used to a fastboot file. Execing or Obeying
 * this file will boot RiscBSD. This can be used during the RiscOS bootup
 * to enable a fast boot.
 */

/*
 * Open the autoboot file. Just skip if file cannot be opened.
 */

    if (strstr(__cli, "nofb") == 0)
      {
        error = swix(OS_Find, IN(R0|R1)|OUT(R0), 0x80, FASTBOOT_FILENAME, &filehandle);
        if (error == NULL && filehandle != 0)
          {
            swi(OS_GBPB, IN(R0|R1|R2|R3|R4), 2, filehandle, __cli,
              strlen(__cli), 0);
            swi(OS_GBPB, IN(R0|R1|R2|R3|R4), 2, filehandle, "\n", 1, 0);

/* Close the file */

            swi(OS_Find, IN(R0|R1), 0, filehandle);

            swi(OS_File, IN(R0|R1|R2), 18, FASTBOOT_FILENAME, 0xfeb);
          }
        else
          {
            printf("Warning: Cannot write fastboot file %s\n", FASTBOOT_FILENAME);
          }
      }

/* Set the screen mode ... */

/* I know this is messy. It is currently just a hack to try things out
 * Why didn't Acorn add a SWI call to interpret the mode string and return
 * a mode specifer ?
 */

/* Also this is temporary as eventually the console driver will set the VIDC
 * up as required.
 * It sort of expects the screenmode= options to be at the end of the string.
 */

    {
      char *modeptr;
      int modespec[6];

      modeptr = strstr(__cli, "screenmode=");
      if (modeptr)
        {
          modeptr += 11;
          modespec[0] = 0x00000001;
          modespec[1] = 0x00000000;
          modespec[2] = 0x00000000;
          modespec[3] = 0x00000003;
          modespec[4] = 0x00000000;
          modespec[5] = -1;

          while (*modeptr)
            {
              switch (*modeptr)
                {
                  case 'X':
                  case 'x':
                    ++modeptr;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[1] = (modespec[1] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    break;

                  case 'Y':
                  case 'y':
                    ++modeptr;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[2] = (modespec[2] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    break;
/*
                  case 'C':
                  case 'c':
                  case 'G':
                  case 'g':
                    ++modeptr;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[3] = (modespec[3] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    break;
*/
                  case 'F':
                  case 'f':
                    ++modeptr;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[4] = (modespec[4] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    break;

                  default:
                    ++modeptr;
                    break;
                }
            }
          if (modespec[4] == 0) modespec[4] = -1;
/*          printf("x=%d y=%d c=%d f=%d\n", modespec[1], modespec[2],
            modespec[3], modespec[4]);*/
          fatal(swix(Wimp_SetMode, IN(R0), &modespec));
          bootconfig.framerate = modespec[4];
        }
      else
        bootconfig.framerate = 0;
    }

/* Announcement time .. */

    if (strstr(__cli, "verbose") != 0)
      printf("RiscBSD BootLoader " VERSION " " __DATE__ "\n");

/* A bit of info */

    if (strstr(__cli, "verbose") != 0)
      printf("Kernel: %s\n\r", kernelname);

/* Get the machine id */

    fatal(swix(OS_ReadSysInfo, IN(R0)|OUT(R3), 2, &bootconfig.machine_id));

    bootconfig.magic = BOOTCONFIG_MAGIC;

/* Get the display variables. Failure on any of these will abort the boot */

    in[0] = 149;
    in[1] = 150;
    in[2] = -1;

    fatal(swix(OS_ReadVduVariables, IN(R0|R1), &in, &out));

    bootconfig.display_start = out[0];
    bootconfig.display_size = out[1];

    fatal(swix(OS_ReadModeVariable, IN(R0|R1) | OUT(R2), -1, 9,
      &bootconfig.bitsperpixel));
    fatal(swix(OS_ReadModeVariable, IN(R0|R1) | OUT(R2), -1, 11,
      &bootconfig.width));
    fatal(swix(OS_ReadModeVariable, IN(R0|R1) | OUT(R2), -1, 12,
      &bootconfig.height));

    {
      	int pageblock[3];

	pageblock[1] = bootconfig.display_start;
	fatal(swix(OS_Memory, IN(R0|R1|R2) | OUT(R1), 0x2200,
	  pageblock, 1, pageblock));
	bootconfig.vdram_phys = pageblock[2];
   }

/* Will the kernel support this mode ? */

    if (bootconfig.bitsperpixel != 3)
      {
        printf("Error: Current kernels only support 8 bpp for booting.\n");
        return(1);
      }

/* Will the kernel support this mode ? */

    if (bootconfig.bitsperpixel > 3)
      {
        printf("Error: Only 1, 2, 4 or 8 bpp modes are currently supported\n");
        return(1);
      }

/* Get the arrangement table for the memory */

    arrangementtable = locate_memory_blocks();

/*
 * Ok we support a.out files. This means that we need to
 * identify the type.
 */

/* Get the size of the file */

    filesize = 0;
    fatal(swix(OS_File, IN(R0|R1)|OUT(R0|R4), 5, kernelname, &filetype, &filesize));

    if (filetype != 1)
      {
        printf("%s is not a object that can be booted\n", kernelname); 
        return(1);
      }

/* Verbose info to the user. This is mainly debugging */

    if (strstr(__cli, "verbose") != 0)
      printf("filesize = %08x\n", filesize);

    if (filesize == 0) {
      printf("Error: Kernel image too small\n");
      return(1);
    }

# ifdef USE_MODULEAREA

/* Allocate memory in module area to hold the data we are loading */

    fatal(swix(OS_Module, IN(R0|R3)|OUT(R2), 6, filesize, &buffer));

# else

/* Allocate memory to hold the data we are loading */

    swix(OS_DynamicArea, IN(R0|R1), 1, LOADAREA);

    fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8)|OUT(R3), 0,
      LOADAREA, filesize, -1, 0x80, filesize, 0, 0, "Kate Data", &buffer));

# endif

/*
 * Open the file.
 */

    swi(OS_Find, IN(R0|R1)|OUT(R0), 0x40, kernelname, &filehandle);
    if (filehandle == 0)
      {
        printf("Error: Cannot read kernel file %s\n", kernelname);
        return(1);
      }

/* Load the file into memory */

    swi(OS_GBPB, IN(R0|R1|R2|R3|R4), 3, filehandle, buffer, filesize, 0);

/* Close the file */

    fatal(swix(OS_Find, IN(R0|R1), 0, filehandle));

/* Take a copy of the possible a.out header */

    aout = *((aout_t *)buffer);

/* Do we have an a.out file ? */

    switch(aout.a_magic & MAGICMASK) {
      case NMAGIC:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is NMAGIC a.out format\n");
        aout.a_syms = 0;
        stringtablesize = 0;
	break;
      case OMAGIC:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is OMAGIC a.out format\n");
        aout.a_syms = 0;
        stringtablesize = 0;
	break;
      case ZMAGIC:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is ZMAGIC a.out format\n");
        stringtablesize = filesize - (unsigned int)(aout.a_text +
          aout.a_data + aout.a_syms);
        if (aout.a_entry < KERNBASE || aout.a_entry > KERNBASE + 0x00100000) {
          printf("Error: Kernel entry point out of range\n");
          return(0);
        }
	break;
      default:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is unrecognised, guessing it is AIF format\n");
	aout.a_text = filesize;
	aout.a_data = 0;
	aout.a_bss = 0;
        aout.a_syms = 0;
        stringtablesize = 0;
        break;
    }

    kernelsize = (unsigned int)(aout.a_text + aout.a_data + aout.a_bss);

/* Add symbol table size if required. */

    if (strstr(__cli, "symtab") != 0) {
      kernelsize += (unsigned int)aout.a_syms + stringtablesize;
    } else {
      aout.a_syms = 0;
      stringtablesize = 0;
    }

    if (strstr(__cli, "verbose") != 0)
      {
        printf("a_text = %08x\n", aout.a_text);
        printf("a_data = %08x\n", aout.a_data);
        printf("a_bss  = %08x\n", aout.a_bss);
        printf("a_syms = %08x\n", aout.a_syms);
        printf("snames = %08x\n", stringtablesize);
      }

/* Give ourselves 16K of spare space and round off to a page */

    kernelsize = (kernelsize + 0x4000) & ~(bootconfig.pagesize-1);

/* Set the virtual address of the kernel in the bootconfig structure */

    bootconfig.kernvirtualbase = KERNBASE;
    bootconfig.kernsize = kernelsize;
    bootconfig.argvirtualbase = bootconfig.kernvirtualbase
                              + bootconfig.kernsize;
    bootconfig.argsize = bootconfig.pagesize;
    bootconfig.scratchvirtualbase = bootconfig.argvirtualbase
                                  + bootconfig.argsize;
    bootconfig.scratchsize = SCRATCHSIZE;

    kernelsize += bootconfig.argsize;

    kernelsize += bootconfig.scratchsize;

/* Verbose info to the user. This is mainly debugging */

    if (strstr(__cli, "verbose") != 0)
      {
        printf("filesize = %08x\n", filesize);
        printf("bootconfig.kernvirtualbase = %08x\n",
          bootconfig.kernvirtualbase);
        printf("bootconfig.kernsize = %08x\n", bootconfig.kernsize);
        printf("bootconfig.argvirtualbase = %08x\n",
          bootconfig.argvirtualbase);
        printf("bootconfig.argsize = %08x\n", bootconfig.argsize);
        printf("bootconfig.scratchvirtualbase = %08x\n",
          bootconfig.scratchvirtualbase);
        printf("bootconfig.scratchsize = %08x\n", bootconfig.scratchsize);
        printf("kernelsize = %08x\n", kernelsize);
	printf("display_start = %08x\n", bootconfig.display_start);
	printf("display_size = %08x\n", bootconfig.display_size);
	printf("physical dstart = %08x\n", bootconfig.vdram_phys);
      }

/* This is redundant at the moment */

    swix(OS_DynamicArea, IN(R0|R1), 1, KERNAREA);

    fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8), 0,
      KERNAREA, 0, KERNBASE, 0x80, 0x1000, 0, 0, "Kate Kernel"));

/* Shutdown RiscOS cleanly ... */

/* Close all open files and shutdown filing systems */

    if (strstr(__cli, "noboot") == 0)
      {
        swix(OS_FSControl, IN(R0), 23);

/* Issue a pre-reset service call to reset the podules */

        swix(OS_ServiceCall, IN(R1), 0x45);

/* Kill the etherH module to avoid locks up on reboot */

/*        swix(OS_Module, IN(R0|R1), 4, "EtherH");*/
      }

/* More user information describing the memory found */

    if (strstr(__cli, "verbose") != 0)
      {
        printf("DRAM bank 0a = %08x %08x\n", bootconfig.dram[0].address,
          bootconfig.dram[0].pages * bootconfig.pagesize);
        printf("DRAM bank 0b = %08x %08x\n", bootconfig.dram[1].address,
          bootconfig.dram[1].pages * bootconfig.pagesize);
        printf("DRAM bank 1a = %08x %08x\n", bootconfig.dram[2].address,
          bootconfig.dram[2].pages * bootconfig.pagesize);
        printf("DRAM bank 1b = %08x %08x\n", bootconfig.dram[3].address,
          bootconfig.dram[3].pages * bootconfig.pagesize);
        printf("VRAM bank 0  = %08x %08x\n", bootconfig.vram[0].address,
          bootconfig.vram[0].pages * bootconfig.pagesize);
    }

/* Jump to SVC26 mode - remember we have no local vars now ! */

    EnterOS();

/* Find the number of the upper most bank of DRAM available */

    loop = 3;
    while (bootconfig.dram[loop].address == 0)
      --loop;

/* Allocate the physical addresses for the kernel in this bank */

    physical = bootconfig.dram[loop].address - kernelsize
             + bootconfig.dram[loop].pages * bootconfig.pagesize;
    bootconfig.kernphysicalbase = physical;
    bootconfig.argphysicalbase = bootconfig.kernphysicalbase
                               + bootconfig.kernsize;
    bootconfig.scratchphysicalbase = bootconfig.argphysicalbase
                                   + bootconfig.argsize;

/* Yet more debugging info */

    if (strstr(__cli, "verbose") != 0)
      {
        printf("buffer = %08x\n", buffer);
        printf("physical = %08x\n", physical);
        printf("bootconfig.kernphysicalbase = %08x\n",
          bootconfig.kernphysicalbase);
        printf("bootconfig.argphysicalbase = %08x\n",
          bootconfig.argphysicalbase);
        printf("bootconfig.scratchphysicalbase = %08x\n",
          bootconfig.scratchphysicalbase);
      }

/*
 * Ok just check to see if anything is mapped where we are about to map
 * the kernel.
 */

/*
    for (logical = KERNBASE; logical < KERNBASE + kernelsize;
     logical += bootconfig.pagesize)
      {
        if (ReadWord(0x02c00000 + (logical >> 10) & 0xfffffffc) != 0)
          {
            printf("Error: Memory required for RiscBSD boot not available\n\r");
            return(0);
          }
      }
*/
/* Get out clause */

    if (strstr(__cli, "noboot") != 0)
      {
        ExitOS();
#ifdef USE_MODULEAREA

/* Free memory in module area to hold the data we were loading */

        fatal(swix(OS_Module, IN(R0|R2), 7, buffer));

#endif
        return(0);
      }

/*
 * Hook the physical pages to the required virtual address directly by
 * writing into RiscOS's page tables. This should be done via a
 * dynamic area handler but I cannot get it to work as documented.
 */

    for (logical = KERNBASE; logical < KERNBASE + kernelsize;
     logical += bootconfig.pagesize)
      {
        WriteWord(0x02c00000 + (logical >> 10) & 0xfffffffc,
          0x00000ffe | (physical & 0xfffff000));
        physical += bootconfig.pagesize;
      }

/* Map the IO up high so we can get at it */

    WriteWord(0x02c0c000 + (0xf6000000 >> 18) & 0xfffffffc,
      0x00000412 | (0x03200000 & 0xfffff000));
    WriteWord(0x02c0c000 + (0xf6100000 >> 18) & 0xfffffffc,
      0x00000412 | (0x03400000 & 0xfffff000));

    memset((char *)bootconfig.display_start, 0xcc, 0x4000);

/* Disable IRQ and FIQ interrupts */

    SetCPSR(I32_bit | F32_bit, I32_bit | F32_bit);

    memset((char *)bootconfig.display_start + 0x4000, 0x55, 0x4000);

    memcpy((char *)bootconfig.argvirtualbase, __cli, bootconfig.argsize);

    memset((char *)bootconfig.display_start + 0x8000, 0x80, 0x4000);

    memset((char *)bootconfig.argvirtualbase, SCRATCHSIZE, 0);

    memset((char *)bootconfig.display_start + 0xC000, 0xbb, 0x4000);

/* Locate the start of the text area */

    switch (aout.a_magic & MAGICMASK) {
      case OMAGIC:
      case NMAGIC:
        buffer += sizeof(aout_t);
	break;
      case ZMAGIC:
      default:
        break;
    }

    switch (aout.a_magic & MAGICMASK) {
      case OMAGIC:
      case NMAGIC:
      case ZMAGIC:
        if (aout.a_syms == 0)
          ((aout_t *)buffer)->a_syms = 0;
        break;
      default:
        break;
    }

    memcpy((char *)bootconfig.kernvirtualbase, buffer, (int)aout.a_text);

    memset((char *)(bootconfig.display_start + 0x10000), 0xee, 0x4000);

    memcpy((char *)(bootconfig.kernvirtualbase + aout.a_text),
      (buffer + aout.a_text), (int)aout.a_data);

    memset((char *)(bootconfig.display_start + 0x14000), 0x66, 0x4000);

    if (aout.a_syms != 0) {
      ptr = bootconfig.kernvirtualbase + (u_int)(aout.a_text
        + aout.a_data + aout.a_bss);
      *((int *)ptr) = (int)aout.a_syms;
      ptr += sizeof(int);     
      memcpy((char *)ptr, (buffer + aout.a_text
        + aout.a_data), (int)aout.a_syms);
      ptr += (int)aout.a_syms;
      memcpy((char *)ptr, (buffer + aout.a_text
        + aout.a_data + aout.a_syms), stringtablesize);
    }

    memset((char *)bootconfig.display_start + 0x18000, 0xaa, 0x4000);

/* Real late debugging get out clause */

    if (strstr(__cli, "nearboot") != 0)
      {
        SetCPSR(I32_bit | F32_bit, 0);
        ExitOS();
        return(0);
      }

/* Punch into SVC32 mode */

    memset((char *)bootconfig.display_start + 0x1C000, 0xff, 0x4000);

    SVC32();

/* Point of no return */

    switch (aout.a_magic & MAGICMASK) {
      case OMAGIC:
      case NMAGIC:
      case ZMAGIC:
        _bsdboot(&bootconfig, (unsigned int)aout.a_entry);
        break;
      default:
        _bsdboot(&bootconfig, KERNBASE);
        break;
    }

    return(0);
  }


/* Report an error */

void fatal(struct Error *error)
  {
    if (error)
        swi(OS_GenerateError, IN(R0), error);
  }


/* Report an error */

void fatal1(int error, char *string)
  {
    int err[64];

    err[0] = error;
    strcpy((char *)&err[1], string);
    swi(OS_GenerateError, IN(R0), err);
  }


/* Locate all the blocks of memory in the system */

unsigned char *locate_memory_blocks(void)
  {
    int loop;
    int page;
    int currentpage;
    int currentpages;
    int currentaddr;
    unsigned char *table;
    unsigned int pagesize;
    unsigned int tablesize;
    int dramblocks = 0;
    int vramblocks = 0;

/* Get table size and page size */

    fatal(swix(OS_Memory, IN(R0)|OUT(R1|R2), 6, &tablesize, &pagesize));

/* Allocate memory for table */

/*# ifdef USE_MODULEAREA*/

    fatal(swix(OS_Module, IN(R0|R3)|OUT(R2), 6, tablesize, &table));

/*# else*/

/* Allocate memory to hold the data we are loading */

/*    swix(OS_DynamicArea, IN(R0|R1), 1, TABLEAREA);

    fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8)|OUT(R3), 0,
      TABLEAREA, tablesize, -1, 0x80, tablesize, 0, 0, "Kate Table", &table));

# endif*/


/* read the table */

    fatal(swix(OS_Memory, IN(R0|R1), 7, table));

/* Loop round locating all the valid blocks of memory */

    currentpage = -1;

    for (loop = 0; loop < tablesize * 2; ++loop)
      {
        page = table[loop / 2];
        if (loop % 2)
          page = page >> 4;

        page = page & 0x07;

        if (page != currentpage)
          {
            switch (currentpage)
              {
                case 1:
                  bootconfig.dram[dramblocks].address = currentaddr * pagesize;
                  bootconfig.dram[dramblocks].pages = currentpages;
                  ++dramblocks;
                  break;

                case 2:
                  bootconfig.vram[vramblocks].address = currentaddr * pagesize;
                  bootconfig.vram[vramblocks].pages = currentpages;
                  ++vramblocks;
                  break;

                default :
                  break;
              }

            currentpage = page;
            currentaddr = loop;
            currentpages = 0;
          }
        ++currentpages;
      }

/* Get the number of dram and vram pages */

    fatal(swix(OS_Memory, IN(R0)|OUT(R1), 0x00000108, &bootconfig.drampages));
    fatal(swix(OS_Memory, IN(R0)|OUT(R1), 0x00000208, &bootconfig.vrampages));

/* Fill in more bootconfig parameters */

    bootconfig.pagesize = pagesize;

    bootconfig.dramblocks = dramblocks;
    bootconfig.vramblocks = vramblocks;

    return(table);
  }


void putchar(int c)
  {
    if (c == '\n')
      swi(OS_WriteC, IN(R0), '\r');
    
    swi(OS_WriteC, IN(R0), c);
  }

/* End of bsdbooter.c */
